首页
商城
  • English
  • 简体中文
首页
商城
  • English
  • 简体中文
  • 产品系列

    • FPGA+ARM

      • GM-3568JHF

        • 一、简介

          • GM-3568JHF 简介
        • 二、快速开始

          • 00 前言
          • 01 环境搭建
          • 02 编译说明
          • 03 烧录指南
          • 04 调试工具
          • 05 软件更新
          • 06 查看信息
          • 07 测试命令
          • 08 应用编译
          • 09 源码获取
        • 三、外设与接口

          • 01 USB
          • 02 显示与触摸
          • 03 以太网
          • 04 WIFI
          • 05 蓝牙
          • 06 TF-Card
          • 07 音频
          • 08 串口
          • 09 CAN
          • 10 RTC
        • 四、应用开发

          • 01 UART读写案例
          • 02 按键检测案例
          • 03 LED灯闪烁案例
          • 04 MIPI屏幕检测案例
          • 05 读取 USB 设备信息案例
          • 06 FAN 检测案例
          • 07 FPGA FSPI 通信案例
          • 08 FPGA DMA 读写案例
          • 09 GPS调试案例
          • 10 以太网测试案例
          • 11 RS485读写案例
          • 12 FPGA IIC 读写案例
          • 13 PN532 NFC读卡案例
          • 14 TF卡读写案例
        • 五、QT开发

          • 01 ARM64交叉编译器环境搭建
          • 02 QT 程序加入开机自启服务
        • 六、RKNN_NPU开发

          • 01 RK3568 NPU 概述
          • 02 开发环境搭建
          • 运行官方 YOLOv5 示例
        • 七、FPGA开发

          • ARM与FPGA通讯
          • FPGA开发手册
        • 八、其他

          • 01 根目录文件系统的修改
          • 02 系统自启服务
        • 九、资料下载

          • 资料下载
    • ShimetaPi

      • M4-R1

        • 一、简介

          • M4-R1简介
        • 二、快速上手

          • 01 OpenHarmony概述
          • 02 镜像烧录
          • 03 应用开发快速上手
          • 04 设备开发快速上手
        • 三、应用开发

          • 01 ArkUI

            • 1 ArkTS语言简介
            • 2 UI 组件-Row 容器介绍
            • 3 UI 组件-Column 容器介绍
            • 4 UI 组件-Text 组件
            • 5 UI 组件-Toggle 组件
            • 6 UI 组件-Slider 组件
            • 7 UI 组件-Animation 组件&Transition 组件
          • 02 资料获取

            • 1 OpenHarmony 官方资料
          • 03 开发须知

            • 1 Full-SDK替换教程
            • 2 引入和使用三方库
            • 3 HDC调试
            • 4 命令行恢复出厂模式
            • 5 升级App为system权限
          • 04 构建第一个应用

            • 1 构建第一个ArkTs应用-HelloWorld
          • 05 案例

            • 01 串口调试助手应用案例
            • 02 手写板应用案例
            • 03 数字时钟应用案例
            • 04 WIFI 信息获取应用案例
        • 四、设备开发

          • 1 Ubuntu环境开发

            • 01 环境搭建
            • 02 下载源码
            • 03 编译源码
          • 2 使用DevEco Device Tool 工具

            • 01 工具简介
            • 02 开发环境的搭建
            • 03 导入SDK
            • 04 HUAWEI DevEco Tool 功能介绍
        • 五、内核外设与接口

          • 01 指南
          • 02 设备树介绍
          • 03 NAPI 入门
          • 04 ArkTS入门
          • 05 NAPI开发实战演示
          • 06 GPIO介绍
          • 07 I2C通讯
          • 08 SPI通信
          • 09 PWM 控制
          • 10 串口通讯
          • 11 TF卡
          • 12 屏幕
          • 13 触摸
          • 14 Ethernet(以太网)
          • 15 M.2 硬盘
          • 16 音频
          • 17 WIFI & BT
          • 18 摄像头
        • 六、资料下载

          • 资料下载
      • M5-R1

        • 一、简介

          • M5-R1 开发文档
        • 二、快速上手

          • 镜像烧录
          • 环境搭建
          • 下载源码
        • 三、外设与接口

          • 树莓派接口
          • GPIO 接口
          • I2C 接口
          • SPI通信
          • PWM控制
          • 串口通讯
          • TF Card
          • 屏幕
          • 触摸
          • 音频
          • RTC
          • Ethernet
          • M.2
          • MINI-PCIE
          • Camera
          • WIFI&BT
        • 四、资料下载

          • 资料下载
      • Pico-G1

        • 一、产品概述

          • 01 芯片与硬件平台介绍
          • 02 SDK版本说明
        • 二、快速入门

          • 01 开发环境搭建
          • 02 镜像编译
          • 03 镜像烧录
          • 04 系统登录
          • 05 网络配置
          • 06 文件传输
          • 07 SDK目录结构
          • 08 部署第一个应用程序
          • 09 部署第一个驱动程序
          • 10 SD卡挂载
        • 三、外设与接口

          • 01 GPIO控制
          • 02 UART串口通信
          • 03 I2C 通信
          • 04 SPI 通信
        • 四、MPP媒体开发

          • 01 MPP媒体处理软件
          • 02 图像处理链路
          • 03 视频输入
          • 04 图像编码
        • 五、NPU与AI

          • 01 NPU驱动与运行库架构
          • 02 .xmm 模型加载
          • 03 SVP视频处理
          • 04 AI降噪 (AI_NR)
        • 六、应用程序示例

          • 01 区域运动检测应用
          • 02 MTCNN 人脸检测应用
    • 开源鸿蒙

      • SC-3568HA

        • 一、简介

          • SC-3568HA简介
        • 二、快速上手

          • OpenHarmony概述
          • 镜像烧录
          • 开发环境准备
          • Hello World应用以及部署
        • 三、应用开发

          • ArkUI

            • 第一章 ArkTS语言简介
            • 第二章 UI组件介绍和实际应用(上)
            • 第三章 UI组件介绍和实际应用(中)
            • 第四章 UI组件介绍和实际应用(下)
          • 拓展

            • 第一章 入门指引
            • 第二章 三方库的引用和使用
            • 第三章 应用编译以及部署
            • 第四章 命令行恢复出厂设置
            • 第五章 系统调试--HDC调试
            • 第六章 APP 稳定性测试
            • 第七章 应用测试
        • 四、设备开发

          • 第一章 环境搭建
          • 第二章 下载源码
          • 第三章 编译源码
        • 五、内核的外设与接口

          • 树莓派接口
          • GPIO 接口
          • I2C 接口
          • SPI通信
          • PWM控制
          • 串口通讯
          • TF Card
          • 屏幕
          • 触摸
          • 音频
          • RTC
          • Ethernet
          • M.2
          • MINI-PCIE
          • Camera
          • WIFI&BT
          • 树莓派拓展板
        • 六、资料下载

          • 资料下载
      • M-K1HSE

        • 一、简介

          • M-K1HSE 简介
        • 二、快速开始

          • 开发环境搭建
          • 源码获取
          • 编译说明
          • 烧录指南
        • 三、应用开发

          • 00 应用开发环境搭建
          • 01 第一个应用-Hello World
        • 四、外设与接口

          • 01 Audio
          • 02 RS485
          • 03 Display
        • 五、系统定制开发

          • 系统移植
          • 系统定制
          • 驱动开发
          • 系统调试
          • OTA升级
        • 六、资料下载

          • 资料下载
    • EVS相机

      • CF-NRS1

        • 一、简介

          • 01-产品介绍
          • 02-相关概念
          • 03-MultiVision Studio 介绍
        • 二、开发

          • 01-ShiMetaPi Hybrid vision SDK 介绍
          • 02-Hybrid_vision_toolkit
          • 03-Hybrid_vision_toolkit API (C++)
          • 04 Hybrid Vision algo
          • 05 Hybrid vision algo API
          • 06 EVS Network Server
          • 07 EVS Time Sync
          • 08 Web Window
        • 三、资料下载

          • 资料下载
        • 四、常见问题

          • 常见问题解决指南
      • CF-CRA2

        • 一、简介

          • CF-NRS2 简介
        • 二、资料下载

          • 资料下载
      • EVS模块

        • 一、相关概念
        • 二、硬件准备与环境配置
        • 三、示例程序使用指南
        • 资料下载
    • AI硬件

      • 1684XB-32T

        • 一、简介

          • AIBOX-1684XB-32简介
        • 二、快速上手

          • 初次使用
          • 网络配置
          • 磁盘使用
          • 内存分配
          • 风扇策略
          • 固件升级
          • 交叉编译
          • 模型量化
        • 三、应用开发

          • 开发简介

            • Sophgo SDK开发
            • SOPHON-DEMO简介
          • 大语言模型

            • 部署Llama3示例
            • Sophon LLM_api_server开发
            • 部署MiniCPM-V-2_6
            • Qwen-2-5-VL图片视频识别DEMO
            • Qwen3-chat-DEMO
            • Qwen3-Qwen Agent-MCP开发
            • Qwen3-langchain-AI Agent
          • 深度学习

            • ResNet(图像分类)
            • LPRNet(车牌识别)
            • SAM(通用图像分割基础模型)
            • YOLOv5(目标检测)
            • OpenPose(人体关键点检测)
            • PP-OCR(光学字符识别)
        • 四、资料下载

          • 资料下载
      • 1684X-416T

        • 简介

          • AIBOX-1684X-416简介
        • Demo简单操作指引

          • shimeta智慧监控demo的简单使用说明
      • RDK-X5

        • 简介

          • RDK-X5 硬件简介
        • 快速开始

          • RDK-X5 快速开始
        • 应用开发

          • AI在线模型开发

            • 实验01-接入火山引擎豆包 AI
            • 实验02-图片分析
            • 实验03-多模态视觉分析定位
            • 实验04-多模态图文比较分析
            • 实验05-多模态文档表格分析
            • 实验06-摄像头运用-AI视觉分析
          • 大语言模型

            • 实验01-语音识别
            • 实验02-语音对话
            • 实验03-多模态图片分析-语音对话
            • 实验04-多模态图片比较-语音对话
            • 实验05-多模态文档分析-语音对话
            • 实验06-多模态视觉运用-语音对话
          • ROS2基础开发

            • 实验01-搭建环境
            • 实验02-工作包的创建及编译
            • 实验03-运行 ROS2 话题通信节点
            • 实验04-ROS2 相机应用
          • 40pin-IO开发

            • 实验01-GPIO 输出(LED闪烁)
            • 实验02-GPIO 输入
            • 实验03-按键控制 LED
            • 实验04-PWM 输出
            • 实验05-串口输出
            • 实验06-IIC 实验
            • 实验07-SPI 实验
          • USB模块开发使用

            • 实验01-USB 语音模块使用
            • 实验02-声源定位模块使用
          • 机器视觉技术实战

            • 实验01-打开 USB 摄像头
            • 实验02-颜色识别检测
            • 实验03-手势识别体验
            • 实验04-YOLOv5物体检测
      • RDK-S100

        • 简介

          • RDK-S100 硬件简介
        • 快速开始

          • RDK-S100 硬件简介
        • 应用开发

          • AI在线模型开发

            • 实验01-接入火山引擎豆包 AI
            • 实验02-图片分析
            • 实验03-多模态视觉分析定位
            • 实验04-多模态图文比较分析
            • 实验05-多模态文档表格分析
            • 实验06-摄像头运用-AI视觉分析
          • 大语言模型

            • 实验01-语音识别
            • 实验02-语音对话
            • 实验03-多模态图片分析-语音对话
            • 实验04-多模态图片比较-语音对话
            • 实验05-多模态文档分析-语音对话
            • 实验06-多模态视觉运用-语音对话
          • ROS2基础开发

            • 实验01-搭建环境
            • 实验02-工作包的创建及编译
            • 实验03-运行 ROS2 话题通信节点
            • 实验04-ROS2 相机应用
          • 40pin-IO开发

            • 实验01-GPIO 输出(LED闪烁)
            • 实验02-GPIO 输入
            • 实验03-按键控制 LED
            • 实验04-PWM 输出
            • 实验05-串口输出
            • 实验06-IIC 实验
            • 实验07-SPI 实验
          • USB模块开发使用

            • 实验01-USB 语音模块使用
            • 实验02-声源定位模块使用
          • 机器视觉技术实战

            • 实验01-打开 USB 摄像头
            • 实验02-图像处理基础
            • 实验03-目标检测
            • 实验04-图像分割
    • 核心板

      • C-3568BQ

        • 简介

          • C-3568BQ 简介
      • C-3588LQ

        • 简介

          • C-3588LQ 简介
      • GC-3568JBAF

        • 简介

          • GC-3568JBAF 简介
      • C-K1BA

        • 简介

          • C-K1BA 简介

