小程序制作证件照过程

利用canvas制作生活中常用的证件照,压缩图片,修改图片dpi。希望给大家带来方便。

证件照小程序制作要点

  1. 上传合适的图片,方便制作证件照
  2. 调用AI接口,将图像进行人像分割。这里我用的是百度AI
  3. 调用人体关键点为分析图片中头部和肩部的位置信息。为后满裁剪图片提供依据
  4. 利用canvas 将头部和肩部位置制作为新的证件照尺寸照片
  5. 改变图片的背景颜色,生成不同要求的背景证件照
  6. 导出图品前将图片修改为符合打印要求的dpi。
  7. 下载最终生成好的证件照

上传合适的图片,方便制作证件照

selectImg(selectid){
let _this = this
let typelist = selectid === 1 ? ['camera'] : ['album']
uni.chooseImage({
count: 1,
sourceType: typelist,
success: (res)=> { }
});
}

调用AI接口,把图像进行人像分割,分析图像中头部肩部位置信息

  1. [参考链接地址] https://cloud.baidu.com/doc/BODY/s/Fk3cpyxua

  2. 该接口中要求上传的图片格式为base64 格式,大小不超过4M. 并且需要access_token

  3. 获取access_token 参照百度AI 的文档 https://ai.baidu.com/ai-doc/REFERENCE/Ck3dwjhhu

  4. 定义好请求地址。和请求的请求方法

const baseUrl = 'https://picapp.gxwj123.top/prod-api/'
const baidubce = 'https://aip.baidubce.com/rest/2.0/image-classify/v1/'
export const tokenUrl = `${baseUrl}txy/zjz/token`
export const body_seg_url = `${baidubce}body_seg?access_token=`
export const body_analysis_url = `${baidubce}body_analysis?access_token=` import {tokenUrl, body_seg_url, body_analysis_url} from './url.js'
export const request = async (url) => {
let header = {
'Content-Type': 'application/json',
};
let result = await new Promise((resolve, reject) => {
uni.request({
url: url,
method: 'post',
header: header,
success(res) {
if (res.statusCode == 200 && res.data.code == 200) {
resolve(res.data.data);
}
},
fail(err) {
reject(err);
}
});
});
return result
};
export const baiduRequest = async (url, data) => {
let header = {
'Content-Type': 'application/x-www-form-urlencoded',
};
let result = await new Promise((resolve, reject) => {
uni.request({
url: url,
method: 'post',
header: header,
data: {
image: data.image
},
success(res) {
resolve(res);
},
fail(err) {
reject(err);
}
});
});
return result
};
export const getAccessToken = (data) => {
return request(tokenUrl, data,)
}
export const body_seg = (data) => {
let url = `${body_seg_url}${data.access_token}`;
return baiduRequest(url, data)
}
export const body_analysis = (data) => {
let url = `${body_analysis_url}${data.access_token}`;
return baiduRequest(url, data)
} export const getImageInfos = (data) => {
return new Promise((resolve, reject) => {
Promise.all([body_seg(data), body_analysis(data)]).then(([seg, analysis]) => {
console.log(seg, analysis)
if (seg.statusCode == 200 && analysis.statusCode == 200) {
let data = {
bodySeg: seg.data,
bodyAns: analysis.data
}
resolve(data)
}else {
reject('请求任务出错')
}
})
})
}
  1. 上传的图片格式调整为base64
toBase64(file) {
let _this = this
uni.getFileSystemManager().readFile({
filePath: file, //选择图片返回的相对路径
encoding: 'base64', //编码格式
success: res => {
// 成功的回调
// 'data:image/jpeg;base64,'
let base64 = res.data;
_this.getImgInfos(base64)
}
});
},

将人像分割接口返回的图片和人体位置信息分析的坐标结合。生成用于制作证件照的素材。下面的将使用1寸证件照的尺寸和dpi 来进行分析。

  1. 从位置信息分析接口中取出要使用的位置,比如头部,肩部。人像分析中取foreground,为去掉原图中人物信息以外的图片
initImgData(bodyAns,bodySeg) {
if (bodyAns.person_num > 1) {
uni.showToast({
title: '图片检测到多个人像,请重新上传',
icon:'none',
duration: 2000
});
return
}
if (bodyAns.person_num == 0) {
uni.showToast({
title: '图片未检测到人像,请重新上传',
icon:'none',
duration: 2000
});
return
}
let widthInfo = bodyAns.person_info[0]
let location = this.imgwidthsum(widthInfo)
this.location = location
let foreground = bodySeg.foreground
this.foreground = foreground
this.previewImg('data:image/png;base64,' + foreground, location).then(filePath => {
this.canvasImages = filePath
this.buildOver = true
})
},
imgwidthsum(data) {
let body_parts = data.body_parts
return {
top_head: body_parts.top_head,
left_shoulder: body_parts.left_shoulder,
right_shoulder: body_parts.right_shoulder
}
},
  1. 使用uni.getImageInfo 读取图片,需要先将上一步中base64d 图片转为本地图片
