用OpenCV在瀏覽器舉行人臉檢測

近來picojs上了Github Trending,這是一個玲瓏的人臉檢測庫,200行JS,2K大小,機能很好,結果也還還行。因而我想有沒其他的能在瀏覽器跑的人臉檢測庫,一查才發明OpenCV已支撐編譯到WebAssembly,也就能夠直接在瀏覽器里運用了。

《用OpenCV在瀏覽器舉行人臉檢測》

編譯OpenCV.js

裝置Emscripten SDK:

git clone https://github.com/juj/emsdk.git
cd emsdk
./emsdk update-tags
./emsdk install latest
./emsdk activate latest
source ./emsdk_env.sh

Emscripten能夠把C/C++順序編譯成asm.js,然後經由過程binaryen的asm2wasm轉成WebAssembly。

接着就能夠編譯OpenCV了:

wget https://github.com/opencv/opencv/archive/3.4.1.zip
unzip 3.4.1.zip
cd opencv-3.4.1
python ./platforms/js/build_js.py build_wasm --build_wasm

編譯的成果在build_wasm/bin

$ ls -lh build_wasm/bin/
total 5.5M
-rw-r--r-- 1 lyp lyp 263K Apr 26 22:10 opencv.js
-rw-r--r-- 1 lyp lyp 262K Apr 26 22:10 opencv_js.js
-rw-r--r-- 1 lyp lyp 5.0M Apr 26 22:10 opencv_js.wasm

我們只須要个中的opencv.jsopencv_js.wasm,能夠複製到其他地方運用,而opencv_js.js是中心天生的asm.js,能夠疏忽。

加載OpenCV

我們能夠直接在HTML頁面里援用opencv.js,它會自動加載opencv_js.wasm然後完成編譯。碰到的第一個題目是,opencv.js默許會加載根目次的opencv_js.wasm,而我們一般會把js文件放在二級目次里。第二個題目是,我們的代碼必須在OpenCV編譯完成以後才挪用,不會代碼就直接出錯了。

更新2018-08-20:在Emscripten v1.38.9locateFile行動已修正,不須要這個hack了。

為了處理以上的題目,要經由過程Module舉行設置:

<script>
var Module = {
    locateFile: function (name) {
        let files = {
            "opencv_js.wasm": '/opencv/opencv_js.wasm'
        }
        return files[name]
    },
    preRun: [() => {
        Module.FS_createPreloadedFile("/", "face.xml", "data/haarcascade_frontalface_default.xml",
            true, false);
    }],
    postRun: [
        run
    ]
};

</script>
<script async src="opencv/opencv.js"></script>

Module是Emscripten天生的全局對象,經由過程它能夠設置和挪用Emscripten的API。比方locateFile用設置文件的現實URL。

preRun會在初始化前前挪用,在這個時刻,OpenCV還沒初始化,我們能夠先用Emscripten的文件體系API預加載以後會用的文件,這裏我加載了一個預訓練好的模子data/haarcascade_frontalface_default.xml,寄存在Emscripten文件體系的”/face.xml”。

postRun會在初始化完成以後實行,這時刻OpenCV編譯完成,能夠運用cv模塊了。

獵取攝像頭圖象

<video  width="640" height="480" id="video" style="display:none"></video>
<canvas width="640" height="480" id="outputCanvas"></canvas>

起首我們須要一個video標籤,然後翻開攝像頭:

async function startCamera() {
    let video = document.getElementById("video");
    let stream = await navigator.mediaDevices.getUserMedia({
            video: {
                width: {
                    exact: videoWidth
                },
                height: {
                    exact: videoHeight
                }
            },
            audio: false
        })
    video.srcObject = stream;
    video.play();
}

然後我們就能夠用cv.VideoCapture來讀取攝像頭了:

// 建立VideoCapture
let cap = new cv.VideoCapture(video);
// 建立寄存圖象的Mat
let src = new cv.Mat(videoHeight, videoWidth, cv.CV_8UC4);
// 讀一幀圖象
cap.read(src);

Haar Cascades人臉檢測

建立人臉檢測器:

faceCascade = new cv.CascadeClassifier();
faceCascade.load("face.xml")

接着就能夠輪迴讀取圖象,搜檢人臉,顯現了:

// Capture a frame
cap.read(src)

// Convert to greyscale
cv.cvtColor(src, gray, cv.COLOR_RGBA2GRAY);

// Downsample
let downSampled = new cv.Mat();
cv.pyrDown(gray, downSampled);
cv.pyrDown(downSampled, downSampled);

// Detect faces
let faces = new cv.RectVector();
faceCascade.detectMultiScale(downSampled, faces)

// Draw boxes
let size = downSampled.size();
let xRatio = videoWidth / size.width;
let yRatio = videoHeight / size.height;
for (let i = 0; i < faces.size(); ++i) {
    let face = faces.get(i);
    let point1 = new cv.Point(face.x * xRatio, face.y * yRatio);
    let point2 = new cv.Point((face.x + face.width) * xRatio, (face.y + face.height) * xRatio);
    cv.rectangle(src, point1, point2, [255, 0, 0, 255])
}

// Show image
cv.imshow(outputCanvas, src)

// Cleanup
downSampled.delete()
faces.delete()

機能在30FPS擺布,結果要比picojs好,價值是須要加載很大的JS和wasm,初始化慢。

完全代碼:learn_ml/opencv.js

    原文作者:oraoto
    原文地址: https://segmentfault.com/a/1190000014639145
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