
本教程详细阐述了如何使用 face-api.js 在浏览器环境中实现稳定且准确的多目标人脸识别。针对常见的多人脸误识别问题,文章深入分析了 `labeledfacedescriptors` 和 `facematcher` 的正确构建与使用方法,确保每个已知人脸都能被独立且准确地识别,并提供了完整的 svelte 代码示例及优化建议。
在现代 Web 应用中集成人脸识别功能,face-api.js 是一个强大且易于使用的 JavaScript 库。然而,开发者在使用过程中常会遇到一个挑战:当系统需要识别多个已知人脸时,可能会出现所有检测到的人脸都被错误地标记为同一个人的情况,即使“未知”人脸能够被正确识别。本文将深入探讨这一问题,并提供一个健壮的解决方案,确保 face-api.js 能够准确地区分和识别画面中的多个人脸。
理解问题:单人识别与多目标误识别
问题的核心在于 face-api.js 中 LabeledFaceDescriptors 的构建方式。原始代码可能存在以下逻辑缺陷:
共享描述符数组: 在为每个已知客户(customer)生成 LabeledFaceDescriptors 时,可能错误地使用了同一个全局数组来存储所有客户的人脸描述符(descriptor)。FaceMatcher 初始化: 如果 FaceMatcher 使用了包含混合描述符的共享数组进行初始化,它将无法区分不同的人脸,因为它认为所有描述符都属于同一个“身份池”。
这导致的结果是,当摄像头前出现多个人脸时,face-api.js 虽然能检测到所有面部,但在进行匹配时,由于内部描述符的混淆,它会倾向于将所有已知面孔都识别为第一个或最接近的匹配项,从而出现“只识别一个人”的假象。
核心概念回顾:face-api.js 中的关键组件
要解决上述问题,我们需要正确理解 face-api.js 中的几个核心概念:
1. 人脸检测与特征提取
detectSingleFace() / detectAllFaces(): 用于从图像或视频中检测人脸。.withFaceLandmarks(): 提取人脸关键点,如眼睛、鼻子、嘴巴的位置。.withFaceDescriptor(): 基于人脸关键点生成一个高维向量(人脸描述符),该描述符是人脸的唯一数字指纹,用于后续的匹配。
2. LabeledFaceDescriptors:标签化人脸描述符
这是解决多目标识别问题的关键。faceapi.LabeledFaceDescriptors 类用于将一个特定的人脸描述符数组与一个唯一的标签(如人名)关联起来。它的构造函数是 new faceapi.LabeledFaceDescriptors(label: string, descriptors: Float32Array[])。
label: 标识这个人的名称。descriptors: 一个包含该人物多个人脸描述符的数组。通常,为了提高识别鲁棒性,我们会为同一个人提供多张不同角度或表情的照片,并从中提取多个描述符。
3. FaceMatcher:人脸匹配器
faceapi.FaceMatcher 类用于将一个未知人脸的描述符与一组已知的 LabeledFaceDescriptors 进行比较,找出最佳匹配。
初始化: new faceapi.FaceMatcher(labeledDescriptors: LabeledFaceDescriptors[], distanceThreshold?: number)。它接收一个 LabeledFaceDescriptors 对象的数组,其中每个对象代表一个已知人物。匹配: faceMatcher.findBestMatch(queryDescriptor: Float32Array)。它会计算查询描述符与所有已知描述符之间的距离,并返回距离最近的匹配项(FaceMatch 对象),包含匹配的标签和距离。distanceThreshold 用于判断匹配的严格程度,距离越小表示相似度越高。
解决方案:构建准确的 LabeledFaceDescriptors
解决多目标误识别问题的核心在于确保每个已知人物都拥有独立且正确关联的 LabeledFaceDescriptors 对象。
错误示例分析
在原始代码中,getLabeledFaceDescriptions 函数可能将所有客户的描述符都推送到一个名为 descriptions 的全局数组中。随后,在 map 函数内部,针对每个 customer 调用 new faceapi.LabeledFaceDescriptors(customer.name, descriptions)。此时,descriptions 数组已经包含了所有(或部分)客户的描述符,导致每个 LabeledFaceDescriptors 对象都引用了同一个混淆的描述符集合,从而使 FaceMatcher 无法区分不同的人。
正确实现方式
我们应该为每个客户创建一个独立的描述符数组,并用该数组来构建其对应的 LabeledFaceDescriptors 对象。
// 假设 $customers 和 $baseURL 是 Svelte store 或可访问的变量// $customers 结构示例: [{ name: 'Customer A', image_url: '/path/to/a.jpg' }, ...]async function getLabeledFaceDescriptors(customers, baseURL) { const labeledDescriptors = await Promise.all( customers.map(async (customer) => { if (!customer.image_url) { console.warn(`Customer ${customer.name} has no image_url, skipping.`); return null; } const descriptorsForThisCustomer = []; // 为每个客户创建一个独立的描述符数组 // 可以根据需要从多张图片或同一图片的不同检测中获取多个描述符 // 这里假设每位客户有一张图片,从中提取一个描述符 try { const img = await faceapi.fetchImage(baseURL + customer.image_url); const detection = await faceapi .detectSingleFace(img) .withFaceLandmarks() .withFaceDescriptor(); if (detection && detection.descriptor) { descriptorsForThisCustomer.push(detection.descriptor); } else { console.warn(`No face detected for ${customer.name} from image: ${baseURL + customer.image_url}`); } } catch (error) { console.error(`Error processing image for ${customer.name}:`, error); return null; } if (descriptorsForThisCustomer.length > 0) { // 使用该客户专属的描述符数组创建 LabeledFaceDescriptors return new faceapi.LabeledFaceDescriptors( customer.name, descriptorsForThisCustomer ); } return null; }) ); // 过滤掉任何未能成功获取描述符的客户 return labeledDescriptors.filter((d) => d !== null);}
实现多目标人脸识别流程
以下是一个完整的 Svelte 组件示例,展示了如何在浏览器中实现多目标人脸识别。
1. 模型加载
在开始人脸识别之前,需要加载 face-api.js 所需的模型。
// Svelte script contextimport { onMount, onDestroy } from 'svelte';import * as faceapi from 'face-api.js';let video;let detections;let width = 640; // 调整为适合您的视频流尺寸let height = 480;let canvas, ctx;let container;let faceMatcher; // 全局 FaceMatcher 实例// 假设 $customers 和 $baseURL 是 Svelte store,需要在实际应用中注入// 例如: import { customers, baseURL } from './stores';// 或者通过 props 传递export let customers = []; // 假设通过 props 传递客户数据export let baseURL = ''; // 假设通过 props 传递基础 URLconst detectionOptions = { withLandmarks: true, withDescriptors: true, minConfidence: 0.5, MODEL_URLS: { Mobilenetv1Model: "https://raw.githubusercontent.com/ml5js/ml5-data-and-models/main/models/faceapi/ssd_mobilenetv1_model-weights_manifest.json", FaceLandmarkModel: "https://raw.githubusercontent.com/ml5js/ml5-data-and-models/main/models/faceapi/face_landmark_68_model-weights_manifest.json", FaceRecognitionModel: "https://raw.githubusercontent.com/ml5js/ml5-data-and-models/main/models/faceapi/face_recognition_model-weights_manifest.json", },};onDestroy(() => { if (video) { video.pause(); if (video.srcObject) { video.srcObject.getTracks().forEach(track => track.stop()); } video.srcObject = null; video.remove(); } if (canvas) { canvas.remove(); }});onMount(() => { initFaceRecognition();});// ... (getLabeledFaceDescriptors 函数如上所示) ...async function initFaceRecognition() { // 获取视频流 video = await getVideo(); // 创建画布 canvas = createCanvas(width, height); ctx = canvas.getContext('2d'); // 加载模型 await Promise.all([ faceapi.nets.ssdMobilenetv1.loadFromUri(detectionOptions.MODEL_URLS.Mobilenetv1Model), faceapi.nets.faceRecognitionNet.loadFromUri(detectionOptions.MODEL_URLS.FaceRecognitionModel), faceapi.nets.faceLandmark68Net.loadFromUri(detectionOptions.MODEL_URLS.FaceLandmarkModel),
以上就是face-api.js 浏览器人脸识别:精确识别多个人脸的实践指南的详细内容,更多请关注创想鸟其它相关文章!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1531027.html
微信扫一扫
支付宝扫一扫