Cambricon CE3226 媒体处理系统 (二):视频输入

    随着 AI 技术的不断发展,从 Sensor 采集数据,经过处理之后给 AI 模型做图像分析已经变成一个很典型的应用。因此,在嵌入式应用行业,无论是汽车,安防,手机等应用中,对 Sensor 采集的数据做图像处理变得非常重要。针对以上需求,Cambricon CE3226 内部集成了多个复杂的硬件组件,并实现了对应的中间层软件库,能够满足客户对视频处理的各种要求。
    本系列博文会对 CNMPS(Cambricon Media Process System,寒武纪媒体处理系统)各个组件功能,工作原理作详细介绍,并对中间层软件作系统分析,方便客户编写自己的应用程序。
    本文是该系列博文的第二篇,介绍视频输入系统的组成,工作原理以及软件架构。


一、基本概念

 VI(Video Input,视频输入)模块,支持通过 MIPI Rx(包括 MIPI 接口和 LVDS 接口)、BT.656、BT.1120、

BT.601 和 DC 等接口采集视频数据。VI 可以将采集数据通过总线直接发送给其他模块,或者先将数据存入指定的内存区域,对数据进行处理后,再发送给其他模块。

  • 视频输入设备

视频输入设备封装视频采集接口,接收接口视频原始数据后,输出到视频输入物理 PIPE(视频数据处理单元)行处理。

  • 视频输入物理 PIPE

视频输入物理 PIPE 绑定视频输入设备,接收设备发送数据,调用 ISP、算法等模块,进行图像分析处理。

  • 视频输入虚拟 PIPE

视频输入虚拟 PIPE 不绑定视频输入设备,接收其他模块或用户发送过来的数据,调用 ISP、算法等模块,进行图像分析处理。(注释:虚拟pipe就是说,数据不是sensor通过dev送进来的,而是用户通过软件,send frame进来的。比如用接口cnviSendPipeRaw 发送数据给虚拟pipe。)

  • 视频物理通道

视频物理通道接收视频输入 PIPE 图像数据后,可以调用其他硬件模块,实现裁剪、缩放、遮挡等功能,再输出图像数据到 DDR。

  • 视频扩展通道

视频扩展通道绑定视频物理通道,调用其他模块实现裁剪、缩放、遮挡、鱼眼等功能,输出多路图像数据到 DDR。

  • PIPE 的工作模式

参考 VI 和 VPPS 工作模式 章节的详细描述。

  • 镜头畸变校正

LDC(Lens Distortion Correction,镜头畸变校正),一些透镜由于制造精度以及组装工艺的偏差会引入畸变,需要针对畸变进行图像校正。

  • 防抖

DIS(Digital Image Stabilization,防抖)模块使用防抖算法计算当前图像与前一帧图像的偏移,得到在各个轴方向上的抖动偏移矩阵,然后对当前图像进行校正,达到防抖目的。

 

二、功能描述

VI 在软件层次上分为输入设备、输入 PIPE(包含物理 PIPE 和虚拟 PIPE)、物理通道(PHY_CHN)和

扩展通道(EXT_CHN)4 个部分,如图(1)所示:


图(1)

CE3226V100 VI 输出通道功能如下图(2):


图(2)

  • 视频输入设备

VI 设备之间相互独立,最多支持 8 个设备。

  • 视频输入 PIPE

VI 的 PIPE 包含 ISP、TDNR 等相关图像处理功能,输出 YUV 图像格式给通道。物理 PIPE 和虚拟 PIPE

可以同时运行,但是总个数不能超过 8 个。

  • 视频物理、扩展通道

– CE3226V100 VI:1 个 PIPE 支持输出 1 个物理通道,输出 8 个扩展通道。

– CE3226V100 VI: 通 道 支 持 的 典 型 分 辨 率 和 帧 率 为 3840x2160@60fps 、 3840x2160@30fps 、

