问题描述
用 K230 处理 MP4 视频时,想加实时人脸检测却卡壳?最新适配的 MP4 人脸检测 Demo 来了 —— 手把手教你从文件读取到视频解码,再到人脸框叠加,跑通全流程,零基础也能让 MP4 视频 “认出” 人脸!
一、硬件环境
K230 开发板
软件环境
1、K230 最新固件包
获取地址:
https://kendryte-download.canaan-creative.com/developer/releases/canmv_k230_micropython/daily_build/
2、demo 代码地址
\CanMV\sdcard\examples\21-AI-With-Others\face_detect_yunet_from_mp4.py
整体流程框架

在 K230 的 MP4 人脸检测 Demo 里,需 5 个 “专业工位” 无缝协作,把数据从 “打包状态” 逐步处理成可检测、可显示的效果:
- MP4解复用:拆 MP4 “快递箱”,分离出 H264/H265 视频流,为后续解码铺路;
- VDEC(视频解码器):解压视频流,输出 YUV420SP 格式的原始图像数据;
- CSC(色彩空间转换):当 “格式转换器”,把 YUV420SP 转成 RGB888P,适配人脸模型输入;
- KPU(AI 推理模块):当 “检测大脑”,对 RGB 图像做 AI 推理,输出人脸框坐标;
- DISPLAY(显示模块):叠加 YUV 视频画面和人脸框,呈现带人脸框的画面。5个模块环环相扣,既保视频流畅,又让 AI 检测不卡顿,全程像条高效的 “数据处理流水线”!
代码小诀窍
1. 各个模块之间连接最好使用 bind 模式
//VDEC与DISPLAY之间bind
bind_info = vdec.bind_info(width=video_info.width, height=video_info.height,chn=vdec.get_vdec_channel())
Display.bind_layer(**bind_info, layer = Display.LAYER_VIDEO1)
//VDEC与CSC模块之间bind
vdec_link = MediaManager.link((VIDEO_DECODE_MOD_ID, VDEC_DEV_ID, vdec.get_vdec_channel()), (NONAI_2D_CSC_MOD_ID, 0, 2))
bind之后,VDEC 的数据会直接送到 Display 和 CSC 模块,不需要应用层取数据再送到下一个模块,有效减少数据拷贝次数。
同时,VDEC 可以与 Display 和 VDEC 模块绑定。
2. 不同模块间的通信数据格式需要匹配才能正常工作
CSC模块就是用于这个用途,比方说 AI 需要的输入格式是 RGB888P,我们可以选择用 CSC 或者AI2D 进行格式转换。
- Display 用的是 YUV420 的数据,所以用 CSC 直接转成 RGB888p 送给 AI,减少一次格式转换
- CSC 输出的数据需要转换成 AI 使用的 numpy,并根据 AI 输入的需要 reshape
img = vf.to_image()
img_np_hwc = img.to_numpy_ref()
shape = img_np_hwc.shape
img_np_nhwc=img_np_hwc.reshape((1,shape[0],shape[1],shape[2]))
3.如何优化速度
- 考虑跳帧检测,比方说两帧检测一帧
//每两帧检测一帧
count += 1
if count % 2 ==0:
vf = vf_info.v_frame
img = vf.to_image()
img_np_hwc = img.to_numpy_ref()
shape = img_np_hwc.shape
img_np_nhwc=img_np_hwc.reshape((1,shape[0],shape[1],shape[2]))
res = face_det.run(img_np_nhwc)
csc.releaseframe(vf_info)
osd_img.clear()
face_det.draw_result(osd_img, res) # 绘制结果
Display.show_image(osd_img)
else:
csc.releaseframe(vf_info)
time.sleep_ms(10)
- 输入的码流缩小分辨率,图片越大,检测速度越慢
- 模型优化,尽量轻量化模型
4. 注意资源的释放和线程退出
-
这里设置了两个地方退出线程:文件播放到结尾和用户主动点击退出
while (main_thread_flag): try: frame_data = k_mp4_frame_data_s() ret = kd_mp4_get_frame(mp4_handle.value, frame_data) if (ret < 0): raise OSError("get frame data failed") #文件播放到结尾,退出两个线程 if (frame_data.eof): sub_thread_flag = False main_thread_flag = False raise OSError("get frame data failed") 。。。。 except KeyboardInterrupt as e: print("user stop: ", e) except BaseException as e: print(f"Exception {e}") #退出两个线程 sub_thread_flag = False main_thread_flag = False time.sleep_ms(100) #资源释放 face_det.deinit() kd_mp4_destroy(mp4_handle.value) vdec.stop() vdec.destroy() Display.deinit() csc.destroy() time.sleep_ms(200) MediaManager.deinit() print("release end")5. debug方法分享
-
IDE log输出
在代码中增加 print,输出各种需要的变量信息
if (track_info.track_type == K_MP4_STREAM_VIDEO):
if (track_info.video_info.codec_id == K_MP4_CODEC_ID_H264 or track_info.video_info.codec_id == K_MP4_CODEC_ID_H265):
video_track = True
video_info = track_info.video_info
print(" codec_id: ", video_info.codec_id)
print(" track_id: ", video_info.track_id)
print(" width: ", video_info.width)
print(" height: ", video_info.height)
else:
print("video not support codecid:",track_info.video_info.codec_id)
-
连接串口,查看串口 log 信息

查看串口信息(波特率115200)

-
后台运行 micropython,使用各种命令 debug 目前情况
在串口按 ctrl+C 退出 micropython ,改成后台运行。
#按ctrl+C退出micropython
[mpy] enter repl
^C[mpy] exit, reset
<3>[4] [Func]:vb_do_exit [Line]:1569 [Info]:vb already exited!
#后台运行micropython
msh />cd /sdcard/
msh /sdcard>micropython &
msh /sdcard>CanMV K230 start in 349970133 us
IDE debugger built Oct 27202509:55:21
[mpy] enter repl
#可以在运行的时候输入各种命令查看后台信息
msh /sdcard>cat /proc/umap/vb
-----VB PUB CONFIG--------------------------------------------------------------
MaxPoolCnt
0
-----VB SUPPLEMENT ATTR---------------------------------------------------------
Config Size VbCnt
0 0 0
参考文献
官网API文档参考:
https://www.kendryte.com/k230_canmv/zh/main/index.html
配套视频教程:
https://www.bilibili.com/video/BV1Ke1nBSEic
快来试试吧!