index.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. /**
  2. * FaceAPI Demo for Browsers
  3. * Loaded via `index.html`
  4. */
  5. import * as faceapi from '../dist/face-api.esm.js'; // use when in dev mode
  6. // import * as faceapi from '@vladmandic/face-api'; // use when downloading face-api as npm
  7. // configuration options
  8. const modelPath = '../model/'; // path to model folder that will be loaded using http
  9. // const modelPath = 'https://cdn.jsdelivr.net/npm/@vladmandic/face-api/model/'; // path to model folder that will be loaded using http
  10. const imgSize = 800; // maximum image size in pixels
  11. const minScore = 0.3; // minimum score
  12. const maxResults = 10; // maximum number of results to return
  13. const samples = ['sample1.jpg', 'sample2.jpg', 'sample3.jpg', 'sample4.jpg', 'sample5.jpg', 'sample6.jpg']; // sample images to be loaded using http
  14. // helper function to pretty-print json object to string
  15. const str = (json) => (json ? JSON.stringify(json).replace(/{|}|"|\[|\]/g, '').replace(/,/g, ', ') : '');
  16. // helper function to print strings to html document as a log
  17. function log(...txt) {
  18. console.log(...txt); // eslint-disable-line no-console
  19. const div = document.getElementById('log');
  20. if (div) div.innerHTML += `<br>${txt}`;
  21. }
  22. // helper function to draw detected faces
  23. function faces(name, title, id, data) {
  24. // create canvas to draw on
  25. const img = document.getElementById(id);
  26. if (!img) return;
  27. const canvas = document.createElement('canvas');
  28. canvas.style.position = 'absolute';
  29. canvas.style.left = `${img.offsetLeft}px`;
  30. canvas.style.top = `${img.offsetTop}px`;
  31. canvas.width = img.width;
  32. canvas.height = img.height;
  33. const ctx = canvas.getContext('2d', { willReadFrequently: true });
  34. if (!ctx) return;
  35. // draw title
  36. ctx.font = '1rem sans-serif';
  37. ctx.fillStyle = 'black';
  38. ctx.fillText(name, 2, 15);
  39. ctx.fillText(title, 2, 35);
  40. for (const person of data) {
  41. // draw box around each face
  42. ctx.lineWidth = 3;
  43. ctx.strokeStyle = 'deepskyblue';
  44. ctx.fillStyle = 'deepskyblue';
  45. ctx.globalAlpha = 0.4;
  46. ctx.beginPath();
  47. ctx.rect(person.detection.box.x, person.detection.box.y, person.detection.box.width, person.detection.box.height);
  48. ctx.stroke();
  49. // draw text labels
  50. ctx.globalAlpha = 1;
  51. ctx.fillText(`${Math.round(100 * person.genderProbability)}% ${person.gender}`, person.detection.box.x, person.detection.box.y - 18);
  52. ctx.fillText(`${Math.round(person.age)} years`, person.detection.box.x, person.detection.box.y - 2);
  53. // draw face points for each face
  54. ctx.fillStyle = 'lightblue';
  55. ctx.globalAlpha = 0.5;
  56. const pointSize = 2;
  57. for (const pt of person.landmarks.positions) {
  58. ctx.beginPath();
  59. ctx.arc(pt.x, pt.y, pointSize, 0, 2 * Math.PI);
  60. ctx.fill();
  61. }
  62. }
  63. // add canvas to document
  64. document.body.appendChild(canvas);
  65. }
  66. // helper function to draw processed image and its results
  67. function print(title, img, data) {
  68. console.log('Results:', title, img, data); // eslint-disable-line no-console
  69. const el = new Image();
  70. el.id = Math.floor(Math.random() * 100000).toString();
  71. el.src = img;
  72. el.width = imgSize;
  73. el.onload = () => faces(img, title, el.id, data);
  74. document.body.appendChild(el);
  75. }
  76. // loads image and draws it on resized canvas so we alwys have correct image size regardless of source
  77. async function image(url) {
  78. return new Promise((resolve) => {
  79. const img = new Image();
  80. // wait until image is actually loaded
  81. img.addEventListener('load', () => {
  82. // resize image so larger axis is not bigger than limit
  83. const ratio = 1.0 * img.height / img.width;
  84. img.width = ratio <= 1 ? imgSize : 1.0 * imgSize / ratio;
  85. img.height = ratio >= 1 ? imgSize : 1.0 * imgSize * ratio;
  86. // create canvas and draw loaded image
  87. const canvas = document.createElement('canvas');
  88. canvas.height = img.height;
  89. canvas.width = img.width;
  90. const ctx = canvas.getContext('2d', { willReadFrequently: true });
  91. if (ctx) ctx.drawImage(img, 0, 0, img.width, img.height);
  92. // return generated canvas to be used by tfjs during detection
  93. resolve(canvas);
  94. });
  95. // load image
  96. img.src = url;
  97. });
  98. }
  99. async function main() {
  100. // initialize tfjs
  101. log('FaceAPI Test');
  102. // if you want to use wasm backend location for wasm binaries must be specified
  103. // await faceapi.tf?.setWasmPaths(`https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-backend-wasm@${faceapi.tf.version_core}/dist/`);
  104. // await faceapi.tf?.setBackend('wasm');
  105. // log(`WASM SIMD: ${await faceapi.tf?.env().getAsync('WASM_HAS_SIMD_SUPPORT')} Threads: ${await faceapi.tf?.env().getAsync('WASM_HAS_MULTITHREAD_SUPPORT') ? 'Multi' : 'Single'}`);
  106. // default is webgl backend
  107. await faceapi.tf.setBackend('webgl');
  108. await faceapi.tf.ready();
  109. // tfjs optimizations
  110. if (faceapi.tf?.env().flagRegistry.CANVAS2D_WILL_READ_FREQUENTLY) faceapi.tf.env().set('CANVAS2D_WILL_READ_FREQUENTLY', true);
  111. if (faceapi.tf?.env().flagRegistry.WEBGL_EXP_CONV) faceapi.tf.env().set('WEBGL_EXP_CONV', true);
  112. if (faceapi.tf?.env().flagRegistry.WEBGL_EXP_CONV) faceapi.tf.env().set('WEBGL_EXP_CONV', true);
  113. await faceapi.tf.enableProdMode();
  114. await faceapi.tf.ready();
  115. // check version
  116. log(`Version: FaceAPI ${str(faceapi?.version || '(not loaded)')} TensorFlow/JS ${str(faceapi?.tf?.version_core || '(not loaded)')} Backend: ${str(faceapi?.tf?.getBackend() || '(not loaded)')}`);
  117. log(`Flags: ${JSON.stringify(faceapi?.tf?.ENV.flags || { tf: 'not loaded' })}`);
  118. // load face-api models
  119. log('Loading FaceAPI models');
  120. await faceapi.nets.tinyFaceDetector.load(modelPath);
  121. await faceapi.nets.ssdMobilenetv1.load(modelPath);
  122. await faceapi.nets.ageGenderNet.load(modelPath);
  123. await faceapi.nets.faceLandmark68Net.load(modelPath);
  124. await faceapi.nets.faceRecognitionNet.load(modelPath);
  125. await faceapi.nets.faceExpressionNet.load(modelPath);
  126. const optionsTinyFace = new faceapi.TinyFaceDetectorOptions({ inputSize: imgSize, scoreThreshold: minScore });
  127. const optionsSSDMobileNet = new faceapi.SsdMobilenetv1Options({ minConfidence: minScore, maxResults });
  128. // check tf engine state
  129. const engine = await faceapi.tf.engine();
  130. log(`TF Engine State: ${str(engine.state)}`);
  131. // loop through all images and try to process them
  132. log(`Start processing: ${samples.length} images ...<br>`);
  133. for (const img of samples) {
  134. document.body.appendChild(document.createElement('br'));
  135. // load and resize image
  136. const canvas = await image(img);
  137. try {
  138. // actual model execution
  139. const dataTinyYolo = await faceapi
  140. // @ts-ignore
  141. .detectAllFaces(canvas, optionsTinyFace)
  142. .withFaceLandmarks()
  143. .withFaceExpressions()
  144. .withFaceDescriptors()
  145. .withAgeAndGender();
  146. // print results to screen
  147. print('TinyFace:', img, dataTinyYolo);
  148. // actual model execution
  149. const dataSSDMobileNet = await faceapi
  150. .detectAllFaces(canvas, optionsSSDMobileNet)
  151. .withFaceLandmarks()
  152. .withFaceExpressions()
  153. .withFaceDescriptors()
  154. .withAgeAndGender();
  155. // print results to screen
  156. print('SSDMobileNet:', img, dataSSDMobileNet);
  157. } catch (err) {
  158. log(`Image: ${img} Error during processing ${str(err)}`);
  159. }
  160. }
  161. }
  162. // start processing as soon as page is loaded
  163. window.onload = main;