小程序制作证件照过程

利用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. 牛客IOI周赛26-提高组 A. 逆序对

    题面 逆序对 有一个长度为 N \tt N N 的排列 a a a,进行 M \tt M M 次操作,操作有 4 \tt 4 4 种: 1 l r :交换 a l \tt a_l al​ 和 a r ...

  2. 【java】非常多!学习路径24-总结目前所有知识(上)

    感谢sikiedu.com的siki老师.几年前就开始看siki的课程,最近突然想写这个笔记系列,顺便回顾一下这些基础的知识,同时也希望能帮助到一些人,有问题一起交流哈. 全文共十章,大约1.5万字, ...

  3. gin如何多次shoubind一个请求参数

    gin多次绑定请求参数 package main import ( "fmt" "net/http" "time" "github ...

  4. Golang实现set

    背景 Golang语言本身未实现set,但是实现了map golang的map是一种无序的键值对的集合,其中键是唯一的 而set是键的不重复的集合,因此可以用map来实现set Empty 由于map ...

  5. JS 模块化 - 03 AMD 规范与 Require JS

    1 AMD 规范介绍 AMD 规范,全称 Asynchronous Module Definition,异步模块定义,模块之间的依赖可以被异步加载. AMD 规范由 Common JS 规范演进而来, ...

  6. Java语言(基础一)

    Java语言 Java的特性和优势 简单性(简单易学) 面向对象(一种思想 万物皆对象) 可移植性(一次编写到处运行 JVM) 高性能(及时编译) 分布式(网络分布式url) 动态性(反射机制) 多线 ...

  7. MinIO Docker 快速入门

    官方文档地址:http://docs.minio.org.cn/docs/master/minio-docker-quickstart-guide 在Docker中运行MinIO单点模式 MinIO ...

  8. Docker安装prometheus

    # 拉取镜像 docker pull prom/prometheus:v2.22.0 # 创建配置文件 mkdir -p /opt/prometheus/ cd /opt/prometheus vim ...

  9. 洛谷P2886 [USACO07NOV]Cow Relays G (矩阵乘法与路径问题)

    本题就是求两点间只经过n条边的最短路径,定义广义的矩阵乘法,就是把普通的矩阵乘法从求和改成了取最小值,把内部相乘改成了相加. 代码包含三个内容:广义矩阵乘法,矩阵快速幂,离散化: 1 #include ...

  10. Tomcat实战之路

    目录 第一节.安装升级 1.1.linux初始化 1.2.安装 1.3.升级 第二节.配置 2.1.虚拟主机 2.2.默认网站首页路径 2.3.跳转 2.4.配置Tomcat日志 第三节.安全 3.1 ...