Skip to content

Instantly share code, notes, and snippets.

@ajaymdesai
Created September 29, 2018 03:51
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ajaymdesai/6684875c681e4f6b177c43276c9e3c63 to your computer and use it in GitHub Desktop.
Save ajaymdesai/6684875c681e4f6b177c43276c9e3c63 to your computer and use it in GitHub Desktop.
OpenCV.js Face Detection (WebAssembly)
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<body>
<div id="container">
<canvas class="center-block" id="canvasOutput" width=320 height=240></canvas>
</div>
<div class="text-center">
<input type="checkbox" id="face" name="classifier" value="face" checked>
<label for="face">face</label>
<input type="checkbox" id="eye" name="cascade" value="eye">
<label for="eye">eye</label>
</div>
<div class="invisible">
<video id="video" class="hidden">Your browser does not support the video tag.</video>
</div>
</div>
</body>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
<script src="https://threejs.org/examples/js/libs/stats.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.5/dat.gui.min.js"></script>
<script>
var Module = {
wasmBinaryFile: 'https://huningxin.github.io/opencv.js/build/wasm/opencv_js.wasm',
preRun: [function() {
Module.FS_createPreloadedFile('/', 'haarcascade_eye.xml', 'https://raw.githubusercontent.com/opencv/opencv/master/data/haarcascades/haarcascade_eye.xml', true, false);
Module.FS_createPreloadedFile('/', 'haarcascade_frontalface_default.xml', 'https://raw.githubusercontent.com/opencv/opencv/master/data/haarcascades/haarcascade_frontalface_default.xml', true, false);
Module.FS_createPreloadedFile('/', 'haarcascade_profileface.xml', 'https://raw.githubusercontent.com/opencv/opencv/master/data/haarcascades/haarcascade_profileface.xml', true, false);
}],
_main: function() {opencvIsReady();}
};
</script>
<script async src="https://huningxin.github.io/opencv.js/build/wasm/opencv.js"></script>
let videoWidth, videoHeight;
// whether streaming video from the camera.
let streaming = false;
let video = document.getElementById('video');
let canvasOutput = document.getElementById('canvasOutput');
let canvasOutputCtx = canvasOutput.getContext('2d');
let stream = null;
let detectFace = document.getElementById('face');
let detectEye = document.getElementById('eye');
function startCamera() {
if (streaming) return;
navigator.mediaDevices.getUserMedia({video: true, audio: false})
.then(function(s) {
stream = s;
video.srcObject = s;
video.play();
})
.catch(function(err) {
console.log("An error occured! " + err);
});
video.addEventListener("canplay", function(ev){
if (!streaming) {
videoWidth = video.videoWidth;
videoHeight = video.videoHeight;
video.setAttribute("width", videoWidth);
video.setAttribute("height", videoHeight);
canvasOutput.width = videoWidth;
canvasOutput.height = videoHeight;
streaming = true;
}
startVideoProcessing();
}, false);
}
let faceClassifier = null;
let eyeClassifier = null;
let src = null;
let dstC1 = null;
let dstC3 = null;
let dstC4 = null;
let canvasInput = null;
let canvasInputCtx = null;
let canvasBuffer = null;
let canvasBufferCtx = null;
function startVideoProcessing() {
if (!streaming) { console.warn("Please startup your webcam"); return; }
stopVideoProcessing();
canvasInput = document.createElement('canvas');
canvasInput.width = videoWidth;
canvasInput.height = videoHeight;
canvasInputCtx = canvasInput.getContext('2d');
canvasBuffer = document.createElement('canvas');
canvasBuffer.width = videoWidth;
canvasBuffer.height = videoHeight;
canvasBufferCtx = canvasBuffer.getContext('2d');
srcMat = new cv.Mat(videoHeight, videoWidth, cv.CV_8UC4);
grayMat = new cv.Mat(videoHeight, videoWidth, cv.CV_8UC1);
faceClassifier = new cv.CascadeClassifier();
faceClassifier.load('haarcascade_frontalface_default.xml');
eyeClassifier = new cv.CascadeClassifier();
eyeClassifier.load('haarcascade_eye.xml');
requestAnimationFrame(processVideo);
}
function processVideo() {
stats.begin();
canvasInputCtx.drawImage(video, 0, 0, videoWidth, videoHeight);
let imageData = canvasInputCtx.getImageData(0, 0, videoWidth, videoHeight);
srcMat.data.set(imageData.data);
cv.cvtColor(srcMat, grayMat, cv.COLOR_RGBA2GRAY);
let faces = [];
let eyes = [];
let size;
if (detectFace.checked) {
let faceVect = new cv.RectVector();
let faceMat = new cv.Mat();
if (detectEye.checked) {
cv.pyrDown(grayMat, faceMat);
size = faceMat.size();
} else {
cv.pyrDown(grayMat, faceMat);
cv.pyrDown(faceMat, faceMat);
size = faceMat.size();
}
faceClassifier.detectMultiScale(faceMat, faceVect);
for (let i = 0; i < faceVect.size(); i++) {
let face = faceVect.get(i);
faces.push(new cv.Rect(face.x, face.y, face.width, face.height));
if (detectEye.checked) {
let eyeVect = new cv.RectVector();
let eyeMat = faceMat.getRoiRect(face);
eyeClassifier.detectMultiScale(eyeMat, eyeVect);
for (let i = 0; i < eyeVect.size(); i++) {
let eye = eyeVect.get(i);
eyes.push(new cv.Rect(face.x + eye.x, face.y + eye.y, eye.width, eye.height));
}
eyeMat.delete();
eyeVect.delete();
}
}
faceMat.delete();
faceVect.delete();
} else {
if (detectEye.checked) {
let eyeVect = new cv.RectVector();
let eyeMat = new cv.Mat();
cv.pyrDown(grayMat, eyeMat);
size = eyeMat.size();
eyeClassifier.detectMultiScale(eyeMat, eyeVect);
for (let i = 0; i < eyeVect.size(); i++) {
let eye = eyeVect.get(i);
eyes.push(new cv.Rect(eye.x, eye.y, eye.width, eye.height));
}
eyeMat.delete();
eyeVect.delete();
}
}
canvasOutputCtx.drawImage(canvasInput, 0, 0, videoWidth, videoHeight);
drawResults(canvasOutputCtx, faces, 'red', size);
drawResults(canvasOutputCtx, eyes, 'yellow', size);
stats.end();
requestAnimationFrame(processVideo);
}
function drawResults(ctx, results, color, size) {
for (let i = 0; i < results.length; ++i) {
let rect = results[i];
let xRatio = videoWidth/size.width;
let yRatio = videoHeight/size.height;
ctx.lineWidth = 3;
ctx.strokeStyle = color;
ctx.strokeRect(rect.x*xRatio, rect.y*yRatio, rect.width*xRatio, rect.height*yRatio);
}
}
function stopVideoProcessing() {
if (src != null && !src.isDeleted()) src.delete();
if (dstC1 != null && !dstC1.isDeleted()) dstC1.delete();
if (dstC3 != null && !dstC3.isDeleted()) dstC3.delete();
if (dstC4 != null && !dstC4.isDeleted()) dstC4.delete();
}
function stopCamera() {
if (!streaming) return;
stopVideoProcessing();
document.getElementById("canvasOutput").getContext("2d").clearRect(0, 0, width, height);
video.pause();
video.srcObject=null;
stream.getVideoTracks()[0].stop();
streaming = false;
}
function initUI() {
stats = new Stats();
stats.showPanel(0);
document.getElementById('container').appendChild(stats.dom);
}
function opencvIsReady() {
console.log('OpenCV.js is ready');
initUI();
startCamera();
}
canvas {
border: 1px solid black;
}
.invisible {
display: none;
}
.text-center {
text-align: center;
}
div {
margin: 10px;
}
.center-block {
display: block;
margin: auto;
}
label {
padding-right: 10px;
width: 25%;
vertical-align: top;
font: 16px 'Lucida Grande', sans-serif;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment