运行硬件:地平线 RDK X3 / X5 模型:fcos_512x512_nv12.bin(COCO80 类目标检测)

核心库:

hobot_dnn(BPU 推理)、

libpostprocess.so(C 语言写的 FCOS 后处理)=》 对模型预测的结果张量进一步处理,便于输出。

hobot_vio(HDMI 显示)

一、整体流程一句话总结

打开 USB 摄像头 → 取画面 → 缩放成 512×512 → BGR 转 NV12 格式 → 交给 BPU 跑 FCOS 检测 调用底层 C 库解析检测框 在原图画上框、类别、置信度 → NV12 送 HDMI 屏幕实时显示,并打印 FPS。

重点:后处理不在 Python 写,而是调用系统自带libpostprocess.so动态库,Python 只负责封装结构体、传指针。

二、分段通俗拆解

1. 开头库导入、信号处理

python

运行

signal.signal(signal.SIGINT, signal_handler)

作用:Ctrl+C 能正常退出程序,不会卡死。

2. 一堆 class ctypes.Structure(最关键难点)

python

运行

class FcosPostProcessInfo_t(ctypes.Structure):
    _fields_ = [
        ("height",ctypes.c_int),
        ("width",ctypes.c_int),
        ("ori_height",ctypes.c_int),
        ("ori_width",ctypes.c_int),
        ("score_threshold",ctypes.c_float),
        ("nms_threshold",ctypes.c_float),
        ("nms_top_k",ctypes.c_int),
        ("is_pad_resize",ctypes.c_int)
    ]

✅ 通俗解释: Python 和 C 语言 libpostprocess.so 不能直接传对象。 这些类 = 在 Python 里复刻 C 语言的结构体内存布局,方便把参数、张量内存指针传给底层 C 函数。

output_tensors 结构体用来把 BPU 推理输出张量的内存地址、量化参数、shape 传给 C 后处理。

3. 加载 C 后处理库

python

运行

libpostprocess = ctypes.CDLL('/usr/lib/libpostprocess.so')

地平线官方预编译库,实现两件核心函数:

  1. FcosdoProcess():解析 FCOS 三层输出(偏移值、置信度、中心度)、做 NMS 过滤框
  2. FcosPostProcess():返回 JSON 格式检测结果字符串

❗ 如果你想自己用 Python 手写 FCOS 后处理,可以不用这个 so 库,但速度慢很多;官方 Demo 为了性能直接调用 C 实现。

4. 工具函数

(1)bgr2nv12_opencv(image)

摄像头读到的图片是 OpenCV 标准 BGR 格式 但是模型fcos_512x512_nv12.bin要求输入是 NV12(YUV420) 函数功能:BGR → I420 → 重组 UV 通道 → 输出 NV12 一维数组,直接送入 BPU 推理。

为什么用 NV12?MIPI 摄像头原生输出 NV12,避免色彩格式转换开销,BPU 推理更快。

(2)get_classes()

COCO 数据集 80 个类别名称:person、car、dog、cell phone……

(3)draw_bboxs()

接收 C 库返回的检测框 JSON,在画面上画矩形框、类别名称 + 置信度。 包含坐标缩放:模型输入是 512×512,最终画面是 HDMI 分辨率(1080P 等),必须把 512 尺度下的框坐标映射回显示屏尺寸。

(4)find_first_usb_camera()

自动扫描/dev/video*,找到第一个可用 USB 摄像头;命令行也可以手动指定摄像头节点。

(5)get_display_res()

读取 HDMI 显示器分辨率(如 1920×1080)。

5. 主程序启动部分

python

运行

models = dnn.load('../models/fcos_512x512_nv12.bin')

加载地平线编译好的 BPU 离线模型.bin 文件。 ⚠️ 不能在 PC 运行!只能 RDK 开发板加载。

然后打印模型输入输出张量信息,调试用。

6. USB 摄像头初始化

python

运行

cap.set(cv2.CAP_PROP_FOURCC, codec)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)

打开 USB 摄像头,设置采集分辨率 640×480 MJPG

注意:摄像头采集分辨率 ≠ 模型输入分辨率;画面会被 resize 到 512×512 送入网络。