1080p@60fps 和 1080p@30fps 等。

  • 绑定关系

– CE3226V100 设备与 MIPI/LVDS/DVP 绑定关系是固定的,不能动态修改绑定关系。

– CE3226V100 设备和时序输入接口的约束关系如下接口的绑定关系参见图(3) CE3226V100 设备与

MIPI/LVDS/BT.1120/BT.656/BT601/DC 接口的绑定关系 。


图(3)

– CE3226V100 LVDS 与 DVP 设备管脚复用关系参见图(4) CE3226V100 LVDS 与 DVP 设备管脚复用关系 。


图(4)

– PIPE 可以绑定任意设备,绑定关系为静态,不能动态修改绑定关系。

– 线性模式和 WDR 模式都是一个物理 PIPE 绑定一个设备

 

 

  • 从模式

VI 的从模式模块,主要用于多 SENSOR 同步拼接。从模式与设备对应关系是固定的,不支持修改。

用户需要根据 SENSOR 管脚的连线和下表确定使用哪个从模式模块,然后选择对应的物理设备号创建

设备,否则会没数据,具体步骤如下:

– 确认硬件原理图上 SENSOR 的管脚连接到了 SENSOR_HSx/SENSOR_VSx/SENSOR_VSOUTx(可选)。

– 根据图(3)确定该 SENSOR 连接到从模式模块编号 x。

– 使用编号为 x 的设备。


图(5)

  

三、API & 关键代码分析

1.驱动交互 API

VI 模块的 API 函数在 /mps/out/include/cn_vi.h 头文件中,具体实现封装在/mps/out/lib/libcn_vi.so

动态库文件中,这一层 API 直接和 /mps/out/ko 路径中对应的驱动模块交互。

cn_vi.h代码片段如图(6)所示,涉及的数据类型在 /mps/out/include/cn_common_vi.h 中定义。


图(6)

  • 针对 VI输入设备,实现了以下主要 API:

cnS32_t cnviSetDevAttr(viDev_t ViDev, const cnviDevAttr_t *pstDevAttr)   //VI设备属性设置

cnS32_t cnviSetDevCrop(viDev_t ViDev, const cnviCropInfo_t  *pstCropInfo)//VI设备剪裁属性

cnS32_t cnviEnableDev(viDev_t ViDev)                                     //VI 设备使能

cnS32_t cnviSetDevBindPipe(viDev_t ViDev, const cnviDevBindPipe_t *pstDevBindPipe)//绑定PIPE

 

  • 针对 VI PIPE,主要实现了:

cnS32_t cnviCreatePipe(viPipe_t ViPipe, const cnviPipeAttr_t *pstPipeAttr)//pipe 创建

cnS32_t cnviSetPipeAttr(viPipe_t ViPipe, const cnviPipeAttr_t *pstPipeAttr)//pipe 设置属性

cnS32_t cnviStartPipe(viPipe_t ViPipe)   //根据 VI PIPE 号,启用 PIPE

cnS32_t cnviSetPipeDumpAttr(viPipe_t ViPipe, const cnviDumpAttr_t *pstDumpAttr)//设置DUMP属性

cnS32_t cnviSendPipeYUV(viPipe_t ViPipe, const cnVideoFrameInfo_t *pstVideoFrame, cnS32_t s32MilliSec)//通过 pipe 发送 YUV数据

cnS32_t cnviSendPipeRaw(viPipe_t ViPipe, const cnVideoFrameInfo_t *pstVideoFrame[],cnU32_t u32FrameNum,                     cnS32_t s32MilliSec) //通过 pipe 发送 RAW数据

cnS32_t cnviSetPipeFrameSource(viPipe_t ViPipe, const cnviEnPipeFrameSource_t enSource)//设置数据源

cnS32_t cnviSetPipeTdnrAttr(viPipe_t ViPipe, const cnviPipeTdnrAttr_t *pstNrAttr)//设置 TDNR 属性

 

  • 针对 VI CHANNEL,实现了以下主要 API:

