效果如上,我用的是阿里云的人脸识别。首先,我们先封装一个阿里云的请求js文件
faceRecognition.js
import CryptoJS from 'crypto-js'//SignatureNonce随机数字
function signNRandom() {const Rand = Math.random()const mineId = Math.round(Rand * 100000000000000)return mineId;
};
//Timestamp
function getTimestamp() {let date = new Date();let YYYY = pad2(date.getUTCFullYear());let MM = pad2(date.getUTCMonth() + 1);let DD = pad2(date.getUTCDate());let HH = pad2(date.getUTCHours());let mm = pad2(date.getUTCMinutes());let ss = pad2(date.getUTCSeconds());return `${YYYY}-${MM}-${DD}T${HH}:${mm}:${ss}Z`;
}
//补位占位
function pad2(num) {if (num < 10) {return '0' + num;}return '' + num;
};
// 排序
function ksort(params) {let keys = Object.keys(params).sort();let newParams = {};keys.forEach((key) => {newParams[key] = params[key];});return newParams;
};
// HmacSHA1加密+base64
function createHmac(stringToSign, key) {const CrypStringToSign = CryptoJS.HmacSHA1(stringToSign, key);const base64 = CryptoJS.enc.Base64.stringify(CrypStringToSign);return base64;
};
//编码
function encode(str) {var result = encodeURIComponent(str);return result.replace(/!/g, '%21').replace(/'/g, '%27').replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/\*/g, '%2A');
};
function sha1(stringToSign, key) {return createHmac(stringToSign, key);
};
function getSignature(signedParams, method, secret) {var stringToSign = `${method}&${encode('/')}&${encode(signedParams)}`;const key = secret + "&";return sha1(stringToSign, key);
};
//参数拼接 &
function objToParam(param) {if (Object.prototype.toString.call(param) !== '[object Object]') {return '';}let queryParam = '';for (let key in param) {if (param.hasOwnProperty(key)) {let value = param[key];queryParam += toQueryPair(key, value);}}return queryParam;
};
function toQueryPair(key, value) {if (typeof value == 'undefined') {return `&${key}=`;}return `&${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
};
function generateUrl(request, httpMethod, endpoint, accessKeySecret) {//参数中key排序const sortParams = ksort(request);//拼成参数const sortQueryStringTmp = objToParam(sortParams);const sortedQueryString = sortQueryStringTmp.substring(1);// 去除第一个多余的&符号//构造待签名的字符串const Signature = getSignature(sortedQueryString, httpMethod, accessKeySecret)//签名最后也要做特殊URL编码request["Signature"] = encodeURIComponent(Signature);//最终生成出合法请求的URLconst finalUrl = "https://" + endpoint + "/?Signature=" + encodeURIComponent(Signature) + sortQueryStringTmp;return finalUrl;
}const callRecognizeBankCard = function (ImageURL,callback) {const accessKeyId = '';const accessKeySecret = '';const endpoint = "facebody.cn-shanghai.aliyuncs.com";const Action = "DetectFace";// API_HTTP_METHOD推荐使用POSTconst API_HTTP_METHOD = "POST";const API_VERSION = "2019-12-30";const request_ = {};//系统参数request_["SignatureMethod"] = "HMAC-SHA1";request_["SignatureNonce"] = signNRandom();request_["AccessKeyId"] = accessKeyId;request_["SignatureVersion"] = "1.0";request_["Timestamp"] = getTimestamp();request_["Format"] = "JSON";request_["RegionId"] = "cn-shanghai";request_["Version"] = API_VERSION;request_["Action"] = Action; request_["ImageURL"] = ImageURLcallApiRequest(request_, API_HTTP_METHOD, endpoint, accessKeySecret, callback);
}var http = {};
http.request = function (option, callback) {var url = option.url;var method = option.method;var data = option.data;var timeout = option.timeout || 0;//创建XMLhttpRequest对象var xhr = new XMLHttpRequest();// var xhr = new plus.net.XMLHttpRequest()// return(timeout > 0) && (xhr.timeout = timeout);//使用open方法设置和服务器的交互信息xhr.open(method, url, true);if (typeof data === 'object') {try {data = JSON.stringify(data);} catch (e) { }}//发送请求xhr.send(data);//如果请求完成,并响应完成,获取到响应数据xhr.onreadystatechange = function () {if (xhr.readyState == 4) {var result = xhr.responseText;try { result = JSON.parse(xhr.responseText); } catch (e) { }callback && callback(null, result);}}.bind(this);//延时处理xhr.ontimeout = function () {callback && callback('timeout');console.log('error', '连接超时');};
};
// post请求
http.post = function (option, callback) {option.method = 'post';option.contentType = 'application/json;charset=UTF-8'this.request(option, callback);
};// 封装请求方法
function httpRequest(url, data, method, callback) {uni.request({url: url,method: method,data: data,header: {'content-type': 'application/json'},success: function (res) {callback(null, res.data);},fail: function (err) {callback(err, null);}});
}//请求数据
const callApiRequest = (request_, API_HTTP_METHOD, endpoint, accessKeySecret, callback) => {const url = generateUrl(request_, API_HTTP_METHOD, endpoint, accessKeySecret);httpRequest(url, null, 'POST', function (err, result) {if (err) {console.error('Error:', err);callback(null);} else {console.log('Result:', result);callback(result);}});
}export default callRecognizeBankCard;
请求之后的响应参数都在文档里面有,https://help.aliyun.com/zh/viapi/developer-reference/api-i5236v?spm=a2c4g.11186623.0.i2
然后开始使用
<view class="re-upload" @click="uploadImage">上传图片</view><u-modal :show="identifyShow" title="" :showConfirmButton="false"><view class="slot-content" style="width: 100%;position: relative;display: flex;justify-content: center;background-color: #00112b;"><image class="ai-bg-top" src="" mode=""></image><view class="ai-image" style="width: 330px !important;height: 350px;margin-top: 200rpx;position: relative;"><view class="animation" v-show="scanShow"><view class="animation-list"></view></view><image :src="aiAvatar" mode="" style="width: 100% !important;height: 100%;"></image><view class="faceRectangles" v-for="(item, index) in avatarPostionList":key="index":style="handlefaceRectanglesStyle(item, index)"></view></view><image class="ai-bg-bottom" src="" mode=""></image></view></u-modal>import faceRecognition from '@/utils/faceRecognition.js'const identifyShow = ref(false)
const aiAvatar = ref('')
const scanShow = ref(false)
const studentForm = ref({ avatar: '' })
const avatarPostionList = ref([])const handlefaceRectanglesStyle = (item, index) => {return {'position': 'absolute','left': `${item[0]}px`,'top': `${item[1]}px`,'width': `${item[2]}px`,'height': `${item[3]}px`}
}const chunkArray = (array, chunkSize) => {const groupedArray = [];for (let i = 0; i < array.length; i += chunkSize) {groupedArray.push(array.slice(i, i + chunkSize));}return groupedArray;
}const handleClear = (boolean) => {avatarPostionList.value = []scanShow.value = booleanidentifyShow.value = boolean
}const handleFaceRecognition = (path) => {faceRecognition(path, avatar => {if(avatar.Code) {uni.showToast({title: avatar.Message,icon: 'none',duration: 5000})identifyShow.value = falsereturn;}if(avatar.Data.FaceCount !== 1) {const avatarList = chunkArray(avatar.Data.FaceRectangles, 4)avatarPostionList.value = avatarListscanShow.value = falsesetTimeout(() => {uni.showModal({title: '提示',content: '当前照片可能存在多张人脸,是否继续上传?',success: res => {if(res.confirm) {studentForm.value.avatar = path}else {uni.showToast({title: '已取消上传',icon: 'none'})}identifyShow.value = false}})}, 1000)}else {avatarPostionList.value = [avatar.Data.FaceRectangles]setTimeout(() => {handleClear(false)studentForm.value.avatar = path}, 1000)}})
}const uploadImage = () => {uni.chooseImage({count: 1,success: (res) => {//uploadFile,封装的上传方法uploadFile(res.tempFilePaths[0], 'avatar', (path:string) => {// path为图片线上地址const newPath = `${path}?x-oss-process=image/resize,limit_0,m_fill,w_330,h_350/quality,q_100`// newPath 加宽高之后的图片aiAvatar.value = newPathhandleClear(true)setTimeout(() => {handleFaceRecognition(newPath)}, 5000)},()=>{})}});
}
css
:deep(.u-popup__content){width: 350px !important;// border-radius: 0 !important;
}
:deep(.u-modal) {width: 100% !important;
}
.ai-bg-top {position: absolute;width: 100%;height: 200rpx;top: 0;left: 0;
}
.ai-bg-bottom {position: absolute;width: 100%;height: 200rpx;bottom: 0;left: 0;
}
.animation{position: absolute;top: 350rpx;left: 0;right: 0;height: 700rpx;}.animation-list{width: 100%;height: 450rpx;background: linear-gradient(to bottom,rgba(216,179,255,0),rgba(216,179,255,1));position: relative;top: 0;animation: myfist 2s linear 1s infinite alternate;}/* 开始执行动画 */@keyframes myfist{0%{background: linear-gradient(to bottom,rgba(216,179,255,0),rgba(216,179,255,1));left: 0;top: -400rpx;}25%{left: 0;top: 100rpx;}50%{left: 0;top: 100rpx;}75%{left: 0;top: 100rpx;}100%{left: 0;top: -400rpx;}}.faceRectangles {border: 4rpx solid red;
}:deep(.u-modal__content){padding: 0 !important;height: 1100rpx;width: 100%;
}
:deep(.u-line){display: none !important;
}