7. HDMI 显示初始化

python

运行

disp = srcampy.Display()
disp.display(0, disp_w, disp_h)

地平线hobot_vio模块,直接操作硬件 HDMI 输出。 最终画好框的图片转 NV12 调用 disp.set_img() 推送到屏幕。

8. FCOS 推理参数配置(结构体填充)

python

运行

fcos_postprocess_info.height = 512
fcos_postprocess_info.width = 512
fcos_postprocess_info.score_threshold = 0.5
fcos_postprocess_info.nms_threshold = 0.6
  • score_threshold=0.5:置信度低于 0.5 的物体直接丢弃
  • nms_threshold=0.6:重叠大于 0.6 的框进行抑制,去掉重复检测
  • is_pad_resize=0:图片直接拉伸 resize,没有 padding 填充

9. 主循环 while True(程序核心流水线)

python

运行

# 1. 读取摄像头一帧
ret ,frame = cap.read()

# 2. 原图缩放至模型输入尺寸512×512
resized_data = cv2.resize(frame, (512,512))

# 3. BGR图片转为NV12一维数组
nv12_data = bgr2nv12_opencv(resized_data)

# 4. BPU推理(送入硬件加速)
outputs = models[0].forward(nv12_data)

outputs 一共 15 个张量: FCOS 使用 5 个尺度(stride=8,16,32,64,128) 每层输出 3 组数据框偏移值 / 类别置信度 / 中心度 5 × 3 =15 个输出 tensor,和代码完全对应。

python

运行

# 5. 循环调用C库 FcosdoProcess,逐层解析输出
for i in range(len(strides)):
    libpostprocess.FcosdoProcess(...)

Python 把每个张量的内存指针塞进结构体传给 C 函数,底层做解码、NMS。

python

运行

# 6. 获取JSON检测结果
result_str = get_Postprocess_result(ctypes.pointer(fcos_postprocess_info))
data = json.loads(result_str[14:])

C 函数返回字符串,前面有一段前缀字符result_str[14:]切掉前缀,得到标准 JSON 数组,内容示例:

json

[
 {"bbox":[x1,y1,x2,y2],"score":0.87,"id":0,"name":"person"},
 {"bbox":[x1,y1,x2,y2],"score":0.72,"id":2,"name":"car"}
]

python

运行

#7. 在原始摄像头画面绘制检测框
box_bgr = draw_bboxs(frame, data, 512,512, disp_w, disp_h)

#8. BGR画面再次转为NV12,送到HDMI显示
box_nv12 = bgr2nv12_opencv(box_bgr)
disp.set_img(box_nv12.tobytes())

10. FPS 统计

每 10 秒统计总帧数,打印实时帧率,方便评估 BPU 性能。

三、关键坑点总结(实战重点)

  1. 模型强制输入 NV12 不要传入 RGB/BGR 直接推理,必须调用bgr2nv12_opencv转换,否则检测全部错乱。

  2. 坐标两套尺寸,极易画歪框 网络内部坐标是 512×512;显示画面是 HDMI 分辨率,draw_bboxs内部做 scale 映射。 如果缩放参数写错,框和物体错位。

  3. 依赖 libpostprocess.so 固件精简版本缺少这个库会直接报错; 备选方案:完全移除 ctypes 代码,用 Python 手动实现 FCOS 后处理(牺牲帧率)。

  4. 15 个输出张量顺序不能乱 顺序固定:5 层偏移 → 5 层置信度 →5 层中心度,代码下标i、i+5、i+10严格对应。

  5. USB 摄像头采集分辨率≠模型输入分辨率 代码摄像头采集 640×480,再缩放至 512 送入网络,你可以自由修改采集分辨率。

四、极简运行链路流程图

USB 摄像头 (BGR 640×480) ↓ cv2.resize → 512×512 BGR ↓ bgr2nv12 () → NV12 ↓ models [0].forward () → BPU 硬件推理 →15 个输出 tensor ↓ ctypes 传递内存指针 → libpostprocess.so 解析 FCOS、NMS ↓ 输出 JSON 检测框 ↓ 在原始画面绘制方框文字 ↓ BGR 转 NV12 → HDMI 实时输出画面

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