1245 lines
39 KiB
Markdown
1245 lines
39 KiB
Markdown
# 车间人脸识别系统 - 三分区距离检测方案 v2.3
|
||
|
||
> **文档版本**: v2.3
|
||
> **适用场景**: 4-6米安装高度,3-9米检测距离,RetinaFace_320 + 三分区
|
||
> **目标平台**: RK3588 (6TOPS NPU)
|
||
> **检测模型**: RetinaFace-MobileNetV3 320×320(先用现有模型跑通)
|
||
> **姿态估计**: 5点关键点近似
|
||
> **更新日期**: 2026-03-10
|
||
|
||
**注意**: v2.3使用现有`RetinaFace_mobile320.rknn`,如需更高精度可升级到640模型
|
||
|
||
---
|
||
|
||
## 1. 方案概述
|
||
|
||
### 1.1 设计目标
|
||
|
||
针对车间环境远距离人脸识别需求,本方案在现有插件化架构基础上,引入**距离分区检测**机制:
|
||
|
||
- **检测范围**: 3~8米(放弃10米极端距离,保证检测可靠性)
|
||
- **核心策略**: 以6米(相机对焦距离)为中心,将画面分为远近两个检测区
|
||
- **算力优化**: ROI裁剪 + 自适应缩放,节省约60%算力
|
||
- **姿态过滤**: 利用现有5点关键点,过滤过度低头情况
|
||
|
||
### 1.2 核心特性
|
||
|
||
| 特性 | 实现方式 | 预期收益 |
|
||
|------|----------|----------|
|
||
| **RetinaFace_320** | 320×320输入(现有模型) | 快速部署,5-7米检测率>90% |
|
||
| ROI裁剪 | 基于3-9米距离范围裁剪画面 | 节省40-50%算力 |
|
||
| **三分区检测** | 近区1.0x,中区1.3x,远区1.8x | 目标人脸20-40px(320最佳范围) |
|
||
| 距离过滤 | 像素y坐标→距离映射,过滤范围外人脸 | 减少误检 |
|
||
| 姿态过滤 | **5点关键点**估计俯仰角,过滤<-15° | 提升识别准确率,无需额外模型 |
|
||
| 独立标定 | 每相机独立Python标定脚本 | 适配不同安装高度/角度 |
|
||
| **可升级** | 320→640模型 | 预留接口,后续无缝升级 |
|
||
|
||
### 1.3 非目标(明确排除)
|
||
|
||
为控制复杂度,以下功能不在本版本范围内:
|
||
- ❌ **9米以上超远距离**(320模型在此距离检测率<50%)
|
||
- ❌ **PFLD独立姿态估计模型**(使用5点关键点近似已足够)
|
||
- ❌ RetinaFace_640模型(v2.4版本升级,当前先用320跑通)
|
||
- ❌ Batch推理优化
|
||
- ❌ 自动在线标定
|
||
- ❌ 畸变校正(假设畸变较小或可忽略)
|
||
|
||
---
|
||
|
||
## 2. 系统架构
|
||
|
||
### 2.1 数据流架构
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ 每路相机独立配置(configs/zone_a/cam_001.json) │
|
||
├─────────────────────────────────────────────────────────────┤
|
||
│ │
|
||
│ [input_rtsp] ──→ [preprocess] ──→ [ai_face_det] ──→ [ai_face_recog] │
|
||
│ │ │ │
|
||
│ ROI裁剪(节省算力) 双分区检测 │
|
||
│ 距离估算 │
|
||
│ ↓ │
|
||
│ [osd]/[publish] │
|
||
│ │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 2.2 三分区检测原理
|
||
|
||
```
|
||
画面垂直方向(y坐标,2560×1440示例):
|
||
┌─────────────────────────────┐ y=0 (画面顶部)
|
||
│ │
|
||
│ 3-5米 (近区) │ ← scale=1.0x
|
||
│ 人脸88-117px │ 原图处理
|
||
│ 320输入: 27-36px │ (320模型最佳范围)
|
||
│ ↑ │
|
||
│ y = boundary_5m │ 5米分界线
|
||
│ ↓ │
|
||
│ 5-7米 (中区) │ ← scale=1.3x
|
||
│ 人脸63-88px │ 放大后处理
|
||
│ 320输入: 25-35px │ (对焦最佳)
|
||
│ ↑ │
|
||
│ y = boundary_7m │ 7米分界线
|
||
│ ↓ │
|
||
│ 7-9米 (远区) │ ← scale=1.8x
|
||
│ 人脸49-63px │ 大幅放大补偿
|
||
│ 320输入: 27-35px │ (9米可能降至60%检测率)
|
||
│ │
|
||
└─────────────────────────────┘ y=1440 (画面底部)
|
||
↑
|
||
ROI裁剪边界 (y_min ~ y_max)
|
||
只保留3-9米对应画面区域
|
||
|
||
目标: 三个区处理后的人脸在320输入中占25-35px(320模型最佳)
|
||
```
|
||
|
||
### 2.3 距离-像素映射
|
||
|
||
基于针孔相机模型,预计算LUT表实现O(1)查询:
|
||
|
||
```
|
||
距离 D → 像素 y:
|
||
y = cy + f × tan(arctan(H/D) - θ)
|
||
|
||
像素 y → 距离 D:
|
||
D = H / tan(θ + arctan((y-cy)/f))
|
||
```
|
||
|
||
其中:
|
||
- H: 安装高度(米)
|
||
- θ: 俯仰角(弧度)
|
||
- f: 像素焦距(像素)
|
||
- cy: 主点y坐标(通常img_h/2)
|
||
|
||
---
|
||
|
||
## 3. 相机模型设计
|
||
|
||
### 3.1 简化相机模型类
|
||
|
||
```cpp
|
||
// include/utils/camera_model.h
|
||
#pragma once
|
||
#include <vector>
|
||
|
||
namespace rk3588 {
|
||
|
||
struct CameraModelParams {
|
||
float height; // 安装高度(米)
|
||
float pitch_deg; // 俯仰角(度)
|
||
float focal_px; // 像素焦距(像素)
|
||
int img_w, img_h; // 图像尺寸
|
||
int cx, cy; // 主点坐标
|
||
|
||
float min_dist = 3.0f; // 最小检测距离
|
||
float max_dist = 8.0f; // 最大检测距离
|
||
};
|
||
|
||
class SimpleCameraModel {
|
||
public:
|
||
explicit SimpleCameraModel(const CameraModelParams& p);
|
||
|
||
// O(1)距离查询(核心功能)
|
||
float PixelToDistance(int y) const;
|
||
|
||
// 计算ROI裁剪区域
|
||
struct ROI { int x, y, w, h; float saving_ratio; };
|
||
ROI ComputeRoi() const;
|
||
|
||
// 计算分区线像素位置
|
||
int GetZoneSplitY(float split_distance = 6.0f) const;
|
||
|
||
// 估算人脸像素大小(用于验证)
|
||
float EstimateFacePixelSize(float distance,
|
||
float real_face_width = 0.16f) const;
|
||
|
||
private:
|
||
CameraModelParams p_;
|
||
std::vector<float> distance_lut_; // 预计算查找表
|
||
|
||
void BuildLut();
|
||
};
|
||
|
||
} // namespace rk3588
|
||
```
|
||
|
||
### 3.2 实现要点
|
||
|
||
- **LUT预计算**: 初始化时计算整张图的距离表(img_h个float,约5-10KB)
|
||
- **无三角函数运行时计算**: 查询时直接查表
|
||
- **边界处理**: y越界时返回inf,由调用方处理
|
||
|
||
---
|
||
|
||
## 4. 分区检测策略
|
||
|
||
### 4.1 检测节点扩展(RetinaFace_320)
|
||
|
||
`ai_face_det` 节点配置更新:
|
||
|
||
```json
|
||
{
|
||
"id": "face_det",
|
||
"type": "ai_face_det",
|
||
"model_path": "./models/RetinaFace_mobile320.rknn",
|
||
"conf": 0.6,
|
||
"nms": 0.4,
|
||
"max_faces": 10,
|
||
"output_landmarks": true,
|
||
"input_format": "rgb",
|
||
"model_w": 320, // 320×320输入
|
||
"model_h": 320,
|
||
|
||
"distance_zones": {
|
||
"enabled": true,
|
||
"boundaries": [416, 672], // 5米和7米分界线y坐标
|
||
"scales": [1.0, 1.3, 1.8] // 320模型:放大为主,目标25-35px
|
||
}
|
||
}
|
||
```
|
||
|
||
**模型现状**:
|
||
- ✅ 已有:`RetinaFace_mobile320.rknn`(1.6MB)
|
||
- ✅ 输入:320×320,5点关键点输出
|
||
- ⚠️ 限制:8-9米检测率可能60-70%(可先跑通功能)
|
||
- 🔄 升级:v2.4无缝切换到640模型(只需改model_path和scales)
|
||
|
||
### 4.2 检测流程(三分区)
|
||
|
||
```cpp
|
||
void DetectWithZones(FramePtr frame) {
|
||
if (!zone_cfg_.enabled) {
|
||
RunSingleScale(frame); // 回退到原有逻辑
|
||
return;
|
||
}
|
||
|
||
const int h = frame->height;
|
||
const int y_5m = zone_cfg_.boundaries[0]; // 5米分界线
|
||
const int y_7m = zone_cfg_.boundaries[1]; // 7米分界线
|
||
|
||
Detections all_dets;
|
||
|
||
// 近区检测 (3-5米,画面上部) - 320模型:1.0x
|
||
if (y_5m > 0) {
|
||
Mat roi = Crop(frame, 0, 0, w, y_5m);
|
||
Mat scaled;
|
||
resize(roi, scaled, Size(roi.w*1.0, roi.h*1.0)); // 原图处理
|
||
auto dets = RunInference(scaled);
|
||
MapBack(dets, scale=1.0, offset_y=0);
|
||
all_dets.insert(all_dets.end(), dets.begin(), dets.end());
|
||
}
|
||
|
||
// 中区检测 (5-7米,画面中部) - 320模型:1.3x放大
|
||
if (y_7m > y_5m) {
|
||
Mat roi = Crop(frame, 0, y_5m, w, y_7m - y_5m);
|
||
Mat scaled;
|
||
resize(roi, scaled, Size(roi.w*1.3, roi.h*1.3)); // 放大补偿
|
||
auto dets = RunInference(scaled);
|
||
MapBack(dets, scale=1.3, offset_y=y_5m);
|
||
all_dets.insert(all_dets.end(), dets.begin(), dets.end());
|
||
}
|
||
|
||
// 远区检测 (7-9米,画面下部) - 320模型:1.8x大幅放大
|
||
if (y_7m < h) {
|
||
Mat roi = Crop(frame, 0, y_7m, w, h - y_7m);
|
||
Mat scaled;
|
||
resize(roi, scaled, Size(roi.w*1.8, roi.h*1.8)); // 大幅放大
|
||
auto dets = RunInference(scaled);
|
||
MapBack(dets, scale=1.8, offset_y=y_7m);
|
||
all_dets.insert(all_dets.end(), dets.begin(), dets.end());
|
||
}
|
||
|
||
// NMS去重(三区可能有重叠)
|
||
Nms(all_dets);
|
||
frame->face_det = make_shared<FaceDetResult>(all_dets);
|
||
}
|
||
```
|
||
|
||
### 4.3 缩放因子选择(320模型)
|
||
|
||
| 分区 | 距离 | 原始人脸 | 缩放 | 320输入 | 占比 | 检测率预期 |
|
||
|------|------|----------|------|---------|------|-----------|
|
||
| 近区 | 3-5m | 88-117px | 1.0x | 88-117px→320 | 27-37% | >90% |
|
||
| 中区 | 5-7m | 63-88px | 1.3x | 82-114px→320 | 26-36% | >90% |
|
||
| 远区 | 7-9m | 49-63px | 1.8x | 88-113px→320 | 28-35% | 70-80% |
|
||
|
||
> **注**:
|
||
> - RetinaFace_320最佳检测范围:25-45px(占320输入的8-14%)
|
||
> - 实际处理后:27-37px,在最佳范围内
|
||
> - 9米处检测率可能降至70%,如不满足可升级到640模型
|
||
> - **升级到640只需改**:model_path + scales改为[0.7, 1.0, 1.4]
|
||
|
||
---
|
||
|
||
## 5. 距离与姿态过滤
|
||
|
||
### 5.1 识别节点扩展(5点关键点姿态估计)
|
||
|
||
```json
|
||
{
|
||
"id": "face_recog",
|
||
"type": "ai_face_recog",
|
||
"model_path": "./models/mobilefacenet_arcface.rknn",
|
||
"align": true,
|
||
"gallery": {
|
||
"backend": "sqlite",
|
||
"path": "./models/face_gallery.db"
|
||
},
|
||
"filters": {
|
||
"distance": {
|
||
"enabled": true,
|
||
"min": 3.0,
|
||
"max": 9.0
|
||
},
|
||
"pose": {
|
||
"enabled": true,
|
||
"min_pitch": -15, // 真实仰角低于-15度不识别
|
||
"camera_pitch": 45, // 相机俯仰角(标定参数)
|
||
"use_landmarks": true // 使用RetinaFace输出的5点关键点
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### 5.2 过滤逻辑
|
||
|
||
```cpp
|
||
void ApplyFilters(FaceRecogItem& item, const FaceDetItem& det) {
|
||
// 1. 距离过滤
|
||
if (filters_.distance.enabled && camera_model_) {
|
||
int center_y = det.bbox.y + det.bbox.h / 2;
|
||
float dist = camera_model_->PixelToDistance(center_y);
|
||
|
||
if (dist < filters_.distance.min || dist > filters_.distance.max) {
|
||
item.unknown = true;
|
||
item.best_name = "out_of_range";
|
||
return;
|
||
}
|
||
item.distance = dist; // 记录距离供后续使用
|
||
}
|
||
|
||
// 2. 姿态过滤(5点关键点近似)
|
||
if (filters_.pose.enabled && det.has_landmarks) {
|
||
float face_pitch = EstimatePitch(det.landmarks);
|
||
float real_pitch = face_pitch - filters_.pose.camera_pitch;
|
||
|
||
if (real_pitch < filters_.pose.min_pitch) {
|
||
item.unknown = true;
|
||
item.best_name = "low_head";
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 俯仰角估计:鼻尖相对于眼睛中心的垂直偏移
|
||
float EstimatePitch(const array<Point2f, 5>& lm) {
|
||
float eye_y = (lm[0].y + lm[1].y) / 2; // 左右眼中心
|
||
float nose_y = lm[2].y; // 鼻尖
|
||
float eye_dist = abs(lm[1].x - lm[0].x);
|
||
|
||
if (eye_dist < 1.0f) return 0.0f;
|
||
|
||
float dy = nose_y - eye_y;
|
||
return atan2(dy, eye_dist) * 180.0f / M_PI;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 6. 标定工具设计
|
||
|
||
### 6.1 工具定位
|
||
|
||
- **独立运行**: 不耦合主系统,手工执行
|
||
- **输出参考**: 生成推荐配置值,供手工复制到配置文件
|
||
- **批量支持**: 可选批量生成多路相机配置
|
||
|
||
### 6.2 使用方法
|
||
|
||
```bash
|
||
# 单相机标定(三分区)
|
||
python tools/calibrate_camera.py \
|
||
--height 5.0 \
|
||
--pitch 45 \
|
||
--focal-estimate 2200 \
|
||
--image-size 2560 1440 \
|
||
--range 3.0 9.0 \
|
||
--zones 3 5 7 9 \
|
||
--scales 0.7 1.0 1.4 \
|
||
--output configs/calibrations/cam_zone_a_001.json \
|
||
--report
|
||
|
||
# 输出示例
|
||
# [INFO] ROI: y=240, h=880 (saving: 38.9%)
|
||
# [INFO] Zone boundaries: y=416 (5m), y=672 (7m)
|
||
# [INFO] 近区(3-5m): scale=0.7x
|
||
# [INFO] 中区(5-7m): scale=1.0x
|
||
# [INFO] 远区(7-9m): scale=1.4x
|
||
# [INFO] Config saved to cam_zone_a_001.json
|
||
```
|
||
|
||
### 6.3 输出配置片段(三分区)
|
||
|
||
工具输出JSON格式配置,可直接复制到主配置:
|
||
|
||
```json
|
||
{
|
||
"preprocess": {
|
||
"roi": {
|
||
"enabled": true,
|
||
"crop": {"x": 0, "y": 240, "w": 2560, "h": 880}
|
||
}
|
||
},
|
||
"face_det": {
|
||
"distance_zones": {
|
||
"enabled": true,
|
||
"boundaries": [416, 672],
|
||
"zones": [
|
||
{
|
||
"name": "near",
|
||
"distance_range": [3, 5],
|
||
"scale": 0.7,
|
||
"y_range": [0, 416]
|
||
},
|
||
{
|
||
"name": "mid",
|
||
"distance_range": [5, 7],
|
||
"scale": 1.0,
|
||
"y_range": [416, 672]
|
||
},
|
||
{
|
||
"name": "far",
|
||
"distance_range": [7, 9],
|
||
"scale": 1.4,
|
||
"y_range": [672, 1440]
|
||
}
|
||
]
|
||
}
|
||
},
|
||
"face_recog": {
|
||
"filters": {
|
||
"distance": {"enabled": true, "min": 3.0, "max": 9.0},
|
||
"pose": {"enabled": true, "min_pitch": -15, "camera_pitch": 45}
|
||
}
|
||
},
|
||
"calibration_params": {
|
||
"height": 5.0,
|
||
"pitch": 45.0,
|
||
"focal_px": 2200,
|
||
"zones": "3-5m(0.7x), 5-7m(1.0x), 7-9m(1.4x)"
|
||
}
|
||
}
|
||
```
|
||
|
||
### 6.4 焦距估算方法
|
||
|
||
若无法精确测量焦距,可用以下方法估算:
|
||
|
||
```
|
||
方法1: 公式估算
|
||
f ≈ (sensor_width_mm / image_width_px) * focal_length_mm
|
||
例如: 1/2.8"传感器(5.6mm宽), 4mm镜头, 2560px
|
||
f ≈ (5.6 / 2560) * 4 ≈ 2200 px
|
||
|
||
方法2: 现场测量反推
|
||
在已知距离D处测量人脸像素宽度W
|
||
f = W * D / 0.16
|
||
例如: 6米处人脸100px
|
||
f = 100 * 6 / 0.16 = 3750 px
|
||
```
|
||
|
||
> **建议**: 先用方法1估算,再用方法2验证,偏差较大时以方法2为准。
|
||
|
||
---
|
||
|
||
## 7. 配置文件集成方案
|
||
|
||
### 7.1 配置结构说明
|
||
|
||
项目使用**统一配置文件**,支持两种模式:
|
||
1. `templates` + `instances` 模式:模板定义流水线,实例传入参数
|
||
2. `graphs` 直接模式:直接定义完整的节点和边
|
||
|
||
### 7.2 Templates + Instances 模式(推荐用于多相机)
|
||
|
||
```json
|
||
{
|
||
"global": {
|
||
"metrics_port": 9000,
|
||
"web_root": "web"
|
||
},
|
||
"queue": { "size": 8, "strategy": "drop_oldest" },
|
||
|
||
"templates": {
|
||
"face_recog_distanced_pipeline": {
|
||
"nodes": [
|
||
{
|
||
"id": "in",
|
||
"type": "input_rtsp",
|
||
"role": "source",
|
||
"enable": true,
|
||
"url": "${url}",
|
||
"fps": 30,
|
||
"width": 2560,
|
||
"height": 1440
|
||
},
|
||
{
|
||
"id": "pre",
|
||
"type": "preprocess",
|
||
"role": "filter",
|
||
"enable": true,
|
||
// ROI裁剪参数(由标定工具生成,通过params传入)
|
||
"roi": {
|
||
"enabled": "${roi_enabled}",
|
||
"crop": {
|
||
"x": 0,
|
||
"y": "${roi_y}",
|
||
"w": 2560,
|
||
"h": "${roi_h}"
|
||
}
|
||
},
|
||
"dst_w": 1280,
|
||
"dst_h": 720,
|
||
"dst_format": "rgb"
|
||
},
|
||
{
|
||
"id": "face_det",
|
||
"type": "ai_face_det",
|
||
"role": "filter",
|
||
"enable": true,
|
||
"model_path": "./models/RetinaFace_mobile320.rknn",
|
||
"conf": 0.6,
|
||
"nms": 0.4,
|
||
"max_faces": 10,
|
||
"output_landmarks": true,
|
||
// 三分区检测参数(通过params传入)
|
||
"distance_zones": {
|
||
"enabled": "${zones_enabled}",
|
||
"boundaries": ["${zone_boundary_5m}", "${zone_boundary_7m}"],
|
||
"scales": ["${zone_scale_near}", "${zone_scale_mid}", "${zone_scale_far}"]
|
||
}
|
||
},
|
||
{
|
||
"id": "face_recog",
|
||
"type": "ai_face_recog",
|
||
"role": "filter",
|
||
"enable": true,
|
||
"model_path": "./models/mobilefacenet_arcface.rknn",
|
||
"align": true,
|
||
"gallery": {
|
||
"backend": "sqlite",
|
||
"path": "${face_gallery_path}"
|
||
},
|
||
// 距离和姿态过滤参数
|
||
"filters": {
|
||
"distance": {
|
||
"enabled": true,
|
||
"min": 3.0,
|
||
"max": 9.0
|
||
},
|
||
"pose": {
|
||
"enabled": true,
|
||
"min_pitch": -15,
|
||
"camera_pitch": "${camera_pitch}"
|
||
}
|
||
}
|
||
},
|
||
{
|
||
"id": "osd",
|
||
"type": "osd",
|
||
"role": "filter",
|
||
"enable": true,
|
||
"draw_face_det": true,
|
||
"draw_face_recog": true
|
||
},
|
||
{
|
||
"id": "pub",
|
||
"type": "publish",
|
||
"role": "sink",
|
||
"enable": true,
|
||
"outputs": [{ "proto": "rtsp_server", "port": "${rtsp_port}", "path": "/live/${name}" }]
|
||
}
|
||
],
|
||
"edges": [
|
||
["in", "pre"],
|
||
["pre", "face_det"],
|
||
["face_det", "face_recog"],
|
||
["face_recog", "osd"],
|
||
["osd", "pub"]
|
||
]
|
||
}
|
||
},
|
||
|
||
// 30+路相机实例,每路传入不同的标定参数
|
||
"instances": [
|
||
{
|
||
"name": "workshop_zoneA_cam01",
|
||
"template": "face_recog_distanced_pipeline",
|
||
"params": {
|
||
"name": "zoneA_cam01",
|
||
"url": "rtsp://192.168.1.101/stream1",
|
||
"rtsp_port": 8554,
|
||
"face_gallery_path": "./models/face_gallery.db",
|
||
// 相机安装参数
|
||
"camera_pitch": 45,
|
||
// ROI参数(标定工具生成)
|
||
"roi_enabled": true,
|
||
"roi_y": 240,
|
||
"roi_h": 880,
|
||
// 三分区参数(标定工具生成)
|
||
"zones_enabled": true,
|
||
"zone_boundary_5m": 416,
|
||
"zone_boundary_7m": 672,
|
||
"zone_scale_near": 0.7,
|
||
"zone_scale_mid": 1.0,
|
||
"zone_scale_far": 1.4
|
||
}
|
||
},
|
||
{
|
||
"name": "workshop_zoneA_cam02",
|
||
"template": "face_recog_distanced_pipeline",
|
||
"params": {
|
||
"name": "zoneA_cam02",
|
||
"url": "rtsp://192.168.1.102/stream1",
|
||
"rtsp_port": 8555,
|
||
"face_gallery_path": "./models/face_gallery.db",
|
||
// 不同安装高度和角度,不同标定参数
|
||
"camera_pitch": 42,
|
||
"roi_enabled": true,
|
||
"roi_y": 280,
|
||
"roi_h": 820,
|
||
"zones_enabled": true,
|
||
"zone_boundary_5m": 432,
|
||
"zone_boundary_7m": 688,
|
||
"zone_scale_near": 0.7,
|
||
"zone_scale_mid": 1.0,
|
||
"zone_scale_far": 1.4
|
||
}
|
||
}
|
||
// ... 更多相机实例
|
||
]
|
||
}
|
||
```
|
||
|
||
### 7.3 Graphs 直接模式(单相机测试)
|
||
|
||
```json
|
||
{
|
||
"queue": { "size": 8, "strategy": "drop_oldest" },
|
||
"graphs": [
|
||
{
|
||
"name": "cam1_face_recog_distanced",
|
||
"nodes": [
|
||
{
|
||
"id": "in_cam1",
|
||
"type": "input_rtsp",
|
||
"role": "source",
|
||
"enable": true,
|
||
"url": "rtsp://192.168.1.101/stream1",
|
||
"width": 2560,
|
||
"height": 1440
|
||
},
|
||
{
|
||
"id": "pre_cam1",
|
||
"type": "preprocess",
|
||
"role": "filter",
|
||
"enable": true,
|
||
"roi": {
|
||
"enabled": true,
|
||
"crop": { "x": 0, "y": 240, "w": 2560, "h": 880 }
|
||
},
|
||
"dst_w": 1280,
|
||
"dst_h": 720,
|
||
"dst_format": "rgb"
|
||
},
|
||
{
|
||
"id": "face_det_cam1",
|
||
"type": "ai_face_det",
|
||
"role": "filter",
|
||
"enable": true,
|
||
"model_path": "./models/RetinaFace_mobile320.rknn",
|
||
"conf": 0.6,
|
||
"nms": 0.4,
|
||
"max_faces": 10,
|
||
"output_landmarks": true,
|
||
"distance_zones": {
|
||
"enabled": true,
|
||
"boundaries": [416, 672],
|
||
"scales": [0.7, 1.0, 1.4]
|
||
}
|
||
},
|
||
{
|
||
"id": "face_recog_cam1",
|
||
"type": "ai_face_recog",
|
||
"role": "filter",
|
||
"enable": true,
|
||
"model_path": "./models/mobilefacenet_arcface.rknn",
|
||
"align": true,
|
||
"gallery": {
|
||
"backend": "sqlite",
|
||
"path": "./models/face_gallery.db"
|
||
},
|
||
"filters": {
|
||
"distance": { "enabled": true, "min": 3.0, "max": 9.0 },
|
||
"pose": { "enabled": true, "min_pitch": -15, "camera_pitch": 45 }
|
||
}
|
||
},
|
||
{
|
||
"id": "osd_cam1",
|
||
"type": "osd",
|
||
"role": "filter",
|
||
"enable": true,
|
||
"draw_face_det": true,
|
||
"draw_face_recog": true
|
||
},
|
||
{
|
||
"id": "pub_cam1",
|
||
"type": "publish",
|
||
"role": "sink",
|
||
"enable": true,
|
||
"outputs": [
|
||
{ "proto": "rtsp_server", "port": 8554, "path": "/live/cam1" }
|
||
]
|
||
}
|
||
],
|
||
"edges": [
|
||
["in_cam1", "pre_cam1"],
|
||
["pre_cam1", "face_det_cam1"],
|
||
["face_det_cam1", "face_recog_cam1"],
|
||
["face_recog_cam1", "osd_cam1"],
|
||
["osd_cam1", "pub_cam1"]
|
||
]
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 8. 部署与验证流程
|
||
|
||
### 8.1 部署流程(统一配置文件)
|
||
|
||
```
|
||
Step 1: 准备统一配置文件(configs/workshop_face_recog.json)
|
||
├─ 定义 template "face_recog_distanced_pipeline"
|
||
└─ 预留 ${params} 占位符
|
||
|
||
Step 2: 为每路相机运行标定工具
|
||
├─ python tools/calibrate_camera.py --height 5.0 --pitch 45 ...
|
||
└─ 记录输出的 params 参数
|
||
|
||
Step 3: 在统一配置文件的 instances 中添加相机
|
||
{
|
||
"name": "workshop_zoneA_cam01",
|
||
"template": "face_recog_distanced_pipeline",
|
||
"params": {
|
||
"name": "zoneA_cam01",
|
||
"url": "rtsp://...",
|
||
// 复制标定工具输出的 params 到这里
|
||
"camera_pitch": 45,
|
||
"roi_y": 240,
|
||
"roi_h": 880,
|
||
"zone_boundary_5m": 416,
|
||
...
|
||
}
|
||
}
|
||
|
||
Step 4: 现场验证
|
||
├─ 在3米、5米、7米、9米处站立测试人员
|
||
├─ 检查检测框是否正常
|
||
├─ 检查距离估算是否准确
|
||
└─ 如有偏差,调整该相机的 focal_px 重新标定
|
||
|
||
Step 5: 批量部署
|
||
└─ 重复Step 2-4为所有30+路相机添加 instances
|
||
```
|
||
|
||
### 8.2 验证检查清单
|
||
|
||
| 检查项 | 方法 | 通过标准 |
|
||
|--------|------|----------|
|
||
| ROI裁剪范围 | 观察OSD输出 | 3-9米范围人脸可见,9米外被裁 |
|
||
| 5米分界线 | 站在5米处观察 | y坐标应与zone_boundary_5m匹配 |
|
||
| 7米分界线 | 站在7米处观察 | y坐标应与zone_boundary_7m匹配 |
|
||
| 近区检测 | 站在4米处 | 人脸25-35px(320输入),检测率>90% |
|
||
| 中区检测 | 站在6米处 | 人脸25-35px(320输入),检测率>90% |
|
||
| 远区检测 | 站在8米处 | 人脸25-35px(320输入),检测率~80% |
|
||
| 远区极限 | 站在9米处 | 人脸~25px,检测率~70%(可接受或升级640) |
|
||
| 距离估算 | 对比激光测距 | 误差<0.3米 |
|
||
| 姿态过滤 | 低头 vs 抬头 | 过度低头(-15°以下)标记为low_head |
|
||
|
||
---
|
||
|
||
## 9. 性能预期
|
||
|
||
### 9.1 理论计算(RetinaFace_320)
|
||
|
||
| 优化项 | 参数 | 实际收益 |
|
||
|--------|------|----------|
|
||
| **RetinaFace_320** | 320×320输入(现有模型) | 快速部署,5-7米检测率>90% |
|
||
| ROI裁剪 | ~40%算力节省 | 单路NPU占用从20%→12% |
|
||
| 三分区检测 | 各区域25-35px | 全范围检测率80-90%(9米可能70%) |
|
||
| 距离过滤 | 3-9米范围 | 减少范围外误检 |
|
||
| 姿态过滤 | 5点关键点 | 误识率降低50%,无需额外模型 |
|
||
| **可升级** | 320→640 | 预留接口,后续无缝升级 |
|
||
|
||
### 9.2 资源占用估算(320模型)
|
||
|
||
- **单路NPU占用**: ~15-20%(RetinaFace_320 + MobileFaceNet)
|
||
- RetinaFace_320: ~8-10ms/帧(640的1/4)
|
||
- MobileFaceNet: ~10-15ms/人
|
||
- **8台RK3588承载**: 每台5-6路(320模型轻量)
|
||
- **内存占用**:
|
||
- 相机模型LUT: ~10KB/路
|
||
- 三分区检测缓冲: ~3×320×320×3 ≈ 0.9MB/路
|
||
- **延迟**: 三分区检测约增加8-10ms
|
||
|
||
> **注**: 320模型计算量小,快速部署验证功能。如8-9米检测率不满足,可无缝升级到640。
|
||
|
||
---
|
||
|
||
## 10. 风险评估与回退方案
|
||
|
||
### 10.1 潜在风险与缓解
|
||
|
||
| 风险 | 可能性 | 影响 | 缓解措施 |
|
||
|------|--------|------|----------|
|
||
| **8-9米检测率不足** | 中 | 320模型远距离检测能力有限 | 可提高far_scale到2.0x,或升级到640 |
|
||
| 标定参数不准 | 中 | 测距误差大 | 提供现场微调指引 |
|
||
| 5米/7米分界线不准 | 中 | 分区检测率不均 | 可手工调整boundary_y |
|
||
| 姿态估计误差 | 低 | 5点近似±5°误差 | 放宽min_pitch到-20°容忍 |
|
||
| **升级到640兼容性** | 低 | 接口预留,已考虑 | 只需改model_path和scales |
|
||
|
||
### 10.2 回退方案
|
||
|
||
若分区检测效果不佳,可快速回退:
|
||
|
||
```json
|
||
{
|
||
"face_det": {
|
||
"distance_zones": {
|
||
"enabled": false // 关闭分区,回退单尺度
|
||
}
|
||
},
|
||
"pre": {
|
||
"roi": {
|
||
"enabled": false // 关闭ROI,全图检测
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 11. 模型准备清单
|
||
|
||
### 11.1 当前版本模型(v2.3)
|
||
|
||
| 模型 | 文件名 | 输入尺寸 | 来源 | 状态 |
|
||
|------|--------|----------|------|------|
|
||
| **RetinaFace_320** | `RetinaFace_mobile320.rknn` | 320×320 | 项目已有 | ✅ 已有 |
|
||
| MobileFaceNet | `mobilefacenet_arcface.rknn` | 112×112 | 项目已有 | ✅ 已有 |
|
||
|
||
### 11.2 升级到640(v2.4预留)
|
||
|
||
当320模型精度不满足时,无缝升级:
|
||
|
||
```json
|
||
// 只需修改配置
|
||
{
|
||
"face_det": {
|
||
"model_path": "./models/RetinaFace_mobile640.rknn",
|
||
"model_w": 640,
|
||
"model_h": 640,
|
||
"distance_zones": {
|
||
"scales": [0.7, 1.0, 1.4] // 640模型用此参数
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**640模型获取**:
|
||
- 方案1:自行转换ONNX到RKNN
|
||
- 方案2:寻找社区预转换模型
|
||
|
||
### 11.3 320 vs 640 对比
|
||
|
||
| 指标 | RetinaFace_320 | RetinaFace_640 | 建议 |
|
||
|------|----------------|----------------|------|
|
||
| 输入尺寸 | 320×320 | 640×640 | - |
|
||
| NPU耗时 | ~8-10ms | ~25-30ms | 320快3倍 |
|
||
| 5-7米检测率 | >90% | >95% | 相近 |
|
||
| 8-9米检测率 | ~70% | >85% | 640优势明显 |
|
||
| 部署难度 | 已有模型 | 需准备 | 先用320 |
|
||
|
||
### 11.4 模型验证
|
||
|
||
```python
|
||
# 验证320模型输出
|
||
# 预期输出:
|
||
# - loc: [1, 16800, 4] (检测框回归)
|
||
# - conf: [1, 16800, 2] (人脸/背景分类)
|
||
# - landms: [1, 16800, 10] (5点关键点)
|
||
```
|
||
|
||
---
|
||
|
||
## 附录A: 标定工具完整代码(三分区版)
|
||
|
||
```python
|
||
#!/usr/bin/env python3
|
||
"""
|
||
calibrate_camera.py - 相机标定工具(三分区版)
|
||
生成ROI和三分区参数供手工复制到配置文件
|
||
|
||
Usage:
|
||
python calibrate_camera.py --height 5.0 --pitch 45 -o cam_calib.json --report
|
||
"""
|
||
|
||
import json
|
||
import argparse
|
||
import numpy as np
|
||
from dataclasses import dataclass
|
||
from typing import List, Tuple
|
||
|
||
|
||
@dataclass
|
||
class CameraParams:
|
||
height: float # 安装高度(m)
|
||
pitch_deg: float # 俯仰角(°)
|
||
focal_px: float # 像素焦距(px)
|
||
img_w: int # 图像宽度
|
||
img_h: int # 图像高度
|
||
|
||
|
||
class CameraCalibrator:
|
||
"""相机标定器 - 支持三分区"""
|
||
|
||
def __init__(self, p: CameraParams):
|
||
self.p = p
|
||
self.cy = p.img_h // 2
|
||
self.cx = p.img_w // 2
|
||
self.theta = np.radians(p.pitch_deg)
|
||
self._build_lut()
|
||
|
||
def _build_lut(self):
|
||
"""预计算距离查找表 LUT[y] = distance(m)"""
|
||
self.lut = np.zeros(self.p.img_h, dtype=np.float32)
|
||
for y in range(self.p.img_h):
|
||
dy = y - self.cy
|
||
angle = self.theta + np.arctan2(dy, self.p.focal_px)
|
||
if abs(angle) > 1e-6 and np.tan(angle) > 0:
|
||
self.lut[y] = self.p.height / np.tan(angle)
|
||
else:
|
||
self.lut[y] = np.inf
|
||
|
||
def pixel_to_distance(self, y: int) -> float:
|
||
"""像素y坐标 -> 距离(m)"""
|
||
if 0 <= y < self.p.img_h:
|
||
return float(self.lut[y])
|
||
return np.inf
|
||
|
||
def distance_to_pixel(self, d: float) -> int:
|
||
"""距离(m) -> 像素y坐标"""
|
||
if d <= 0:
|
||
return self.cy
|
||
angle = np.arctan2(self.p.height, d)
|
||
offset = self.p.focal_px * np.tan(angle - self.theta)
|
||
return int(np.clip(self.cy + offset, 0, self.p.img_h - 1))
|
||
|
||
def calculate_roi(self, min_d: float, max_d: float, margin: int = 20) -> dict:
|
||
"""计算ROI裁剪区域"""
|
||
# 注意:距离越远,y坐标越大(画面下方)
|
||
y_min = self.distance_to_pixel(max_d) # 远距在下
|
||
y_max = self.distance_to_pixel(min_d) # 近距在上
|
||
|
||
# 添加边界余量
|
||
y_min = max(0, y_min - margin)
|
||
y_max = min(self.p.img_h, y_max + margin)
|
||
|
||
saving = 1 - (y_max - y_min) / self.p.img_h
|
||
|
||
return {
|
||
"crop": {
|
||
"x": 0,
|
||
"y": y_min,
|
||
"w": self.p.img_w,
|
||
"h": y_max - y_min
|
||
},
|
||
"saving_percent": round(saving * 100, 1)
|
||
}
|
||
|
||
def get_zone_boundaries(self, boundaries_m: List[float]) -> List[int]:
|
||
"""获取分区边界像素坐标"""
|
||
return [self.distance_to_pixel(d) for d in boundaries_m]
|
||
|
||
def estimate_face_size(self, distance: float, real_width: float = 0.16) -> float:
|
||
"""估算给定距离的人脸像素大小"""
|
||
if distance <= 0:
|
||
return 0
|
||
return self.p.focal_px * real_width / distance
|
||
|
||
def generate_zones_config(self,
|
||
boundaries_m: List[float],
|
||
scales: List[float]) -> dict:
|
||
"""生成分区配置"""
|
||
boundaries_y = self.get_zone_boundaries(boundaries_m)
|
||
|
||
zones = []
|
||
zone_names = ["near", "mid", "far"]
|
||
|
||
for i, (name, scale) in enumerate(zip(zone_names, scales)):
|
||
y_start = boundaries_y[i] if i > 0 else 0
|
||
y_end = boundaries_y[i] if i < len(boundaries_y) else self.p.img_h
|
||
|
||
# 如果是最后一个区
|
||
if i == len(scales) - 1:
|
||
y_end = self.p.img_h
|
||
else:
|
||
y_end = boundaries_y[i]
|
||
|
||
# 重新计算正确的y范围
|
||
if i == 0:
|
||
y_range = [0, boundaries_y[0]]
|
||
d_range = [3.0, boundaries_m[0]]
|
||
elif i == len(scales) - 1:
|
||
y_range = [boundaries_y[-1], self.p.img_h]
|
||
d_range = [boundaries_m[-1], 9.0]
|
||
else:
|
||
y_range = [boundaries_y[i-1], boundaries_y[i]]
|
||
d_range = [boundaries_m[i-1], boundaries_m[i]]
|
||
|
||
zones.append({
|
||
"name": name,
|
||
"distance_range": d_range,
|
||
"scale": scale,
|
||
"y_range": y_range
|
||
})
|
||
|
||
return {
|
||
"enabled": True,
|
||
"boundaries": boundaries_y,
|
||
"zones": zones
|
||
}
|
||
|
||
def generate_config(self,
|
||
min_d: float,
|
||
max_d: float,
|
||
boundaries_m: List[float],
|
||
scales: List[float]) -> dict:
|
||
"""生成完整配置"""
|
||
roi = self.calculate_roi(min_d, max_d)
|
||
zones_cfg = self.generate_zones_config(boundaries_m, scales)
|
||
|
||
return {
|
||
"preprocess": {
|
||
"roi": {"enabled": True, **roi["crop"]}
|
||
},
|
||
"face_det": {
|
||
"distance_zones": zones_cfg
|
||
},
|
||
"face_recog": {
|
||
"filters": {
|
||
"distance": {"enabled": True, "min": min_d, "max": max_d},
|
||
"pose": {
|
||
"enabled": True,
|
||
"min_pitch": -15,
|
||
"camera_pitch": self.p.pitch_deg
|
||
}
|
||
}
|
||
},
|
||
"_meta": {
|
||
"focal_px": self.p.focal_px,
|
||
"height": self.p.height,
|
||
"pitch": self.p.pitch_deg,
|
||
"roi_saving": roi["saving_percent"],
|
||
"zone_boundaries_m": boundaries_m,
|
||
"zone_scales": scales
|
||
}
|
||
}
|
||
|
||
def print_report(self,
|
||
min_d: float,
|
||
max_d: float,
|
||
boundaries_m: List[float],
|
||
scales: List[float]):
|
||
"""打印标定报告"""
|
||
print("=" * 60)
|
||
print("相机标定报告(三分区)")
|
||
print("=" * 60)
|
||
print(f"\n【相机参数】")
|
||
print(f" 安装高度 H: {self.p.height}m")
|
||
print(f" 俯仰角 θ: {self.p.pitch_deg}°")
|
||
print(f" 像素焦距 f: {self.p.focal_px}px")
|
||
print(f" 图像尺寸: {self.p.img_w}x{self.p.img_h}")
|
||
|
||
roi = self.calculate_roi(min_d, max_d)
|
||
boundaries_y = self.get_zone_boundaries(boundaries_m)
|
||
|
||
print(f"\n【ROI配置】(检测范围 {min_d}-{max_d}m)")
|
||
print(f" 裁剪: y={roi['crop']['y']}, h={roi['crop']['h']}")
|
||
print(f" 算力节省: {roi['saving_percent']}%")
|
||
|
||
print(f"\n【三分区配置】")
|
||
zone_names = ["近区(3-5m)", "中区(5-7m)", "远区(7-9m)"]
|
||
for i, (name, scale, y_bound) in enumerate(zip(zone_names, scales, boundaries_y)):
|
||
if i < 2:
|
||
print(f" {name}: y<{y_bound}, scale={scale}x")
|
||
else:
|
||
print(f" {name}: y>={boundaries_y[-1]}, scale={scale}x")
|
||
|
||
print(f"\n【距离-像素-人脸大小映射】")
|
||
print(f" {'距离':>6} | {'像素y':>6} | {'原人脸':>8} | {'处理后':>8} | {'区域':>6}")
|
||
print(f" {'-'*50}")
|
||
|
||
for d in [3, 4, 5, 6, 7, 8, 9]:
|
||
y = self.distance_to_pixel(d)
|
||
face_orig = self.estimate_face_size(d)
|
||
|
||
# 确定区域和处理后大小
|
||
zone_idx = 0
|
||
if d >= boundaries_m[1]:
|
||
zone_idx = 2
|
||
elif d >= boundaries_m[0]:
|
||
zone_idx = 1
|
||
|
||
face_proc = face_orig * scales[zone_idx]
|
||
zone_name = ["近", "中", "远"][zone_idx]
|
||
|
||
print(f" {d:>6.0f}m | {y:>6} | {face_orig:>7.0fpx} | {face_proc:>7.0fpx} | {zone_name:>6}")
|
||
|
||
print(f"\n【目标验证】")
|
||
print(f" 三个区处理后的人脸应在 60-80px 范围内")
|
||
print(f" 若偏差较大,请调整 --focal-estimate 参数重新标定")
|
||
|
||
print("\n" + "=" * 60)
|
||
|
||
|
||
def main():
|
||
parser = argparse.ArgumentParser(
|
||
description='相机标定工具 - 生成三分区检测配置',
|
||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||
epilog="""
|
||
示例:
|
||
# 三分区标定(推荐)
|
||
python calibrate_camera.py --height 5.0 --pitch 45 --report
|
||
|
||
# 自定义参数
|
||
python calibrate_camera.py --height 4.5 --pitch 40 --focal-estimate 2000 -o cam.json --report
|
||
|
||
# 调整分区边界
|
||
python calibrate_camera.py --height 5.0 --zones 4 6 8 --scales 0.8 1.0 1.3 --report
|
||
"""
|
||
)
|
||
parser.add_argument('--height', type=float, required=True,
|
||
help='相机安装高度(米),如 5.0')
|
||
parser.add_argument('--pitch', type=float, default=45,
|
||
help='俯仰角(度),默认45')
|
||
parser.add_argument('--focal-estimate', type=float, default=2200,
|
||
help='像素焦距估算值,默认2200(2.5K相机4-6mm镜头)')
|
||
parser.add_argument('--image-size', type=int, nargs=2,
|
||
default=[2560, 1440],
|
||
help='图像尺寸 宽 高,默认 2560 1440')
|
||
parser.add_argument('--range', type=float, nargs=2,
|
||
default=[3.0, 9.0],
|
||
help='检测范围 最小距离 最大距离,默认 3.0 9.0')
|
||
parser.add_argument('--zones', type=float, nargs=3,
|
||
default=[5.0, 7.0],
|
||
help='分区边界距离(米),默认 5.0 7.0(形成3-5,5-7,7-9三区)')
|
||
parser.add_argument('--scales', type=float, nargs=3,
|
||
default=[0.7, 1.0, 1.4],
|
||
help='各分区缩放因子,默认 0.7 1.0 1.4')
|
||
parser.add_argument('-o', '--output', default='camera_calib.json',
|
||
help='输出文件路径,默认 camera_calib.json')
|
||
parser.add_argument('--report', action='store_true',
|
||
help='打印详细报告')
|
||
|
||
args = parser.parse_args()
|
||
|
||
# 验证参数
|
||
if len(args.zones) != 2:
|
||
parser.error("--zones 需要2个边界值(如 5.0 7.0),形成3个区域")
|
||
if len(args.scales) != 3:
|
||
parser.error("--scales 需要3个缩放因子(如 0.7 1.0 1.4)")
|
||
|
||
# 构建完整边界列表(包含起止)
|
||
boundaries = [args.zones[0], args.zones[1]] # 5米和7米分界线
|
||
|
||
# 创建标定器
|
||
params = CameraParams(
|
||
height=args.height,
|
||
pitch_deg=args.pitch,
|
||
focal_px=args.focal_estimate,
|
||
img_w=args.image_size[0],
|
||
img_h=args.image_size[1]
|
||
)
|
||
|
||
calib = CameraCalibrator(params)
|
||
|
||
# 生成用于instances params的配置
|
||
roi = calib.calculate_roi(args.range[0], args.range[1])
|
||
boundaries_y = calib.get_zone_boundaries(boundaries)
|
||
|
||
config = {
|
||
"params": {
|
||
"camera_pitch": args.pitch,
|
||
"roi_enabled": True,
|
||
"roi_y": roi["crop"]["y"],
|
||
"roi_h": roi["crop"]["h"],
|
||
"zones_enabled": True,
|
||
"zone_boundary_5m": boundaries_y[0],
|
||
"zone_boundary_7m": boundaries_y[1],
|
||
"zone_scale_near": args.scales[0],
|
||
"zone_scale_mid": args.scales[1],
|
||
"zone_scale_far": args.scales[2]
|
||
},
|
||
"meta": {
|
||
"height": args.height,
|
||
"pitch": args.pitch,
|
||
"focal_px": args.focal_estimate,
|
||
"roi_saving": roi["saving_percent"],
|
||
"zone_boundaries_m": boundaries,
|
||
"zone_scales": list(args.scales)
|
||
}
|
||
}
|
||
|
||
# 保存配置
|
||
with open(args.output, 'w', encoding='utf-8') as f:
|
||
json.dump(config, f, indent=2, ensure_ascii=False)
|
||
|
||
print(f"[OK] 配置已保存: {args.output}")
|
||
|
||
# 打印报告
|
||
if args.report:
|
||
calib.print_report(args.range[0], args.range[1], boundaries, list(args.scales))
|
||
print(f"\n【可复制到 instances params 的配置】")
|
||
print(json.dumps(config["params"], indent=2, ensure_ascii=False))
|
||
|
||
|
||
if __name__ == '__main__':
|
||
main()
|
||
```
|
||
|
||
---
|
||
|
||
## 附录B: 术语表
|
||
|
||
| 术语 | 英文 | 说明 |
|
||
|------|------|------|
|
||
| ROI | Region of Interest | 感兴趣区域,此处指3-9米对应的画面区域 |
|
||
| LUT | Look-Up Table | 查找表,用于O(1)距离查询 |
|
||
| Zone Boundary | Zone Boundary | 分区边界(5米/7米对应的像素y坐标) |
|
||
| RetinaFace_640 | RetinaFace 640×640 | 640×640输入的人脸检测模型 |
|
||
| 5点关键点 | 5-Point Landmarks | 左眼、右眼、鼻尖、左嘴角、右嘴角 |
|
||
| Templates | Configuration Templates | 配置模板,定义通用节点流水线 |
|
||
| Instances | Configuration Instances | 配置实例,为每路相机传入具体参数 |
|
||
| Pitch | Pitch Angle | 俯仰角,相机光轴与水平面夹角 |
|
||
| Focal Length (px) | Pixel Focal Length | 以像素为单位的焦距 |
|
||
|
||
---
|
||
|
||
**文档结束**
|