02 MTCNN 人脸检测应用

本章节介绍一个基于 GK7206 NPU 的完整人脸检测应用示例 —— face_recognize。该应用利用 MTCNN(Multi-task Cascaded Convolutional Networks)三阶段级联网络,在板端实现实时人脸检测与关键点定位,并通过内嵌 Web 服务器提供浏览器可视化界面。

应用源码位于 SDK 目录 app_sample/face_recognize/,是一个独立的、自包含的示例工程,涵盖了视频采集、NPU 推理、Web 展示的完整链路,适合作为开发自定义 AI 视觉应用的参考模板。

1 应用概述

1.1 功能特性

  • MTCNN 三阶段级联检测:P-Net(粗筛)→ R-Net(精筛)→ O-Net(关键点),逐级过滤人脸候选框
  • 5 点面部关键点定位:左眼、右眼、鼻尖、左嘴角、右嘴角
  • 实时 Web 可视化:内嵌 HTTP 服务器,浏览器直接查看 MJPEG 视频流和检测结果叠加画面
  • 帧间跟踪稳定:基于 IoU 匹配的帧间人脸跟踪 + EMA 平滑,消除检测框抖动
  • 图像金字塔多尺度检测:支持不同尺度的人脸检测

1.2 技术参数

参数值
传感器分辨率2560 × 1440(SC465SL)
检测输入分辨率320 × 180(16:9,匹配传感器宽高比)
NPU 推理帧率~8 FPS(受限于 P-Net 滑窗数量)
Web 服务端口80
视频编码格式MJPEG
模型格式.xmm(GK7206 NPU 专用格式)
最小人脸尺寸48 × 48 像素(检测图中)
级联阈值P-Net: 0.50 / R-Net: 0.70 / O-Net: 0.30

