NanoTrack C++ --- RK3588 实时目标跟踪
基于 Rockchip RK3588 芯片的纯硬件加速 NanoTrack 单目标跟踪器。利用 MPP 硬解码 + RGA 格式转换 + 三核 NPU 并行推理,实现对 H.264 视频流的实时目标跟踪。
项目地址:https://github.com/jiangguolong/NanoTrack-C-for-RK3588/tree/main
演示效果
- 输入: H.264 裸码流 (1920×1080)
- 输出: 逐帧绘制跟踪框的图片序列
- NPU 三核并行: T-backbone (Core 0) + X-backbone (Core 1) + Head (Core 2)
整体架构
┌─────────────────────────────────────────────────────────────────┐
│ main.cpp (双线程) │
│ │
│ 线程1: 解码线程 线程2: 推理线程 │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ MPP 硬解码 H.264 │ │ NanoTracker │ │
│ │ (NV12 帧) │ │ │ │
│ │ ↓ │ 队列 │ init() 模板分支 │ │
│ │ RGA 硬转换 │ ───→ │ T-backbone │ │
│ │ (NV12→RGB888) │ ←─── │ (NPU Core 0) │ │
│ │ ↓ │ 条件变量 │ ↓ │ │
│ │ DMA 缓冲池(×4) │ │ track() 搜索分支 │ │
│ │ push → queue │ │ X-backbone │ │
│ └──────────────────┘ │ (NPU Core 1) │ │
│ │ ↓ │ │
│ │ Head 融合网络 │ │
│ │ (NPU Core 2) │ │
│ │ ↓ │ │
│ │ 后处理 → bbox │ │
│ │ ↓ │ │
│ │ 画框 + 保存图片 │ │
│ └──────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
特性
- 全硬件加速: MPP 硬解码 → RGA 硬转换 → NPU 硬推理,CPU 仅做轻量级后处理
- 三核 NPU 并行: T-backbone / X-backbone / Head 分别部署在 NPU Core 0/1/2
- DMA 零拷贝: 解码→RGA→NPU 全程使用 DMA-BUF 传递,避免 CPU 拷贝
- 多线程流水线: 解码线程和推理线程通过有界队列异步协作
- 边界 Padding: 当目标靠近图像边缘时,用通道均值填充,保证模型输入一致性
- EMA 平滑: 指数移动平均更新跟踪框,抑制抖动
- 尺度+长宽比惩罚: 抑制跟踪框大小和形状的剧烈突变
硬件要求
| 组件 | 要求 |
|---|---|
| 芯片 | Rockchip RK3588 |
| NPU | 三核 (Core 0/1/2),每个至少 1 TOPS |
| 内存 | ≥ 4 GB |
| 系统 | Linux (aarch64),Glibc ≥ 2.29 |
软件依赖
| 库 | 用途 |
|---|---|
| RKNN2 | NPU 推理运行时 |
| MPP | 硬件视频解码 |
| librga | RGA 硬件图像处理 |
| OpenCV 4.x | 图像裁剪/缩放/绘制 (CPU) |
| GCC 9+ (aarch64交叉编译) | C++17 编译 |
快速开始
1. 克隆仓库
bash
git clone https://github.com/YOUR_USERNAME/NanoTrack-Cpp.git
cd NanoTrack-Cpp
2. 准备模型文件
将 RKNN 模型放入 weights/ 目录:
weights/
├── track_backbone_T.rknn # 模板分支 backbone
├── track_backbone_X.rknn # 搜索分支 backbone
└── head.rknn # 融合 head 网络
3. 交叉编译
修改 CMakeLists.txt 中的 RKNN_MODEL_ZOO_ROOT 为你的实际路径,然后:
bash
mkdir build && cd build
cmake .. && make -j$(nproc)
4. 运行
bash
./nanotrack_demo test.h264 ./output_frames/
参数说明:
| 参数 | 默认值 | 含义 |
|---|---|---|
argv[1] |
test.h264 |
H.264 裸码流路径 |
argv[2] |
./output_frames |
输出图片目录 |
项目结构
.
├── CMakeLists.txt # CMake 构建配置 (交叉编译)
├── README.md
├── include/
│ ├── bbox.h # 辅助边界框函数
│ ├── config.h # 超参数配置
│ ├── dma_alloc.h # DMA 缓冲区分配 API
│ ├── mpp_decoder.h # MPP 硬件解码器
│ ├── nanotrack.h # NanoTrack 跟踪器
│ └── rknn_model.h # RKNN 模型封装
├── src/
│ ├── main.cpp # 主程序 (双线程框架)
│ ├── nanotrack.cpp # 跟踪器实现 (预处理 + 后处理)
│ ├── rknn_model.cpp # RKNN 推理封装
│ └── hw_decoder/
│ ├── dma_alloc.cpp # DMA 缓冲区分配实现
│ └── mpp_decoder.cpp # MPP 解码器实现
└── weights/ # RKNN 模型文件 (需自行准备)
├── track_backbone_T.rknn
├── track_backbone_X.rknn
└── head.rknn
NanoTrack 算法流程
NanoTrack 是一种基于 Siamese 网络的单目标跟踪算法,分为两个阶段:
初始化阶段 (init)
用户框选目标 (x, y, w, h)
↓
计算上下文裁剪区域 s_z (含 50% 背景扩展)
↓
Crop + Resize → 127×127 BGR 图像
↓
T-backbone NPU 推理 → 模板特征图 [48×8×8]
↓ 存储为 t_output_feature,后续帧复用
跟踪阶段 (track)
当前帧 + 上一帧目标位置 → 计算搜索区域 s_x
↓
Crop + Resize → 255×255 BGR 图像
↓
X-backbone NPU 推理 → 搜索特征图 [48×16×16]
↓
Head 融合 (模板特征 + 搜索特征) NPU 推理
↓
├─ Score 输出 [2×16×16] → Softmax → 前景概率
└─ BBox 输出 [4×16×16] → 解码 → 边界框坐标
↓
尺度惩罚 + 长宽比惩罚 + Hanning 窗权重
↓
选最高分 → 反归一化 → EMA 平滑 → 边界裁剪
↓
输出最终跟踪框 (x, y, w, h, score)
关键公式
Softmax 置信度:
p_score = exp(s1) / (exp(s0) + exp(s1))
边界框解码 (相对于锚点):
x1 = px - dx1 y1 = py - dy1
x2 = px + dx2 y2 = py + dy2
尺度惩罚:
s_c = max(sz_pred / sz_orig, sz_orig / sz_pred)
penalty = exp(-(r_c × s_c - 1) × PENALTY_K)
EMA 平滑更新:
lr = penalty × p_score × LR
new_size = old_size × (1 - lr) + pred_size × lr
多线程设计
解码线程 (Producer) 推理线程 (Consumer)
│ │
MPP 解码一帧 等待队列非空 (wait)
│ │
队列满? ──wait──→ (休眠) 取出队首帧
│ │
push 到队列 init / track 推理
│ │
notify 消费者 画框 + 保存图片
│ │
↓ notify 生产者
循环 ↓
循环
MAX_QUEUE_SIZE = 2: 限制缓存,防止内存膨胀g_is_running: 全局退出信号,协调两个线程安全退出std::condition_variable: 条件变量实现阻塞等待/唤醒
配置参数
在 include/config.h 中可调整跟踪行为:
| 参数 | 默认值 | 含义 |
|---|---|---|
POINT::STRIDE |
16 | 锚点步长 |
TRACK::EXEMPLAR_SIZE |
127 | 模板裁剪尺寸 |
TRACK::INSTANCE_SIZE |
255 | 搜索区域裁剪尺寸 |
TRACK::BASE_SIZE |
7 | score 图基础尺寸 |
TRACK::CONTEXT_AMOUNT |
0.5 | 上下文扩展比例 |
TRACK::PENALTY_K |
0.15 | 尺度/长宽比惩罚强度 |
TRACK::WINDOW_INFLUENCE |
0.49 | 中心偏好权重 |
TRACK::LR |
0.385 | EMA 平滑学习率 |
代码统计
语言: C++17
总行数: ~1100 行
文件数: 7 头文件 + 5 源文件
| 文件 | 行数 | 职责 |
|---|---|---|
src/nanotrack.cpp |
283 | 核心跟踪算法 |
src/main.cpp |
134 | 主线程框架 |
src/hw_decoder/mpp_decoder.cpp |
173 | MPP 硬解码 + RGA 转换 |
src/hw_decoder/dma_alloc.cpp |
150 | DMA 物理内存分配 |
src/rknn_model.cpp |
84 | RKNN 模型加载/推理 |
致谢
- 本项目参考了 NanoTrack-RK3588-python 的 Python 实现
- 模型权重基于 STARK 的 NanoTrack 变体
License
MIT License