const fsm = wx.getFileSystemManager();
const FILE_BASE_NAME = 'tmp_base64src'; const base64src = function(base64data, pathName) {
return new Promise((resolve, reject) => {
const [, format, bodyData] = /data:image\/(\w+);base64,(.*)/.exec(base64data) || [];
if (!format) {
reject(new Error('ERROR_BASE64SRC_PARSE'));
}
const filePath = `${wx.env.USER_DATA_PATH}/${FILE_BASE_NAME+pathName}.${format}`;
const buffer = wx.base64ToArrayBuffer(bodyData);
fsm.writeFile({
filePath,
data: buffer,
encoding: 'binary',
success() {
resolve(filePath);
},
fail() {
reject(new Error('ERROR_BASE64SRC_WRITE'));
},
});
});
}; export default base64src;
  1. 将图片按照要一定的比列绘制在canvas 中
let IMG_RATIO
let ratio = 295/413
let initWidth = 295
let initHeight = 413
let scrollTop = 250 let IMG_REAL_W,IMG_REAL_H
IMG_REAL_H = initHeight
IMG_REAL_W = IMG_REAL_H*IMG_RATIO
let canH = imgW * IMG_REAL_H / IMG_REAL_W
const ctx = uni.createCanvasContext("myselfCanvas", _this);
if (color) {
ctx.setFillStyle(color)
ctx.fillRect(0,0,IMG_REAL_W,IMG_REAL_H)
}
// 绘制的时候将选中的背景颜色填充到画布中
ctx.drawImage(res.path, 0, 0, IMG_REAL_W, IMG_REAL_H);
  1. 根据原图中头像位置坐标。换算出需要在原图上裁剪出来的区域
let x = location.right_shoulder.x //右肩位置的坐标 x
let y = location.top_head.y - scrollTop // 头部坐标位置 减去一定比列的坐标 y
let x1 = location.left_shoulder.x // 左肩位置坐标 x var canvasW = ((x1 - x) / imgW) * IMG_REAL_W;
// 左肩坐标 减去右肩坐标 和原图的宽度比列 计算出 在上一步绘制的图中裁剪的宽度
var canvasH = canvasW/ratio // 根据证件照的比列 计算出 裁剪的高度
var canvasL = (x / imgW) * IMG_REAL_W;
var canvasT = (y / imgH) * IMG_REAL_H;
// 计算裁剪的起始坐标位置
  1. 在canvas 绘制图后导出证件照需要的尺寸
ctx.draw(false,(ret)=>{
uni.showToast({
icon:'success',
mask:true,
title: '绘制完成',
}); uni.canvasToTempFilePath({ // 保存canvas为图片
x: canvasL,
y: canvasT,
width: canvasW, //canvasH,
height: canvasH, //canvasH,
destWidth: initWidth,
destHeight: initHeight,
canvasId: 'myselfCanvas',
quality: 1,
fileType: color? 'jpg': 'png',
complete: function(res) {
resolve(res.tempFilePath)
} ,
})
});

导出证件照之前,还需要修改图片的dpi

  1. 修改图片dpi 是将图片转为base64 格式后修改。本项目使用changedpi 插件
  2. npm install changedpi
import {changeDpiDataUrl} from 'changedpi'
export const changeDpi = (url, dpi) => {
return new Promise((resolve) => {
if (dpi) {
uni.getFileSystemManager().readFile({
filePath: url, //选择图片返回的相对路径
encoding: 'base64', //编码格式
success: res => {
// 成功的回调
// 'data:image/jpeg;base64,' let base64 = res.data;
let str = changeDpiDataUrl('data:image/jpeg;base64,' + base64, dpi)
base64src(str).then(filePath => { resolve(filePath)
})
}
});
}else {
resolve(url)
}
})
}
  1. 在小程序中使用需要注意 插件中直接使用了btoa atob 两个函数。 但是在小程序是不支持直接调用的。需要重写这两个方法
  2. 重写的方法
