最近刷臉支付很火,老闆們固然要追趕時代潮流,因而就有了刷臉支付這個項目。前端實現關鍵的技術是攝像頭錄像,拍照和人臉比對,本文來探討一下如何在html5環境中如何實現刷臉支付以及開發過程當中遇到的問題。javascript
html5中獲取手機上的圖片,有兩種方式,使用input,以下能夠打開攝像頭拍照:html
<input type="file" capture="camera" accept="image/*"/>
另外若是想打開相冊,能夠這樣:前端
<input type="file" accept="img/*">
可是這兩種方式都會有兼容性問題,用過的同窗可能都知道。html5
getUserMedia是html5一個新的api,官方一點的定義是:java
MediaDevices.getUserMedia()
會提示用戶給予使用媒體輸入的許可,媒體輸入會產生一個MediaStream
,裏面包含了請求的媒體類型的軌道。此流能夠包含一個視頻軌道(來自硬件或者虛擬視頻源,好比相機、視頻採集設備和屏幕共享服務等等)、一個音頻軌道(一樣來自硬件或虛擬音頻源,好比麥克風、A/D轉換器等等),也多是其它軌道類型。git
簡單一點說就是能夠獲取到用戶攝像頭。github
同上面input同樣,這種方式也有兼容性問題,不過可使用其餘方式解決,這裏能夠參考MediaDevices.getUserMedia(),文檔中有介紹"在舊的瀏覽器中使用新的API"。我這裏在網上也找了一些參考,總結出一個相對全面的getUserMedia版本,代碼以下:web
// 訪問用戶媒體設備 getUserMedia(constrains, success, error) { if (navigator.mediaDevices.getUserMedia) { //最新標準API navigator.mediaDevices.getUserMedia(constrains).then(success).catch(error); } else if (navigator.webkitGetUserMedia) { //webkit內核瀏覽器 navigator.webkitGetUserMedia(constrains).then(success).catch(error); } else if (navigator.mozGetUserMedia) { //Firefox瀏覽器 navagator.mozGetUserMedia(constrains).then(success).catch(error); } else if (navigator.getUserMedia) { //舊版API navigator.getUserMedia(constrains).then(success).catch(error); } else { this.scanTip = "你的瀏覽器不支持訪問用戶媒體設備" } }
獲取設備方法有兩個回調函數,一個是成功,一個是失敗。成功了就開始播放視頻,播放視屏其實就是給video設置一個url,並調用play方法,這裏設置url要考慮不一樣瀏覽器兼容性,代碼以下:canvas
success(stream) { this.streamIns = stream // 設置播放地址,webkit內核瀏覽器 this.URL = window.URL || window.webkitURL if ("srcObject" in this.$refs.refVideo) { this.$refs.refVideo.srcObject = stream } else { this.$refs.refVideo.src = this.URL.createObjectURL(stream) } this.$refs.refVideo.onloadedmetadata = e => { // 播放視頻 this.$refs.refVideo.play() this.initTracker() } }, error(e) { this.scanTip = "訪問用戶媒體失敗" + e.name + "," + e.message }
注意:後端
視屏在video中播放成功以後就開始識別人臉了,這裏使用到一個第三方的功能tracking.js,是國外的大神寫的JavaScript圖像識別插件。關鍵代碼以下:
// 人臉捕捉 initTracker() { this.context = this.$refs.refCanvas.getContext("2d") // 畫布 this.tracker = new tracking.ObjectTracker(['face']) // tracker實例 this.tracker.setStepSize(1.7) // 設置步長 this.tracker.on('track', this.handleTracked) // 綁定監聽方法 try { tracking.track('#video', this.tracker) // 開始追蹤 } catch (e) { this.scanTip = "訪問用戶媒體失敗,請重試" } }
捕獲到人臉以後,能夠在頁面上用一個小方框標註出來,這樣有點交互效果。
// 追蹤事件 handleTracked(e) { if (e.data.length === 0) { this.scanTip = '未檢測到人臉' } else { if (!this.tipFlag) { this.scanTip = '檢測成功,正在拍照,請保持不動2秒' } // 1秒後拍照,僅拍一次 if (!this.flag) { this.scanTip = '拍照中...' this.flag = true this.removePhotoID = setTimeout(() => { this.tackPhoto() this.tipFlag = true }, 2000) } e.data.forEach(this.plot) } }
在頁面中畫一些方框,標識出人臉:
<div class="rect" v-for="item in profile" :style="{ width: item.width + 'px', height: item.height + 'px', left: item.left + 'px', top: item.top + 'px'}"></div>
// 繪製跟蹤框 plot({x, y, width: w, height: h}) { // 建立框對象 this.profile.push({ width: w, height: h, left: x, top: y }) }
拍照,就是使用video做爲圖片源,在canvas中保存一張圖片下來,注意這裏使用toDataURL方法的時候能夠設置第二個參數quality,從0到1,0表示圖片比較粗糙,可是文件比較小,1表示品質最好。
// 拍照 tackPhoto() { this.context.drawImage(this.$refs.refVideo, 0, 0, this.screenSize.width, this.screenSize.height) // 保存爲base64格式 this.imgUrl = this.saveAsPNG(this.$refs.refCanvas) // this.compare(imgUrl) this.close() }, // Base64轉文件 getBlobBydataURI(dataURI, type) { var binary = window.atob(dataURI.split(',')[1]); var array = []; for(var i = 0; i < binary.length; i++) { array.push(binary.charCodeAt(i)); } return new Blob([new Uint8Array(array)], { type: type }); }, // 保存爲png,base64格式圖片 saveAsPNG(c) { return c.toDataURL('image/png', 0.3) }
拍照完成以後就能夠把文件發送給後端,讓後端進行對比驗證,這裏後端使用的是阿里雲的接口。
最後,demo我已經放在github上了,感興趣能夠打開看一下。
效果以下:
最後放在項目中,無非就是最後一個步驟,去調用接口比對,根據比對結果成功是成功仍是失敗,決定是人臉支付仍是繼續使用原來的密碼支付,效果以下:
ps:這裏人臉比對失敗了,是由於我帶着口罩,就不呲牙露臉了。後端調用阿里雲的接口地址:https://help.aliyun.com/document_detail/154615.html?spm=a2c4g.11186623.6.625.632a37b9brzAoi