uniapp人臉識別解決方案

APP端:
因為APP端無法使用uni的camera組件,最開始考慮使用內嵌webview的方式,通過原生dom調用video渲染畫面然后通過canvas截圖。但是此方案兼容性在ios幾乎為0,如果app只考慮安卓端的話可以采用此方案。后面又想用live-pusher組件來實現,但是發現快照api好像需要真實流地址才能截取圖像。因為種種原因,也是安卓ios雙端兼容性不佳。最終決定采用5+api實現。經實測5+api兼容性還算可以,但是畢竟是調用原生能力,肯定是沒有原生開發那么絲滑的,難免會出現一些不可預測的兼容性問題。所以建議app和手機硬件交互強的話還是不要用uni開發了。不然真的是翻文檔能翻死人。社區也找不到靠譜的解決方案。

5+api 文檔
https://www.html5plus.org/doc/zh_cn/video.html#plus.video.createLivePusher

image.png

就是使用這個api調用原生的camera完成。并且可以直接在預覽模式下完成快照,也不需要真實的推流地址。

<template>
    <view class="content">
        <view class="c-footer">
            <view class="msg-box">
                <view class="msg">
                    1、請保證本人驗證。
                </view>
                <view class="msg">
                    2、請使頭像正面位于畫框中。
                </view>
                <view class="msg">
                    3、請使頭像盡量清晰。
                </view>
                <view class="msg">
                    4、請保證眼鏡不反光,雙眼可見。
                </view>
                <view class="msg">
                    5、請保證無墨鏡,口罩,面膜等遮擋物。
                </view>
                <view class="msg">
                    6、請不要化濃妝,不要戴帽子。
                </view>
            </view>
            <view class="but" @click="snapshotPusher" v-if="!cilckSwitch">采集本人人臉</view>
        </view>
    </view>
</template>

