一般去拍证件照时底色是蓝色或者红色,但有的证件需要其他颜色。要办的证件很多,如果每办一次就要去拍很麻烦,
那么通过百度的人像分割。再稍加一点代码即可实现照片换底色功能,很省事很便捷。
这里直接从接口开始。没有百度账号,第一次使用百度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": "图片背景色修改" }
查看演示效果