123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205 |
- <!DOCTYPE html>
- <html>
- <head>
- <script async src="../../opencv.js" type="text/javascript"></script>
- <script src="../../utils.js" type="text/javascript"></script>
- <script type='text/javascript'>
- var netDet = undefined, netRecogn = undefined;
- var persons = {};
- //! [Run face detection model]
- function detectFaces(img) {
- var blob = cv.blobFromImage(img, 1, {width: 192, height: 144}, [104, 117, 123, 0], false, false);
- netDet.setInput(blob);
- var out = netDet.forward();
- var faces = [];
- for (var i = 0, n = out.data32F.length; i < n; i += 7) {
- var confidence = out.data32F[i + 2];
- var left = out.data32F[i + 3] * img.cols;
- var top = out.data32F[i + 4] * img.rows;
- var right = out.data32F[i + 5] * img.cols;
- var bottom = out.data32F[i + 6] * img.rows;
- left = Math.min(Math.max(0, left), img.cols - 1);
- right = Math.min(Math.max(0, right), img.cols - 1);
- bottom = Math.min(Math.max(0, bottom), img.rows - 1);
- top = Math.min(Math.max(0, top), img.rows - 1);
- if (confidence > 0.5 && left < right && top < bottom) {
- faces.push({x: left, y: top, width: right - left, height: bottom - top})
- }
- }
- blob.delete();
- out.delete();
- return faces;
- };
- //! [Run face detection model]
- //! [Get 128 floating points feature vector]
- function face2vec(face) {
- var blob = cv.blobFromImage(face, 1.0 / 255, {width: 96, height: 96}, [0, 0, 0, 0], true, false)
- netRecogn.setInput(blob);
- var vec = netRecogn.forward();
- blob.delete();
- return vec;
- };
- //! [Get 128 floating points feature vector]
- //! [Recognize]
- function recognize(face) {
- var vec = face2vec(face);
- var bestMatchName = 'unknown';
- var bestMatchScore = 0.5; // Actually, the minimum is -1 but we use it as a threshold.
- for (name in persons) {
- var personVec = persons[name];
- var score = vec.dot(personVec);
- if (score > bestMatchScore) {
- bestMatchScore = score;
- bestMatchName = name;
- }
- }
- vec.delete();
- return bestMatchName;
- };
- //! [Recognize]
- function loadModels(callback) {
- var utils = new Utils('');
- var proto = 'https://raw.githubusercontent.com/opencv/opencv/4.x/samples/dnn/face_detector/deploy_lowres.prototxt';
- var weights = 'https://raw.githubusercontent.com/opencv/opencv_3rdparty/dnn_samples_face_detector_20180205_fp16/res10_300x300_ssd_iter_140000_fp16.caffemodel';
- var recognModel = 'https://raw.githubusercontent.com/pyannote/pyannote-data/master/openface.nn4.small2.v1.t7';
- utils.createFileFromUrl('face_detector.prototxt', proto, () => {
- document.getElementById('status').innerHTML = 'Downloading face_detector.caffemodel';
- utils.createFileFromUrl('face_detector.caffemodel', weights, () => {
- document.getElementById('status').innerHTML = 'Downloading OpenFace model';
- utils.createFileFromUrl('face_recognition.t7', recognModel, () => {
- document.getElementById('status').innerHTML = '';
- netDet = cv.readNetFromCaffe('face_detector.prototxt', 'face_detector.caffemodel');
- netRecogn = cv.readNetFromTorch('face_recognition.t7');
- callback();
- });
- });
- });
- };
- function main() {
- // Create a camera object.
- var output = document.getElementById('output');
- var camera = document.createElement("video");
- camera.setAttribute("width", output.width);
- camera.setAttribute("height", output.height);
- // Get a permission from user to use a camera.
- navigator.mediaDevices.getUserMedia({video: true, audio: false})
- .then(function(stream) {
- camera.srcObject = stream;
- camera.onloadedmetadata = function(e) {
- camera.play();
- };
- });
- //! [Open a camera stream]
- var cap = new cv.VideoCapture(camera);
- var frame = new cv.Mat(camera.height, camera.width, cv.CV_8UC4);
- var frameBGR = new cv.Mat(camera.height, camera.width, cv.CV_8UC3);
- //! [Open a camera stream]
- //! [Add a person]
- document.getElementById('addPersonButton').onclick = function() {
- var rects = detectFaces(frameBGR);
- if (rects.length > 0) {
- var face = frameBGR.roi(rects[0]);
- var name = prompt('Say your name:');
- var cell = document.getElementById("targetNames").insertCell(0);
- cell.innerHTML = name;
- persons[name] = face2vec(face).clone();
- var canvas = document.createElement("canvas");
- canvas.setAttribute("width", 96);
- canvas.setAttribute("height", 96);
- var cell = document.getElementById("targetImgs").insertCell(0);
- cell.appendChild(canvas);
- var faceResized = new cv.Mat(canvas.height, canvas.width, cv.CV_8UC3);
- cv.resize(face, faceResized, {width: canvas.width, height: canvas.height});
- cv.cvtColor(faceResized, faceResized, cv.COLOR_BGR2RGB);
- cv.imshow(canvas, faceResized);
- faceResized.delete();
- }
- };
- //! [Add a person]
- //! [Define frames processing]
- var isRunning = false;
- const FPS = 30; // Target number of frames processed per second.
- function captureFrame() {
- var begin = Date.now();
- cap.read(frame); // Read a frame from camera
- cv.cvtColor(frame, frameBGR, cv.COLOR_RGBA2BGR);
- var faces = detectFaces(frameBGR);
- faces.forEach(function(rect) {
- cv.rectangle(frame, {x: rect.x, y: rect.y}, {x: rect.x + rect.width, y: rect.y + rect.height}, [0, 255, 0, 255]);
- var face = frameBGR.roi(rect);
- var name = recognize(face);
- cv.putText(frame, name, {x: rect.x, y: rect.y}, cv.FONT_HERSHEY_SIMPLEX, 1.0, [0, 255, 0, 255]);
- });
- cv.imshow(output, frame);
- // Loop this function.
- if (isRunning) {
- var delay = 1000 / FPS - (Date.now() - begin);
- setTimeout(captureFrame, delay);
- }
- };
- //! [Define frames processing]
- document.getElementById('startStopButton').onclick = function toggle() {
- if (isRunning) {
- isRunning = false;
- document.getElementById('startStopButton').innerHTML = 'Start';
- document.getElementById('addPersonButton').disabled = true;
- } else {
- function run() {
- isRunning = true;
- captureFrame();
- document.getElementById('startStopButton').innerHTML = 'Stop';
- document.getElementById('startStopButton').disabled = false;
- document.getElementById('addPersonButton').disabled = false;
- }
- if (netDet == undefined || netRecogn == undefined) {
- document.getElementById('startStopButton').disabled = true;
- loadModels(run); // Load models and run a pipeline;
- } else {
- run();
- }
- }
- };
- document.getElementById('startStopButton').disabled = false;
- };
- </script>
- </head>
- <body onload="cv['onRuntimeInitialized']=()=>{ main() }">
- <button id="startStopButton" type="button" disabled="true">Start</button>
- <div id="status"></div>
- <canvas id="output" width=640 height=480 style="max-width: 100%"></canvas>
- <table>
- <tr id="targetImgs"></tr>
- <tr id="targetNames"></tr>
- </table>
- <button id="addPersonButton" type="button" disabled="true">Add a person</button>
- </body>
- </html>
|