<script>
    import permission from '../../util/permission.js'
    export default {
        data() {
            return {
                type: '', //是否是補簽拉起的人臉識別
                imgData: '',
                pusher: null,
                scanWin: null,
                faceInitTimeout: null,
                snapshTimeout: null,
                uploadFileTask: null,
                cilckSwitch: false, //防止多次點擊
            };
        },
        methods: {
            //人臉比對
            handleFaceContrast(param) {
                uni.hideLoading()
                this.$http({
                    url: '/API_AUTH/AppIaiFace/faceContrast.api',
                    data: {
                        ...param,
                        userid: uni.getStorageSync('userInfo').id
                    }
                }).then(res => {
                    console.log(res)
                    if (res.data.compareResult == 1) {
                        let pages = getCurrentPages(); //獲取所有頁面棧實例列表
                        let nowPage = pages[pages.length - 1]; //當前頁頁面實例
                        let prevPage = pages[pages.length - 2]; //上一頁頁面實例
                        if (this.type == 'signOut') {
                            prevPage.$vm.signOutXh = param.xh;
                            prevPage.$vm.signOutPhotoPath = param.path
                        } else {
                            prevPage.$vm.xh = param.xh;
                            prevPage.$vm.photoPath = param.path
                        }
                        uni.navigateBack()
                    } else {
                        uni.showToast({
                            title: '人臉比對不通過,請重試',
                            icon: 'none'
                        })
                        this.cilckSwitch = false
                    }
                }).catch(err => {
                    uni.showToast({
                        title: '人臉比對不通過,請重試',
                        icon: 'none'
                    })
                    this.cilckSwitch = false
                })
            },
            //初始化
             faceInit() {
                 uni.showLoading({
                    title: '請稍后...'
                 })
                this.faceInitTimeout = setTimeout(async () => {
                    //創建livepusher
                    if (uni.getSystemInfoSync().platform === 'android') {
                        const data1 = await permission.requestAndroidPermission(
                            "android.permission.RECORD_AUDIO")
                        const data2 = await permission.requestAndroidPermission("android.permission.CAMERA")
                        console.log(data1,data2,1111)
                        if (data1 == 1 && data2 == 1) {
                            this.pusherInit();
                        }
                    } else {
                        this.pusherInit();
                    }
                    //覆蓋在視頻之上的內容,根據實際情況編寫
                    // this.scanWin = plus.webview.create('/hybrid/html/faceTip.html', '', {
                    //  background: 'transparent'
                    // });
                    //新引入的webView顯示
                    // this.scanWin.show();
                    //初始化上傳本地文件的api
                    this.initUploader()
                }, 500);
            },
            //初始化播放器
            pusherInit() {
                const currentWebview = this.$mp.page.$getAppWebview();
                this.pusher = plus.video.createLivePusher('livepusher', {
                    url: '',
                    top: '0px',
                    left: '0px',
                    width: '100%',
                    height: '50%',
                    position: 'absolute',
                    aspect: '9:16',
                    muted: false,
                    'z-index': 999999,
                    'border-radius': '50%',
                });
                currentWebview.append(this.pusher);
                //反轉攝像頭
                this.pusher.switchCamera();
                //開始預覽
                this.pusher.preview();
                uni.hideLoading()
            },
            //初始化讀取本地文件
            initUploader() {
                let that = this
                this.uploadFileTask = plus.uploader.createUpload(
                    "完整的接口請求地址", {
                        method: "POST",
                        headers: {
                            // 修改請求頭Content-Type類型 此類型為文件上傳
                            "Content-Type": "multipart/form-data"
                        }
                    },
                    // data:服務器返回的響應值 status: 網絡請求狀態碼
                    (data, status) => {
                        // 請求上傳文件成功
                        if (status == 200) {
                            console.log(data)
                            // 獲取data.responseText之后根據自己的業務邏輯做處理
                            let result = JSON.parse(data.responseText);
                            console.log(result.data.xh)
                            that.handleFaceContrast({
                                xh: result.data.xh,
                                path: result.data.path
                            })
                        }
                        // 請求上傳文件失敗
                        else {
                            uni.showToast({
                                title: '上傳圖片失敗',
                                icon: 'none'
                            })
                            console.log("上傳失敗", status)
                            that.cilckSwitch = false
                            uni.hideLoading()
                        }
                    }
                );
            },
            //快照
            snapshotPusher() {
                if (this.cilckSwitch) {
                    uni.showToast({
                        title: '請勿頻繁點擊',
                        icon: 'none'
                    })
                    return
                }
                this.cilckSwitch = true
                uni.showLoading({
                    title: '正在比對,請勿退出'
                })
                let that = this
                this.snapshTimeout = setTimeout(() => {
                    this.pusher.snapshot(
                        e => {
                            // this.pusher.close();
                            // this.scanWin.hide();
                            //拿到本地文件路徑
                            var src = e.tempImagePath;
                            this.uploadImg(src)
                            //獲取圖片base64
                            // this.getMinImage(src);
                        },
                        function(e) {
                            plus.nativeUI.alert('snapshot error: ' + JSON.stringify(e));
                            that.cilckSwitch = false
                            uni.hideLoading()
                        }
                    );
                }, 500);
            },
            //調用原生能力讀取本地文件并上傳
            uploadImg(imgPath) {
                this.uploadFileTask.addFile('file://' + imgPath, {
                    key: "file" // 填入圖片文件對應的字段名
                });
                //添加其他表單字段(參數) 兩個參數可能都只支持傳字符串
                // uploadFileTask.addData("參數名", 參數值);
                this.uploadFileTask.start();
            },
            //獲取圖片base64
            getMinImage(imgPath) {
                plus.zip.compressImage({
                        src: imgPath,
                        dst: imgPath,
                        overwrite: true,
                        quality: 40
                    },
                    zipRes => {
                        setTimeout(() => {
                            var reader = new plus.io.FileReader();
                            reader.onloadend = res => {
                                var speech = res.target.result; //base64圖片
                                console.log(speech.length);
                                console.log(speech)
                                this.imgData = speech;
                            };
                            reader.readAsDataURL(plus.io.convertLocalFileSystemURL(zipRes.target));
                        }, 1000);
                    },
                    function(error) {
                        console.log('Compress error!', error);
                    }
                );
            },
        },
        onLoad(option) {
            //#ifdef APP-PLUS
            this.type = option.type
            this.faceInit();
            
            //#endif
        },
        onHide() {
            console.log('hide')
            this.faceInitTimeout && clearTimeout(this.faceInitTimeout);
            this.snapshTimeout && clearTimeout(this.snapshTimeout);
            // this.scanWin.hide();
        },
        onBackPress() {
            // let pages = getCurrentPages(); //獲取所有頁面棧實例列表
            // let nowPage = pages[pages.length - 1]; //當前頁頁面實例
            // let prevPage = pages[pages.length - 2]; //上一頁頁面實例
            // prevPage.$vm.xh = '11111';
            // prevPage.$vm.photoPath = '22222' 
            this.faceInitTimeout && clearTimeout(this.faceInitTimeout);
            this.snapshTimeout && clearTimeout(this.snapshTimeout);
            // this.scanWin.hide();
        },
        onUnload() {
            this.faceInitTimeout && clearTimeout(this.faceInitTimeout);
            this.snapshTimeout && clearTimeout(this.snapshTimeout);
            // this.scanWin.hide();
        },
    };
</script>

<style lang="scss" scoped>
    .but {
        margin: 50rpx auto 0;
        border-radius: 10px;
        width: 80%;
        height: 100rpx;
        display: flex;
        align-items: center;
        justify-content: center;
        background-color: #008AFF;
        font-size: 16px;
        color: #FFFFFF;
    }

    .c-footer {

        width: 100%;
        position: fixed;
        top: 50%;
        left: 0;
        z-index: 10;
        padding: 30rpx 0;

        .msg-box {
            width: 80%;
            margin: 0 auto;
            text-align: left;

            .msg {
                margin-bottom: 15rpx;
                font-size: 13px;
                color: #666;
            }
        }
    }

    .img-data {
        width: 100%;
        height: auto;
    }

    .content {
        background-color: #fff;

    }
</style>

以上是完整的包含邏輯的代碼。

關鍵代碼
//初始化播放器
            pusherInit() {
                const currentWebview = this.$mp.page.$getAppWebview();
                this.pusher = plus.video.createLivePusher('livepusher', {
                    url: '',
                    top: '0px',
                    left: '0px',
                    width: '100%',
                    height: '50%',
                    position: 'absolute',
                    aspect: '9:16',
                    muted: false,
                    'z-index': 999999,
                    'border-radius': '50%',
                });
                currentWebview.append(this.pusher);
                //反轉攝像頭
                this.pusher.switchCamera();
                //開始預覽
                this.pusher.preview();
                uni.hideLoading()
            },

 //快照
            snapshotPusher() {
                if (this.cilckSwitch) {
                    uni.showToast({
                        title: '請勿頻繁點擊',
                        icon: 'none'
                    })
                    return
                }
                this.cilckSwitch = true
                uni.showLoading({
                    title: '正在比對,請勿退出'
                })
                let that = this
                this.snapshTimeout = setTimeout(() => {
                    this.pusher.snapshot(
                        e => { 
                            //拿到本地文件路徑
                            var src = e.tempImagePath;
  //這里因為接口參數需要加密,用base64的話加密出來的參數太大了,所以選擇了直接讀取本地文件上傳文件流的方式。
                            this.uploadImg(src)
                            //獲取圖片base64
                            // this.getMinImage(src);
                        },
                        function(e) {
                            plus.nativeUI.alert('snapshot error: ' + JSON.stringify(e));
                            that.cilckSwitch = false
                            uni.hideLoading()
                        }
                    );
                }, 500);
            },
            //獲取圖片base64
            getMinImage(imgPath) {
                plus.zip.compressImage({
                        src: imgPath,
                        dst: imgPath,
                        overwrite: true,
                        quality: 40
                    },
                    zipRes => {
                        setTimeout(() => {
                            var reader = new plus.io.FileReader();
                            reader.onloadend = res => {
                                var speech = res.target.result; //base64圖片
                                console.log(speech.length);
                                console.log(speech)
                                this.imgData = speech;
                            };
                            reader.readAsDataURL(plus.io.convertLocalFileSystemURL(zipRes.target));
                        }, 1000);
                    },
                    function(error) {
                        console.log('Compress error!', error);
                    }
                );
            },
            //初始化讀取本地文件
            initUploader() {
                let that = this
                this.uploadFileTask = plus.uploader.createUpload(
                    "完整的接口請求地址", {
                        method: "POST",
                        headers: {
                            // 修改請求頭Content-Type類型 此類型為文件上傳
                            "Content-Type": "multipart/form-data"
                        }
                    },
                    // data:服務器返回的響應值 status: 網絡請求狀態碼
                    (data, status) => {
                        // 請求上傳文件成功
                        if (status == 200) {
                            console.log(data)
                            // 獲取data.responseText之后根據自己的業務邏輯做處理
                            let result = JSON.parse(data.responseText);
                            console.log(result.data.xh)
                            that.handleFaceContrast({
                                xh: result.data.xh,
                                path: result.data.path
                            })
                        }
                        // 請求上傳文件失敗
                        else {
                            uni.showToast({
                                title: '上傳圖片失敗',
                                icon: 'none'
                            })
                            console.log("上傳失敗", status)
                            that.cilckSwitch = false
                            uni.hideLoading()
                        }
                    }
                );
            },
     //調用原生能力讀取本地文件并上傳
            uploadImg(imgPath) {
                this.uploadFileTask.addFile('file://' + imgPath, {
                    key: "file" // 填入圖片文件對應的字段名
                });
                //添加其他表單字段(參數) 兩個參數可能都只支持傳字符串
                // uploadFileTask.addData("參數名", 參數值);
                this.uploadFileTask.start();
            },