cnS32_t cnviSetChnAttr(viPipe_t ViPipe,viChn_t ViChn,const cnviChnAttr_t *pstChnAttr)//设置CHN属性

cnS32_t cnviEnableChn(viPipe_t ViPipe, viChn_t ViChn)//使能 CHN

cnS32_t cnviSetChnRotation(viPipe_t viPipe, viChn_t viChn, const cnEnRotation_t enRotation)//旋转

cnS32_t cnviSetChnLDCAttr(viPipe_t viPipe, viChn_t viChn, const cngdcLdcAttr_t *pstLDCAttr)//畸变

cnS32_t cnviSetChnSpreadAttr(viPipe_t viPipe, viChn_t viChn, const cngdcSpreadAttr_t *pstSpreadAttr)

cnS32_t cnviSetChnDISConfig(viPipe_t viPipe, viChn_t viChn,  

const cnviDisConfig_t *pstDisConfig)//防抖属性参数

cnS32_t cnviSetChnDISAttr(viPipe_t viPipe, viChn_t viChn,

const cnviDisAttr_t *pstDisAttr)//设置防抖控制属性

cnS32_t cnviSetExtChnAttr(viPipe_t ViPipe, viChn_t ViChn, const cnviExtChnAttr_t

*pstExtChnAttr)//设置扩展通道属性

cnS32_t cnviGetChnFrame(viPipe_t ViPipe, viChn_t ViChn, cnVideoFrameInfo_t *pstFrameInfo, cnS32_t s32MilliSec)//获取通道帧数据信息

 

2.用户 API

 为了方便客户应用开发,MPS 在以上 API 基础上,继续封装了一层 API,供开发人员直接对 VI 模块进行 设置、初始化、启动等操作。相关函数通过 /mps/sample/common/cnsample_comm.h 导出。具体函数实现在/mps/sample/common/cnsample_comm_vi.cpp 文件中实现。

 

2.1

cnsample_comm_vi.cpp 文件中首先按照不同的 SensorType 分别创建并填充了几组 cnviDevAttr_t、cnviPipeAttr_t、cnviChnAttr_t 结构体。三种结构体的定义如图(7)所示。分别抽象了 VI DEV、VI PIPE、

VI Channel 的属性。


图(7)

以 Sony327 的 SONY_IMX327_MIPI_2M_30FPS_12BIT 配置为例,三个结构具体成员内容见图(8)


图(8)

 

2.2

接下来实现了 cnsampleCommViGetFrameRateBySensor()、cnsampleCommViGetSizeBySensor()、cnsampleCommViGetWdrModeBySensor(),根据 sensor 的设置方式确定对应的帧率、pic_size、WDR模式的枚举值。

 

2.3

设置并启动一个 VI 通过图(9)所示的函数实现,该函数供 APP 创建 VI 时直接调用。


图(9)

cnsampleCommViStartVi()主要实现了 设置参数,创建 VI 设备,启动 Mipi,创建 ISP,以及设置 sensor 的从模式,用于 APS 全景拼接。

cnsampleCommViSetParam(pstViConfig) 为空函数,直接返回 CN_SUCESS;

cnsampleCommViCreateVi(pstViConfig) 是 VI 模块中最重要的函数,主要函数调用层次如下(描红为直接与驱动程序交互的 API):

 |--cnsampleCommViCreateVi(pstViConfig)

    |--cnsampleCommViCreateSingleVi(pstViInfo)

        |--cnsampleCommViStartDev(pstViInfo)

|--cnsampleCommViGetDevAttrBySns(cnEnSampleSnsType_t enSnsType, cnviDevAttr_t* pstViDevAttr)

|--cnviSetDevAttr(ViDev, &stViDevAttr)

|--cnviGetDevCrop(ViDev, &stCropInfo)

