HTML5+tracking.js實作刷臉支付
最近刷臉支付很火,老闆們當然要追趕時代潮流,于是就有了刷臉支付這個項目。前端實作關鍵的技術是攝像頭錄像,拍照和人臉比對,本文來探讨一下如何在html5環境中如何實作刷臉支付以及開發過程中遇到的問題。
1.攝像頭
1.1 input擷取攝像頭
html5中擷取手機上的圖檔,有兩種方式,使用input,如下可以打開攝像頭拍照:
另外如果想打開相冊,可以這樣:
但是這兩種方式都會有相容性問題,用過的同學可能都知道。
1.2 getUserMedia擷取攝像頭
getUserMedia是html5一個新的api,官方一點的定義是:
MediaDevices.getUserMedia() 會提示使用者給予使用媒體輸入的許可,媒體輸入會産生一個MediaStream,裡面包含了請求的媒體類型的軌道。此流可以包含一個視訊軌道(來自硬體或者虛拟視訊源,比如相機、視訊采集裝置和螢幕共享服務等等)、一個音頻軌道(同樣來自硬體或虛拟音頻源,比如麥克風、A/D轉換器等等),也可能是其它軌道類型。
簡單一點說就是可以擷取到使用者攝像頭。
同上面input一樣,這種方式也有相容性問題,不過可以使用其他方式解決,這裡可以參考MediaDevices.getUserMedia(),文檔中有介紹"在舊的浏覽器中使用新的API"。我這裡在網上也找了一些參考,總結出一個相對全面的getUserMedia版本,代碼如下:
// 通路使用者媒體裝置
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 = "你的浏覽器不支援通路使用者媒體裝置"
}
}
1.3 播放視屏
擷取裝置方法有兩個回調函數,一個是成功,一個是失敗。成功了就開始播放視訊,播放視屏其實就是給video設定一個url,并調用play方法,這裡設定url要考慮不同浏覽器相容性,代碼如下:
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
注意:
播放視屏方法最好寫在onloadmetadata回調函數中,否則可能會報錯。
播放視訊的時候出于安全性考慮,必須在本地環境中測試,也就是
http://localhost/xxxx中測試,或者帶有
https://xxxxx環境中測試,不然的話或有跨域問題。
下面用到的initTracker()方法也好放在這個onloadedmetadata回調函數裡,不然也會報錯。
-
捕捉人臉
2.1 使用tracking.js捕捉人臉
視屏在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)
}
在頁面中畫一些方框,辨別出人臉:
: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 })
2.2 拍照
拍照,就是使用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)
拍照完成之後就可以把檔案發送給後端,讓後端進行對比驗證,這裡後端使用的是阿裡雲的接口。
3. 最後效果
3.1 參考代碼demo
最後,demo我已經放在github上了,感興趣可以打開看一下。
效果如下:
3.2 在項目中落地
最後放在項目中,無非就是最後一個步驟,去調用接口比對,根據比對結果成功是成功還是失敗,決定是人臉支付還是繼續使用原來的密碼支付,效果如下:
ps:這裡人臉比對失敗了,是因為我帶着口罩,就不呲牙露臉了。後端調用阿裡雲的接口位址:
https://help.aliyun.com/document_detail/154615.html?spm=a2c4g.11186623.6.625.632a37b9brzAoi作者:Tyler Ning
出處:
http://www.cnblogs.com/tylerdonet/