You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Angular+face-api.js的本地人脸匹配:代码无法返回匹配图片问题

修复face-api.js Angular应用无法返回匹配人脸图像的问题

你的代码存在两个关键问题导致无法返回匹配图像,以下是具体分析和修复方案:

问题1:匹配逻辑错误

在onUpload方法的匹配环节,你使用了错误的判断逻辑:

const matchedImageURLs = this.faceDescriptors
  .filter(descriptor => faceMatcher.findBestMatch(detection.descriptor).distance < threshold)
  .map(descriptor => descriptor.label);

faceMatcher.findBestMatch()会在所有已加载的本地人脸描述符中找出与上传图像最匹配的那一个,并返回它的距离。这意味着你遍历每个本地描述符时,都在使用同一个最佳匹配的距离做判断,导致要么所有本地图像都被判定为匹配,要么都不匹配,完全不符合预期。

正确的做法是逐个计算上传图像描述符与每个本地描述符的距离,再和阈值比较。可以直接使用face-api.js的工具方法手动计算欧氏距离。

问题2:缺失dataURLToBlob函数

代码中调用了this.dataURLToBlob()但没有实现这个方法,会导致图像转换失败,后续人脸检测流程无法执行。

修复后的完整代码

selectedFile: any;
localImages = ['assets/photo/face1.png', 'assets/photo/face2.png', 'assets/photo/faces.jpg'];
faceDescriptors: faceapi.LabeledFaceDescriptors[] = [];
images: string[] = [];

async ngOnInit() {
  await this.loadModels();
  await this.loadLocalImages();
}

async loadModels() {
  try {
    await faceapi.nets.ssdMobilenetv1.loadFromUri('/assets/models');
    await faceapi.nets.faceLandmark68Net.loadFromUri('/assets/models');
    await faceapi.nets.faceRecognitionNet.loadFromUri('/assets/models');
    console.log('Models loaded successfully');
  } catch (error) {
    console.error('Error loading models:', error);
  }
}

async loadLocalImages() {
  try {
    for (const imagePath of this.localImages) {
      const img = await faceapi.fetchImage(imagePath);
      const detections = await faceapi.detectSingleFace(img).withFaceLandmarks().withFaceDescriptor();
      if (detections) {
        const label = imagePath;
        this.faceDescriptors.push(new faceapi.LabeledFaceDescriptors(label, [detections.descriptor]));
        console.log(`Loaded descriptor for ${label}:`, detections.descriptor);
      } else {
        console.log(`No face detected in ${imagePath}`);
      }
    }
    console.log('Local images loaded and processed:', this.faceDescriptors);
  } catch (error) {
    console.error('Error loading local images:', error);
  }
}

onFileSelected(event: any) {
  this.selectedFile = event.target.files[0];
  console.log('File selected:', this.selectedFile);
}

// 补充实现dataURLToBlob函数
private dataURLToBlob(dataURL: string): Blob | null {
  const parts = dataURL.split(';base64,');
  if (parts.length !== 2) return null;
  
  const contentType = parts[0].split(':')[1];
  const raw = window.atob(parts[1]);
  const rawLength = raw.length;
  const uInt8Array = new Uint8Array(rawLength);
  
  for (let i = 0; i < rawLength; ++i) {
    uInt8Array[i] = raw.charCodeAt(i);
  }
  
  return new Blob([uInt8Array], { type: contentType });
}

async onUpload() {
  if (this.selectedFile) {
    const reader = new FileReader();
    reader.onload = async () => {
      try {
        const imageBlob = this.dataURLToBlob(reader.result as string);
        if (imageBlob) {
          const img = await faceapi.bufferToImage(imageBlob);
          const detection = await faceapi.detectSingleFace(img).withFaceLandmarks().withFaceDescriptor();
          if (detection) {
            console.log('Uploaded image descriptor:', detection.descriptor);
            const threshold = 0.6;
            const matchedImageURLs: string[] = [];

            // 逐个匹配本地描述符与上传图像描述符
            this.faceDescriptors.forEach(labeledDescriptor => {
              // 计算当前描述符与上传图像的距离
              const distance = faceapi.euclideanDistance(detection.descriptor, labeledDescriptor.descriptors[0]);
              if (distance < threshold) {
                matchedImageURLs.push(labeledDescriptor.label);
              }
            });

            if (matchedImageURLs.length > 0) {
              console.log('Matched image URLs:', matchedImageURLs);
              this.images = matchedImageURLs;
            } else {
              this.images = [];
              console.log('No exact matches found.');
            }
          } else {
            this.images = [];
            console.log('No face detected in the uploaded image.');
          }
        } else {
          this.images = [];
          console.error('Failed to convert Data URL to Blob.');
        }
      } catch (error) {
        console.error('Error processing uploaded image:', error);
      }
    };
    reader.readAsDataURL(this.selectedFile);
  }
}

关键修改说明

  1. 补充dataURLToBlob函数:实现了DataURL到Blob的转换逻辑,确保图像能正常被face-api.js处理。
  2. 修正匹配逻辑:使用faceapi.euclideanDistance()逐个计算上传图像描述符与每个本地描述符的距离,再和阈值比较,筛选出真正匹配的图像。
  3. 可以根据实际场景调整threshold值(一般0.5-0.7之间效果较好),值越小匹配精度越高,值越大容错性越强。

内容的提问来源于stack exchange,提问作者Ganesh phadtare

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 14:06:00