(function(f) {

  'use strict';

  /* istanbul ignore else */
if (typeof exports === 'object' && exports != null &&
typeof exports.nodeType !== 'number') {
module.exports = f ();
} else if (typeof define === 'function' && define.amd != null) {
define ([], f);
} else {
var base64 = f ();
var global = typeof self !== 'undefined' ? self : $.global;
if (typeof global.btoa !== 'function') global.btoa = base64.btoa;
if (typeof global.atob !== 'function') global.atob = base64.atob;
} } (function() { 'use strict'; var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; function InvalidCharacterError(message) {
this.message = message;
}
InvalidCharacterError.prototype = new Error ();
InvalidCharacterError.prototype.name = 'InvalidCharacterError'; // encoder
// [https://gist.github.com/999166] by [https://github.com/nignag]
function btoa(input) {
var str = String (input);
for (
// initialize result and counter
var block, charCode, idx = 0, map = chars, output = '';
// if the next str index does not exist:
// change the mapping table to "="
// check if d has no fractional digits
str.charAt (idx | 0) || (map = '=', idx % 1);
// "8 - idx % 1 * 8" generates the sequence 2, 4, 6, 8
output += map.charAt (63 & block >> 8 - idx % 1 * 8)
) {
charCode = str.charCodeAt (idx += 3 / 4);
if (charCode > 0xFF) {
throw new InvalidCharacterError ("'btoa' failed: The string to be encoded contains characters outside of the Latin1 range.");
}
block = block << 8 | charCode;
}
return output;
} // decoder
// [https://gist.github.com/1020396] by [https://github.com/atk]
function atob(input) {
var str = (String (input)).replace (/[=]+$/, ''); // #31: ExtendScript bad parse of /=
// if (str.length % 4 === 1) {
// throw new InvalidCharacterError ("'atob' failed: The string to be decoded is not correctly encoded.");
// }
for (
// initialize result and counters
var bc = 0, bs, buffer, idx = 0, output = '';
// get next character
buffer = str.charAt (idx++); // eslint-disable-line no-cond-assign
// character found in table? initialize bit storage and add its ascii value;
~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer,
// and if not first of each 4 characters,
// convert the first 8 bits to one ascii character
bc++ % 4) ? output += String.fromCharCode (255 & bs >> (-2 * bc & 6)) : 0
) {
// try to find character in table (0-63, not found => -1)
buffer = chars.indexOf (buffer);
}
return output;
} return {btoa: btoa, atob: atob}; }));
  1. 在源码中修改调用
const polyfill = require('../../../util/btoa.js');
const {btoa, atob} = polyfill;

下载证件照到手机相册

export const savePoster = (url) => {
const that = this
wx.saveImageToPhotosAlbum({
filePath: url,
success: function() {
wx.showToast({
title: '保存成功',
icon: 'none',
duration: 1500
});
},
fail(err) {
if (err.errMsg === "saveImageToPhotosAlbum:fail:auth denied" || err.errMsg === "saveImageToPhotosAlbum:fail auth deny" || err.errMsg === "saveImageToPhotosAlbum:fail authorize no response") {
wx.showModal({
title: '提示',
content: '需要您授权保存相册',
showCancel: false,
success: modalSuccess => {
wx.openSetting({
success(settingdata) {
if (settingdata.authSetting['scope.writePhotosAlbum']) {
wx.saveImageToPhotosAlbum({
filePath: url,
success: function () {
wx.showToast({
title: '保存成功',
icon: 'success',
duration: 2000
})
},
})
} else {
wx.showToast({
title: '授权失败,请稍后重新获取',
icon: 'none',
duration: 1500
});
}
}
})
}
})
}
}
})
}

下面是利用canvas 做的小应用,欢迎大家扫描体验,并提出建议。让我们共同进步



[项目代码] https://gitee.com/eyes-star/txy-mp.git



[项目代码] https://gitee.com/eyes-star/zjz-mp.git

