一般去拍证件照时底色是蓝色或者红色,但有的证件需要其他颜色。要办的证件很多,如果每办一次就要去拍很麻烦,
那么通过百度的人像分割。再稍加一点代码即可实现照片换底色功能,很省事很便捷。
这里直接从接口开始。没有百度账号,第一次使用百度AI建议看接入指南哦 https://ai.baidu.com/ai-doc/REFERENCE/Ck3dwjgn3
本文讲解使用Java语言
-------------后端代码-------------
1.创建一个springboot项目,推荐使用maven构建
lombok https://mvnrepository.com/artifact/org.projectlombok/lombok
aip-sdk https://mvnrepository.com/artifact/com.baidu.aip/java-sdk
commons-fileupload https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload
fastjson https://mvnrepository.com/artifact/com.alibaba/fastjson
hutool-all https://mvnrepository.com/artifact/cn.hutool/hutool-all
2.创建类,单例加载百度AISDK
import com.baidu.aip.bodyanalysis.AipBodyAnalysis;
/**
* 加载模块对象
* @author 小帅丶
*/
public class BDFactory {
private static AipBodyAnalysis aipBodyAnalysis;
private static String appid_body = "";
private static String apikey_body = "";
private static String secretkey_body = "";
public static AipBodyAnalysis getAipBodyAnalysis(){
if(aipBodyAnalysis==null){
synchronized (AipBodyAnalysis.class) {
if(aipBodyAnalysis==null){
aipBodyAnalysis = new AipBodyAnalysis(appid_body, apikey_body, secretkey_body);
}
}
}
return aipBodyAnalysis;
}
}
3.创建Controller,编写上传图片接口
此功能会实现人像分割、图片底色更换
import cn.hutool.core.codec.Base64;
import cn.ydxiaoshuai.tools.factory.BDFactory;
import cn.ydxiaoshuai.tools.util.PngColoringUtil;
import cn.ydxiaoshuai.tools.vo.AcnespotmoleBean;
import cn.ydxiaoshuai.tools.vo.BodySegBean;
import com.alibaba.fastjson.JSON;
import com.baidu.aip.bodyanalysis.AipBodyAnalysis;
import lombok.extern.slf4j.Slf4j;
import org.json.JSONObject;
import org.springframework.context.annotation.Scope;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.ServletRequestUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.util.HashMap;
/**
* @author 小帅丶
* @className IdPhotoRestController
* @Description 证件照处理
* @Date 2020/5/21-10:20
**/
@Controller
@RequestMapping(value = "/idphoto")
@Scope("prototype")
@Slf4j
public class IdPhotoRestController extends ApiRestController{
AipBodyAnalysis aipBodyAnalysis = BDFactory.getAipBodyAnalysis();
/**
* @Description 照片底色替换
* @param file 图片文件
* @return void
* @Author 小帅丶
* @Date 2020年6月5日
**/
@RequestMapping(value = "/replace", method = {RequestMethod.POST}, produces="application/json;charset=UTF-8")
public ResponseEntity idphotoReplace(@RequestParam(value = "file") MultipartFile file,
HttpServletRequest request,
HttpServletResponse response) {
log.info("方法路径{}", request.getRequestURI());
AcnespotmoleBean bean = new AcnespotmoleBean();
//颜色
String colorStr = ServletRequestUtils.getStringParameter(request, "color","red");
Color backgroudColor = getColor(colorStr);
HashMap options = new HashMap<>();
try {
startTime = System.currentTimeMillis();
options.put("type", "foreground");
JSONObject object = aipBodyAnalysis.bodySeg(file.getBytes(), options);
BodySegBean bodySegBean = JSON.parseObject(object.toString(),BodySegBean.class);
if(bodySegBean.getPerson_num()>=1){
//返回处理后的图片
String imagebase64 = PngColoringUtil.changePNGBackgroudColorByBase64(Base64.decode(bodySegBean.getForeground()), backgroudColor);
AcnespotmoleBean.Data data = new AcnespotmoleBean.Data();
data.setImage_base64(imagebase64);
bean.success("success", "成功", data);
}else{
bean.fail("fail", "处理失败 未检测到人脸", 20200522);
}
} catch (Exception e) {
errorMsg = e.getMessage();
log.info("背景图变化接口出错了" + errorMsg);
bean.error("system error", "系统错误");
}
//耗时
timeConsuming = String.valueOf(System.currentTimeMillis() - startTime);
log.info("耗时{},接口返回内容",timeConsuming);
//响应的内容
return new ResponseEntity(JSON.toJSONString(bean), httpHeaders, HttpStatus.OK);
}
private Color getColor(String colorStr) {
Color backgroudColor = null;
if(colorStr.equals("red")){
backgroudColor = Color.RED;
}
if(colorStr.equals("blue")){
backgroudColor = Color.BLUE;
}
if(colorStr.equals("black")){
backgroudColor = Color.BLACK;
}
if(colorStr.equals("white")){
backgroudColor = Color.WHITE;
}
return backgroudColor;
}
}
4.页面返回的对象
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author 小帅丶
* @className AcnespotmoleBean
* @Description 接口返回页面的对象
* @Date 2020/4/10-15:11
**/
@Data
@AllArgsConstructor
@NoArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)
public class AcnespotmoleBean extends BaseBean{
//具体返回的内容
private Data data;
@lombok.Data
public static class Data{
private String image_base64;
private Integer acne_count=0;
private Integer speckle_count=0;
private Integer mole_count=0;
}
public AcnespotmoleBean success(String msg,String msg_zh, Data data) {
this.msg = msg;
this.msg_zh = msg_zh;
this.code = 200;
this.data = data;
return this;
}
public AcnespotmoleBean fail(String msg,String msg_zh, Integer code) {
this.msg = msg;
this.msg_zh = msg_zh;
this.code = code;
return this;
}
public AcnespotmoleBean error(String msg,String msg_zh) {
this.msg = msg;
this.msg_zh = msg_zh;
this.code = 500;
return this;
}
}
5.基础Bean
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author 小帅丶
* @className BaseBean
* @Description 基类
* @Date 2019/7/18-14:48
**/
@Data
@AllArgsConstructor
@NoArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)
public class BaseBean {
public Integer code;
public String msg;
public String msg_zh;
public String author;
}
6.人像分割返回的JSON字符串转的Java对象
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* @author 小帅丶
* @className BodySegBean
* @Description 人像分割
* @Date 2020/5/22-15:18
**/
@NoArgsConstructor
@Data
public class BodySegBean {
private int person_num;
private String foreground;
private long log_id;
private List person_info;
@NoArgsConstructor
@Data
public static class PersonInfoBean {
private double height;
private double width;
private double top;
private double score;
private double left;
}
}
7.所需工具类。即对图片进行底色替换的方法
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.Base64;
/**
* @Description 透明背景上色
* @author 小帅丶
* @className PngColoringUtil
* @Date 2019/10/11-17:03
**/
public class PngColoringUtil {
/**
* @Description 给PNG图片增加背景色
* @Author 小帅丶
* @param sourceImage 原始图片 最好是PNG透明的
* @param targetImage 修改后的图片
* @param backgroudColor 背景色
**/
public static void changePNGBackgroudColor(String sourceImage, String targetImage, Color backgroudColor) {
try {
BufferedImage result = changePNGBackgroudColor(sourceImage, backgroudColor);
File output = new File(targetImage);
ImageIO.write(result, "jpg", output);
} catch (IOException e) {
System.out.println("有问题了" + e.getMessage());
}
}
/**
* @Description 给PNG图片增加背景色 返回base64
* @Author 小帅丶
* @param sourceImage 原始图片 最好是PNG透明的
* @param backgroudColor 背景色
* @return java.lang.String
**/
public static String changePNGBackgroudColorByBase64(byte[] sourceImage, Color backgroudColor){
try {
String base64 = "";
BufferedImage result = changePNGBackgroudColor(sourceImage, backgroudColor);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(result, "jpg", baos );
baos.flush();
byte[] imageInByte = baos.toByteArray();
baos.close();
final Base64.Encoder encoder = Base64.getEncoder();
base64 = encoder.encodeToString(imageInByte);
return base64;
}catch (Exception e){
System.out.println("有问题了" + e.getMessage());
return null;
}
}
/**
* @Description 给PNG图片增加背景色 返回base64
* @Author 小帅丶
* @param sourceImage 原始图片 最好是PNG透明的
* @param backgroudColor 背景色
* @return java.lang.String
**/
public static String changePNGBackgroudColorByBase64(String sourceImage, Color backgroudColor){
try {
String base64 = "";
BufferedImage result = changePNGBackgroudColor(sourceImage, backgroudColor);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(result, "jpg", baos );
baos.flush();
byte[] imageInByte = baos.toByteArray();
baos.close();
final Base64.Encoder encoder = Base64.getEncoder();
base64 = encoder.encodeToString(imageInByte);
return base64;
}catch (Exception e){
System.out.println("有问题了" + e.getMessage());
return null;
}
}
/**
* @Description 给PNG图片增加背景色 返回BufferedImage
* @Author 小帅丶
* @param sourceImage 原始图片 最好是PNG透明的
* @param backgroudColor 背景色
* @return BufferedImage
**/
public static BufferedImage changePNGBackgroudColor(byte[] sourceImage, Color backgroudColor) {
try {
ByteArrayInputStream in = new ByteArrayInputStream(sourceImage);
BufferedImage image = ImageIO.read(in);
BufferedImage result = new BufferedImage(
image.getWidth(),
image.getHeight(),
BufferedImage.TYPE_INT_RGB);
Graphics2D graphic = result.createGraphics();
graphic.drawImage(image, 0, 0, backgroudColor, null);
graphic.dispose();
return result;
} catch (IOException e) {
System.out.println("有问题了" + e.getMessage());
return null;
}
}
/**
* @Description 给PNG图片增加背景色 返回BufferedImage
* @Author 小帅丶
* @param sourceImage 原始图片 最好是PNG透明的
* @param backgroudColor 背景色
* @return BufferedImage
**/
public static BufferedImage changePNGBackgroudColor(String sourceImage, Color backgroudColor) {
try {
File input = new File(sourceImage);
BufferedImage image = ImageIO.read(input);
BufferedImage result = new BufferedImage(
image.getWidth(),
image.getHeight(),
BufferedImage.TYPE_INT_RGB);
Graphics2D graphic = result.createGraphics();
graphic.drawImage(image, 0, 0, backgroudColor, null);
graphic.dispose();
return result;
} catch (IOException e) {
System.out.println("有问题了" + e.getMessage());
return null;
}
}
}
-------------前端代码-------------
需要使用ColorUI 请自行下载
1.index.js代码
var app = getApp();
var api = require('../../utils/baiduai.js');
Page({
data: {
motto: '照片底色修改',
result: [],
images: {},
img: '',
color:'red',
windowWidth: 0,
base64img: '',
access_token: '',
action_type: 'TO_OLD',
tempFilePath: null
},
//单选修改
radiochange: function (e) {
console.log('radio发生change事件,携带的value值为:', e.detail.value)
var that = this;
that.data.color = e.detail.value;
if (that.data.img == '') {
wx.showModal({
content: '未选择图片哦',
showCancel: false,
confirmText: '明白了'
})
} else {
wx.showLoading({
title: "修改中...",
mask: true
}),
that.bgColor();
}
},
onShareAppMessage: function () {
return {
title: '背景色修改',
path: '/pages/idphoto/idphoto',
imageUrl: '../../images/sharefaceeditattr.png',
success: function (res) {
if (res.errMsg == 'shareAppMessage:ok') {
wx.showToast({
title: '分享成功',
icon: 'success',
duration: 500
});
}
},
fail: function (res) {
if (res.errMsg == 'shareAppMessage:fail cancel') {
wx.showToast({
title: '分享取消',
icon: 'loading',
duration: 500
})
}
}
}
},
clear: function (event) {
console.info(event);
wx.clearStorage();
},
//事件处理函数
bindViewTap: function () {
wx.navigateTo({
url: '../logs/logs'
})
},
uploads: function () {
var that = this
var takephonewidth
var takephoneheight
wx.chooseImage({
count: 1, // 默认9
sizeType: ['compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
success: function (res) {
that.setData({
tempFilePath: res.tempFilePaths[0]
})
wx.getImageInfo({
src: res.tempFilePaths[0],
success(res) {
takephonewidth = res.width,
takephoneheight = res.height
}
})
// 返回选定照片的本地文件路径列表,tempFilePath可以作为img标签的src属性显示图片
if (res.tempFiles[0].size > (4096 * 1024)) {
wx.showToast({
title: '图片文件过大哦',
icon: 'none',
mask: true,
duration: 1500
})
} else {
wx.showLoading({
title: "修改中...",
mask: true
}),
that.setData({
img: res.tempFilePaths[0]
})
}
that.bgColor();//修改图片背景色的方法
},
})
},
//照片背景色变化
bgColor: function () {
var that = this;
wx.uploadFile({
url: api.bgColorUrl,
filePath: that.data.tempFilePath,
header: {
'content-type': 'multipart/form-data'
},
formData: {
color: that.data.color
},
name: 'file',
success: function (res) {
wx.hideLoading();
var data = res.data;
var str = JSON.parse(data);
if (str.code == 200) {
that.setData({
img: 'data:image/jpg;base64,' + str.data.image_base64
})
} else {
wx.showModal({
title: '温馨提示',
content: str.msg_zh,
showCancel: false,
confirmText: '知道了'
})
}
},
fail: function (res) {
wx.hideLoading();
wx.showToast({
title: '服务错误,稍后再试',
duration: 2000
})
}
})
},
onLoad: function () {
},
/**
* 点击查看图片,可以进行保存
*/
preview(e) {
var that = this;
if (null == that.data.img || that.data.img == '') {
wx.showModal({
title: '温馨提示',
content: '未选择任何图片',
showCancel: false,
confirmText: '知道了'
})
} else {
wx.previewImage({
urls: [that.data.img],
current: that.data.img
})
}
}
});
2.baiduai.js代码
// 自己的服务器域名 线上必须HTTPS 且备案的域名 本地测试可以局域网IP
const host = 'https://domain/';
//背景色修改
const bgColorUrl = host+'apiName';
//暴露出去的接口
module.exports = {
bgColorUrl: bgColorUrl
}
3.index.wxss代码
@import "../../ui/main.wxss";
@import "../../ui/icon.wxss";
.up {
color: rgb(255, 255, 255);
font-size: 20px;
font-family: 微软雅黑;
width: 200px;
height: 50px;
vertical-align: middle;
text-align: center;
line-height: 45px;
border-radius: 25px;
background-color: rgb(26, 160, 225);
}
.page-body-wrapper image{
background: #ececec;
}
image {
width: 100%;
height: 100%;
max-height: 1
}
.msg {
margin: 10px 0;
text-align: center;
}
.table {
margin-top: 10rpx;
border: 0px solid darkgray;
width: 100%;
}
.tr {
display: flex;
width: 100%;
justify-content: center;
height: 80rpx;
}
.td {
font-family: 微软雅黑;
font-size: 28rpx;
width:100%;
display: flex;
justify-content: center;
text-align: center;
align-items: center;
}
.bg-g{
background: white;
}
.baikeform{
font-size: 20rpx;
color: #c0c0c0;
border-top: 1rpx solid #eeeeee;
margin:30rpx 40rpx 0rpx 40rpx;
padding: 20rpx;
}
.th {
font-size: 28rpx;
width: 48%;
justify-content: center;
background: #3366FF;
color: #fff;
display: flex;
height: 80rpx;
align-items: center;
}
.preview-tips {
margin: 50rpx 0 30rpx;
}
.video {
margin: 20rpx auto;
width: 100%;
height: 300px;
}
switch{
zoom: 0.8;
}
page {
background-color: #F8F8F8;
height: 100%;
font-size: 32rpx;
line-height: 1.6;
}
.weui-cell_ft{
font-size: 32rpx;
}
.page-body-wrapper {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
}
.btn-area {
margin-top: 40rpx;
box-sizing: border-box;
width: 100%;
padding: 0 30rpx;
}
.footer{
font-size: 30rpx;
text-align: center;
color: #7367F0;
}
.reason_txt{
font-size: 32rpx;
color: #1AA0E1;
display: table;
width: auto;
white-space: nowrap;
border-spacing: 0.5rem 0;
margin-left: 28rpx;
margin-right: 28rpx;
margin-top: 28rpx;
}
.reason_txt::before,.reason_txt::after{
display: table-cell;
content: "";
width: 50%;
background: linear-gradient(#1AA0E1, #1AA0E1) repeat-x center;
background-size: 0.1rem 0.1rem;
}
.reminder-content{
width: 89%;
margin: 0 auto;
font-size: 24rpx;
color:#bfbfbf;
}
4.index.wxml代码
由于社区显示html代码会被解析,因此页面代码放在gitee
https://gitee.com/xshuai/codes/c5kn1yeudfw7aqrt4903m14
5.index.json代码
{
"navigationBarTitleText": "图片背景色修改"
}
查看演示效果