SSD_SENSOR应用开发参考
1. 概述¶
1.1. 总体框架¶
本文仅描述各个模块的基本使用流程,详细的接口及参数描述请查阅对应模块的API手册。
软件的总体框架如下图,开发人员通过limmi_xxx.so
库提供的接口来调用实际的硬件模块。
1.2. 常用模块¶
简称 | 全称 | 功能职责 |
---|---|---|
SNR | Sensor | 获取摄像头接口信息、调整分辨率和帧率等功能 |
VIF | Video Input interface | MIPI/BT656信号采集模块,VIF支持预缩放功能以在特定的场景下减少系统频宽占用,一路Sensor视频输入,VIF可以同时输出一路源尺寸和一路缩放尺寸(¼)到DRAM上 |
VPE | Video Process Engine | 对视频数据进行缩放、裁剪、降噪、图像增强等 |
DIVP | Deinterlace & Video Post Process Engine | DIVP Engine主要有如下两个功能: 1、对Interlace的数据进行De处理; 2、对视频数据做Scale、Rotate等。 |
VENC | Video Encoder | H264/H265/MotionJpeg编码器,对输入的YUV数据进行编码处理,输出ES流 |
RGN | Region Overlay&Cover | RGN模块主要提供两个功能: 1、Overlay支持在VPE、DIVP上叠加,如OSD字幕; 2、Cover支持在VPE/DIVP上叠加遮挡区域。 |
IVE | Intelligent Video Engine | 提供图像智能识别算法中的基本算子支持 |
AI | Audio Input Interface | 负责从Micro In采集音频数据 |
AO | Audio Output Interface | 支持PCM格式音频输出到Lineout/IIS Out |
IPU | Intelligent Process Unit | 对接Artificial Intelligence网络模型 |
各个模块之间通过sys模块的bind接口串联起来,模块之间的buffer传递如下图:
-
Input portbuffer是上一级输出的OutputportBuf;
-
模块之间buffer的传递通过sys模块实现。
由上图可以看到Bind有frame mode跟realtime mode两种模式
模式 | 特点 | 使用场景 |
---|---|---|
Realtime mode | 硬件直连,不占用DRAM内存 | 只有单sensor时可以使用 |
Frame mode | 占用多张yuv内存。(由depth设置决定) | 单/多sensor都可以使用 |
Frame mode的depth概念
特点:
-
YUV在模块使用时申请, 最大不超过QueueDepth的设定值, 当所有该YUV的消费者都使用完后立即释放。
-
QueueDepth如果设置太小,在CPU繁忙的时候可能会由于没有缓冲队列,而导致丢帧。
可以通过如下接口设置模块输出口depth参数(如前文所述,模块Input port buffer是上一级输出的Output port buffer)
/* * BufQueueDepth:port能同时申请的最多buffer块数,默认为4; * UserFrameDepth: 用户能通过MI_SYS_ChnOutputPortGetBuf()同时取到的最多buffer块数; * BufQueueDepth >= UserFrameDepth; */ MI_S32 MI_SYS_SetChnOutputPortDepth(MI_SYS_ChnPort_t *pstChnPort , MI_U32 u32UserFrameDepth , MI_U32 u32BufQueueDepth);
2. 基本模块介绍¶
以下所有的模块均依赖sys模块,在整个app的生命周期里必须且只能初始化一次MI_SYS_Init()
。
2.1. Sensor模块¶
SNR(sensor)模块实现获取摄像头接口信息、调整分辨率和帧率的功能。
初始化流程:
// 设置plane mode MI_SNR_SetPlaneMode(eSnrPadId, true) // 查询sensor支持的分辨率 MI_SNR_QueryResCount(eSnrPadId, &u32ResCount) // 查询index对应的分辨率参数 for(u8ResIndex = 0; u8ResIndex < u32ResCount; u8ResIndex++) MI_SNR_GetRes(eSnrPadId, u8ResIndex, &stRes) // 设置出图分辨率 MI_SNR_SetRes(eSnrPadId, u8ChocieRes) // 设置sensor出图帧率 MI_SNR_SetFps(eSnrPadId, SENSOR_FPS) // 使能sensor MI_SNR_Enable(eSnrPadId) // 查询当前出图分辨率 MI_SNR_GetCurRes(eSnrPadId, &u8CurResIdx, &stCurRes)
2.2. VIF模块¶
VIF模块只有输出,没有输入,其基本使用流程为:
/ vifDev = (s32vifDev == 2) ? 1 : s32vifDev; vifChn = vifDev * 4; // 设置vif device属性 MI_VIF_SetDevAttr(vifDev, &stDevAttr) // 使能vif device MI_VIF_EnableDev(vifDev) // 设置output port属性 MI_VIF_SetChnPortAttr(VifChn, VifPort, &stChnPortAttr) // 使能output port MI_VIF_EnableChnPort(VifChn, VifPort)
2.3. VPE模块¶
VPE模块对一幅输入的图像首先进行图像质量调整,包括降噪,锐化,亮度调整等,然后再分别缩放到一定的分辨率通过各个output port 口输出。该模块另外包含HDR,旋转,裁剪等功能。
VPE单端输入多端输出,且不同芯片的处理流程不完全一致,从MI_VPE_API中可以找到SSD222芯片的VPE模块的处理逻辑如下图:
各个端口可以分别配置不同的功能,在使用时需根据内部处理的流程配置合适的参数,基本的使用流程如下:
VpeChannel = s32vifDev MI_VPE_SetChannelParam(VpeChannel, &stVpeChnParam) MI_VPE_CreateChannel(VpeChannel, &stChannelVpeAttr) MI_VPE_StartChannel(VpeChannel) MI_VPE_GetPortMode(VpeChannel, VpePort, &stVpeMode) // Change some parameter MI_VPE_SetPortMode(VpeChannel, VpePort, &stVpeMode) if(need crop) MI_VPE_SetPortCrop(VpeChannel, VpePort, &stOutCropInfo) MI_SYS_SetChnOutputPortDepth(&stChnPort, 0, 5) MI_VPE_EnablePort(VpeChannel, VpePort)
2.4. DIVP模块¶
DIVP模块支持图像裁剪、图像像素格式转换、图像的旋转、图像的镜像操作以及图像的拉伸和缩放,单输入输出。不同芯片支持的功能不完全一致,从MI DIVP API中可以找到DIVP模块处理流程为:
在同时开启DIVP的多个功能(crop、rotate、mirror等)时需注意以上处理逻辑的先后顺序,以免无法得到预期的结果。DIVP模块的基本使用流程为:
MI_DIVP_CreateChn(u32DivpChn, &stDivpAttr) MI_DIVP_StartChn(u32DivpChn) MI_DIVP_SetOutputPortAttr(u32DivpChn, &stDivpOutputAttr)
2.5. DISP模块¶
DISP是一个视频显示单元,主要功能是对前端输出的图像做硬件拼图,并对硬件拼图后的图像进行颜色空间转换,最终通过HDMI/VGA/MIPI/TTL等接口输出到显示器或LCD。
DISP模块流程框图为:
基本使用流程为:
DispDev = 0 DispLayer = 0 // Set disp pub attribute MI_DISP_SetPubAttr(DispDev, &stPubAttr) MI_DISP_Enable(DispDev) // Set layer MI_DISP_BindVideoLayer(DispLayer,DispDev) MI_DISP_SetVideoLayerAttr(DispLayer, &stLayerAttr) MI_DISP_EnableVideoLayer(DispLayer) // Set input port attribute MI_DISP_SetInputPortAttr(DispLayer, u8DispInport, &stInputPortAttr) MI_DISP_EnableInputPort(DispLayer, u8DispInport) MI_DISP_SetInputPortSyncMode(DispLayer, u8DispInport, E_MI_DISP_SYNC_MODE_FREE_RUN)
2.6. PANEL模块¶
MI_PANEL_IntfType_e eLinkType = E_MI_PNL_INTF_TTL /* 如果是SPI屏需先给屏下发SPI参数! */ MI_PANEL_Init(eLinkType) // 获取panel分辨率参数,给divp/disp使用 MI_PANEL_GetPanelParam(eLinkType, &stParamCfg)
2.7. RGN模块¶
RGN模块可以用于绘制OSD(时间、人脸框等)或区域遮盖,它可以贴在vpe或divp的output port上。
// init MI_RGN_Init() // 传入调色板 MI_RGN_Create() MI_RGN_AttachToChn() // get canvas MI_RGN_GetCanvasInfo() // Draw on canvas // Update canvas MI_RGN_UpdateCanvas()
2.8. IQSERVER¶
IQSERVER(Image Quality tuning Server)图像质量调节服务,用来完成调节工具(IQ Tool)和开发板之间的数据通信,包括ISP参数设置、获取图像、上传/下载相关文件等功能。在创建完vpe通道后调用OPEN接口即可。
/* * width: Sensor输出分辨率宽度 * height: Sensor输出分辨率高度 * vpeChn: vpe通道号 */ MI_IQSERVER_Open(width, height, vpeChn)
IQ Tool工具连接主板调节完参数后会生成一个IQ参数文件给用户app使用。
2.9. ISP模块¶
ISP模块是对Video source输入的数据进行分析、处理,设定相关视频参数以及进行Camera的调整,以实现黑电平校正、镜头校正、3A、2D/3D降噪、CCM、Gamma…等功能。一般情况下普通用户app只会用这个模块提供的接口来加载调好的IQ参数文件,加载流程如下:
do { MI_ISP_IQ_GetParaInitStatus(Vpechn,&bstatus) if(bstatus.stParaAPI.bFlag != 1) { // 检查退出条件以免死循环 if(timeout) { break; } continue; } // pConfigPath为已调好的IQ参数文件路径 MI_ISP_API_CmdLoadBinFile(Vpechn, (char*)pConfigPath, 1234) } while();
3. 双Sensor应用示例¶
3.1. 基本数据流¶
常见的用户场景为:
两路sensor(RGB+IR)分别出图送到算法处理,同时通过RTSP出流。RGB Sensor送到LCD上显示。
添加一个switch用于支持切换lcd的显示内容,可切换显示sensor-0、sensor-1或黑屏(通过Inject Task注入黑色数据,为了在不出图的情况下正常显示GUI)
使用中如果divp不够用可以根据实际场景将crop/scale/rotate
放到vpe0中处理,这边拆分到显示部分的divp中处理是为了去除接口耦合,以便用户能更灵活的配置“竖屏横用”或“横屏竖用”等场景。
3.2. DIVP抓图流程¶
通过sys模块接口获取到divp output port描述符,使用此接口读取divp的输出数据,基本代码流程如下:
MI_SYS_GetFd(&stChnPort, &s32Fd) MI_SYS_SetChnOutputPortDepth(&stDivpChnOutput, 1, 5) while(1) { FD_ZERO(&read_fds); FD_SET(s32Fd, &read_fds); TimeoutVal.tv_sec = 1; TimeoutVal.tv_usec = 0; /*Waiting for frame*/ s32Ret = select(s32Fd + 1, &read_fds, NULL, NULL, &TimeoutVal); if(s32Ret > 0 && FD_ISSET(s32Fd, &read_fds)) { /* Get a frame from divp */ if(MI_SUCCESS == MI_SYS_ChnOutputPortGetBuf(&stDivpChnOutput, &stBufInfo, &hHandle)) { #if 0 if(stBufInfo.stFrameData.ePixelFormat == E_MI_SYS_PIXEL_FRAME_YUV_SEMIPLANAR_420) { //memcpy(dstbuf, (char *)stBufInfo.stFrameData.pVirAddr[0], stBufInfo.stFrameData.u16Width * stBufInfo.stFrameData.u16Height * 3 >> 1); } else if((stBufInfo.stFrameData.ePixelFormat == E_MI_SYS_PIXEL_FRAME_YUV422_YUYV)) { //memcpy(dstbuf, stBufInfo.stFrameData.pVirAddr[0], stBufInfo.stFrameData.u16Width * stBufInfo.stFrameData.u16Height * 2); } else if((stBufInfo.stFrameData.ePixelFormat == E_MI_SYS_PIXEL_FRAME_RGB565)) { //memcpy(dstbuf, (char *)stBufInfo.stFrameData.pVirAddr[0], stBufInfo.stFrameData.u16Width * stBufInfo.stFrameData.u16Height * 2); } else if(stBufInfo.stFrameData.ePixelFormat == E_MI_SYS_PIXEL_FRAME_ARGB8888) { //memcpy(dstbuf, (char *)stBufInfo.stFrameData.pVirAddr[0], stBufInfo.stFrameData.u16Width * stBufInfo.stFrameData.u16Height * 4); } #endif /*Free frame*/ s32Ret = MI_SYS_ChnOutputPortPutBuf(hHandle); if(s32Ret != MI_SUCCESS) { ST_ERR("MI_SYS_ChnOutputPortPutBuf failed.s32Ret:0x%x !\n", s32Ret); } } else { ST_ERR("MI_SYS_ChnOutputPortGetBuf failed!\n"); } } } MI_SYS_CloseFd(s32Fd)
3.3. RGN及FB绑定位置选择¶
RGN及FB支持绑定到VPE或DIVP的 输出口,从绑定位置起后续所有的数据流均带有绑定上去的数据,这意味着后续所有的对数据流的处理均会影响到绑定上去的内容。选择绑定位置的原则是根据输出口的属性(主要是分辨率参数)及数据流的影响范围确定一个最合适的位置。
比如:
-
通过RGN画人脸框,算法识别的是原始尺寸的图片,给的人脸框坐标必然也是基于原图计算出来的,要将人脸框显示到正确的位置的话那么就将RGN attach到vpe0的port2;
-
LCD竖屏横用时,在FB上画GUI,FB应绑定到divp2(for crop and scale)的output,因为该输出口的图像就是实际显示到LCD上的图像,后续的rotate处理时GUI也将同时旋转以获取预期的显示效果。
4. 常用Debug信息¶
在/proc/mi_modules/
目录各个模块的子文件夹下可以查看当前运行的debug信息,如:
-
Sensor debug
-
Vpe debug
-
Divp debug
-
Venc debug