node-match.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. /**
  2. * FaceAPI Demo for NodeJS
  3. * - Analyzes face descriptors from source (image file or folder containing multiple image files)
  4. * - Analyzes face descriptor from target
  5. * - Finds best match
  6. */
  7. const fs = require('fs');
  8. const path = require('path');
  9. const log = require('@vladmandic/pilogger');
  10. const tf = require('@tensorflow/tfjs-node'); // in nodejs environments tfjs-node is required to be loaded before face-api
  11. const faceapi = require('../dist/face-api.node.js'); // use this when using face-api in dev mode
  12. // const faceapi = require('@vladmandic/face-api'); // use this when face-api is installed as module (majority of use cases)
  13. let optionsSSDMobileNet;
  14. const minConfidence = 0.1;
  15. const distanceThreshold = 0.5;
  16. const modelPath = 'model';
  17. const labeledFaceDescriptors = [];
  18. async function initFaceAPI() {
  19. await faceapi.nets.ssdMobilenetv1.loadFromDisk(modelPath);
  20. await faceapi.nets.faceLandmark68Net.loadFromDisk(modelPath);
  21. await faceapi.nets.faceExpressionNet.loadFromDisk(modelPath);
  22. await faceapi.nets.faceRecognitionNet.loadFromDisk(modelPath);
  23. optionsSSDMobileNet = new faceapi.SsdMobilenetv1Options({ minConfidence, maxResults: 1 });
  24. }
  25. async function getDescriptors(imageFile) {
  26. const buffer = fs.readFileSync(imageFile);
  27. const tensor = tf.node.decodeImage(buffer, 3);
  28. const faces = await faceapi.detectAllFaces(tensor, optionsSSDMobileNet)
  29. .withFaceLandmarks()
  30. .withFaceExpressions()
  31. .withFaceDescriptors();
  32. tf.dispose(tensor);
  33. return faces.map((face) => face.descriptor);
  34. }
  35. async function registerImage(inputFile) {
  36. if (!inputFile.toLowerCase().endsWith('jpg') && !inputFile.toLowerCase().endsWith('png') && !inputFile.toLowerCase().endsWith('gif')) return;
  37. log.data('Registered:', inputFile);
  38. const descriptors = await getDescriptors(inputFile);
  39. for (const descriptor of descriptors) {
  40. const labeledFaceDescriptor = new faceapi.LabeledFaceDescriptors(inputFile, [descriptor]);
  41. labeledFaceDescriptors.push(labeledFaceDescriptor);
  42. }
  43. }
  44. async function findBestMatch(inputFile) {
  45. const matcher = new faceapi.FaceMatcher(labeledFaceDescriptors, distanceThreshold);
  46. const descriptors = await getDescriptors(inputFile);
  47. const matches = [];
  48. for (const descriptor of descriptors) {
  49. const match = await matcher.findBestMatch(descriptor);
  50. matches.push(match);
  51. }
  52. return matches;
  53. }
  54. async function main() {
  55. log.header();
  56. if (process.argv.length !== 4) {
  57. log.error(process.argv[1], 'Expected <source image or folder> <target image>');
  58. process.exit(1);
  59. }
  60. await initFaceAPI();
  61. log.info('Input:', process.argv[2]);
  62. if (fs.statSync(process.argv[2]).isFile()) {
  63. await registerImage(process.argv[2]); // register image
  64. } else if (fs.statSync(process.argv[2]).isDirectory()) {
  65. const dir = fs.readdirSync(process.argv[2]);
  66. for (const f of dir) await registerImage(path.join(process.argv[2], f)); // register all images in a folder
  67. }
  68. log.info('Comparing:', process.argv[3], 'Descriptors:', labeledFaceDescriptors.length);
  69. if (labeledFaceDescriptors.length > 0) {
  70. const bestMatch = await findBestMatch(process.argv[3]); // find best match to all registered images
  71. log.data('Match:', bestMatch);
  72. } else {
  73. log.warn('No registered faces');
  74. }
  75. }
  76. main();