1.3 目录结构

app_sample/face_recognize/
├── Makefile                  # 构建脚本
├── src/
│   └── main.c               # 主程序(~1800 行,含 NPU 推理 + Web 服务)
├── models/
│   ├── pnet.xmm             # P-Net 模型(粗筛,输入 12×12)
│   ├── rnet.xmm             # R-Net 模型(精筛,输入 24×24)
│   └── onet.xmm             # O-Net 模型(关键点,输入 48×48)
└── web/
    ├── index.html            # Web 前端页面
    ├── app.js                # 前端逻辑(轮询 API、绘制检测框)
    └── style.css             # 页面样式

2 编译与部署

2.1 前置条件

在编译本应用之前,请确保已完成以下准备工作:

  1. SDK 环境已搭建:参照 SDK 编译 完成交叉编译工具链和 SDK 配置
  2. SDK 已完整编译一次:应用依赖 SDK 的公共库和头文件,需先执行完整编译生成 out/ 目录
  3. 板端驱动已加载:确保 NPU 内核模块 xm_npu.ko 已加载

2.2 编译应用

进入应用目录,直接使用 SDK 构建系统编译:

# 进入应用目录
cd <SDK_PATH>/app_sample/face_recognize

# 编译(SDK Makefile 会自动使用交叉编译工具链)
make clean && make