以上就是關鍵的代碼。
接下來補充幾個坑的地方。創建出來的livepusher層級很高,無法在同一頁面被別的元素遮擋。所以想要在他上面寫樣式是行不通了。只能再創建一個webview。然后將這個webview覆蓋在livepusher上,達到人臉識別頁面的樣式。

//覆蓋在視頻之上的內容,根據實際情況編寫
                     this.scanWin = plus.webview.create('/hybrid/html/faceTip.html', '', {
                      background: 'transparent'
                     });
                    //新引入的webView顯示
                   this.scanWin.show();
                   //新引入的webView影藏
                     this.scanWin.hide();

這種方案在ios基本沒問題。至少目前沒遇到過。但是安卓就一言難盡了。首先這個組件默認調起的是后置攝像頭,這顯然不符合我們的需求。但是官方提供的文檔里也沒有明確支持可以配置優先調起哪個攝像頭。好早提供了一個switchCamera的api可以翻轉攝像頭。

但是在安卓系統上,尤其是鴻蒙系統,調用這個api就會導致程序閃退,而且發生頻率還特別高。這個問題至今不知道該怎么解決。

除了閃退問題,安卓還存在一個麻煩事兒,那就是首次進入app,翻轉攝像頭的api沒有用,拉起的還是后置攝像頭。但是后續再進入app就無此問題了。后面折騰來折騰去,發現好像是首次進入拉起授權彈窗的時候才會出現這種問題。
然后寫了個定時器做測試,五秒之后再拉起攝像頭再去翻轉攝像頭。然后再五秒內趕緊把授權給同意了。結果發現翻轉竟然生效了。
然后決定再渲染推流元素之前先讓用戶通過權限授權,然后再拉起攝像頭。 也就是上文完整代碼中的

 //創建livepusher
                    if (uni.getSystemInfoSync().platform === 'android') {
                        const data1 = await permission.requestAndroidPermission(
                            "android.permission.RECORD_AUDIO")
                        const data2 = await permission.requestAndroidPermission("android.permission.CAMERA")
                        console.log(data1,data2,1111)
                        if (data1 == 1 && data2 == 1) {
                            this.pusherInit();
                        }
                    } else {
                        this.pusherInit();
                    }

具體的意思就不過多贅述了,自行看permission的文檔。或者看他的代碼。很簡單
permission下載地址
https://ext.dcloud.net.cn/plugin?id=594
以上就是調用原生能力拉起攝像頭實現快照功能的所有內容了。

下面也記錄一下web端如果實現這種功能,畢竟當時搞出來也不容易,但是最終還是敗在了兼容性上

方案的話大致有兩種,一種是借助tracking js 有興趣的可以了解一下,一個web端人臉識別庫。他可以識別畫面中是否出現人臉。以及一下更高級的功能我就沒有去探索了。有需要的可以自行研究

