uniapp實現(xiàn)人臉識別功能的具體實現(xiàn)代碼
前言
對于前端來說,需要后端提供一個人臉識別接口,前端傳入圖片,接口識別并返回結(jié)果,如此看來,其實前端只需實現(xiàn)圖片傳入即可,但是其實不然,在傳入圖片時,需要進行以下幾點操作:
- 判斷圖片格式,市場上比較常見的是
.jpg
、.jpeg
、.png
- 計算文件大小,一般要求不超過5MB
- 對圖片進行base64加密
其實前2點具體要看接口要求,但是第3點,是實現(xiàn)人臉識別必備步驟,下文重點講述一下移動端實現(xiàn)人臉識別的base64加密方法
問題
項目主要使用的技術(shù)棧是uniapp,uniapp的優(yōu)點是上手快,基于vue開發(fā),但缺點也很明顯,多環(huán)境兼容導(dǎo)致兼容性較差,真機調(diào)試和運行較慢。比如h5端可以輕松實現(xiàn)base64加密,但是安卓環(huán)境完全不行,因為本地上傳圖片時,會返回一個blob流,但是uniapp的blob流是以<http://localhost>…
(安卓環(huán)境無法識別localhost
)開始,導(dǎo)致無法進行base64加密
解決辦法
經(jīng)過多方實現(xiàn)后,借用html5+ api的多個結(jié)合方法(plus.zip.compressImage
、plus.io.resolveLocalFileSystemURL
、plus.io.FileReader
)實現(xiàn)加密,主要代碼如下:
//app壓縮圖片 用for循環(huán) 來處理圖片壓縮 的問題,原因是 plus.zip.compressImage 方法 是異步執(zhí)行的,for循環(huán)很快, 同時手機可執(zhí)行的壓縮方法有限制:應(yīng)該是3個吧。超出直接就不執(zhí)行了。所以 原理就是 在圖片壓縮成功后 繼續(xù) 回調(diào) 壓縮函數(shù)。 以到達循環(huán)壓縮圖片的功能。 app_img(num, rem) { let that = this; let index = rem.tempFiles[num].path.lastIndexOf('.'); //獲取圖片地址最后一個點的位置 let img_type = rem.tempFiles[num].path.substring(index + 1, rem.tempFiles[num].path.length); //截取圖片類型如png jpg let img_yuanshi = rem.tempFiles[num].path.substring(0, index); //截取圖片原始路徑 let d2 = new Date().getTime(); //時間戳 //壓縮圖片 plus.zip.compressImage( { src: rem.tempFiles[num].path, //你要壓縮的圖片地址 dst: img_yuanshi + d2 + '.' + img_type, //壓縮之后的圖片地址(注意壓縮之后的路徑最好和原生路徑的位置一樣,不然真機上報code-5) quality: 70 //[10-100] }, function (e) { //壓縮之后路徑轉(zhuǎn)base64位的 //通過URL參數(shù)獲取目錄對象或文件對象 plus.io.resolveLocalFileSystemURL(e.target, function (entry) { // 可通過entry對象操作test.html文件 entry.file(function (file) { //獲取文件數(shù)據(jù)對象 var fileReader = new plus.io.FileReader(); // 文件系統(tǒng)中的讀取文件對象,用于獲取文件的內(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]); // 【注意】在此人臉認證中,只會傳一張圖片,故不考慮多張圖片情況 //利用遞歸循環(huán)來實現(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 }); } ); },
詳細實現(xiàn)思路
其實對于uniapp實現(xiàn)人臉識別功能來講,大概要經(jīng)過這么幾個步驟
onImage()
:打開手機相冊上傳圖片,獲取blob流(本地臨時地址)#ifdef APP-PLUS
/#ifndef APP-PLUS
:判斷系統(tǒng)環(huán)境,是h5還是安卓環(huán)境,然后在進行圖片壓縮和加密,具體實現(xiàn)代碼如下:
//#ifdef APP-PLUS //圖片壓縮 that.app_img(0, res); //#endif // #ifndef APP-PLUS that.blobTobase64(res.tempFilePaths[0]); // #endif
app_img()
/blobTobase64()
:對要識別的圖片進行base64加密onSave()
—>upImage()
:附件上傳,并處理識別信息
具體代碼
<!-- 人臉認證 --> <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('前一個頁面數(shù)據(jù)', this.lastData); uni.setNavigationBarTitle({ title: this.lastData.CnName + '-人臉認證 ' }); }, methods: { onSave() { if (!this.imageSrc) { this.$refs.uToast.show({ title: '請先拍照', type: 'error' }); } // 人臉上傳,附件上傳,勞務(wù)人員信息修改 this.upImage(); }, // h5壓縮圖片的方式,url為圖片流 blobTobase64(url) { console.log('進來了2', url); let imgFile = url; let _this = this; uni.request({ url: url, method: 'GET', responseType: 'arraybuffer', success: res => { let base64 = uni.arrayBufferToBase64(res.data); //把arraybuffer轉(zhuǎn)成base64 _this.base64Img = 'data:image/jpeg;base64,' + base64; //不加上這串字符,在頁面無法顯示 } }); }, //app壓縮圖片 用for循環(huán) 來處理圖片壓縮 的問題,原因是 plus.zip.compressImage 方法 是異步執(zhí)行的,for循環(huán)很快, 同時手機可執(zhí)行的壓縮方法有限制:應(yīng)該是3個吧。超出直接就不執(zhí)行了。所以 原理就是 在圖片壓縮成功后 繼續(xù) 回調(diào) 壓縮函數(shù)。 以到達循環(huán)壓縮圖片的功能。 app_img(num, rem) { let that = this; let index = rem.tempFiles[num].path.lastIndexOf('.'); //獲取圖片地址最后一個點的位置 let img_type = rem.tempFiles[num].path.substring(index + 1, rem.tempFiles[num].path.length); //截取圖片類型如png jpg let img_yuanshi = rem.tempFiles[num].path.substring(0, index); //截取圖片原始路徑 let d2 = new Date().getTime(); //時間戳 //壓縮圖片 plus.zip.compressImage( { src: rem.tempFiles[num].path, //你要壓縮的圖片地址 dst: img_yuanshi + d2 + '.' + img_type, //壓縮之后的圖片地址(注意壓縮之后的路徑最好和原生路徑的位置一樣,不然真機上報code-5) quality: 70 //[10-100] }, function(e) { //壓縮之后路徑轉(zhuǎn)base64位的 //通過URL參數(shù)獲取目錄對象或文件對象 plus.io.resolveLocalFileSystemURL(e.target, function(entry) { // 可通過entry對象操作test.html文件 entry.file(function(file) { //獲取文件數(shù)據(jù)對象 var fileReader = new plus.io.FileReader(); // 文件系統(tǒng)中的讀取文件對象,用于獲取文件的內(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]); // 【注意】在此人臉認證中,只會傳一張圖片,故不考慮多張圖片情況 //利用遞歸循環(huán)來實現(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 }); } ); }, // 打開手機相機相冊功能 onImage() { const that = this; // 安卓系統(tǒng)無法默認打開前置攝像頭,具體請看下面app-plus原因, uni.chooseImage({ count: 1, //默認9 sizeType: ['original', 'compressed'], //可以指定是原圖還是壓縮圖,默認二者都有 sourceType: ['camera'], // 打開攝像頭-'camera',從相冊選擇-'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: '無文件信息', 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表示輔攝像頭。如果沒有設(shè)置則使用系統(tǒng)默認主攝像頭。 // 平臺支持【注意注意注意】 // Android - 2.2+ (不支持) : // 暫不支持設(shè)置默認使用的攝像頭,忽略此屬性值。打開拍攝界面后可操作切換。 // 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 }, // 上傳附件至[人臉認證]服務(wù)器 upImage() { if (!this.base64Img) { this.$refs.uToast.show({ title: '無圖片信息', type: 'error' }); return; } const params = { identityId: this.lastData.IdCard, //身份證號碼 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: '認證成功', type: 'success' }); // 上傳至附件服務(wù)器+修改勞務(wù)人員信息 this.uploadFile(); } else { this.$refs.uToast.show({ title: '認證失敗,' + 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,因為that.imageSrc的blob流為地址頭為localhost(本地臨時文件) uni.uploadFile({ url: `${sysConfig().fileServer}/UploadFile/UploadByProject?${str}`, filePath: that.imageSrc, formData: { ...obj }, header: { // 必須傳token,不然會報[系統(tǒng)標識不能為空] 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實現(xiàn)人臉識別功能的文章就介紹到這了,更多相關(guān)uniapp人臉識別功能內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript實現(xiàn)隨機產(chǎn)生字符串的方法分享
這篇文章主要為大家詳細介紹了JavaScript中實現(xiàn)隨機產(chǎn)生字符串的方法,文中的示例代碼簡潔易懂,對我們學(xué)習(xí)JavaScript有一定的幫助,需要的可以參考一下2022-11-11javascript Array.prototype.slice使用說明
slice 可以用來獲取數(shù)組片段,它返回新數(shù)組,不會修改原數(shù)組。2010-10-10JS實現(xiàn)統(tǒng)計字符串中字符出現(xiàn)個數(shù)及最大個數(shù)功能示例
這篇文章主要介紹了JS實現(xiàn)統(tǒng)計字符串中字符出現(xiàn)個數(shù)及最大個數(shù)功能,結(jié)合實例形式分析了javascript字符串遍歷、統(tǒng)計相關(guān)操作技巧,需要的朋友可以參考下2018-06-06JS實現(xiàn)動畫兼容性的transition和transform實例分析
這篇文章主要介紹了JS實現(xiàn)動畫兼容性的transition和transform方法,結(jié)合實例形式分析了transition和transform方法針對手機端瀏覽器兼容性問題上的相關(guān)處理技巧,需要的朋友可以參考下2016-12-12Javascript連接Access數(shù)據(jù)庫完整實例
這篇文章主要介紹了Javascript連接Access數(shù)據(jù)庫的方法,涉及javascript針對access數(shù)據(jù)庫的連接、關(guān)閉及增刪改查等常用操作技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-08-08實例分析JS中的相等性判斷===、 ==和Object.is()
這篇文章主要給大家介紹了關(guān)于JS中相等性判斷===、 ==和Object.is()的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家學(xué)習(xí)或者使用JS具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11