编译原理

Makefile 通过 include $(SDK_DIR)/build/base.mk 和 include $(SAMPLE_DIR)/sample_base.mk 引入 SDK 的编译规则,自动配置交叉编译器、头文件路径和链接库。无需手动设置编译工具链。

编译成功后,在当前目录下生成可执行文件 face_recognize。

2.3 部署到板端

相关信息

由于应用程序体积较大,需挂载 SD 卡后运行。

mkdir -p /sd_card
mount /dev/mmcblk1p1 /sd_card

使用 SCP 将以下文件传输到开发板:

目录结构要求

  1. Web 前端文件必须放置在板端 /www/ 目录下(这是 web_server 模块默认的静态文件根目录 WEB_WWW_ROOT)
  2. models 模型文件必须放在应用的工作目录下
# 在开发主机上执行
# 1. 创建板端应用目录
ssh root@<板端IP> "mkdir -p /sd_card/models /www"

# 2. 传输可执行文件
scp face_recognize root@<板端IP>:/sd_card/

# 3. 传输模型文件
scp -Or models root@<板端IP>:/sd_card/

# 4. 传输 Web 前端文件
scp web/* root@<板端IP>:/www/

2.4 运行应用

# 进入应用目录
cd /sd_card

# 添加执行权限(首次)
chmod +x face_recognize

# 运行
./face_recognize

启动后,终端会输出以下日志信息:

=== MTCNN Face Detection for GK7206 ===
[init] sensor: 2560x1440 @ 30fps
[npu] Initializing...
[npu] Found 1 device(s)
[npu] Loading P-Net...
[npu] ./models/pnet.xmm loaded
[npu] Loading R-Net...
[npu] ./models/rnet.xmm loaded
[npu] Loading O-Net...
[npu] ./models/onet.xmm loaded
[npu] All models loaded!
[init] Pipeline: VI→VPSS→VENC(MJPEG) + VPSS→NPU(320x180 MTCNN)
[main] System ready. Open http://<board-ip>/ in browser

2.5 浏览器访问

在电脑浏览器中打开 http://<板端IP>/,即可看到实时的人脸检测画面:

  • 页面中央显示 MJPEG 视频流
  • 检测到的人脸会以绿色矩形框标注
  • 每个人脸框上方显示置信度百分比
  • 人脸关键点(5 点)以蓝色圆点标注,并用虚线连接
  • 底部状态栏显示检测 FPS 和当前人脸数量

2.6 停止应用

在终端按 Ctrl+C 发送 SIGINT 信号即可正常退出。应用会依次停止 NPU 推理线程、卸载模型、释放视频管线资源。

提示

若使用 Ctrl+C 无法正常退出或退出耗时较长,可通过以下命令查看 ./face_recognize 进程的 PID,并强制终止该进程:

ps | grep face_recognize
kill -9 <PID>

3 系统架构

3.1 整体数据流

应用的整体架构遵循「视频采集 → 前处理 → NPU 推理 → Web 展示」的经典嵌入式 AI 视觉管线:

3.2 线程模型

应用采用多线程架构,各线程职责如下:

线程职责关键操作
主线程运行 Web 服务器(阻塞循环)接收 HTTP 请求、分发路由、MJPEG 流推送
NPU 推理线程循环获取帧并执行 MTCNN 检测VPSS 获取帧 → YUV→RGB → MTCNN → 更新检测结果
ISP 线程(SDK 内部)图像信号处理3A(AE/AWB/AF)、降噪、色彩校正

线程间通过 pthread_mutex_t g_face_mutex 互斥锁保护检测结果(g_faces 数组和 g_face_count),确保 NPU 线程写入与 HTTP 线程读取的数据一致性。

4 内部执行逻辑详解

4.1 启动流程

main() 函数的启动流程分为 5 个阶段:

// 阶段 1:初始化视频管线(VI → VPSS → VENC)
init_system();

// 阶段 2:初始化 NPU(加载 3 个 MTCNN 模型)
init_npu();

// 阶段 3:启动 NPU 推理线程
g_npu_running = XMEDIA_TRUE;
pthread_create(&g_npu_thread, NULL, npu_inference_thread, NULL);

// 阶段 4:注册 MJPEG 处理器并启动 Web 服务器(阻塞)
web_server_set_mjpeg_handler(mjpeg_send_stream, mjpeg_request_stop);
web_server_run(project_route_get);

// 阶段 5:停止时清理资源
g_npu_running = XMEDIA_FALSE;
pthread_join(g_npu_thread, NULL);
deinit_npu();
deinit_system();

4.2 视频管线初始化

init_system() 按照以下顺序初始化 MPP 视频管线:

  1. 系统初始化:配置 VB(Video Buffer)内存池

    • Pool 0:VI 采集缓冲区(全分辨率)
    • Pool 1:VPSS 全分辨率 / VENC 编码缓冲区
    • Pool 2:VPSS NPU 输入缓冲区(320 × 180)
  2. 模块初始化:依次初始化 VI、VPSS、VENC 模块

  3. ISP 初始化:配置图像信号处理参数(帧率、像素格式、分辨率等)

  4. VI 启动:启动视频输入,从 Sensor 获取图像

  5. VPSS 配置:配置两个输出通道

    • ochn0:全分辨率输出 → 送 VENC 编码 MJPEG
    • ochn1:缩放到 320 × 180 → 送 NPU 推理
  6. 绑定关系:VI → VPSS → VENC

VI(pipe=0, chn=0) ──bind──→ VPSS(pipe=0, ochn=0) ──bind──→ VENC(chn=0, MJPEG)
                          └→ VPSS(pipe=0, ochn=1) ──手动获取──→ NPU 推理线程

NPU 帧获取方式

VENC 通过 bind 模式自动获取 VPSS 输出帧,而 NPU 推理线程使用 xmedia_vpss_acquire_ochn_frame() 手动获取 VPSS ochn1 的帧。这是因为 NPU 推理速度慢于视频帧率,不适合 bind 模式。

4.3 NPU 模型加载

init_npu() 函数加载 3 个 MTCNN 模型,每个模型的加载流程如下:

// 1. 查询模型所需内存大小
xmedia_cl_graph_querysize_from_file(path, &worksize, &weightsize);

// 2. 分配 MMZ 内存(物理连续内存,NPU DMA 访问需要)
mmz_alloc_map("npu_work",  &work_phy,  &work_buf,  worksize);
mmz_alloc_map("npu_weight", &weight_phy, &weight_buf, weightsize);

// 3. 加载模型到 NPU
xmedia_cl_graph_loadmodel_from_file_withmem(&ctx, path,
    work_buf, worksize, weight_buf, weightsize, &graph);

// 4. 获取输入/输出 Tensor 信息(两趟查询:第 1 次获取数量,第 2 次获取详情)
xmedia_cl_graph_get_input(graph, 0, &input);           // 第 1 次:获取数量
input.tensor = malloc(sizeof(...) * input.num);
xmedia_cl_graph_get_input(graph, input.num, &input);   // 第 2 次:获取详情

// 5. 分配输入/输出缓冲区
mmz_alloc_map("npu_in",  &input_phy,  &input_buf,  inputsize);
mmz_alloc_map("npu_out", &output_phy, &output_buf, outputsize);

// 6. 设置 Tensor 地址并绑定
xmedia_cl_graph_set_inout(graph, &input, &output);

内存分配注意

NPU 需要访问的缓冲区(workspace、weight、input、output)必须使用 xmedia_mmz_alloc() 分配物理连续内存(MMZ),不能使用普通的 malloc()。这是因为 NPU 通过 DMA 直接访问物理内存。

三个 MTCNN 模型的规格如下:

模型输入尺寸输出 Tensor说明
P-Net[1, 3, 12, 12]score(2ch) + bbox(4ch)粗筛网络,通过图像金字塔+滑窗提取候选
R-Net[1, 3, 24, 24]score(2ch) + bbox(4ch)精筛网络,逐候选裁剪推理
O-Net[1, 3, 48, 48]score(2ch) + bbox(4ch) + landmark(10ch)输出网络,精修 + 5 点关键点

4.4 NPU 推理线程

npu_inference_thread() 是核心推理循环,每帧执行以下步骤:

模型推理流程

4.5 MTCNN 检测算法详解

4.5.1 P-Net:图像金字塔 + 滑窗检测

P-Net 是一个全卷积网络,接收 12 × 12 的图像块,判断是否包含人脸。由于人脸大小不一,需要构建图像金字塔实现多尺度检测:

// 构建金字塔:从 12/min_face_size 开始,逐层缩小
scale = (float)PNET_PATCH_SIZE / PNET_MIN_FACE_SIZE;  // 初始: 12/48 = 0.25

while (scaled_image >= 12×12) {
    resize_rgb(src, DET_WIDTH, DET_HEIGHT, scaled, sw, sh);

    // 滑窗扫描
    for (y = 0; y <= sh - 12; y += PNET_STRIDE) {
        for (x = 0; x <= sw - 12; x += PNET_STRIDE) {
            // 提取 12×12 块 → NPU 推理 → 置信度过滤
            // 坐标映射回原始图像 + bbox 回归
        }
    }

    scale *= PNET_SCALE_FACTOR;  // 0.707 (= 1/√2,标准 MTCNN)
}

关键参数说明:

参数值说明
PNET_PATCH_SIZE12P-Net 输入尺寸
PNET_STRIDE6滑窗步长;6=快速,4=平衡,2=最佳召回率
PNET_MIN_FACE_SIZE48最小可检测人脸尺寸(检测图中的像素数)
PNET_SCALE_FACTOR0.707金字塔缩放因子(1/√2)
FACE_CONF_THRESHOLD0.50P-Net 置信度阈值

4.5.2 R-Net:候选精筛

R-Net 接收 P-Net 输出的候选框,从原始图像中裁剪对应区域并缩放到 24 × 24 进行二次筛选:

for (i = 0; i < pnet_count; i++) {
    // 1. 裁剪候选区域并缩放到 24×24
    crop_resize(rgb_img, DET_WIDTH, DET_HEIGHT, &cands[i], crop, 24);

    // 2. 复制到模型输入(处理 NCHW 布局)
    copy_rgb_to_model_input(&g_rnet, crop, 24, 24);

    // 3. NPU 推理
    xmedia_cl_graph_process(g_rnet.graph);

    // 4. 解量化 + 置信度过滤 + bbox 回归
    dequantize_output(&g_rnet, score_idx, s_vals, 2);
    dequantize_output(&g_rnet, box_idx, b_vals, 4);

    if (prob >= 0.70f) {  // R-Net 阈值更高,严格过滤
        bbox_reg(&cands[out_count], b_vals);
        out_count++;
    }
}
// NMS 去重
out_count = nms(cands, out_count, 0.50f);

4.5.3 O-Net:最终精修 + 关键点

O-Net 在 R-Net 基础上进一步精修框位置,并输出 5 个面部关键点:

for (i = 0; i < rnet_count; i++) {
    // 裁剪 → 48×48 → NPU 推理
    crop_resize(rgb_img, DET_WIDTH, DET_HEIGHT, &cands[i], crop, 48);
    copy_rgb_to_model_input(&g_onet, crop, 48, 48);
    xmedia_cl_graph_process(g_onet.graph);

    // 解量化:score(2) + bbox(4) + landmark(10)
    dequantize_output(&g_onet, score_idx, s_vals, 2);
    dequantize_output(&g_onet, box_idx, b_vals, 4);
    dequantize_output(&g_onet, lm_idx, l_vals, 10);

    // 关键点回归(模型输出 planar 格式: [x0..x4, y0..y4])
    for (k = 0; k < 5; k++) {
        landmarks[k*2]     = x1 + l_vals[k]     * bw;  // X 坐标
        landmarks[k*2 + 1] = y1 + l_vals[5 + k] * bh;  // Y 坐标
    }
}

关键点顺序:左眼(LE) → 右眼(RE) → 鼻尖(N) → 左嘴角(ML) → 右嘴角(MR)。

4.6 后处理与帧间跟踪

形状过滤

NMS 后,filter_implausible() 会根据以下启发式规则过滤不合理的检测框:

// 拒绝非人脸形状的框
- 宽高比 < 0.20 或 > 3.0(注意:量化 MTCNN 的 bbox 回归会系统性收窄框)
- 宽或高 < 6 像素(太小的框)
- 面积 > 检测图像 90%(太大的框)

帧间跟踪

update_tracks() 实现基于 IoU 匹配的简单帧间跟踪,用于:

  1. 稳定显示:通过 EMA(指数移动平均)平滑检测框位置,消除帧间抖动
  2. 延迟消失:人脸暂时被遮挡或检测丢失时,保持显示若干帧(MAX_MISS = 3)
  3. 唯一 ID:每条跟踪记录分配唯一 ID
// 每帧处理流程:
// 1. 用 IoU 匹配新检测与已有跟踪
// 2. 匹配成功 → EMA 平滑更新位置
//    匹配失败 → 新建跟踪 或 miss_count++
// 3. miss_count > MAX_MISS → 移除跟踪

4.7 输出量化与反量化

NPU 输出的是 INT8 量化数据,需要反量化为浮点值才能使用:

// 反量化公式
float_value = (int8_value - zero_point) × scale

// 代码实现
static void dequantize_output(const npu_model_t *m, int tensor_idx,
                              float *out, int count)
{
    unsigned char *data = get_output_data(m, tensor_idx);
    float scale = m->output.tensor[tensor_idx].quant.scale;
    int zp = m->output.tensor[tensor_idx].quant.zp;
    for (i = 0; i < count; i++)
        out[i] = ((float)data[i] - zp) * scale;
}

4.8 Web 服务与 API

应用内嵌了一个轻量级 HTTP 服务器(web_server.c),提供以下路由:

路由方法功能
/GET返回 index.html 页面
/mjpegGETMJPEG 视频流(multipart/x-mixed-replace)
/api/facesGET返回 JSON 格式的检测结果
/api/statusGET返回简要状态信息
/style.css、/app.jsGET静态资源文件

/api/faces 返回的 JSON 格式:

{
  "count": 2,
  "fps": 18,
  "faces": [
    {
      "x": 0.3125,
      "y": 0.2056,
      "w": 0.1875,
      "h": 0.3333,
      "score": 0.998,
      "landmarks": [
        { "x": 0.35, "y": 0.29 },
        { "x": 0.44, "y": 0.285 },
        { "x": 0.395, "y": 0.34 },
        { "x": 0.36, "y": 0.41 },
        { "x": 0.43, "y": 0.405 }
      ]
    }
  ]
}

其中坐标值均已归一化到 0.0 ~ 1.0 范围(相对于检测图像尺寸)。

5 如何编写类似应用

本节以 face_recognize 为参考模板,讲解如何开发一个基于 GK7206 NPU 的自定义 AI 视觉应用。

5.1 开发步骤总览

步骤 1: 准备模型 → 训练/下载模型 → XMTVM 工具转换为 .xmm 格式
步骤 2: 创建工程 → 复制 Makefile 模板 → 编写 main.c
步骤 3: 初始化管线 → VI + VPSS + VENC 配置
步骤 4: 加载模型 → xmedia_cl_* API 加载 .xmm
步骤 5: 推理循环 → 获取帧 → 前处理 → NPU 推理 → 后处理
步骤 6: 结果输出 → Web 展示 / RTSP 推流 / 其他方式

5.2 步骤 1:准备模型

使用 XMTVM 模型转换工具将训练好的模型(ONNX/PyTorch)转换为 GK7206 NPU 专用的 .xmm 格式:

# 示例:使用 XMTVM 工具转换模型
python3 convert.py --model yolov5s.onnx \
    --input_shape 1,3,320,180 \
    --input_format RGB \
    --quantize int8 \
    --output model.xmm

模型转换注意事项

  • 模型的 input_format 设置决定了输入数据格式(RGB/YUV 等)
  • 量化方式(INT8/UINT8)会影响推理精度和后处理的反量化参数
  • 建议在 YAML 配置中指定 (pixel - 127.5) / 128 归一化,由 NPU 内部完成,减轻 CPU 负担

5.3 步骤 2:创建工程

参照 face_recognize 的目录结构创建新工程:

mkdir -p my_app/src my_app/models my_app/web

编写 Makefile(可直接复制 face_recognize/Makefile 并修改):

ifeq ($(CFG_SDK_EXPORT_FLAG),)
        SDK_DIR := $(shell cd $(CURDIR)/../.. && /bin/pwd)
endif

include $(SDK_DIR)/build/base.mk
include $(SAMPLE_DIR)/sample_base.mk

TARGET := my_app                    # 修改为你的应用名

LIBS := -lxmedia_svp -lxmedia_npu $(SAMPLE_LIBS) $(SAMPLE_COMMON_LIB) -lpthread

INCLUDES := $(SAMPLE_INCLUDES)
INCLUDES += -I$(SDK_DIR)/project/common    # 如果使用 web_server.c

CFLAGS := $(SAMPLE_CFLAGS) $(LIBS) $(INCLUDES)

SRCS := $(wildcard src/*.c) $(SDK_DIR)/project/common/web_server.c
OBJS := $(patsubst %.c, %.o, $(SRCS))

.PHONY: all clean

all: $(OBJS)
	$(AT)$(CC) -o $(TARGET) $^ $(CFLAGS)

%.o : %.c
	$(AT)$(CC) -c -o $@ $< $(CFLAGS)

clean:
	$(AT)rm -rf $(OBJS) $(TARGET)

5.4 步骤 3:初始化视频管线

视频管线的初始化代码可以直接复用 face_recognize 的 init_system() 函数框架,需要根据实际需求调整以下参数:

// 1. 根据你的模型输入尺寸调整 NPU 检测分辨率
#define DET_WIDTH   320     // 匹配模型输入宽度
#define DET_HEIGHT  180     // 匹配模型输入高度

// 2. VB 内存池配置(3 个 Pool)
//    - Pool 0: VI 采集缓冲
//    - Pool 1: VPSS 全分辨率 / VENC
//    - Pool 2: VPSS NPU 输入(DET_WIDTH × DET_HEIGHT)

// 3. VPSS 输出通道配置
//    - ochn0: 全分辨率 → VENC (用于 Web 展示)
//    - ochn1: 缩放到 DET_WIDTH×DET_HEIGHT → NPU 推理

5.5 步骤 4:加载 NPU 模型

加载模型的代码可以直接复用 load_one_model() 函数。该函数封装了完整的模型加载流程:

// 定义模型结构体(封装 graph + tensor + buffer)
typedef struct {
    xmedia_cl_graph graph;
    xmedia_cl_tensor_info_inout input;
    xmedia_cl_tensor_info_inout output;
    void *work_buf, *weight_buf, *input_buf, *output_buf;
    xmedia_u64 work_phy, weight_phy, input_phy, output_phy;
} npu_model_t;

// 加载模型
npu_model_t my_model;
load_one_model(&my_model, g_cl_ctx, "./models/my_model.xmm");

5.6 步骤 5:编写推理循环

推理循环的核心模式是「获取帧 → 前处理 → NPU 推理 → 后处理」:

void *inference_thread(void *arg)
{
    while (running) {
        // 1. 从 VPSS 获取帧
        xmedia_vpss_acquire_ochn_frame(pipe, ochn, &frame, timeout);

        // 2. 前处理(根据模型需求)
        //    - mmap 帧数据到用户空间
        //    - YUV → RGB 转换(如果模型需要 RGB)
        //    - resize / crop 到模型输入尺寸
        //    - 处理数据布局(HWC → NCHW 等)
        void *frame_y  = xmedia_mmz_map(frame.addr.y_phy_addr, ...);
        void *frame_uv = xmedia_mmz_map(frame.addr.c_phy_addr, ...);
        yuv420sp_to_rgb888(frame_y, frame_uv, rgb_buf, ...);
        copy_rgb_to_model_input(&model, rgb_buf, width, height);

        // 3. 执行 NPU 推理
        xmedia_cl_graph_process(model.graph);

        // 4. 后处理
        //    - 从输出 buffer 读取并反量化
        dequantize_output(&model, tensor_idx, output, count);
        //    - 解析检测结果(NMS、阈值过滤等)
        //    - 更新共享状态(加锁保护)

        // 5. 释放帧
        xmedia_vpss_release_ochn_frame(pipe, ochn, &frame);
        xmedia_mmz_unmap(frame_y);
        xmedia_mmz_unmap(frame_uv);
    }
}

5.7 关键编程要点

MMZ 内存管理

NPU 相关的缓冲区必须使用 MMZ(Media Memory Zone)分配物理连续内存:

// 分配 + 映射
xmedia_u64 phy = xmedia_mmz_alloc(mmz_name, buf_name, size);
void *virt = xmedia_mmz_map(phy, size, cached);

// 使用完毕后
xmedia_mmz_unmap(virt);
xmedia_mmz_free(phy);

数据布局转换

NPU 模型通常使用 NCHW 布局 [1, C, H, W],而相机输出的 RGB 数据是 HWC 布局。copy_rgb_to_model_input() 自动检测模型布局并进行转换:

if (model_input_is_nchw(m)) {
    // HWC → NCHW: 分离 R/G/B 三个通道平面
    for (c = 0; c < 3; c++)
        for (y = 0; y < h; y++)
            for (x = 0; x < w; x++)
                dst[c*plane + y*w + x] = src[(y*w + x)*3 + c];
} else {
    memcpy(dst, src, w * h * 3);  // 直接拷贝
}

量化输出处理

NPU 输出的是量化后的整数数据,需要根据输出 Tensor 的量化参数(scale 和 zero_point)进行反量化:

float scale = m->output.tensor[idx].quant.scale;
int zp = m->output.tensor[idx].quant.zp;
float value = (float)(int8_data[i] - zp) * scale;

输出 Tensor 索引

当模型有多个输出 Tensor 时,需要通过通道数来识别各输出的含义:

// 通过输出通道数查找对应的 Tensor 索引
int score_idx = find_output_by_ch(&model, 2);    // 2 通道 = 分类得分
int box_idx   = find_output_by_ch(&model, 4);    // 4 通道 = bbox 回归
int lm_idx    = find_output_by_ch(&model, 10);   // 10 通道 = 5 点关键点

5.8 常见问题排查

问题可能原因解决方案
NPU 加载模型失败.xmm 文件路径错误或格式不匹配检查路径是否正确,确认模型输入尺寸与代码匹配
检测不到人脸置信度阈值过高 / YUV→RGB 转换错误降低阈值调试;dump 首帧 RGB 验证颜色转换
检测框位置偏移bbox 回归坐标未正确映射检查坐标从缩放图像到原图的映射计算
帧率过低P-Net 滑窗步长太小 / 金字塔层数过多增大 PNET_STRIDE 或 PNET_MIN_FACE_SIZE
Web 页面无法访问前端文件未放置在正确目录确保 HTML/JS/CSS 在 Web 服务器的静态文件目录中
MMZ 分配失败系统内存不足减少 VB Pool 数量或块大小

6 参考链接

  • NPU 驱动与运行库架构
  • .xmm 模型加载详解
  • MPP 媒体处理平台
  • SDK 编译指南
  • 文件传输
在 GitHub 上编辑此页
上次更新:
贡献者: ljh
Prev
01 区域运动检测应用