RKMPI 实例使用指南
1 简介
利用 RKMPI 库实现摄像头图像捕获、预处理、硬件编码,结合 opencv-mobile 进行图像处理,可以将 RKNN 推理结果标注在图像上后作为作为流媒体服务器进行 rtsp 推流,在 局域网 下的 PC 可以使用 VLC 软件拉取并观察图像。
实例包括 RKMPI 库基本使用、opencv-mobile 对数据帧处理两种方式、RTSP 推流结合 RKNN 推理两种方式。主要实现效果:
基于 retinaface 人脸检测网络摄像头
基于 yolov5 物体检测网络摄像头
2 平台支持
DEMO | CPU | 系统 | 摄像头 |
---|---|---|---|
luckfox_pico_rtsp_opencv | RV1103(需修改分辨率)、RV1106 | buildroot | sc3336 |
luckfox_pico_rtsp_opencv_capture | RV1103、RV1106 | buildroot | sc3336 |
luckfox_pico_rtsp_retinaface | RV1103、RV1106 | buildroot | sc3336 |
luckfox_pico_rtsp_retinaface_osd | RV1103、RV1106 | buildroot | sc3336 |
luckfox_pico_rtsp_yolov5 | RV1106 | buildroot | sc3336 |
- RV1103:
Luckfox Pico
Luckfox Pico Mini A
Luckfpx Pico Miini B
Luckfox Pico Plus
- RV1106:
Luckfox Pico Pro
Luckfox Pico Max
Luckfox Pico Ultra
Luckfox Pico Ultra W
3 环境搭建
确认 Luckfox-pico Sdk 的正确安装,确认交叉编译链的环境变量设置正确
arm-rockchip830-linux-uclibcgnueabihf-gdb --version
- 显示交叉编译链的版本信息说明,说明环境变量设置正确。
使用编译好的 buildroot 镜像或从网盘中获取镜像。确保摄像头驱动、 rknpu 驱动和 rockit 驱动、 rkisp 驱动等成功被加载。 在 Luckfox-pico 板端上执行
lsmod
验证。# lsmod
Module Size Used by Tainted: G
rockit 154528 0
rknpu 24552 0
mpp_vcodec 375759 1 rockit
rga3 86680 1 rockit
sc3336 9940 1
os04a10 15198 0
phy_rockchip_csi2_dphy 7674 0
phy_rockchip_csi2_dphy_hw 8161 0
video_rkisp 148435 1 rockit
video_rkcif 130130 0
rk_dvbm 5813 2 mpp_vcodec,video_rkisp- 注意: 要确保镜像中的根文件系统有足够的空闲空间,推荐使用运行在 SD 卡上的系统。
4 RKMPI 库使用
4.1 系统概览
Rockchip 提供的媒体处理接口( Rockchip Media Process Interface,简称 RKMPI ),可支持多媒体应用软件快速开发。该平台整合了 Rockchip 芯片的硬件资源,对应用软件屏蔽了芯片相关的复杂底层处理,使用户在软件层面调用相应接口就可以完成媒体处理开发,如输入视频捕获、H264 / H265 / JPEG 编码、视频图像处理等。
系统框架
- 应用层:基于 RKMPI 及其他驱动,由用户开发的应用软件系统。
- RKMPI 层:基于适配层( 已有的 Rockchip 芯片适配封装接口 ),完成媒体处理功能。
- 操作系统适配层:基于 Rockchip 芯片已有的硬件模块对外提供的驱动接口。
- 操作系统层:基于 Linux 系统。
- 硬件层:由 Rockchip 芯片加上必要的外围器件构成。
IPC(网络摄像机)应用框架
- VI 模块捕获视频图像,做剪切、缩放等处理,可以输出多路不同分辩率的图像数据。
- VPSS 模块接收 VI 模块传输的图像,可对图像进行裁剪、缩放、旋转、像素格式转换等处理。
- VENC 模块可以直接接收 VI 模块捕获的图像或 VPSS 处理后输出的图像数据,可叠加用户通过 RGN 模块设置的 OSD 图像,然后按不同协议进行编码并输出相应码流。
- 各个模块通道之间进行绑定,处理后的数据流会直接传输到绑定的下一个多媒体模块。
4.2 内存管理
RKMPI 库的各个组件主要使用内存缓冲池来进行内存管理,内存缓冲池通过内存块进行内存分配和回收,使内存资源在各个媒体处理模块中合理使用。内存缓冲池与其他应用内存相隔离,保证了 RKMPI 库的稳定运行,但是应用数据无法直接对内存缓冲池内的内存进行操作。
内存缓冲池运行方式
- 内部分配
私有模式下,内存缓冲池由媒体处理模块的通道自行申请,所申请的大小、个数由通道内部根据所设参数申请。
- 外部分配
共有模式下,内存缓冲池可以由用户自己创建,设置内存缓冲块的大小、属性与数据,传递给指定模块使用。
4.3 视频输入 VI
视频输入组件( VI )实现了将 MIPI Rx( 包含 MIPI 接口、LVDS 接口 )等接口接收视频数据的功能。 VI 组件将接收到的数据存入到指定的内存区域,实现视频数据的采集。
VI 内部数据处理
VI 从软件上划分未输入设备( dev )、输入管道( pipe )、输入通道( channel )三个层级。
- VI 设备( dev ):支持若干种时序输入,负责对时序进行解析。
- VI 管道( pipe ):绑定在设备后端,负责设备解析后的数据再处理,主要用于载入 ISP 算法。
- VI 通道( channel ):视频输入最后一级获取通道。
VI 基础使用
- 在进行 VI 模块初始化前请先运行 ISP 算法实现自动曝光控制、自动增益控制、自动白平衡、色彩校正等操作,保证捕获图像的质量。
// CamID: 摄像头 ID
// hdr_mode: HDR 模式
// multi_sensor:多路摄像头
// iq_dir: 摄像头 ISP 算法配置文件 iqfile 所在目录
SAMPLE_COMM_ISP_Init(CamId, hdr_mode, multi_sensor, iq_dir); - 运行 ISP 算法
SAMPLE_COMM_ISP_Run(CamId);
- 启动 VI 设备
RK_MPI_VI_EnableDev(devID);
- 绑定 VI 设备与 VI 管道
RK_MPI_VI_SetDevBindPipe(devID, &stBindPipe);
- 设置 VI 通道属性
RK_MPI_VI_SetChnAttr(PipeID, ChnID, &Chn_attr);
- 注意: VI 通道的输出分辨率参考摄像头模组支持分辨率比率,避免出现图像畸变的情况。
- 启动 VI 通道
RK_MPI_VI_EnableChn(PipeID, ChnID);
- 绑定其他多媒体模块
MPP_CHN_S viChn;
viChn.enModId = RK_ID_VI;
viChn.s32DevID = DevID;
viChn.s32ChnID = ChnID;
RK_MPI_SYS_Bind(&viChn, &otherChn); - 与其他多媒体取消绑定
RK_MPI_SYS_UnBind(&viChn,&otherChn);
- 关闭 VI 通道与设备
RK_MPI_VI_DisableChn(PipeID,ChnID);
RK_MPI_VI_DisableDev(DevID);
4.4 视频处理子系统 VPSS
视频处理子系统( VPSS )支持对输入图像镜像缩放,固定角度旋转,像素格式转换、裁剪等处理。
VPSS 上下文关系
VPSS 可与 VI 和 VENC 等模块进行绑定,前者为 VPSS 的输入源,后者为 VPSS 的接收者。用户可通过 VPSS 接口对组进行管理,每个组仅可与一个输入源绑定。
- VPSS 组( group ):控制输入,各个组分时复用硬件设备,硬件依次处理各个组提交的任务。
- VPSS 通道( channel ):控制输出,每个通道具有缩放、裁剪等功能,通道处理后的图像发送给 VPSS 的接收者。
VPSS 基础使用
创建 VPSS 组
RK_MPI_VPSS_CreateGrp(GrpID, &VpssGrpAttr);
设置 VPSS 通道属性
RK_MPI_VPSS_SetChnAttr(GrpID, ChnID, &VpssChnAttr);
- 注意: VPSS 组负责管理输入,设置属性时注意设置
enPixelFormat
属性与绑定的输入源一致,u32MaxW
和u32MaxH
大于输入源的捕获图像大小。
- 注意: VPSS 组负责管理输入,设置属性时注意设置
启动 VPSS 通道
RK_MPI_VPSS_EnableChn(GrpID, ChnID);
启动 VPSS 组
RK_MPI_VPSS_StartGrp(GrpID);
输出 VPSS 数据
- 绑定其它多媒体模块
MPP_CHN_S vpssChn;
vpssChn.enModId = RK_ID_VI;
vpssChn.s32DevID = DevID;
vpssChn.s32ChnID = ChnID;
RK_MPI_SYS_Bind(&vpssChn, &otherChn); - 直接获取帧数据
RK_MPI_VPSS_GetChnFrame(GrpID, ChnID, &VpssFrame,-1);
void *data = RK_MPI_MB_Handle2VirAddr(VpssFrame.stVFrame.pMbBlk);
- 绑定其它多媒体模块
停止并关闭 VPSS 组
RK_MPI_VPSS_StopGrp(GrpID);
RK_MPI_VPSS_DestroyGrp(GrpID);
4.5 视频编码 VENC
视频编码组件( VENC )支持多路实时编码且每路编码独立,支持在编码的同时调度 Region 模块对编码图像内容进行叠加和遮挡。主要支持的编码格式有 H264 、 H265 、 JPEG 、 MJPEG 。
VENC 内部数据处理
VENC 从数据帧中获取图像,进行图像处理、 RGN 模块处理后传入编码通道,结合数据帧的时间戳等信息进行编码。
- VENC 通道( channel ):管理图像的输入与编码属性,使用
RK_MPI_VENC_StartRecvFrame
接口来启动视频编码接收输入图像数据。
VENC 基础使用
- 创建并设置 VENC 通道
RK_MPI_VENC_CreateChn(chnId, &stAttr);
- VENC 通道需要设置
enType
和u32Profile
参数来设置编码类型// 设置编码类型为 H264
stAttr.stVencAttr.enType = RK_VIDEO_ID_AVC;
stAttr.stVencAttr.u32Profile = H264E_PROFILE_MAIN; - VENC 需要根据设置的编码类型设置相应的结构体参数
// 设置 H264 编码属性
stAttr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR;
stAttr.stRcAttr.stH264Cbr.u32BitRate = BitRate;
stAttr.stRcAttr.stH264Cbr.u32Gop = Gop;
- VENC 通道需要设置
- 开始接收帧数据
RK_MPI_VENC_StartRecvFrame(ChnId, &stRecvParam);
- 从 VENC 输出中获取帧数据
RK_MPI_VENC_GetStream(ChnId, &stFrame, -1);
- 获取 VENC 帧内存块虚拟地址
void *Data = RK_MPI_MB_Handle2VirAddr(stFrame.pstPack->pMbBlk);
- 停止接收帧数据
RK_MPI_VENC_StopRecvFrame(ChnId);
- 关闭 VENC 通道
RK_MPI_VENC_DestroyChn(ChnId);
5 RKRTSP 库使用
RKRTSP 是 RockChip 提供的一套仅供测试使用的 rtsp 服务器软件接口,仅作为 RKMPI 库的功能验证使用,可以通过软件接口快速实现 rtsp 推流功能。
5.1 RKRTSP 基础使用
创建 rtsp 实例
g_rtsplive = create_rtsp_demo(port);
创建 rtsp 接口
g_rtsp_session = rtsp_new_session(g_rtsplive, "/live/0");
设置 rtsp 传输属性
rtsp_set_video(g_rtsp_session, RTSP_CODEC_ID_VIDEO_H264, NULL, 0);
- 注意: 目前 RKRTSP 库软件接口暂不支持 MJPEG 推流。
同步 rtsp 时间戳
rtsp_sync_video_ts(g_rtsp_session, rtsp_get_reltime(), rtsp_get_ntptime());
更新视频流设置并驱动事件
rtsp_tx_video(g_rtsp_session, (uint8_t *)pData, PackLen, PTS);
rtsp_do_event(g_rtsplive);删除 rtsp 实例
rtsp_del_demo(g_rtsplive);
6 opencv-mobile 标注推流帧数实例
6.1 图像帧获取
RKMPI 库内部内存使用内存缓冲进行管理,在应用层想要实现对捕获图像进行处理再进行 rtsp 推流有两种方式。
使用 VI 组件进行捕获
将捕获的图像经过 VPSS 转换像素格式后获取帧数据,转换为
cv::Mat
类型后使用 opencv-mobile 进行处理,最后拷贝回帧数据内存缓冲块虚拟地址,使用RK_MPI_VENC_SendFrame
将 VPSS 帧数据传输到 VENC 组件进行编码。使用 opencv-mobile 进行捕获
创建内存缓冲池,从内存缓冲池中获取内存缓冲块存放图像数据,使用 opencv-mobile 捕获图像进行处理,转换像素格式拷贝入内存缓冲块,最后调用
RK_MPI_VENC_SendFrame
将存放内存缓冲块地址、时间戳、图像大小等信息的VIDEO_FRAME_INFO_S
结构体数据传输到 VENC 组件进行编码。
6.2 基于 VI 图像捕获 rtsp 推流实例
运行效果
使用 Luckfox-pico pro 的 VI 组件捕获 2304 × 1296 图像,使用 opencv-mobile 标注帧率后编码为 H264 进行 rtsp 推流,可以达到 23 帧左右。
注意: 使用 Luckfox-pico mini / plus 进行测试时可以因为内存限制无法正常运行,请尝试降低捕获分辨率。
实现流程
RKRTSP、 RKMPI、 RKAIQ 初始化
// RKAIQ Init
SAMPLE_COMM_ISP_Init(CamID, hdr_mode, multi_sensor, iq_dir);
SAMPLE_COMM_ISP_Run(CamID);
// RTSP Init
g_rtsplive = create_rtsp_demo(554);
g_rtsp_session = rtsp_new_session(g_rtsplive, "/live/0");
rtsp_set_video(g_rtsp_session, RTSP_CODEC_ID_VIDEO_H264, NULL, 0);
rtsp_sync_video_ts(g_rtsp_session, rtsp_get_reltime(), rtsp_get_ntptime());
// RKMPI Init
RK_MPI_SYS_Init();VI、 VPSS、 VENC 组件初始化
// VI Init
vi_dev_init();
vi_chn_init(0, width, height);
// VPSS Init
vpss_init(0, width, height);
// VENC Init
RK_CODEC_ID_E enCodecType = RK_VIDEO_ID_AVC;
venc_init(0, width, height, enCodecType);- VI 通道的像素格式要与 VPSS 组的像素格式一致
vi_chn_attr.enPixelFormat = RK_FMT_YUV420SP; // VI 通道属性
stGrpVpssAttr.enPixelFormat = RK_FMT_YUV420SP; // VPSS 组属性 - 为了方便将数据传入 RKNN 模型中进行推理,使用 VPSS 组件将像素格式转换为 RGB888
stVpssChnAttr.enPixelFormat = RK_FMT_RGB888;
- VENC 设置进行 H264 编码的相关属性
stAttr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR;
stAttr.stRcAttr.stH264Cbr.u32BitRate = BitRate;
stAttr.stRcAttr.stH264Cbr.u32Gop = GOP;- 注意: 例程中使用的 GOP 为 1 , 设置所有数据帧都为 I 帧,在 PC 端使用 VLC 软件设置缓存时间不能低于 500 ms,可以尝试修改进行优化。
- VI 通道的像素格式要与 VPSS 组的像素格式一致
绑定 VI 通道到 VPSS 上
stSrcChn.enModId = RK_ID_VI;
stSrcChn.s32DevId = 0;
stSrcChn.s32ChnId = 0;
stvpssChn.enModId = RK_ID_VPSS;
stvpssChn.s32DevId = 0;
stvpssChn.s32ChnId = 0;
s32Ret = RK_MPI_SYS_Bind(&stSrcChn, &stvpssChn);获取 VPSS 数据帧和对应内存缓冲块虚拟地址
RK_MPI_VPSS_GetChnFrame(DevId, ChnId, &stVpssFrame, -1);
void *data = RK_MPI_MB_Handle2VirAddr(stVpssFrame.stVFrame.pMbBlk);使用 opencv-mobile 标注帧率
cv::Mat frame(height,width,CV_8UC3,data);
cv::putText(frame,fps_text,
cv::Point(40, 40),
cv::FONT_HERSHEY_SIMPLEX,1,
cv::Scalar(0,255,0),2);发送数据到 VENC 中编码为 H264 格式
//为了保证数据的完整性使用阻塞发送。
RK_MPI_VENC_SendFrame(vpssChn, &stVpssFrame,-1);进行 rtsp 推流
RK_MPI_VENC_GetStream(vencChn, &stFrame, -1);
RK_MPI_MB_Handle2VirAddr(stFrame.pstPack->pMbBlk);
rtsp_tx_video(g_rtsp_session, (uint8_t *)pData, stFrame.pstPack->u32Len,
stFrame.pstPack->u64PTS);
rtsp_do_event(g_rtsplive);资源释放
RK_MPI_SYS_UnBind(&stSrcChn, &stvpssChn);
RK_MPI_VI_DisableChn(DevId, ChnId);
RK_MPI_VI_DisableDev(DevId);
RK_MPI_VPSS_StopGrp(GrpId);
RK_MPI_VPSS_DestroyGrp(Grpid);
RK_MPI_VENC_StopRecvFrame(ChnId);
RK_MPI_VENC_DestroyChn(ChnId);
// RKRTSP
rtsp_del_demo(g_rtsplive);
// RKIRQ
SAMPLE_COMM_ISP_Stop(CamId);
// RKMPI
RK_MPI_SYS_Exit();
6.3 基于 opencv-mobile 图像捕获 rtsp 推流例程
运行效果
在 Luckfox-pico pro 上使用 opencv-mobile 捕获图像标注帧率后编码为 H264 进行 rtsp 推流,可以达到 9 帧左右。
注意: 使用 opencv-mobile 捕获摄像头图像的方式较为简单,但是失去了 VI 组件和 VPSS 组件的硬件加速,在性能上有明显损失,推荐使用 VI 来实现图像捕获。
实现流程
创建内存缓存池,分配内存缓存块
MB_POOL src_Pool = RK_MPI_MB_CreatePool(&PoolCfg);
MB_BLK src_Blk = RK_MPI_MB_GetMB(src_Pool, width * height * 3, RK_TRUE);- 创建内存缓冲池的设置的内存块数量不能小于捕获图像像素的数量
PoolCfg.u64MBSize = width * height * channel ;
PoolCfg.u32MBCnt = num;
PoolCfg.enAllocType = MB_ALLOC_TYPE_DMA;
- 创建内存缓冲池的设置的内存块数量不能小于捕获图像像素的数量
RKRTSP、 RKMPI初始化
// RTSP Init
g_rtsplive = create_rtsp_demo(554);
g_rtsp_session = rtsp_new_session(g_rtsplive, "/live/0");
rtsp_set_video(g_rtsp_session, RTSP_CODEC_ID_VIDEO_H264, NULL, 0);
rtsp_sync_video_ts(g_rtsp_session, rtsp_get_reltime(), rtsp_get_ntptime());
// RKMPI Init
RK_MPI_SYS_Init();VENC 组件初始化
// VENC Init
RK_CODEC_ID_E enCodecType = RK_VIDEO_ID_AVC;
venc_init(0, width, height, enCodecType);- VENC 设置进行 H264 编码的相关属性
stAttr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR;
stAttr.stRcAttr.stH264Cbr.u32BitRate = BitRate;
stAttr.stRcAttr.stH264Cbr.u32Gop = GOP;- 注意: 例程中使用的 GOP 为 1 , 设置所有数据帧都为 I 帧,在 PC 端使用 VLC 软件设置缓存时间不能低于 500 ms,可以尝试修改进行优化。
- VENC 设置进行 H264 编码的相关属性
构建
VIDEO_FRAME_INFO_S
数据帧结构体VIDEO_FRAME_INFO_S h264_frame;
h264_frame.stVFrame.u32Width = width;
h264_frame.stVFrame.u32Height = height;
h264_frame.stVFrame.u32VirWidth = width;
h264_frame.stVFrame.u32VirHeight = height;
h264_frame.stVFrame.enPixelFormat = RK_FMT_RGB888;
h264_frame.stVFrame.u32FrameFlag = 160;
h264_frame.stVFrame.pMbBlk = src_Blk;
unsigned char *data = (unsigned char *)RK_MPI_MB_Handle2VirAddr(src_Blk);- 数据帧像素格式要与 VENC 通道像素格式一致。
使用 opencv-mobile 捕获图像并标注帧率,将图像数据以 RGB888 的格式拷贝到数据帧的内存缓冲块中
cap >> bgr;
cv::putText(bgr,fps_text,
cv::Point(40, 40),
cv::FONT_HERSHEY_SIMPLEX,1,
cv::Scalar(0,255,0),2);
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
cv::Vec3b pixel = bgr.at<cv::Vec3b>(y, x);
data[(y * width + x) * 3 + 0] = pixel[2]; // Red
data[(y * width + x) * 3 + 1] = pixel[1]; // Green
data[(y * width + x) * 3 + 2] = pixel[0]; // Blue
}
}发送数据到 VENC 中编码为 H264 格式
//为了保证数据的完整性使用阻塞发送。
RK_MPI_VENC_SendFrame(vpssChn, &h264_Frame,-1);进行 rtsp 推流
RK_MPI_VENC_GetStream(vencChn, &stFrame, -1);
RK_MPI_MB_Handle2VirAddr(stFrame.pstPack->pMbBlk);
rtsp_tx_video(g_rtsp_session, (uint8_t *)pData, stFrame.pstPack->u32Len,
stFrame.pstPack->u64PTS);
rtsp_do_event(g_rtsplive);资源释放
// Destory MB
RK_MPI_MB_ReleaseMB(src_Blk);
// Destory Pool
RK_MPI_MB_DestroyPool(src_Pool);
// VENC
RK_MPI_VENC_StopRecvFrame(ChnId);
RK_MPI_VENC_DestroyChn(ChnId);
// RKRTSP
rtsp_del_demo(g_rtsplive);
// RKMPI
RK_MPI_SYS_Exit();
7 RKNN 推理图像 rtsp 推流实例
为了最大化提高 rtsp 推流的帧率,实例使用 VI 组件来实现摄像头图像捕获。在 RKNN 推理结果的标注上,采用两种方式来实现:
- 将标注好的图像上传到 VENC 组件进行编码传输
- 在图像上传到 VENC 组件后再使用 RGN 模块以打 OSD 的形式标注结果
7.1 基于 opencv-mobile 绘制 RKNN 推理结果
运行效果
使用 Luckfox-pico pro 的 VI 组件捕获 720 × 480 分辩率图像,经过 VPSS 组件像素格式转换,opencv-mobile 缩放后进行 RKNN 模型推理,将获取到的结果标注在原数据帧上传输到 VENC 组件进行编码传输。
注意: 受内存限制,Luckfox-pico mini / plus 无法运行 yolov5 物体检测例程。
使用 retinaface 人脸检测模型推流帧率可达到 11 帧左右。
使用 yolov5 物体检测模型推流帧率可达到 7 帧左右。
实现流程
RKRTSP、 RKMPI、 RKAIQ 和 RKNN 初始化
// RKNN Init
init_rknn_model(model_path, &rknn_app_ctx);
// RKAIQ Init
SAMPLE_COMM_ISP_Init(CamID, hdr_mode, multi_sensor, iq_dir);
SAMPLE_COMM_ISP_Run(CamID);
// RTSP Init
g_rtsplive = create_rtsp_demo(554);
g_rtsp_session = rtsp_new_session(g_rtsplive, "/live/0");
rtsp_set_video(g_rtsp_session, RTSP_CODEC_ID_VIDEO_H264, NULL, 0);
rtsp_sync_video_ts(g_rtsp_session, rtsp_get_reltime(), rtsp_get_ntptime());
// RKMPI Init
RK_MPI_SYS_Init();VI、 VPSS、 VENC 组件初始化
// VI Init
vi_dev_init();
vi_chn_init(0, width, height);
// VPSS Init
vpss_init(0, width, height);
// VENC Init
RK_CODEC_ID_E enCodecType = RK_VIDEO_ID_AVC;
venc_init(0, width, height, enCodecType);- VI 通道的像素格式要与 VPSS 组的像素格式一致
vi_chn_attr.enPixelFormat = RK_FMT_YUV420SP; // VI 通道属性
stGrpVpssAttr.enPixelFormat = RK_FMT_YUV420SP; // VPSS 组属性 - 为了方便将数据传入 RKNN 模型中进行推理,使用 VPSS 组件将像素格式转换为 RGB888。
stVpssChnAttr.enPixelFormat = RK_FMT_RGB888;
- VENC 设置进行 H264 编码的相关属性
stAttr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR;
stAttr.stRcAttr.stH264Cbr.u32BitRate = BitRate;
stAttr.stRcAttr.stH264Cbr.u32Gop = GOP;- 注意: 例程中使用的 GOP 为 1 , 设置所有数据帧都为 I 帧,在 PC 端使用 VLC 软件设置缓存时间不能低于 500 ms,可以尝试修改进行优化。
- VI 通道的像素格式要与 VPSS 组的像素格式一致
绑定 VI 通道到 VPSS 上
stSrcChn.enModId = RK_ID_VI;
stSrcChn.s32DevId = 0;
stSrcChn.s32ChnId = 0;
stvpssChn.enModId = RK_ID_VPSS;
stvpssChn.s32DevId = 0;
stvpssChn.s32ChnId = 0;
s32Ret = RK_MPI_SYS_Bind(&stSrcChn, &stvpssChn);获取 VPSS 数据帧和对应内存缓冲块虚拟地址
RK_MPI_VPSS_GetChnFrame(DevId, ChnId, &stVpssFrame, -1);
void *data = RK_MPI_MB_Handle2VirAddr(stVpssFrame.stVFrame.pMbBlk);opencv-mobile 读取 VPSS 数据帧后,将图像处理为模型推理的需求大小
cv::Mat frame(height,width,CV_8UC3, data);
cv::Mat model_input;
cv::resize(frame, model_input, cv::Size(model_width,model_height), 0, 0, cv::INTER_LINEAR);- 注意: 如果使用对原图像比例要求较高的特征值获取模型,不能简单对图像进行缩放,需要使用缩放并填充黑边的操作来处理图像。
获取推理结果标注在 VPSS 数据帧图像上。
memcpy(rknn_app_ctx.input_mems[0]->virt_addr, model_input.data, 640*640*3);
inference_rknn_model(&rknn_app_ctx, &od_results);发送数据到 VENC 中编码为 H264 格式
//为了保证数据的完整性使用阻塞发送
RK_MPI_VENC_SendFrame(vpssChn, &stVpssFrame,-1);进行 rtsp 推流
RK_MPI_VENC_GetStream(vencChn, &stFrame, -1);
RK_MPI_MB_Handle2VirAddr(stFrame.pstPack->pMbBlk);
rtsp_tx_video(g_rtsp_session, (uint8_t *)pData, stFrame.pstPack->u32Len,
stFrame.pstPack->u64PTS);
rtsp_do_event(g_rtsplive);资源释放
RK_MPI_SYS_UnBind(&stSrcChn, &stvpssChn);
RK_MPI_VI_DisableChn(DevId, ChnId);
RK_MPI_VI_DisableDev(DevId);
RK_MPI_VPSS_StopGrp(GrpId);
RK_MPI_VPSS_DestroyGrp(Grpid);
RK_MPI_VENC_StopRecvFrame(ChnId);
RK_MPI_VENC_DestroyChn(ChnId);
// RKRTSP
rtsp_del_demo(g_rtsplive);
// RKIRQ
SAMPLE_COMM_ISP_Stop(CamId);
// RKMPI
RK_MPI_SYS_Exit();
// RKNN
release_rknn_model(&rknn_app_ctx);
7.2 基于 OSD 绘制 RKNN 推理结果
运行效果
使用 Luckfox-pico pro 的 VI 组件捕获 720 × 480 分辩率图像,设置输出两个通道,一个通道与 VPSS 组绑定,一个通道与 VENC 通道绑定,开启两个线程,一个线程负责 rtsp 推流,一个线程运行 RKNN 模型推理,将推理结果以 OSD 的形式显示 VENC 码流上。
实现流程
RKRTSP、 RKMPI、 RKAIQ 和 RKNN 初始化
// RKNN Init
init_rknn_model(model_path, &rknn_app_ctx);
// RKAIQ Init
SAMPLE_COMM_ISP_Init(CamID, hdr_mode, multi_sensor, iq_dir);
SAMPLE_COMM_ISP_Run(CamID);
// RTSP Init
g_rtsplive = create_rtsp_demo(554);
g_rtsp_session = rtsp_new_session(g_rtsplive, "/live/0");
rtsp_set_video(g_rtsp_session, RTSP_CODEC_ID_VIDEO_H264, NULL, 0);
rtsp_sync_video_ts(g_rtsp_session, rtsp_get_reltime(), rtsp_get_ntptime());
// RKMPI Init
RK_MPI_SYS_Init();VI、 VPSS、 VENC 组件初始化
// VI Init
vi_dev_init();
vi_chn_init(0, width, height);
// VPSS Init
vpss_init(0, width, height);
// VENC Init
RK_CODEC_ID_E enCodecType = RK_VIDEO_ID_AVC;
venc_init(0, width, height, enCodecType);VI 通道的像素格式要与绑定的接收者一致
vi_chn_attr.enPixelFormat = RK_FMT_YUV420SP; // VI 通道属性
stGrpVpssAttr.enPixelFormat = RK_FMT_YUV420SP; // VPSS 组属性
stChnVencAttr.stVencAttr.enPixelFormat = RK_FMT_YUV420SP; // VENC 通道属性为了方便将数据传入 RKNN 模型中进行推理,使用 VPSS 组件将像素格式转换为 RGB888
stVpssChnAttr.enPixelFormat = RK_FMT_RGB888;
VENC 设置进行 H264 编码的相关属性
stAttr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR;
stAttr.stRcAttr.stH264Cbr.u32BitRate = BitRate;
stAttr.stRcAttr.stH264Cbr.u32Gop = GOP;- 注意: 例程中使用的 GOP 为 1 , 设置所有数据帧都为 I 帧,在 PC 端使用 VLC 软件设置缓存时间不能低于 500 ms,可以尝试修改进行优化。
通道绑定
VI 通道 0 与 VENC 通道绑定
stSrcChn.enModId = RK_ID_VI;
stSrcChn.s32DevId = 0;
stSrcChn.s32ChnId = 0;
stvencChn.enModId = RK_ID_VENC;
stvencChn.s32DevId = 0;
stvencChn.s32ChnId = 0;
s32Ret = RK_MPI_SYS_Bind(&stSrcChn, &stvencChn);VI 通道 1 与 VPSS 组绑定
stSrcChn.enModId = RK_ID_VI;
stSrcChn.s32DevId = 0;
stSrcChn.s32ChnId = 1;
stvpssChn.enModId = RK_ID_VPSS;
stvpssChn.s32DevId = 0;
stvpssChn.s32ChnId = 0;
s32Ret = RK_MPI_SYS_Bind(&stSrcChn, &stvpssChn);
RKNN 推理线程
- 获取 VPSS 数据帧和对应内存缓冲块虚拟地址
RK_MPI_VPSS_GetChnFrame(DevId, ChnId, &stVpssFrame, -1);
void *data = RK_MPI_MB_Handle2VirAddr(stVpssFrame.stVFrame.pMbBlk); - opencv-mobile 读取 VPSS 数据帧图像后,将图像处理为模型推理的需求大小
cv::Mat frame(height,width,CV_8UC3, data);
cv::Mat model_input;
cv::resize(frame, model_input, cv::Size(model_width,model_height), 0, 0, cv::INTER_LINEAR);- 注意: 如果使用对原图像比例要求较高的特征值获取模型,不能简单对图像进行缩放,需要使用缩放并填充黑边的操作来处理图像。
- 获取推理结果并将结果以 OSD 的形式显示在 VENC 码流上
memcpy(rknn_app_ctx.input_mems[0]->virt_addr, model_input.data, 640*640*3);
inference_rknn_model(&rknn_app_ctx, &od_results);- 注意: RGN 模块将 OSD 显示在码流上使用的是覆盖 ( overlay ) 的形式,对内存占用较高,例程中只实现了对一张人脸进行标注。
- 获取 VPSS 数据帧和对应内存缓冲块虚拟地址
推流线程进行 rtsp 推流
RK_MPI_VENC_GetStream(vencChn, &stFrame, -1);
RK_MPI_MB_Handle2VirAddr(stFrame.pstPack->pMbBlk);
rtsp_tx_video(g_rtsp_session, (uint8_t *)pData, stFrame.pstPack->u32Len,
stFrame.pstPack->u64PTS);
rtsp_do_event(g_rtsplive);资源释放
RK_MPI_SYS_UnBind(&stSrcChn0, &stvencChn);
RK_MPI_SYS_UnBind(&stSrcChn1, &stvpssChn);
RK_MPI_VI_DisableChn(DevId, ChnId0);
RK_MPI_VI_DisableChn(DevId, ChnId1);
RK_MPI_VI_DisableDev(DevId);
RK_MPI_VPSS_StopGrp(GrpId);
RK_MPI_VPSS_DestroyGrp(Grpid);
RK_MPI_VENC_StopRecvFrame(ChnId);
RK_MPI_VENC_DestroyChn(ChnId);
// RKRTSP
rtsp_del_demo(g_rtsplive);
// RKIRQ
SAMPLE_COMM_ISP_Stop(CamId);
// RKMPI
RK_MPI_SYS_Exit();
// RKNN
release_rknn_model(&rknn_app_ctx);
8 编译运行
8.1 编译(在PC上执行)
获取 git 仓库源码
git clone https://github.com/LuckfoxTECH/luckfox_pico_rkmpi_example.git
设置环境变量
export LUCKFOX_SDK_PATH=< luckfox-pico Sdk 地址>
注意:使用绝对地址。
执行
./build.sh
后选择编译的例程1) luckfox_pico_rtsp_opencv
2) luckfox_pico_rtsp_opencv_capture
3) luckfox_pico_rtsp_retinaface
4) luckfox_pico_rtsp_retinaface_osd
5) luckfox_pico_rtsp_yolov5
Enter your choice [1-5]:
8.2 运行(在 Luckfox Pico 板端执行 )
编译完成后会在
luckfox_pico_rkmpi_example/install
文件夹下生成对应的部署文件夹luckfox_pico_rtsp_opencv_demo
luckfox_pico_rtsp_opencv_capture_demo
luckfox_pico_rtsp_retinaface_demo
luckfox_pico_rtsp_retinaface_osd_demo
luckfox_pico_rtsp_yolov5_demo将生成的部署文件夹完整上传到 Luckfox Pico 上 (可使用adb ssh等方式) ,板端进入文件夹运行
# 在 Luckfox Pico 板端运行,<Demo Target> 是部署文件夹中的可执行程序
chmod a+x <Demo Target>
./<Demo Target>使用 VLC 打开网络串流
rtsp://172.32.0.93/live/0
(按实际情况修改 IP 地址拉取图像)
注意
- 在运行demo前请执行
RkLunch-stop.sh
关闭 Luckofox Pico 开机默认开启的后台程序rkicp
,解除对摄像头的占用。 - RV1103 的系统资源较少,无法正常运行时请降低视频捕获的分辨率。