js_face_recognition.html 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <script async src="../../opencv.js" type="text/javascript"></script>
  5. <script src="../../utils.js" type="text/javascript"></script>
  6. <script type='text/javascript'>
  7. var netDet = undefined, netRecogn = undefined;
  8. var persons = {};
  9. //! [Run face detection model]
  10. function detectFaces(img) {
  11. netDet.setInputSize(new cv.Size(img.cols, img.rows));
  12. var out = new cv.Mat();
  13. netDet.detect(img, out);
  14. var faces = [];
  15. for (var i = 0, n = out.data32F.length; i < n; i += 15) {
  16. var left = out.data32F[i];
  17. var top = out.data32F[i + 1];
  18. var right = (out.data32F[i] + out.data32F[i + 2]);
  19. var bottom = (out.data32F[i + 1] + out.data32F[i + 3]);
  20. left = Math.min(Math.max(0, left), img.cols - 1);
  21. top = Math.min(Math.max(0, top), img.rows - 1);
  22. right = Math.min(Math.max(0, right), img.cols - 1);
  23. bottom = Math.min(Math.max(0, bottom), img.rows - 1);
  24. if (left < right && top < bottom) {
  25. faces.push({
  26. x: left,
  27. y: top,
  28. width: right - left,
  29. height: bottom - top,
  30. x1: out.data32F[i + 4] < 0 || out.data32F[i + 4] > img.cols - 1 ? -1 : out.data32F[i + 4],
  31. y1: out.data32F[i + 5] < 0 || out.data32F[i + 5] > img.rows - 1 ? -1 : out.data32F[i + 5],
  32. x2: out.data32F[i + 6] < 0 || out.data32F[i + 6] > img.cols - 1 ? -1 : out.data32F[i + 6],
  33. y2: out.data32F[i + 7] < 0 || out.data32F[i + 7] > img.rows - 1 ? -1 : out.data32F[i + 7],
  34. x3: out.data32F[i + 8] < 0 || out.data32F[i + 8] > img.cols - 1 ? -1 : out.data32F[i + 8],
  35. y3: out.data32F[i + 9] < 0 || out.data32F[i + 9] > img.rows - 1 ? -1 : out.data32F[i + 9],
  36. x4: out.data32F[i + 10] < 0 || out.data32F[i + 10] > img.cols - 1 ? -1 : out.data32F[i + 10],
  37. y4: out.data32F[i + 11] < 0 || out.data32F[i + 11] > img.rows - 1 ? -1 : out.data32F[i + 11],
  38. x5: out.data32F[i + 12] < 0 || out.data32F[i + 12] > img.cols - 1 ? -1 : out.data32F[i + 12],
  39. y5: out.data32F[i + 13] < 0 || out.data32F[i + 13] > img.rows - 1 ? -1 : out.data32F[i + 13],
  40. confidence: out.data32F[i + 14]
  41. })
  42. }
  43. }
  44. out.delete();
  45. return faces;
  46. };
  47. //! [Run face detection model]
  48. //! [Get 128 floating points feature vector]
  49. function face2vec(face) {
  50. var blob = cv.blobFromImage(face, 1.0, {width: 112, height: 112}, [0, 0, 0, 0], true, false)
  51. netRecogn.setInput(blob);
  52. var vec = netRecogn.forward();
  53. blob.delete();
  54. return vec;
  55. };
  56. //! [Get 128 floating points feature vector]
  57. //! [Recognize]
  58. function recognize(face) {
  59. var vec = face2vec(face);
  60. var bestMatchName = 'unknown';
  61. var bestMatchScore = 30; // Threshold for face recognition.
  62. for (name in persons) {
  63. var personVec = persons[name];
  64. var score = vec.dot(personVec);
  65. if (score > bestMatchScore) {
  66. bestMatchScore = score;
  67. bestMatchName = name;
  68. }
  69. }
  70. vec.delete();
  71. return bestMatchName;
  72. };
  73. //! [Recognize]
  74. function loadModels(callback) {
  75. var utils = new Utils('');
  76. var detectModel = 'https://media.githubusercontent.com/media/opencv/opencv_zoo/main/models/face_detection_yunet/face_detection_yunet_2023mar.onnx';
  77. var recognModel = 'https://media.githubusercontent.com/media/opencv/opencv_zoo/main/models/face_recognition_sface/face_recognition_sface_2021dec.onnx';
  78. document.getElementById('status').innerHTML = 'Downloading YuNet model';
  79. utils.createFileFromUrl('face_detection_yunet_2023mar.onnx', detectModel, () => {
  80. document.getElementById('status').innerHTML = 'Downloading OpenFace model';
  81. utils.createFileFromUrl('face_recognition_sface_2021dec.onnx', recognModel, () => {
  82. document.getElementById('status').innerHTML = '';
  83. netDet = new cv.FaceDetectorYN("face_detection_yunet_2023mar.onnx", "", new cv.Size(320, 320), 0.9, 0.3, 5000);
  84. netRecogn = cv.readNet('face_recognition_sface_2021dec.onnx');
  85. callback();
  86. });
  87. });
  88. };
  89. function main() {
  90. if(!cv.FaceDetectorYN){
  91. alert(`Error: This sample require OpenCV.js built with FaceDetectorYN. Please rebuild it with FaceDetectorYN or use the latest version of OpenCV.js.`);
  92. return;
  93. }
  94. // Create a camera object.
  95. var output = document.getElementById('output');
  96. var camera = document.createElement("video");
  97. camera.setAttribute("width", output.width);
  98. camera.setAttribute("height", output.height);
  99. // Get a permission from user to use a camera.
  100. navigator.mediaDevices.getUserMedia({video: true, audio: false})
  101. .then(function(stream) {
  102. camera.srcObject = stream;
  103. camera.onloadedmetadata = function(e) {
  104. camera.play();
  105. };
  106. });
  107. //! [Open a camera stream]
  108. var cap = new cv.VideoCapture(camera);
  109. var frame = new cv.Mat(camera.height, camera.width, cv.CV_8UC4);
  110. var frameBGR = new cv.Mat(camera.height, camera.width, cv.CV_8UC3);
  111. //! [Open a camera stream]
  112. //! [Add a person]
  113. document.getElementById('addPersonButton').onclick = function() {
  114. var rects = detectFaces(frameBGR);
  115. if (rects.length > 0) {
  116. var face = frameBGR.roi(rects[0]);
  117. var name = prompt('Say your name:');
  118. var cell = document.getElementById("targetNames").insertCell(0);
  119. cell.innerHTML = name;
  120. persons[name] = face2vec(face).clone();
  121. var canvas = document.createElement("canvas");
  122. canvas.setAttribute("width", 112);
  123. canvas.setAttribute("height", 112);
  124. var cell = document.getElementById("targetImgs").insertCell(0);
  125. cell.appendChild(canvas);
  126. var faceResized = new cv.Mat(canvas.height, canvas.width, cv.CV_8UC3);
  127. cv.resize(face, faceResized, {width: canvas.width, height: canvas.height});
  128. cv.cvtColor(faceResized, faceResized, cv.COLOR_BGR2RGB);
  129. cv.imshow(canvas, faceResized);
  130. faceResized.delete();
  131. }
  132. };
  133. //! [Add a person]
  134. //! [Define frames processing]
  135. var isRunning = false;
  136. const FPS = 30; // Target number of frames processed per second.
  137. function captureFrame() {
  138. var begin = Date.now();
  139. cap.read(frame); // Read a frame from camera
  140. cv.cvtColor(frame, frameBGR, cv.COLOR_RGBA2BGR);
  141. var faces = detectFaces(frameBGR);
  142. faces.forEach(function(rect) {
  143. cv.rectangle(frame, {x: rect.x, y: rect.y}, {x: rect.x + rect.width, y: rect.y + rect.height}, [0, 255, 0, 255]);
  144. if(rect.x1>0 && rect.y1>0)
  145. cv.circle(frame, {x: rect.x1, y: rect.y1}, 2, [255, 0, 0, 255], 2)
  146. if(rect.x2>0 && rect.y2>0)
  147. cv.circle(frame, {x: rect.x2, y: rect.y2}, 2, [0, 0, 255, 255], 2)
  148. if(rect.x3>0 && rect.y3>0)
  149. cv.circle(frame, {x: rect.x3, y: rect.y3}, 2, [0, 255, 0, 255], 2)
  150. if(rect.x4>0 && rect.y4>0)
  151. cv.circle(frame, {x: rect.x4, y: rect.y4}, 2, [255, 0, 255, 255], 2)
  152. if(rect.x5>0 && rect.y5>0)
  153. cv.circle(frame, {x: rect.x5, y: rect.y5}, 2, [0, 255, 255, 255], 2)
  154. var face = frameBGR.roi(rect);
  155. var name = recognize(face);
  156. cv.putText(frame, name, {x: rect.x, y: rect.y}, cv.FONT_HERSHEY_SIMPLEX, 1.0, [0, 255, 0, 255]);
  157. });
  158. cv.imshow(output, frame);
  159. // Loop this function.
  160. if (isRunning) {
  161. var delay = 1000 / FPS - (Date.now() - begin);
  162. setTimeout(captureFrame, delay);
  163. }
  164. };
  165. //! [Define frames processing]
  166. document.getElementById('startStopButton').onclick = function toggle() {
  167. if (isRunning) {
  168. isRunning = false;
  169. document.getElementById('startStopButton').innerHTML = 'Start';
  170. document.getElementById('addPersonButton').disabled = true;
  171. } else {
  172. function run() {
  173. isRunning = true;
  174. captureFrame();
  175. document.getElementById('startStopButton').innerHTML = 'Stop';
  176. document.getElementById('startStopButton').disabled = false;
  177. document.getElementById('addPersonButton').disabled = false;
  178. }
  179. if (netDet == undefined || netRecogn == undefined) {
  180. document.getElementById('startStopButton').disabled = true;
  181. loadModels(run); // Load models and run a pipeline;
  182. } else {
  183. run();
  184. }
  185. }
  186. };
  187. document.getElementById('startStopButton').disabled = false;
  188. };
  189. </script>
  190. </head>
  191. <body onload="cv['onRuntimeInitialized']=()=>{ main() }">
  192. <button id="startStopButton" type="button" disabled="true">Start</button>
  193. <div id="status"></div>
  194. <canvas id="output" width=640 height=480 style="max-width: 100%"></canvas>
  195. <table>
  196. <tr id="targetImgs"></tr>
  197. <tr id="targetNames"></tr>
  198. </table>
  199. <button id="addPersonButton" type="button" disabled="true">Add a person</button>
  200. </body>
  201. </html>