【人像分割】照片底色说换就换【微信小程序】
置顶
756665228 发布于2020-06-05 17:38 浏览:3536 回复:9
3
收藏

一般去拍证件照时底色是蓝色或者红色,但有的证件需要其他颜色。要办的证件很多,如果每办一次就要去拍很麻烦,

那么通过百度的人像分割。再稍加一点代码即可实现照片换底色功能,很省事很便捷。

这里直接从接口开始。没有百度账号,第一次使用百度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": "图片背景色修改"
}

查看演示效果

立即体验

 

人像分割-证件照版(邀测) https://ai.baidu.com/ai-doc/BODY/Qka6rjc93 此接口在邀测了。感兴趣的开发者可以去体验一下哦

目前邀测接口存在的问题:

1.默认颜色只有黑色,不支持开发者自行给定颜色RGB

2.接口不支持指定尺寸大小限制

希望后续接口能迭代这些功能吧

收藏
点赞
3
个赞
共9条回复 最后由wangwei8638回复于2020-07-28 19:38
#10wangwei8638回复于2020-07-28 19:38:51

考试证件处理,需求量很大

0
#9笔墨哥回复于2020-07-28 09:51:35

体验了一下,非常的方便,小帅的小程序值得收藏~

0
#8大手拉小手0123回复于2020-06-18 13:01:28

太棒了吧 这也,谢谢分享

0
#7wangwei8638回复于2020-06-17 18:55:52

学习了

0
#6rose20135188回复于2020-06-17 09:06:43

谢谢楼主分享,学习了。

0
#5何必固執回复于2020-06-17 08:51:59

羡慕,我也想说换就换

0
#4756665228回复于2020-06-05 18:05:59
#3 melissayoung回复
帅总,反馈和建议都收到了~

嗯嗯。证件照版 这个名称 貌似就没体现出来证件照的优势在哪里。哈哈。

如果接口能迭代我这样的功能就好了。再外加一个尺寸选择(X寸照片、各种国家的签证照片尺寸) 那这个接口直接就能落地了

1
#3melissayoung回复于2020-06-05 17:50:42

帅总,反馈和建议都收到了~

1
#2756665228回复于2020-06-05 17:42:41

页面代码。gitee需要登录才可以看代码片段

https://gitee.com/xshuai/codes/c5kn1yeudfw7aqrt4903m14
1
TOP
切换版块