<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <title>人臉識別</title>
        <script src="../html/js/tracking-min.js"></script>
        <script src="../html/js/face-min.js"></script>
        <style>
            video,
            canvas {
                position: absolute;
            }
        </style>
    </head>
    <body>
        <div class="demo-container">
            <video id="video" width="320" height="240" preload autoplay loop muted></video>
            <canvas id="canvas" width="320" height="240"></canvas>
        </div>
        <script>
            window.onload = function() {
                console.log(123123123)
                // 視頻顯示
                var video = document.getElementById('video');
                //   繪圖
                var canvas = document.getElementById('canvas');
                var context = canvas.getContext('2d');
                var time = 10000;
                var tracker = new tracking.ObjectTracker('face');
                //   設置識別的放大比例
                tracker.setInitialScale(4);
                //   設置步長
                tracker.setStepSize(2);
                //   邊緣密度
                tracker.setEdgesDensity(0.1);
                //   啟動攝像頭,并且識別視頻內容
                var trackerTask = tracking.track('#video', tracker, {
                    camera: true
                });
                var flag = true;
                tracker.on('track', function(event) {
                    if (event.data.length === 0) {
                        console.log('未檢測到人臉')
                        context.clearRect(0, 0, canvas.width, canvas.height);
                    } else if (event.data.length > 1) { 
                        console.log('檢測到多張人臉')
                    } else {
                        context.clearRect(0, 0, canvas.width, canvas.height);
                        event.data.forEach(function(rect) {
                            context.strokeStyle = '#ff0000';
                            context.strokeRect(rect.x, rect.y, rect.width, rect.height);
                            context.fillStyle = "#ff0000";
                            //console.log(rect.x, rect.width, rect.y, rect.height);
                        });
                        if (flag) {
                            console.log("拍照");
                            context.drawImage(video, 0, 0, 320, 240);
                            saveAsLocalImage();
                            context.clearRect(0, 0, canvas.width, canvas.height);
                            flag = false;
                            setTimeout(function() {
                                flag = true;
                            }, time);
                        } else {
                            //console.log("冷卻中");
                        }
                    }
                });
            };

            function saveAsLocalImage() {
                var myCanvas = document.getElementById("canvas");
                // here is the most important part because if you dont replace you will get a DOM 18 exception.  
                // var image = myCanvas.toDataURL("image/png").replace("image/png", "image/octet-stream;Content-Disposition: attachment;filename=foobar.png");  
                var image = myCanvas.toDataURL("image/png").replace("image/png", "image/octet-stream");
                // window.location.href = image; // it will save locally  
                // create temporary link  
                return
                var tmpLink = document.createElement('a');
                tmpLink.download = 'image.png'; // set the name of the download file 
                tmpLink.href = image;

                // temporarily add link to body and initiate the download  
                document.body.appendChild(tmpLink);
                tmpLink.click();
                document.body.removeChild(tmpLink);
            }
        </script>

    </body>

</html>