|--cnviSetDevCrop(ViDev, &stCropInfo)

|--cnviEnableDev(ViDev)

        |--cnsampleCommViBindPipeDev(pstViInfo)

            |--cnviSetDevBindPipe(pstViInfo->stDevInfo.ViDev, &stDevBindPipe)

        |--cnsampleCommViStartViPipe(pstViInfo)

            |--cnsampleCommViGetPipeAttrBySns(pstViInfo->stSnsInfo.enSnsType, &stPipeAttr)

            |--cnsampleCommViStartSingleViPipe(ViPipe, &stPipeAttr)

                |--cnviCreatePipe(ViPipe, pstPipeAttr)

                |--cnviStartPipe(ViPipe)

        |--cnsampleCommViStartViChn(pstViInfo)

            |--cnsampleCommViGetChnAttrBySns(pstViInfo->stSnsInfo.enSnsType, &stChnAttr)

            |--cnviSetChnAttr(ViPipe, ViChn, &stChnAttr)

            |--cnviEnableChn(ViPipe, ViChn)

 

以下介绍上述代码关键的实现过程,具体细节有兴趣的读者自行分析。

输入参数结构体 pstViConfig 的原型见图(10)


图(10)

包含了 VI 模块的设备、Pipe、Chanel 的设备编号信息以及Sensor 的数量、型号等信息,如图(11)这些参数的填充是 APP 程序首要任务。


 图(11)

pstViConfig 结构体填充完成后,首先cnsampleCommViCreateVi(cnsampleViConfig_t* pstViConfig) 函数根据输入参数中的 s32WorkingViNum 数量,循环调用 cnsampleCommViCreateSingleVi(pstViInfo),创建对应数量的 VI 实体。流程如下

  • 调用 cnsampleCommViStartDev(pstViInfo),开启 VI 输入设备。该函数根据传入的 SensorType,获取对应的 Dev 属性,并确定 Dev 设备编号,宽动态范围参数,调用 cnviSetDevAttr(ViDev, &stViDevAttr) 开启 Dev。
  • 调用 cnsampleCommViBindPipeDev(pstViInfo) 绑定 Dev 和 Pipe。
  • 调用 cnsampleCommViStartViPipe(pstViInfo),根据 SensorType 获取对应的 pipe 属性,并确定 pipe 编号、是否启动降噪。最后在 cnsampleCommViStartSingleViPipe(ViPipe, &stPipeAttr)函数中创建并启动 Pipe。
  • 调用 cnsampleCommViStartViChn(pstViInfo),根据 SensorType 获取对应的 channel 属性,并确定动态域、视频格式、像素格式、是否压缩,调用 cnviSetChnAttr(ViPipe, ViChn, &stChnAttr) 设置 Channel,

最后根据 VI&VPPS 的在线离线模式,执行 cnviEnableChn(ViPipe, ViChn) 启动 Channel。

 

cnsampleCommViStartMipi(pstViConfig) 函数用于设置 MIPI 的时钟,时序等参数。

cnsampleCommViCreateIsp(pstViConfig) 函数用于启动 ISP,一般设置为 BYPASS,ISP 的内容较为复杂,不在本文中赘述。

cnsampleCommViStartSlaveMode(pstViConfig) 用于 APS 全景拼接,参考后续章节。

 

2.4

cnsampleCommViGetSensorInfo(cnsampleViConfig_t* pstViConfig) 函数用于获取 Sensor 的信息,并填充 pstViConfig 结构体,该函数也是直接供 APP 程序直接调用的重要函数。具体分析见 《Cambricon CE3226  媒体处理系统 (五):应用编程》。

 

参考资料:

    寒武纪媒体处理系统开发者手册-CN-v0.8.0.pdf

★博文内容均由个人提供,与平台无关,如有违法或侵权,请与网站管理员联系。

★文明上网,请理性发言。内容一周内被举报5次,发文人进小黑屋喔~

评论