uniapp實(shí)現(xiàn)人臉識(shí)別功能的具體實(shí)現(xiàn)代碼
前言
對(duì)于前端來(lái)說(shuō),需要后端提供一個(gè)人臉識(shí)別接口,前端傳入圖片,接口識(shí)別并返回結(jié)果,如此看來(lái),其實(shí)前端只需實(shí)現(xiàn)圖片傳入即可,但是其實(shí)不然,在傳入圖片時(shí),需要進(jìn)行以下幾點(diǎn)操作:
- 判斷圖片格式,市場(chǎng)上比較常見(jiàn)的是
.jpg
、.jpeg
、.png
- 計(jì)算文件大小,一般要求不超過(guò)5MB
- 對(duì)圖片進(jìn)行base64加密
其實(shí)前2點(diǎn)具體要看接口要求,但是第3點(diǎn),是實(shí)現(xiàn)人臉識(shí)別必備步驟,下文重點(diǎn)講述一下移動(dòng)端實(shí)現(xiàn)人臉識(shí)別的base64加密方法
問(wèn)題
項(xiàng)目主要使用的技術(shù)棧是uniapp,uniapp的優(yōu)點(diǎn)是上手快,基于vue開(kāi)發(fā),但缺點(diǎn)也很明顯,多環(huán)境兼容導(dǎo)致兼容性較差,真機(jī)調(diào)試和運(yùn)行較慢。比如h5端可以輕松實(shí)現(xiàn)base64加密,但是安卓環(huán)境完全不行,因?yàn)楸镜厣蟼鲌D片時(shí),會(huì)返回一個(gè)blob流,但是uniapp的blob流是以<http://localhost>…
(安卓環(huán)境無(wú)法識(shí)別localhost
)開(kāi)始,導(dǎo)致無(wú)法進(jìn)行base64加密
解決辦法
經(jīng)過(guò)多方實(shí)現(xiàn)后,借用html5+ api的多個(gè)結(jié)合方法(plus.zip.compressImage
、plus.io.resolveLocalFileSystemURL
、plus.io.FileReader
)實(shí)現(xiàn)加密,主要代碼如下:
//app壓縮圖片 用for循環(huán) 來(lái)處理圖片壓縮 的問(wèn)題,原因是 plus.zip.compressImage 方法 是異步執(zhí)行的,for循環(huán)很快, 同時(shí)手機(jī)可執(zhí)行的壓縮方法有限制:應(yīng)該是3個(gè)吧。超出直接就不執(zhí)行了。所以 原理就是 在圖片壓縮成功后 繼續(xù) 回調(diào) 壓縮函數(shù)。 以到達(dá)循環(huán)壓縮圖片的功能。 app_img(num, rem) { let that = this; let index = rem.tempFiles[num].path.lastIndexOf('.'); //獲取圖片地址最后一個(gè)點(diǎn)的位置 let img_type = rem.tempFiles[num].path.substring(index + 1, rem.tempFiles[num].path.length); //截取圖片類(lèi)型如png jpg let img_yuanshi = rem.tempFiles[num].path.substring(0, index); //截取圖片原始路徑 let d2 = new Date().getTime(); //時(shí)間戳 //壓縮圖片 plus.zip.compressImage( { src: rem.tempFiles[num].path, //你要壓縮的圖片地址 dst: img_yuanshi + d2 + '.' + img_type, //壓縮之后的圖片地址(注意壓縮之后的路徑最好和原生路徑的位置一樣,不然真機(jī)上報(bào)code-5) quality: 70 //[10-100] }, function (e) { //壓縮之后路徑轉(zhuǎn)base64位的 //通過(guò)URL參數(shù)獲取目錄對(duì)象或文件對(duì)象 plus.io.resolveLocalFileSystemURL(e.target, function (entry) { // 可通過(guò)entry對(duì)象操作test.html文件 entry.file(function (file) { //獲取文件數(shù)據(jù)對(duì)象 var fileReader = new plus.io.FileReader(); // 文件系統(tǒng)中的讀取文件對(duì)象,用于獲取文件的內(nèi)容 //alert("getFile:" + JSON.stringify(file)); fileReader.readAsDataURL(file); //以URL編碼格式讀取文件數(shù)據(jù)內(nèi)容 fileReader.onloadend = function (evt) { //讀取文件成功完成的回調(diào)函數(shù) that.base64Img = evt.target.result.split(',')[1]; //拿到‘data:image/jpeg;base64,‘后面的 console.log('that.base64Img', that.base64Img); // rem.tempFiles[num].Base64_Path = evt.target.result.split(',')[1]; }; }); }); // that.base64Img = that.base64Img.concat(rem.tempFiles[num]); // 【注意】在此人臉認(rèn)證中,只會(huì)傳一張圖片,故不考慮多張圖片情況 //利用遞歸循環(huán)來(lái)實(shí)現(xiàn)多張圖片壓縮 // if (num == rem.tempFiles.length - 1) { // return; // } else { // that.app_img(num + 1, rem); // } }, function (error) { console.log('Compress error!'); console.log(JSON.stringify(error)); uni.showToast({ title: '編碼失敗' + error }); } ); },
詳細(xì)實(shí)現(xiàn)思路
其實(shí)對(duì)于uniapp實(shí)現(xiàn)人臉識(shí)別功能來(lái)講,大概要經(jīng)過(guò)這么幾個(gè)步驟
onImage()
:打開(kāi)手機(jī)相冊(cè)上傳圖片,獲取blob流(本地臨時(shí)地址)#ifdef APP-PLUS
/#ifndef APP-PLUS
:判斷系統(tǒng)環(huán)境,是h5還是安卓環(huán)境,然后在進(jìn)行圖片壓縮和加密,具體實(shí)現(xiàn)代碼如下:
//#ifdef APP-PLUS //圖片壓縮 that.app_img(0, res); //#endif // #ifndef APP-PLUS that.blobTobase64(res.tempFilePaths[0]); // #endif
app_img()
/blobTobase64()
:對(duì)要識(shí)別的圖片進(jìn)行base64加密onSave()
—>upImage()
:附件上傳,并處理識(shí)別信息
具體代碼
<!-- 人臉認(rèn)證 --> <template> <view> <view class="u-margin-30 text-center"><u-avatar size="600" :src="imageSrc"></u-avatar></view> <view class="u-margin-60"> <u-button type="primary" class="u-margin-top-60" @click="onImage">{{ !imageSrc ? '拍照' : '重拍' }}</u-button> <!-- <u-button type="primary" class="u-margin-top-30">重拍</u-button> --> <u-button type="primary" class="u-margin-top-50" @click="onSave">保存</u-button> </view> <u-toast ref="uToast" /> </view> </template> <script> import { registerOrUpdateFaceInfo, UpdateLaborPersonnel } from '@/api/mww/labor.js'; import { UploadByProject } from '@/api/sys/upload.js'; import { sysConfig } from '@/config/config.js'; import storage from 'store'; import { ACCESS_TOKEN } from '@/store/mutation-types'; export default { name: 'face-authentication', data() { return { imageSrc: '', lastData: {}, base64Img: '', base64: '' }; }, onLoad(option) { this.lastData = JSON.parse(decodeURIComponent(option.lastData)); console.log('前一個(gè)頁(yè)面數(shù)據(jù)', this.lastData); uni.setNavigationBarTitle({ title: this.lastData.CnName + '-人臉認(rèn)證 ' }); }, methods: { onSave() { if (!this.imageSrc) { this.$refs.uToast.show({ title: '請(qǐng)先拍照', type: 'error' }); } // 人臉上傳,附件上傳,勞務(wù)人員信息修改 this.upImage(); }, // h5壓縮圖片的方式,url為圖片流 blobTobase64(url) { console.log('進(jìn)來(lái)了2', url); let imgFile = url; let _this = this; uni.request({ url: url, method: 'GET', responseType: 'arraybuffer', success: res => { let base64 = uni.arrayBufferToBase64(res.data); //把a(bǔ)rraybuffer轉(zhuǎn)成base64 _this.base64Img = 'data:image/jpeg;base64,' + base64; //不加上這串字符,在頁(yè)面無(wú)法顯示 } }); }, //app壓縮圖片 用for循環(huán) 來(lái)處理圖片壓縮 的問(wèn)題,原因是 plus.zip.compressImage 方法 是異步執(zhí)行的,for循環(huán)很快, 同時(shí)手機(jī)可執(zhí)行的壓縮方法有限制:應(yīng)該是3個(gè)吧。超出直接就不執(zhí)行了。所以 原理就是 在圖片壓縮成功后 繼續(xù) 回調(diào) 壓縮函數(shù)。 以到達(dá)循環(huán)壓縮圖片的功能。 app_img(num, rem) { let that = this; let index = rem.tempFiles[num].path.lastIndexOf('.'); //獲取圖片地址最后一個(gè)點(diǎn)的位置 let img_type = rem.tempFiles[num].path.substring(index + 1, rem.tempFiles[num].path.length); //截取圖片類(lèi)型如png jpg let img_yuanshi = rem.tempFiles[num].path.substring(0, index); //截取圖片原始路徑 let d2 = new Date().getTime(); //時(shí)間戳 //壓縮圖片 plus.zip.compressImage( { src: rem.tempFiles[num].path, //你要壓縮的圖片地址 dst: img_yuanshi + d2 + '.' + img_type, //壓縮之后的圖片地址(注意壓縮之后的路徑最好和原生路徑的位置一樣,不然真機(jī)上報(bào)code-5) quality: 70 //[10-100] }, function(e) { //壓縮之后路徑轉(zhuǎn)base64位的 //通過(guò)URL參數(shù)獲取目錄對(duì)象或文件對(duì)象 plus.io.resolveLocalFileSystemURL(e.target, function(entry) { // 可通過(guò)entry對(duì)象操作test.html文件 entry.file(function(file) { //獲取文件數(shù)據(jù)對(duì)象 var fileReader = new plus.io.FileReader(); // 文件系統(tǒng)中的讀取文件對(duì)象,用于獲取文件的內(nèi)容 //alert("getFile:" + JSON.stringify(file)); fileReader.readAsDataURL(file); //以URL編碼格式讀取文件數(shù)據(jù)內(nèi)容 fileReader.onloadend = function(evt) { //讀取文件成功完成的回調(diào)函數(shù) that.base64Img = evt.target.result.split(',')[1]; //拿到‘data:image/jpeg;base64,‘后面的 console.log('that.base64Img', that.base64Img); // rem.tempFiles[num].Base64_Path = evt.target.result.split(',')[1]; }; }); }); // that.base64Img = that.base64Img.concat(rem.tempFiles[num]); // 【注意】在此人臉認(rèn)證中,只會(huì)傳一張圖片,故不考慮多張圖片情況 //利用遞歸循環(huán)來(lái)實(shí)現(xiàn)多張圖片壓縮 // if (num == rem.tempFiles.length - 1) { // return; // } else { // that.app_img(num + 1, rem); // } }, function(error) { console.log('Compress error!'); console.log(JSON.stringify(error)); uni.showToast({ title: '編碼失敗' + error }); } ); }, // 打開(kāi)手機(jī)相機(jī)相冊(cè)功能 onImage() { const that = this; // 安卓系統(tǒng)無(wú)法默認(rèn)打開(kāi)前置攝像頭,具體請(qǐng)看下面app-plus原因, uni.chooseImage({ count: 1, //默認(rèn)9 sizeType: ['original', 'compressed'], //可以指定是原圖還是壓縮圖,默認(rèn)二者都有 sourceType: ['camera'], // 打開(kāi)攝像頭-'camera',從相冊(cè)選擇-'album' success: function(res) { console.log('文件結(jié)果', res); if (res.tempFilePaths.length > 0) { // Blob流地址 that.imageSrc = res.tempFilePaths[0]; //#ifdef APP-PLUS //圖片壓縮 that.app_img(0, res); //#endif // #ifndef APP-PLUS that.blobTobase64(res.tempFilePaths[0]); // #endif } else { that.$refs.uToast.show({ title: '無(wú)文件信息', type: 'error' }); } }, fail: function(res) { console.log('失敗了', res.errMsg); that.$refs.uToast.show({ title: res.errMsg, type: 'error' }); } }); // #ifdef APP-PLUS // console.log('app環(huán)境了'); // 指定要獲取攝像頭的索引值,1表示主攝像頭,2表示輔攝像頭。如果沒(méi)有設(shè)置則使用系統(tǒng)默認(rèn)主攝像頭。 // 平臺(tái)支持【注意注意注意】 // Android - 2.2+ (不支持) : // 暫不支持設(shè)置默認(rèn)使用的攝像頭,忽略此屬性值。打開(kāi)拍攝界面后可操作切換。 // iOS - 4.3+ (支持) // var cmr = plus.camera.getCamera(1); // var res = cmr.supportedImageResolutions[0]; // var fmt = cmr.supportedImageFormats[0]; // console.log('Resolution: ' + res + ', Format: ' + fmt); // cmr.captureImage( // function(path) { // alert('Capture image success: ' + path); // }, // function(error) { // alert('Capture image failed: ' + error.message); // }, // { resolution: res, format: fmt } // ); // #endif }, // 上傳附件至[人臉認(rèn)證]服務(wù)器 upImage() { if (!this.base64Img) { this.$refs.uToast.show({ title: '無(wú)圖片信息', type: 'error' }); return; } const params = { identityId: this.lastData.IdCard, //身份證號(hào)碼 imgInfo: this.base64Img, //頭像采用base64編碼 userId: this.lastData.Id, //勞務(wù)人員Id userName: this.lastData.CnName //勞務(wù)姓名 }; uni.showLoading(); registerOrUpdateFaceInfo(params) .then(res => { if (res.success) { this.$refs.uToast.show({ title: '認(rèn)證成功', type: 'success' }); // 上傳至附件服務(wù)器+修改勞務(wù)人員信息 this.uploadFile(); } else { this.$refs.uToast.show({ title: '認(rèn)證失敗,' + res.message, type: 'error' }); uni.hideLoading(); } }) .catch(err => { uni.hideLoading(); uni.showModal({ title: '提示', content: err }); }); }, // 上傳附件至附件服務(wù)器 uploadFile() { const obj = { project: this.lastData.OrgCode || this.$store.getters.projectCode.value, module: 'mww.personnelCertification', segment: this.lastData.OrgCode, businessID: this.lastData.Id, storageType: 1 }; let str = `project=${obj.project}&module=${obj.module}&segment=${obj.segment}&businessID=${obj.businessID}&storageType=${obj.storageType}`; console.log('str', str); // const url = ''; // console.log('url', url); // const formData = new FormData(); // formData.append('file', this.imageSrc, '.png'); // UploadByProject(str, formData).then(res => { // if (res.success) { // this.$refs.uToast.show({ // title: '上傳成功', // type: 'success' // }); // } else { // this.$refs.uToast.show({ // title: res.message, // type: 'error' // }); // } // }); const token = uni.getStorageSync(ACCESS_TOKEN); const that = this; // 需要使用uniapp提供的api,因?yàn)閠hat.imageSrc的blob流為地址頭為localhost(本地臨時(shí)文件) uni.uploadFile({ url: `${sysConfig().fileServer}/UploadFile/UploadByProject?${str}`, filePath: that.imageSrc, formData: { ...obj }, header: { // 必須傳token,不然會(huì)報(bào)[系統(tǒng)標(biāo)識(shí)不能為空] authorization: `Bearer ${token}` }, name: 'file', success: res => { that.$refs.uToast.show({ title: '上傳成功', type: 'success' }); that.lastData.CertificationUrl = res.data[0].virtualPath; that.lastData.Certification = 1; that.updateLaborPersonnel(); }, fail: err => { console.log('上傳失敗了', err); that.$refs.uToast.show({ title: '上傳失敗,' + err, type: 'error' }); uni.hideLoading(); } }); }, // 修改勞務(wù)人員信息 updateLaborPersonnel() { UpdateLaborPersonnel(this.lastData) .then(res => { if (res.success) { this.$refs.uToast.show({ title: '修改成功', type: 'success' }); // uni.showToast({ // title: '成功了' // }); setTimeout(() => { uni.navigateBack({ delta: 1 }); }, 800); } else { this.$refs.uToast.show({ title: '修改失敗,' + res.message, type: 'error' }); } }) .finally(() => { uni.hideLoading(); }); } } }; </script> <style scoped lang="less"></style>
總結(jié)
到此這篇關(guān)于uniapp實(shí)現(xiàn)人臉識(shí)別功能的文章就介紹到這了,更多相關(guān)uniapp人臉識(shí)別功能內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript實(shí)現(xiàn)隨機(jī)產(chǎn)生字符串的方法分享
這篇文章主要為大家詳細(xì)介紹了JavaScript中實(shí)現(xiàn)隨機(jī)產(chǎn)生字符串的方法,文中的示例代碼簡(jiǎn)潔易懂,對(duì)我們學(xué)習(xí)JavaScript有一定的幫助,需要的可以參考一下2022-11-11javascript Array.prototype.slice使用說(shuō)明
slice 可以用來(lái)獲取數(shù)組片段,它返回新數(shù)組,不會(huì)修改原數(shù)組。2010-10-10JS實(shí)現(xiàn)統(tǒng)計(jì)字符串中字符出現(xiàn)個(gè)數(shù)及最大個(gè)數(shù)功能示例
這篇文章主要介紹了JS實(shí)現(xiàn)統(tǒng)計(jì)字符串中字符出現(xiàn)個(gè)數(shù)及最大個(gè)數(shù)功能,結(jié)合實(shí)例形式分析了javascript字符串遍歷、統(tǒng)計(jì)相關(guān)操作技巧,需要的朋友可以參考下2018-06-06JS實(shí)現(xiàn)動(dòng)畫(huà)兼容性的transition和transform實(shí)例分析
這篇文章主要介紹了JS實(shí)現(xiàn)動(dòng)畫(huà)兼容性的transition和transform方法,結(jié)合實(shí)例形式分析了transition和transform方法針對(duì)手機(jī)端瀏覽器兼容性問(wèn)題上的相關(guān)處理技巧,需要的朋友可以參考下2016-12-12echarts浮動(dòng)顯示單位的實(shí)現(xiàn)方法示例
這篇文章主要給大家介紹了關(guān)于echarts浮動(dòng)顯示單位的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12Javascript連接Access數(shù)據(jù)庫(kù)完整實(shí)例
這篇文章主要介紹了Javascript連接Access數(shù)據(jù)庫(kù)的方法,涉及javascript針對(duì)access數(shù)據(jù)庫(kù)的連接、關(guān)閉及增刪改查等常用操作技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-08-08實(shí)例分析JS中的相等性判斷===、 ==和Object.is()
這篇文章主要給大家介紹了關(guān)于JS中相等性判斷===、 ==和Object.is()的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用JS具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11Js實(shí)現(xiàn)中國(guó)公民身份證號(hào)碼有效性驗(yàn)證實(shí)例代碼
這篇文章主要介紹了Js實(shí)現(xiàn)中國(guó)公民身份證號(hào)碼有效性驗(yàn)證實(shí)例代碼,可以識(shí)別身份證號(hào)碼的正確性,有興趣的可以了解一下2017-05-05Javascript模塊化機(jī)制實(shí)現(xiàn)原理詳解
這篇文章主要介紹了Javascript模塊化機(jī)制實(shí)現(xiàn)原理詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04