另外一種就是純video+canvas截取一張視頻中的畫面。

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>人臉采集</title>
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <link rel="stylesheet" type="text/css" href="./css/index.css" />
        <script src="./js/jq.js" type="text/javascript" charset="utf-8"></script>
        <!-- uni 的 SDK -->
        <script type="text/javascript" src="https://js.cdn.aliyun.dcloud.net.cn/dev/uni-app/uni.webview.1.5.2.js">
        </script>
        <script src="https://cdn.bootcdn.net/ajax/libs/vConsole/3.0.0/vconsole.min.js" type="text/javascript"
            charset="utf-8"></script>
        <script>
            // init vConsole,app中查看
            var vConsole = new VConsole();
            // console.log('Hello world');
        </script>
        <style>
            .mui-content {
                margin: 0 auto;
                text-align: center;
                border: 0px solid red;
            }
            /*攝像頭翻轉180度*/
            video {
                transform: rotateY(180deg);
                -webkit-transform: rotateY(180deg);
                /* Safari 和 Chrome */
                -moz-transform: rotateY(180deg);
            }
        </style>
    </head>
    <body class='body'>
        <div class="mui-content">
            <div style="margin: 40px auto;">
                <!-- <input type="file" id='image' accept="image/*" capture='user'> -->
                <video id="video" style="margin: 0 auto; border-radius: 150px;"></video>
                <canvas id='canvas' width="300" height="300"
                    style="border: 0px solid red;margin: auto; display: none;"></canvas>
            </div>
            <div class="msg-box">
                <div class="msg">
                    1、請保證本人驗證。
                </div>
                <div class="msg">
                    2、請使頭像正面位于畫框中。
                </div>
                <div class="msg">
                    3、請使頭像盡量清晰。
                </div>
                <div class="msg">
                    4、請保證眼鏡不反光,雙眼可見。
                </div>
                <div class="msg">
                    5、請保證無墨鏡,口罩,面膜等遮擋物。
                </div>
                <div class="msg">
                    6、請不要化濃妝,不要戴帽子。
                </div>
            </div>
            <div style="width: 80%; position: absolute; bottom: 20px; left: 50%; transform: translate(-50%, -50%);">
                <div class="but" id="start">采集本人人臉</div>
            </div>
        </div>
    </body>
    <script type="text/javascript">
        var video, canvas, vendorUrl, interval, videoHeight, videoWidth, time = 0;
        // 獲取webview頁面數據
        // var data = JSON.parse(getUrlParam('data'))
        // var info = data.info;
        // var userInfo = data.userInfo;
        // const userId = data.userId; // 當前登錄用戶id
        $(function() {
            // 初始化
            initVideo()
            // uni.app事件
            document.addEventListener('UniAppJSBridgeReady', function() {
                uni.getEnv(function(res) {
                    console.log('當前環境:' + JSON.stringify(res));
                });
                setInterval(() => {
                    uni.postMessage({
                        data: {
                            action: 'postMessage'
                        }
                    });
                }, 1000)
            });
        })
        // 獲取url攜帶的數據
        function getUrlParam(name) {
            var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
            var r = window.location.search.substr(1).match(reg);
            if (r != null) return unescape(r[2]);
            return null;
        }
        // 攝像頭初始化
        function initVideo() {
            console.log('攝像頭初始化')
            video = document.getElementById("video");
            videoHeight = 300
            videoWidth = 300
            setTimeout(() => {
                console.log(navigator)
                navigator.mediaDevices
                    .getUserMedia({
                        video: {
                            width: {
                                ideal: videoWidth,
                                max: videoWidth
                            },
                            height: {
                                ideal: videoHeight,
                                max: videoHeight
                            },
                            facingMode: 'user', //前置攝像頭
                            // facingMode: { exact: "environment" }, //后置攝像頭
                            frameRate: {
                                ideal: 30,
                                min: 10
                            }
                        }
                    })
                    .then(videoSuccess)
                    .catch(videoError);
                if (
                    navigator.mediaDevices.getUserMedia ||
                    navigator.getUserMedia ||
                    navigator.webkitGetUserMedia ||
                    navigator.mozGetUserMedia ||
                    navigator.mediaCapabilities
                ) {
                    console.log('調用用戶媒體設備, 訪問攝像頭')
                    //調用用戶媒體設備, 訪問攝像頭
                    getUserMedia({
                            video: {
                                width: {
                                    ideal: videoWidth,
                                    max: videoWidth
                                },
                                height: {
                                    ideal: videoHeight,
                                    max: videoHeight
                                },
                                facingMode: 'user', //前置攝像頭
                                // facingMode: { exact: "environment" }, //后置攝像頭
                                frameRate: {
                                    ideal: 30,
                                    min: 10
                                }
                            }
                        },
                        videoSuccess,
                        videoError
                    );
                } else {}
            }, 300);
        }
        // 獲取用戶設備
        function getUserMedia(constraints, success, error) {
            if (navigator.mediaDevices.getUserMedia) {
                //最新的標準API
                navigator.mediaDevices
                    .getUserMedia(constraints)
                    .then(success)
                    .catch(error);
            } else if (navigator.webkitGetUserMedia) {
                //webkit核心瀏覽器
                navigator.webkitGetUserMedia(constraints, success, error);
            } else if (navigator.mozGetUserMedia) {
                //firfox瀏覽器
                navigator.mozGetUserMedia(constraints, success, error);
            } else if (navigator.getUserMedia) {
                //舊版API
                navigator.getUserMedia(constraints, success, error);
            }
        }
        // 開始有畫面
        function videoSuccess(stream) {
            //this.mediaStreamTrack = stream;
            console.log("=====stream")
            video.srcObject = stream;
            video.play();
            //$("#max-bg").css('background-color', 'rgba(0,0,0,0)')
            // 這里處理我的的東西
        }

        function videoError(error) {
            alert('攝像頭獲取錯誤')
            console.log('攝像頭獲取錯誤')
            setTimeout(() => {
                initVideo()
            }, 6000)
        }
        // 單次拍照
        function getFaceImgBase64() {
            canvas = document.getElementById('canvas');
            //繪制canvas圖形
            canvas.getContext('2d').drawImage(video, 0, 0, 300, 300);
            //把canvas圖像轉為img圖片
            var bdata = canvas.toDataURL("image/jpeg");
            //img.src = canvas.toDataURL("image/png");
            return bdata.split(',')[1]; //照片壓縮成base位數據
        }
        $('#start').on('click', function() {
            time = 0;
            console.log("開始人臉采集,請正對屏幕");
            faceGather();
        })
        // 人臉采集
        function faceGather() {
            const faceImgBase64 = getFaceImgBase64();
            console.log(faceImgBase64);
        }
    </script>
</html>

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 223,924評論 6 521
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 95,776評論 3 402
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 170,954評論 0 366
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,576評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 69,589評論 6 399
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 53,105評論 1 314
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 41,480評論 3 427
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 40,457評論 0 278
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 46,998評論 1 324
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 39,026評論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 41,174評論 1 354
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 36,812評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 42,502評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,979評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 34,101評論 1 275
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 49,674評論 3 380
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 46,204評論 2 363

推薦閱讀更多精彩內容