微信小程序canvas 证件照制作的更多相关文章

  1. 原创:WeZRender:微信小程序Canvas增强组件

    WeZRender是一个微信小程序Canvas增强组件,基于HTML5 Canvas类库ZRender. 使用 WXML: <canvas style="width: 375px; h ...

  2. 微信小程序如何开发制作

    微信小程序如何开发制作 微容SMO是一款微信小程序的免费在线制作工具,用户在微容平台上无需编辑代码,可通过拖拽式操作即可完成小程序的制作,真正意义上实现了小程序零代码免费制作! 消除技术门槛:无需代码 ...

  3. 微信小程序-canvas绘制文字实现自动换行

    在使用微信小程序canvas绘制文字时,时常会遇到这样的问题:因为canvasContext.fillText参数为 我们只能设置文本的最大宽度,这就产生一定的了问题.如果我们绘制的文本长度不确定或者 ...

  4. 微信小程序 canvas 字体自动换行(支持换行符)

    微信小程序 canvas 自动适配 自动换行,保存图片分享到朋友圈  https://github.com/richard1015/News 微信IDE演示代码https://developers.w ...

  5. 微信小程序--canvas画布实现图片的编辑

    技术:微信小程序   概述 上传图片,编辑图片大小,添加文字,改变文字颜色等 详细 代码下载:http://www.demodashi.com/demo/14789.html 概述 微信小程序--ca ...

  6. 微信小程序canvas生成并保存图片

    ---恢复内容开始--- 微信小程序canvas生成并保存图片,具体实现效果如下图     实现效果需要做以下几步工作 一.先获取用户屏幕大小,然后才能根据屏幕大小来定义canvas的大小 二.获取图 ...

  7. 技术博客--微信小程序canvas实现图片编辑

    技术博客--微信小程序canvas实现图片编辑 我们的这个小程序不仅仅是想给用户提供一个保存和查找的平台,还希望能给用户一个展示自己创意的舞台,因此我们实现了图片的编辑部分.我们对对图片的编辑集成了很 ...

  8. 微信小程序 canvas 绘图问题总结

    业务中碰到微信小程序需要生成海报进行朋友圈分享,这个是非常常见的功能,没想到实际操作的时候花了整整一天一夜才搞好,微信的 canvas 绘图实在是太难用了,官方快点优化一下吧. 业务非常简单,只需要将 ...

  9. 微信 小程序 canvas

    测试手机为IPHONE6,开发者工具版本0.10.102800.开发者工具0.11.112301版本也一样 微信小程序里的canvas 非 h5 canvas有很多不一样的地方,以下把微信小程序的ca ...

随机推荐

  1. 有一个线性表,采用带头结点的单链表L来存储,设计一个算法将其逆置,且不能建立新节点,只能通过表中已有的节点的重新组合来完成。

    有一个线性表,采用带头结点的单链表L来存储,设计一个算法将其逆置,且不能建立新节点,只能通过表中已有的节点的重新组合来完成. 分析:线性表中关于逆序的问题,就是用建立链表的头插法.而本题要求不能建立新 ...

  2. HDU 6222 Heron and His Triangle (pell 方程)

    题面(本人翻译) A triangle is a Heron's triangle if it satisfies that the side lengths of it are consecutiv ...

  3. 微服务网关Gateway实践总结

    有多少请求,被网关截胡: 一.Gateway简介 微服务架构中,网关服务通常提供动态路由,以及流量控制与请求识别等核心能力,在之前的篇幅中有说过Zuul组件的使用流程,但是当下Gateway组件是更常 ...

  4. 第九十三篇:ESLint:可组装的javaScript和JSX检查工具

    好家伙, 1.什么是ESLint? 代码检查工具,用来检查你的代码是否符合指定的规范 2.ESLint有什么用? 统一JavaScript代码风格的工具 在合作开发的时候, 每个成员的代码风格都有可能 ...

  5. IP地址最后一位斜杠是什么意思?比如192.168.1.10/27?还有IP地址和子网掩码相加得到的网络地址是什么意思

    IP地址最后一位斜杠是什么意思?比如192.168.1.10/27?还有IP地址和子网掩码相加得到的网络地址是什么意思 IP地址最后一位斜杠是什么意思?比如192.168.1.10/27?还有IP地址 ...

  6. .Net7 内容汇总(1)

    .Net7 RC1发布 在9月14号,.Net7 RC1正式发布了. 按照微软的说法 This is the first of two release candidates (RC) for .NET ...

  7. MySQL5.7之在线DDL不会锁表

    MySQL5.7在线修改varchar字段不在锁表,测试过程如下: mysql> select version(); +------------+ | version() | +-------- ...

  8. Elasticsearch基础但非常有用的功能之一:别名

    文章转载自: https://mp.weixin.qq.com/s?__biz=MzI2NDY1MTA3OQ==&mid=2247484454&idx=1&sn=43e95a2 ...

  9. 通过开启swap分区来解决小内存阿里云服务器的内存瓶颈

    swap分区大小设置 阿里云的linux云服务器默认是没有启用swap分区(交换分区)的.一般情况下swapswap分区的大小可以参考以下规则进行设定: 内存大小 swap大小 MEM_SIZE &l ...

  10. 为什么同行业,同个软件,有些 ERP 成功,有的失败了?

    企业的差异性是各类系统部署必须正视的关键问题!同行业,同个软件,有些 ERP 成功,有的失败,基本上是企业差异性没有得到重视的,所以一点也不应该感到奇怪.规模不同.行业不同.发展阶段不同.生产模式不同 ...