环境准备好之后,接下来利用瑞芯微的NPU进行道路视频中的车辆识别,根据识别到的车辆的数量估计道路的流量情况,实现智慧交通中的流量监控功能。
YOLOv8的RKNN模型
我们使用YOLOv8框架进行目标的检测。YOLOv8(You Only Look Once version 8)是一个深度学习框架,用于实现实时对象检测。YOLOv8 继承了前代模型的优点,并在此基础上进行了多项改进,包括更复杂的网络架构、更优化的训练流程和更强大的特征提取能力。
瑞芯微在其Github仓库中提供了大量已经优化和测试验证过的模型,其中就包括YOLOv8:
https://github.com/airockchip/rknn_model_zoo/blob/main/examples/yolov8/README.md。
瑞芯微提供的模型是一个优化后的模型,与官方原始模型不同。以yolov8n.onnx为例来展示它们之间的差异。
1、它们输出信息的对比如下。左边是官方原始模型的输出,右边是优化后的模型输出。如图所示,原始模型的输出被分为三个部分。例如,在输出集合([1,64,80,80],[1,80,80,80],[1,1,80,80])中,[1,64,80,80]是边界框的坐标,[1,80,80,80]是对应于80个类别的边界框置信度,而[1,1,80,80]是80个类别置信度的总和。
请注意,这里的解释是基于常见目标检测模型(如YOLO系列)的输出格式,具体细节(如维度含义)可能因模型版本或实现而异。但一般来说,上述解释提供了关于YOLO类模型输出结构的通用理解。
2、以输出集合([1,64,80,80],[1,80,80,80],[1,1,80,80])为例,瑞芯微在模型中移除了两个卷积节点之后的子图,保留了这两个卷积的输出([1,64,80,80],[1,80,80,80]),并增加了一个reducesum+clip分支来计算80个类别置信度的总和([1,1,80,80])。
这里的“reducesum”操作通常用于对某个维度上的元素进行求和,而“clip”操作用于限制求和结果的取值范围,以避免数值溢出或保持数值在特定范围内。
瑞芯微提供的YOLOv8模型的训练方法和官方的完全相同,只是在导出的时候做了一些修改,有关导出 RKNPU 适配模型说明请见:https://github.com/airockchip/ultralytics_yolov8/blob/main/RKOPT_README.zh-CN.md。
程序的部署
YOLOv8程序在RK3588上的部署,我们参考了风筝2100的博文https://blog.csdn.net/weixin_42206548/article/details/138507491,在此表示感谢。
和RKNN_model_zoo 中的
examples 提供的YOLOv8 的相关 demo,该程序有两点改进:
1)借助rknn-multi-threaded(https://github.com/leafqycc/rknn-multi-threaded)使用多线程推理提高NPU的占用率,参考:https://blog.csdn.net/2401_84011132/article/details/137803348
2)优化了Python 后处理部分去除PyTorch 依赖,将后处理耗时从几百毫秒降低到了几十毫秒。
在 main.py 文件中,可以修改模型、线程数,还可以修改成实时推理摄像头。
cap = cv2.VideoCapture('./720p60hz.mp4')
cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FRAME_WIDTH,640)cap.set(cv2.CAP_PROP_FRAME_HEIGHT,480)
程序启动后会显示RNKK的相关信息:
python3 main.py
I RKNN: [19:25:32.433] RKNN Runtime Information, librknnrt version: 2.0.0b0 (35a6907d79@2024-03-24T10:31:14)
I RKNN: [19:25:32.433] RKNN Driver Information, version: 0.9.3
I RKNN: [19:25:32.434] RKNN Model Information, version: 6, toolkit version: 1.6.0+81f21f4d(compiler version: 1.6.0 (585b3edcf@2023-12-11T07:42:56)), target: RKNPU v2, target platform: rk3588, framework name: ONNX, framework layout: NCHW, model inference type: static_shape
./rknnModel/yolov8s.rknn done
I RKNN: [19:25:32.644] RKNN Runtime Information, librknnrt version: 2.0.0b0 (35a6907d79@2024-03-24T10:31:14)
I RKNN: [19:25:32.644] RKNN Driver Information, version: 0.9.3
I RKNN: [19:25:32.644] RKNN Model Information, version: 6, toolkit version: 1.6.0+81f21f4d(compiler version: 1.6.0 (585b3edcf@2023-12-11T07:42:56)), target: RKNPU v2, target platform: rk3588, framework name: ONNX, framework layout: NCHW, model inference type: static_shape
./rknnModel/yolov8s.rknn done
I RKNN: [19:25:32.770] RKNN Runtime Information, librknnrt version: 2.0.0b0 (35a6907d79@2024-03-24T10:31:14)
I RKNN: [19:25:32.770] RKNN Driver Information, version: 0.9.3
I RKNN: [19:25:32.771] RKNN Model Information, version: 6, toolkit version: 1.6.0+81f21f4d(compiler version: 1.6.0 (585b3edcf@2023-12-11T07:42:56)), target: RKNPU v2, target platform: rk3588, framework name: ONNX, framework layout: NCHW, model inference type: static_shape
./rknnModel/yolov8s.rknn done
流量统计
我们在每帧推理结束后,统计其中"car"、"motorbike "、 "bus"和"truck"对象的数量,作为流量统计的依据。
def myFunc(rknn_lite, IMG):
IMG2 = cv2.cvtColor(IMG, cv2.COLOR_BGR2RGB)
IMG2, ratio, padding = letterbox(IMG2)
IMG2 = np.expand_dims(IMG2, 0)
outputs = rknn_lite.inference(inputs=[IMG2],data_format=['nhwc'])
boxes, classes, scores = yolov8_post_process(outputs)
global car_num
global truck_num
global motorbike_num
global bus_num
car_num = 0
truck_num = 0
motorbike_num = 0
bus_num = 0
if classes is not None:
for box, score, cl in zip(boxes, scores, classes):
if CLASSES[cl] == 'car':
car_num = car_num + 1
elif CLASSES[cl] == 'motorbike':
motorbike_num = motorbike_num + 1
elif CLASSES[cl] == 'bus':
bus_num = bus_num + 1
elif CLASSES[cl] == 'truck':
truck_num = truck_num + 1
if boxes is not None:
draw(IMG, boxes, scores, classes, ratio, padding)
return IMG
为了避免上传到云服务器的数据过多,每30帧上传一次数据。