视觉 · 人脸识别
1. 模块概述
- 主要功能:基于 ArcFace(MobileFaceNet)的人脸特征提取与相似度比对。输入一张 112×112 的人脸图片,输出高维特征向量(embedding),通过计算两个特征向量的余弦相似度判断是否为同一人。通常与人脸检测(YOLOv5-Face)配合使用。
- 规格或特性:
- 支持模型:ArcFace MobileFaceNet
- 输入尺寸:
[1, 3, 112, 112] - 量化类型:int8
- 输出:人脸特征向量(embedding)
- 相似度阈值:0.6(默认,可调)
- 推理后端:ONNX Runtime + SpaceMITExecutionProvider
- 接口形态:C++(
vision_service.h)、Python(spacemit_visionwheel:VisionServiceNative)
- 相关目录结构:
examples/arcface/
├── config/arcface.yaml # 配置文件
├── cpp/arcface.cpp # C++ 示例
├── python/arcface.py # Python 示例
└── scripts/ # 模型下载脚本
src/deploy/arcface/ # 部署实现(C++ / Python)
2. 环境准备
前置条件
SDK 源码获取和基础编译环境配置统一参考 2.3-构建编译。完成 SDK 初始化后,回到本文继续执行"构建编译"。
后续命令默认在 spacemit_robot SDK 根目录执行。
构建编译
系统缺少依赖时先安装:
sudo apt install python3-spacemit-ort opencv-spacemit libeigen3-dev spacemit-onnxruntime libyaml-cpp-dev
在 SDK 根目录加载构建环境后编译视觉组件:
source build/envsetup.sh
cd components/model_zoo/vision
mm
SDK 集成构建会把 arcface 等示例程序安装到 output/staging/bin,加载 build/envsetup.sh 后可直接运行。
运行 Python 示例前,需先安装 spacemit_vision wheel:
cd components/model_zoo/vision
python3 -m pip install -U pybind11 build setuptools wheel
cmake -S . -B build && cmake --build build -j
python3 -m pip install --force-reinstall src/python/dist/*.whl
python3 -c "from spacemit_vision import VisionServiceNative; print('ok')"
模型权重默认存放路径为 ~/.cache/models/vision/arcface/。须先手动执行下载脚本;缺失时程序会直接报错(Model file not found)。
3. 示例使用(从 0 跑通)
3.1 ArcFace 人脸识别(Python)
前置:见 §2。
步骤 1:下载模型
cd components/model_zoo/vision
bash examples/arcface/scripts/download_models.sh
预期现象:模型文件下载至 ~/.cache/models/vision/arcface/arcface_mobilefacenet_cut.q.onnx。
步骤 2:下载测试素材
bash scripts/download_assets.sh
预期现象:测试人脸图片下载至 ~/.cache/assets/image/003_face0.png 和 ~/.cache/assets/image/004_face1.png。
步骤 3:运行人脸比对
python3 examples/arcface/python/arcface.py --config examples/arcface/config/arcface.yaml
3.2 ArcFace 人脸识别(C++)
前置:见 §2,C++ 编译完成。
步骤 1:下载模型(同 §3.1 步骤 1)
步骤 2:运行人脸比对
arcface examples/arcface/config/arcface.yaml
3.3 运行结果示例
终端输出示例:
相似度: 0.1931
判断: 非同一人
4. 应用开发
本章面向应用开发者,说明如何在自己的 C++ 或 Python 应用中集成人脸识别组件。完整接口定义以 include/vision_service.h 和 src/python/spacemit_vision/vision_service_native.py 为准;本节介绍常用公开接口和典型调用方式。
4.1 接口说明
人脸识别组件基于 ArcFace 模型,通过提取人脸特征向量(embedding)并计算相似度来判断是否为同一人。应用侧需要先使用人脸检测模型定位人脸区域,裁剪对齐后再送入 ArcFace 提取特征。
4.1.1 常用数据结构
| 类型 | 说明 |
|---|---|
| vision::Embedding | 人脸特征向量结果,字段 std::vector<float> embedding(通常 128 维或 512 维)与 float score。 |
| VisionServiceResponse | 统一推理响应,包含 results(vision::ResultList,即 std::variant 列表)、ok、error_message。 |
| VisionServiceStatus | 接口返回的状态码枚举,VISION_SERVICE_OK 表示成功。 |
4.1.2 服务初始化
C++ 接口
| 接口 | 说明 | 参数 | 返回值 |
|---|---|---|---|
| VisionService::Create | 从 YAML 配置文件创建人脸识别服务实例 | config_path:YAML 配置文件路径 | VisionService 智能指针 |
| VisionService::LastCreateError | 获取最近一次创建失败的错误信息 | 无 | 错误描述字符串 |
Python 接口
| 接口 | 说明 | 参数 | 返回值 |
|---|---|---|---|
| VisionServiceNative.create | 从 YAML 配置文件创建人脸识别服务 | config_path:YAML 路径;model_path_override:可选覆盖模型路径 | VisionServiceNative 实例 |
| VisionServiceNative.last_create_error | 获取最近一次创建失败的错误信息 | 无 | 错误描述字符串 |
4.1.3 人脸识别
C++ 接口
| 接口 | 说明 | 参数 | 返回值 |
|---|---|---|---|
| Infer | 统一推理入口,对人脸图像提取特征(结果以 vision::Embedding 变体写入 response) | image_path:人脸图像文件路径;response:输出 VisionServiceResponse | VisionServiceStatus(VISION_SERVICE_OK 表示成功) |
| Infer | 统一推理入口,对 cv::Mat 图像提取特征 | image:OpenCV Mat 对象;response:输出 VisionServiceResponse | VisionServiceStatus(VISION_SERVICE_OK 表示成功) |
| EmbeddingSimilarity | 计算两个特征向量的余弦相似度(静态方法) | embedding_a:特征向量 A;embedding_b:特征向量 B | float(相似度,范围 -1 到 1) |
| LastError | 获取最近一次推理的错误信息 | 无 | 错误描述字符串 |
Python 接口
| 接口 | 说明 | 参数 | 返回值 |
|---|---|---|---|
| infer_embedding | 提取人脸特征向量 | image_or_path:BGR numpy 数组或图像路径 | (VisionServiceStatus, embedding 列表) |
| VisionServiceNative.embedding_similarity | 计算两个特征向量的余弦相似度(静态方法) | embedding_a、embedding_b:特征向量 | float(-1 到 1) |
| last_error | 获取最近一次推理错误 | 无 | 错误描述字符串 |
4.1.4 性能监控
C++ 接口
| 接口 | 说明 | 参数 | 返回值 |
|---|---|---|---|
| SetTimingOptions | 启用/禁用性能计时 | options:VisionServiceTimingOptions(设置 enabled 字段) | void |
| GetLastTiming | 获取最近一次推理的各阶段耗时 | 无 | VisionServiceTiming 结构体(包含 model_infer_ms、infer_ms 等字段) |
4.2 典型调用流程
4.2.1 C++ 人脸比对(1:1)
#include "vision_service.h"
#include <opencv2/opencv.hpp>
#include <variant>
#include <vector>
#include <iostream>
int main() {
// 1. 创建服务
auto service = VisionService::Create("examples/arcface/config/arcface.yaml");
if (!service) {
std::cerr << "Failed to create service: "
<< VisionService::LastCreateError() << std::endl;
return -1;
}
// 2. 启用性能计时(可选)
VisionServiceTimingOptions timing_options;
timing_options.enabled = true;
service->SetTimingOptions(timing_options);
// 3. 提取人脸特征:统一 Infer 接口,结果以 vision::Embedding 变体返回
auto extract_embedding = [&](const std::string& path,
std::vector<float>* out) -> bool {
VisionServiceResponse response;
if (service->Infer(path, &response) != VISION_SERVICE_OK) {
std::cerr << "Infer failed: " << service->LastError() << std::endl;
return false;
}
if (response.results.empty()) return false;
const vision::Embedding* emb =
std::get_if<vision::Embedding>(&response.results[0]);
if (emb == nullptr) return false;
*out = emb->embedding;
return true;
};
std::vector<float> embedding_a, embedding_b;
if (!extract_embedding("face_a.jpg", &embedding_a)) return -1;
if (!extract_embedding("face_b.jpg", &embedding_b)) return -1;
// 4. 计算相似度(静态方法)
float similarity = VisionService::EmbeddingSimilarity(embedding_a, embedding_b);
std::cout << "Similarity: " << similarity << std::endl;
// 5. 判断是否为同一人(阈值 0.6)
if (similarity > 0.6) {
std::cout << "Same person" << std::endl;
} else {
std::cout << "Different person" << std::endl;
}
// 6. 查看性能指标(可选)
auto timing = service->GetLastTiming();
std::cout << "Inference: " << timing.infer_ms << " ms" << std::endl;
return 0;
}
4.2.2 C++ 人脸搜索(1:N)
#include "vision_service.h"
#include <opencv2/opencv.hpp>
#include <variant>
#include <vector>
#include <string>
// 提取单张人脸的特征向量
static bool ExtractEmbedding(VisionService* service,
const std::string& path,
std::vector<float>* out) {
VisionServiceResponse response;
if (service->Infer(path, &response) != VISION_SERVICE_OK) return false;
if (response.results.empty()) return false;
const vision::Embedding* emb =
std::get_if<vision::Embedding>(&response.results[0]);
if (emb == nullptr) return false;
*out = emb->embedding;
return true;
}
int main() {
auto service = VisionService::Create("examples/arcface/config/arcface.yaml");
// 1. 构建人脸库(提取所有注册人脸的特征)
std::vector<std::string> names = {"Alice", "Bob", "Charlie"};
std::vector<std::vector<float>> face_database;
for (const auto& name : names) {
std::vector<float> embedding;
ExtractEmbedding(service.get(), "database/" + name + ".jpg", &embedding);
face_database.push_back(embedding);
}
// 2. 提取待识别人脸的特征
std::vector<float> query_embedding;
ExtractEmbedding(service.get(), "query.jpg", &query_embedding);
// 3. 在人脸库中搜索最相似的人脸
float max_similarity = -1.0f;
int best_match_idx = -1;
for (size_t i = 0; i < face_database.size(); ++i) {
float similarity = VisionService::EmbeddingSimilarity(
query_embedding, face_database[i]);
if (similarity > max_similarity) {
max_similarity = similarity;
best_match_idx = i;
}
}
// 4. 输出结果
if (max_similarity > 0.6) {
std::cout << "Matched: " << names[best_match_idx]
<< " (similarity: " << max_similarity << ")" << std::endl;
} else {
std::cout << "No match found (max similarity: "
<< max_similarity << ")" << std::endl;
}
return 0;
}
4.2.3 Python 人脸比对(1:1)
import cv2
from spacemit_vision import VisionServiceNative, VisionServiceStatus
recognizer = VisionServiceNative.create("examples/arcface/config/arcface.yaml")
st1, embedding_a = recognizer.infer_embedding(cv2.imread("face_a.jpg"))
st2, embedding_b = recognizer.infer_embedding(cv2.imread("face_b.jpg"))
if st1 != VisionServiceStatus.OK or st2 != VisionServiceStatus.OK:
raise RuntimeError(recognizer.last_error())
similarity = VisionServiceNative.embedding_similarity(embedding_a, embedding_b)
print(f"Similarity: {similarity:.4f}")
print("Same person" if similarity > 0.6 else "Different person")
4.2.4 Python 人脸搜索(1:N)
import cv2
import numpy as np
from spacemit_vision import VisionServiceNative, VisionServiceStatus
recognizer = VisionServiceNative.create("examples/arcface/config/arcface.yaml")
names = ["Alice", "Bob", "Charlie"]
face_database = []
for name in names:
st, emb = recognizer.infer_embedding(cv2.imread(f"database/{name}.jpg"))
if st != VisionServiceStatus.OK:
raise RuntimeError(recognizer.last_error())
face_database.append(emb)
st, query = recognizer.infer_embedding(cv2.imread("query.jpg"))
if st != VisionServiceStatus.OK:
raise RuntimeError(recognizer.last_error())
similarities = [VisionServiceNative.embedding_similarity(query, db) for db in face_database]
best = int(np.argmax(similarities))
print(f"Matched: {names[best]} ({similarities[best]:.4f})" if similarities[best] > 0.6 else "No match")
4.3 配置说明
YAML 配置文件是模型加载和推理参数的核心,以下是完整配置项说明:
# 模型文件路径(ArcFace MobileFaceNet 模型)
model_path: ~/.cache/models/vision/arcface/arcface_mobilefacenet_cut.q.onnx
# 测试图像路径(用于示例程序)
test_image1: ~/.cache/assets/image/003_face0.png
test_image2: ~/.cache/assets/image/004_face1.png
# 模型输入尺寸 [height, width](ArcFace 标准输入为 112×112)
image_size: [112, 112]
# 部署类名(C++ 模型工厂注册名,Python 通过 yaml 路径间接使用)
class: deploy.arcface.ArcFaceRecognizer
# 推理参数
default_params:
# 推理线程数(示例默认 8;K3 平台 intra-op 最大建议 8,可按场景调低)
num_threads: 8
# 推理后端(优先使用 SpaceMITExecutionProvider)
providers:
- SpaceMITExecutionProvider
- CPUExecutionProvider # 备用后端
参数调优建议:
- image_size:ArcFace 标准输入为 [112, 112],不建议修改。
- num_threads:示例 yaml 默认为 8,K3 平台 intra-op 线程数最大建议 8。若 CPU 竞争或延迟不稳定,可尝试降至 4 观察效果,不建议超过 8。
- providers:优先使用 SpaceMITExecutionProvider 以获得最佳性能。
- 相似度阈值:0.6 为经验值,可根据实际场景调整(提高阈值减少误识,降低阈值增加召回)。
- 输入要求:输入必须为裁剪对齐后的人脸图像(112×112),建议先用人脸检测模型定位人脸区域。
4.4 性能监控
通过启用性能计时,可以分析推理各阶段的耗时,用于性能优化和瓶颈定位。
C++ 示例:
VisionServiceTimingOptions timing_options;
timing_options.enabled = true;
service->SetTimingOptions(timing_options);
VisionServiceResponse response;
service->Infer("face.jpg", &response);
auto timing = service->GetLastTiming();
std::cout << "Model inference: " << timing.model_infer_ms << " ms" << std::endl;
std::cout << "Total inference: " << timing.infer_ms << " ms" << std::endl;
性能优化建议:
- ArcFace 模型非常轻量(MobileFaceNet),单次推理耗时极低(< 3ms)。
- 整体流水线性能主要受前端人脸检测影响。
- 对于 1:N 搜索,特征向量相似度计算是 CPU 操作,N 较大时可考虑使用向量检索库(如 Faiss)加速。
参考 demo 路径:examples/arcface/
注意事项:
- 输入图片应为裁剪对齐后的人脸图片(112×112),建议先用人脸检测模型(见 4.2.4-人脸检测)定位人脸区域后裁剪
- 相似度阈值 0.6 为经验值,可根据实际场景调整
- 人脸识别与检测、分类等任务共用统一的
Infer接口,区别在于人脸识别的结果以vision::Embedding变体写入response.results
5. 调试指南
- 启用计时:通过
SetTimingOptions启用后用GetLastTiming查看model_infer_ms、infer_ms字段 - 相似度异常低:确认输入为裁剪对齐的人脸图片,非全身照
- 确认模型文件为 ArcFace 专用模型(
arcface_mobilefacenet_cut.q.onnx)
6. 常见问题
| 现象 | 可能原因 | 处理 |
|---|---|---|
Model file not found | 模型未下载 | 执行 bash examples/arcface/scripts/download_models.sh |
No module named 'spacemit_vision' | wheel 未安装 | 按 §2 编译并 pip install src/python/dist/*.whl |
| 相似度始终很低 | 输入非人脸图片或未裁剪对齐 | 先用人脸检测裁剪人脸区域,缩放至 112×112 |
| 同一人相似度低于 0.6 | 光照、角度差异大 | 正常现象,可适当降低阈值 |
附录:性能与测试数据
以下数据摘自 Vision 组件 README.md 附录「不包含前后处理」(纯 ONNX 模型推理,不含图像预处理与后处理)。为 K3 平台阶段性实测结果,完整表见 README。
K3 平台
| 具体模型 | 输入大小 | 数据类型 | 帧率 (4核) | 帧率 (8核) |
|---|---|---|---|---|
| arcface_mobilefacenet | [1,3,112,112] | int8 | 373.7 | 531.7 |
复现方法:使用 onnxruntime_perf_test 工具(4 线程):
onnxruntime_perf_test ~/.cache/models/vision/arcface/arcface_mobilefacenet_cut.q.onnx -e spacemit -r 20 -x 1 -S 1 -s -I -c 1 -i "SPACEMIT_EP_INTRA_THREAD_NUM|4"