各个单模块可能是指不同的计算机程序或系统组件,具体含义如下:
CVE:可能指的是“通用视频编码器(Common Video Encoder)”,它是一种用于将原始视频数据编码成特定格式的计算机程序或硬件组件。
DSP:可能指的是“数字信号处理器(Digital Signal Processor)”,它是一种用于处理数字信号的专用处理器。
VDEC:可能指的是“视频解码器(Video Decoder)”,它是一种用于将经过压缩的视频数据解码还原成原始视频数据的计算机程序或硬件组件。
VENC:可能指的是“视频编码器(Video Encoder)”,它是一种用于将原始视频数据编码成特定格式的计算机程序或硬件组件。
VGU:可能指的是“视频图形单元(Video Graphics Unit)”,它是一种用于处理视频图像数据的计算机硬件组件。
今天实践 DSP 资源的使用。基于《CNVE-LIB开发者手册》
1、配置好交叉编译工具
2、配置好NFS服务,以及板端的NFS客户端连接
3、检查rootfs 文件(这个在安装完EF2301 sdk后,会在bsp/filesystem/rootfs目录)
DSP 可用于:图像缩放,色彩空间转换,滤波,用于人工智能的预处理等。
会涉及的硬件为 CPU和 VPU,软件包括SDK环境以及配套工具。
1、cn_dsp.elf文件,这个应该是dsp的一些固件
2、/mps/ko路径下,依次执行 ./load_cnUDM_drv.sh 和 ./load_mps_drv.sh 两个脚本,安装驱动。
这个文件定义了
cnS32_t cnveDspPowerOn(cnveEnDspID_t enDspId);
cnS32_t cnveDspPowerOff(cnveEnDspID_t enDspId);
cnS32_t cnveDspLoadBin(const cnChar_t *pszBinFileName, cnveEnDspID_t enDspId);
cnS32_t cnveDspEnableCore(cnveEnDspID_t enDspId);
cnS32_t cnveDspDisableCore(cnveEnDspID_t enDspId);
cnS32_t cnveDspRPC(cnveDspHandle_t *phHandle, const cnveDspMessage_t *pstMsg, cnveEnDspID_t enDspId, cnveEnDspPri_t enPri);
cnS32_t cnveDspQuery(cnveEnDspID_t enDspId, cnveDspHandle_t hHandle, cnBool_t *pbFinish, cnBool_t bBlock);
cnS32_t cnveDspCoreStatStart(cnveEnDspID_t enDspId, const cnveDspStatCfgParam_t *pParams);
cnS32_t cnveDspCoreStatGet(cnveEnDspID_t enDspId, cnveDspStatExport_t *pParams);
cnS32_t cnveDspCoreStatStop(cnveEnDspID_t enDspId);
这些函数看起来是关于一个DSP(数字信号处理器)系统的一部分。DSP是一种专用的微处理器,用于处理数字信号,通常用于音频、视频处理、数据压缩和解压缩等任务。这些函数的参数和返回类型都是特定于这个DSP系统的。
以下是对这些函数的推测解释:
cnveDspPowerOn
: 这个函数可能是用来开启一个特定的DSP。enDspId
可能是一个标识符,用于指定要开启的DSP。返回类型cnS32_t
可能是一个错误代码或状态码,用于指示操作是否成功。cnveDspPowerOff
: 这个函数可能是用来关闭一个特定的DSP。enDspId
可能是一个标识符,用于指定要关闭的DSP。返回类型cnS32_t
可能是一个错误代码或状态码,用于指示操作是否成功。cnveDspLoadBin
: 这个函数可能是用来加载二进制代码到某个DSP。pszBinFileName
是二进制文件的路径,enDspId
可能是一个标识符,用于指定要加载代码的DSP。返回类型cnS32_t
可能是一个错误代码或状态码,用于指示操作是否成功。cnveDspEnableCore
: 这个函数可能是用来启用DSP的核心。enDspId
可能是一个标识符,用于指定要启用的DSP。返回类型cnS32_t
可能是一个错误代码或状态码,用于指示操作是否成功。cnveDspDisableCore
: 这个函数可能是用来禁用DSP的核心。enDspId
可能是一个标识符,用于指定要禁用的DSP。返回类型cnS32_t
可能是一个错误代码或状态码,用于指示操作是否成功。cnveDspRPC
: 这个函数可能是用来进行远程过程调用(RPC)到某个DSP。phHandle
可能是用于接收RPC请求的句柄,pstMsg
可能是包含RPC消息的数据结构,enDspId
可能是一个标识符,用于指定要调用的DSP,enPri
可能是优先级参数。返回类型cnS32_t
可能是一个错误代码或状态码,用于指示操作是否成功。cnveDspQuery
: 这个函数可能是用来查询某个DSP的状态。enDspId
可能是一个标识符,用于指定要查询的DSP,hHandle
可能是用于查询的句柄,pbFinish
可能是用于接收查询完成标志的指针,bBlock
可能是一个标志,指示是否应阻塞等待查询结果。返回类型cnS32_t
可能是一个错误代码或状态码,用于指示操作是否成功。cnveDspCoreStatStart
: 这个函数可能是用来开始记录某个DSP的核心统计信息。enDspId
可能是一个标识符,用于指定要记录统计信息的DSP,pParams
可能是包含统计信息记录配置的数据结构。返回类型cnS32_t
可能是一个错误代码或状态码,用于指示操作是否成功。cnveDspCoreStatGet
: 这个函数可能是用来获取某个DSP的核心统计信息。enDspId
可能是一个标识符,用于指定要获取统计信息的DSP,pParams
可能是用于接收统计信息的结构体指针。返回类型cnS32_t
可能是一个错误代码或状态码,用于指示操作是否成功。cnveDspCoreStatStop
: 这个函数可能是用来停止记录某个DSP的核心统计信息。/*
*Malloc memory
*/
cnS32_t sampleCommDspMemAlloc(cnU64_t *pu64PhyAddr, cnVoid_t **ppu64VirAddr, cnU32_t u32Len);
我们看看怎么 Malloc memory :
这是一个C语言函数,函数名为sampleCommDspMemAlloc
。这个函数的主要目的是为DSP(数字信号处理器)分配内存。
函数参数列表中,有三个参数:
cnU64_t *pu64PhyAddr
:一个指向cnU64_t
类型变量的指针,这个变量可能是用来保存物理内存地址的。cnVoid_t **ppu64VirAddr
:一个指向cnVoid_t
类型指针的指针,这个指针可能是用来保存虚拟内存地址的。cnU32_t u32Len
:一个cnU32_t
类型的变量,表示需要分配的内存长度。cnS32_t sampleCommDspMemAlloc(cnU64_t *pu64PhyAddr, cnVoid_t **ppu64VirAddr, cnU32_t u32Len)
{
cnS32_t s32Ret;
cnMemInfo_t mem_info;
mem_info.u32Size = u32Len;
mem_info.enCmpMode = COMPRESS_MODE_NONE;
snprintf(mem_info.acMmzName, MAX_MMZ_NAME_LEN, "zone_anon");
snprintf(mem_info.acMmbName, MAX_MMB_NAME_LEN, "dspMem");
s32Ret = cnsysMmzAlloc(&mem_info, (cnU64_t *)pu64PhyAddr, (cnVoid_t **)ppu64VirAddr);
SAMPLE_DSP_CHECK_EXPR_RET(CN_SUCCESS != s32Ret, CN_ERR_CNVE_DSP_NOMEM, SAMPLE_DSP_ERR_LEVEL_ERROR, "Error(%d):cnrtMallocExt failed!\n", s32Ret);
return CN_SUCCESS;
}
在函数体中,首先定义了一个cnS32_t
类型的变量s32Ret
,用来保存函数返回值。然后定义了一个cnMemInfo_t
类型的变量mem_info
,用来保存内存信息。
接着,给mem_info
结构体的成员变量赋值。其中,mem_info.u32Size = u32Len;
是将需要分配的内存长度赋值给mem_info.u32Size
。mem_info.enCmpMode = COMPRESS_MODE_NONE;
是将压缩模式设为无压缩模式。通过snprintf
函数,将"zone_anon"赋值给mem_info.acMmzName
,将"dspMem"赋值给mem_info.acMmbName
。
然后,通过调用cnsysMmzAlloc
函数,根据mem_info
的信息,分配内存,并将物理内存地址和虚拟内存地址保存到对应的参数中。
接着,通过调用SAMPLE_DSP_CHECK_EXPR_RET
宏函数,检查s32Ret
是否等于0,如果不是,则输出错误信息,并返回错误码。
最后,如果一切正常,函数返回CN_SUCCESS
。
/*
*Create image memory
*/
cnS32_t sampleCommDspCreateImage(cnveImage_t *pstImg, cnveEnImageType_t enType, cnU32_t u32Width, cnU32_t u32Height);
/*
*Create Image memory
*/
cnS32_t sampleCommDspCreateImage(cnveImage_t *pstImg, cnveEnImageType_t enType, cnU32_t u32Width, cnU32_t u32Height)
{
cnS32_t s32Ret;
cnU32_t u32Size = 0;
pstImg->enType = enType;
pstImg->u32Width = u32Width;
pstImg->u32Height = u32Height;
pstImg->au32Stride[0] = sampleCommDspAlign(pstImg->u32Width, SAMPLE_DSP_ALIGN_32);
switch(enType)
{
case CNVE_IMAGE_TYPE_RGB_PACKAGE:
case CNVE_IMAGE_TYPE_BGR_PACKAGE:
{
u32Size = pstImg->au32Stride[0] * pstImg->u32Height * 3;
s32Ret = sampleCommDspMemAlloc(&pstImg->au64PhyAddr[0], (cnVoid_t**)&pstImg->au64VirAddr[0], u32Size);
SAMPLE_DSP_CHECK_EXPR_RET(CN_SUCCESS != s32Ret, s32Ret, SAMPLE_DSP_ERR_LEVEL_ERROR, "Error(%#x):sampleCommDspMemAlloc failed!\n", s32Ret);
pstImg->au32Stride[1] = pstImg->au32Stride[0];
pstImg->au32Stride[2] = pstImg->au32Stride[0];
pstImg->au64VirAddr[1] = pstImg->au64VirAddr[0] + 1;
pstImg->au64VirAddr[2] = pstImg->au64VirAddr[1] + 1;
pstImg->au64PhyAddr[1] = pstImg->au64PhyAddr[0] + 1;
pstImg->au64PhyAddr[2] = pstImg->au64PhyAddr[1] + 1;
break;
}
default:
break;
}
return CN_SUCCESS;
}
这是一个C语言函数,函数名为sampleCommDspCreateImage
,它用于创建图像对象。
函数参数列表中,有三个参数:
cnveImage_t *pstImg
:一个指向cnveImage_t
类型变量的指针,这个变量可能用来保存图像信息。cnveEnImageType_t enType
:一个cnveEnImageType_t
类型的变量,表示图像类型。cnU32_t u32Width
:一个cnU32_t
类型的变量,表示图像宽度。cnU32_t u32Height
:一个cnU32_t
类型的变量,表示图像高度。在函数体中,首先定义了一个cnS32_t
类型的变量s32Ret
,用来保存函数返回值。然后定义了一个cnU32_t
类型的变量u32Size
,用来保存图像的大小。
接着,通过赋值操作,设置了pstImg
结构体的成员变量,包括图像类型、宽度、高度和步长。然后根据图像类型,执行不同的操作。
当图像类型为CNVE_IMAGE_TYPE_RGB_PACKAGE
或CNVE_IMAGE_TYPE_BGR_PACKAGE
时,计算图像的大小,并调用sampleCommDspMemAlloc
函数分配内存。然后根据分配的内存大小,设置图像的虚拟地址和物理地址。同时,设置其他相关信息,如步长和颜色通道的虚拟地址和物理地址。
如果图像类型不是CNVE_IMAGE_TYPE_RGB_PACKAGE
或CNVE_IMAGE_TYPE_BGR_PACKAGE
,则不执行任何操作。
最后,函数返回CN_SUCCESS
,表示成功创建了图像对象。
static cnVoid_t cnveSampleResize(Sample_Image_table_t para, cnChar_t *rootdir)
{
cnChar_t fname[128];
cnveImage_t srcImage, dstImage;
cnveResizeCtrl_t resizeCtrl;
cnveEnErrCode_t ret;
cnU32_t srcSize, dstSize;
cnS32_t Ret;
srcImage.u32Width = para.srcWidth;
srcImage.u32Height = para.srcHeight;
srcImage.au32Stride[0] = para.srcStride;
srcImage.enType = para.srcType;
dstImage.u32Width = para.dstWidth;
dstImage.u32Height = para.dstHeight;
dstImage.au32Stride[0] = para.dstStride;
dstImage.enType = para.dstType;
Ret = sampleCommDspCreateImage(&srcImage, srcImage.enType, srcImage.u32Width, srcImage.u32Height);
SAMPLE_DSP_CHECK(Ret);
Ret = sampleCommDspCreateImage(&dstImage, dstImage.enType, dstImage.u32Width, dstImage.u32Height);
SAMPLE_DSP_CHECK(Ret);
srcSize = get_image_size(srcImage.u32Width, srcImage.u32Height, srcImage.enType);
dstSize = get_image_size(dstImage.u32Width, dstImage.u32Height, dstImage.enType);
sprintf(fname, "%s", para.srcName);
read_bin(fname, (cnChar_t *)srcImage.au64VirAddr[0], srcSize);
resizeCtrl.alignCornerType = CNVE_ALIGN_CORNER_FALSE;
resizeCtrl.resizeType = CNVE_RESIZE_NORMAL;
resizeCtrl.interType = CNVE_INTER_BILINEAR;
resizeCtrl.batchSize = 1;
ret = cnveResize(&srcImage, &dstImage, &resizeCtrl, g_enCoreId, g_enPri, g_bBlock);
if(ret != CNVE_ERR_SUCCESS)
{
printf("cnveResize failed!\n");
exit(-1);
}
memset(fname, 0, 128);
sprintf(fname, "%s%s", rootdir, para.dstName);
write_bin(fname, (cnChar_t *)dstImage.au64VirAddr[0], dstSize);
sampleCommDspDestroyImage(&srcImage);
sampleCommDspDestroyImage(&dstImage);
printf("cnveResize success!\n");
}
这是一个C语言的函数,函数名为cnveSampleResize
,它用于对图像进行缩放。
函数有两个参数,一个是Sample_Image_table_t
类型的para
,另一个是cnChar_t
类型的rootdir
。
在这个函数中,首先定义了一些变量,包括两个cnveImage_t
类型的变量srcImage
和dstImage
,用于保存源图像和目标图像的信息。一个cnveResizeCtrl_t
类型的变量resizeCtrl
,用于保存缩放控制信息。一个cnveEnErrCode_t
类型的变量ret
,用于保存函数返回值。两个cnU32_t
类型的变量srcSize
和dstSize
,用于保存源图像和目标图像的大小。一个cnS32_t
类型的变量Ret
,用于保存函数返回值。一个字符数组fname
,用于保存文件名。
然后,通过赋值操作,设置了源图像和目标图像的宽度、高度、步长和类型等信息。
接下来,通过调用sampleCommDspCreateImage
函数,创建了源图像和目标图像。然后通过调用get_image_size
函数,计算了源图像和目标图像的大小。
接着,通过调用sprintf
函数,将源文件名赋值给了fname
。然后通过调用read_bin
函数,读取了源文件的内容,并保存到了源图像的虚拟地址中。
然后,通过赋值操作,设置了缩放控制信息。
接着,通过调用cnveResize
函数,对源图像进行缩放,并将结果保存到目标图像中。如果缩放失败,则输出错误信息并退出程序。
然后,通过调用memset
函数,清空了fname
数组。接着通过调用sprintf
函数,将目标文件名赋值给了fname
。然后通过调用write_bin
函数,将目标图像的内容写入了目标文件中。
最后,通过调用sampleCommDspDestroyImage
函数,销毁了源图像和目标图像,并输出了一条成功的消息。
/*!
* @brief
*
* This function performs the Resize operation.
*
* @param[in] pstSrc
* Input. Pointer to the input cnveSrcImage_t struct.
* @param[out] pstDst
* Output. Pointer to the output cnveDstImage_t struct.
* @param[in] pstResizeCtrl
* Input. Pointer to the cnveResizeCtrl_t struct.
* @param[in] enCoreId
* Input. DSP ID.
* @param[in] enPri
* Input. DSP task priority.
* @param[in] bBlock
* Input. Specify whether to block until the result is returned.
* @retval CNVE_ERR_SUCCESS
* The function ends normally.
* @par Requirements
* - None.
* @par Example
* - None.
*
*/
cnveEnErrCode_t cnveResize(cnveSrcImage_t *pstSrc,
cnveDstImage_t *pstDst,
cnveResizeCtrl_t *pstResizeCtrl,
cnveEnDspID_t enCoreId,
cnveEnDspPri_t enPri,
cnBool_t bBlock);
具体的代码实现,我们看不到。
关于图像拼接处理的函数,主要功能是将四个相机捕获的图像拼接成一个全景图像。
函数参数说明:
src
:输入参数,四路相机捕获的原始图像数据,从上到下依次为“前左后右”。output
:输出参数,拼接后的全景图像数据。drawCar
:是否粘贴车辆模型,true表示粘贴。代码逻辑解析:
src_image
。remap
函数对src_image
进行重映射操作,分别得到tmp[0]
和tmp[1]
两个图像。重映射是一种图像变换技术,可以通过改变像素之间的映射关系来达到图像变换的效果。这里使用重映射可能是为了纠正图像畸变、调整图像大小等。tmp[0]
和tmp[1]
进行像素值调整,乘以相应的alpha值,并除以255进行归一化处理。tmp[0]
和tmp[1]
进行拼接,得到全景图像output
。总结:该函数的主要目的是将四个相机捕获的图像拼接成一个全景图像,并可选择是否在拼接后的图像上粘贴车辆模型。
remap
函数是一种图像处理函数,用于对图像进行重映射操作。它可以通过改变像素之间的映射关系来实现图像的变换、纠正畸变、调整大小等功能。
remap
函数的一般用法:void remap(InputArray src, OutputArray dst, InputArray map1, InputArray map2, int interpolation, int borderMode, const Scalar& borderColor);
参数解释如下:
src
:输入图像,可以是彩色图像或灰度图像。dst
:输出图像,即重映射后的图像。map1
:映射关系数组1,用于指定第一个映射关系。map2
:映射关系数组2,用于指定第二个映射关系。interpolation
:插值方式,用于指定像素值的计算方式。常见的插值方式有线性插值(INTER_LINEAR
)和最近邻插值(INTER_NEAREST
)等。borderMode
:边界模式,用于指定像素值的边界处理方式。常见的边界模式有复制边界(BORDER_CONSTANT
)和镜像边界(BORDER_REFLECT
)等。borderColor
:边界颜色,用于指定复制边界时的颜色。注意事项:
remap
函数需要提供两个映射关系数组,第一个数组指定输入图像到输出图像的水平映射关系,第二个数组指定输入图像到输出图像的垂直映射关系。映射关系数组中的每个元素表示对应的映射关系,通常是双线性插值或最近邻插值。remap
函数时,需要注意图像的大小和类型,以确保输入图像与输出图像的尺寸和数据类型一致。remap
函数的两个映射关系数组的尺寸(即行数和列数)是根据输入图像的尺寸来确定的。具体来说,映射关系数组的行数应该与输入图像的行数相同,映射关系数组的列数应该与输入图像的列数相同。
1、通过外参计算 image plane to world coords 转移矩阵 M
2、计算出 IPM这个 单应矩阵 homographies
3、使用 cv2.warpPerspective(img, IPM, (outputRes[1], outputRes[0]), flags=interpMode)进行投影计算。
根据摄像机的内参、畸变系数和其他运动参数来初始化并计算一个校准和矫正图像的映射。然后,它将处理过的映射数据合并为一个完整的映射并返回。
/*!
* @brief
*
* This function performs the Ipm operation.
*
* @param[in] pstSrcFront
* Input. Pointer to the input cnveSrcImage_t struct.
* @param[in] pstSrcLeft
* Input. Pointer to the input cnveSrcImage_t struct.
* @param[in] pstSrcRight
* Input. Pointer to the input cnveSrcImage_t struct.
* @param[in] pstSrcRear
* Input. Pointer to the input cnveSrcImage_t struct.
* @param[out] pstDst
* Output. Pointer to the output cnveDstImage_t struct.
* @param[in] pstIpmCtrl
* Input. Pointer to the input cnveIpmCtrl_t struct.
* @param[in] enCoreId
* Input. DSP ID.
* @param[in] enPri
* Input. DSP task priority.
* @param[in] bBlock
* Input. Specify whether to block until the result is returned.
* @retval CNVE_ERR_SUCCESS
* The function ends normally.
* @par Requirements
* - None.
* @par Example
* - None.
*
*/
cnveEnErrCode_t cnveIpm(cnveSrcImage_t *pstSrcFront, cnveSrcImage_t *pstSrcLeft,
cnveSrcImage_t *pstSrcRight, cnveSrcImage_t *pstSrcRear,
cnveSrcImage_t *pstParams, cnveDstImage_t *pstDst,
cnveIpmCtrl_t *pstIpmCtrl, cnveEnDspID_t enCoreId,
cnveEnDspPri_t enPri, cnBool_t bBlock);
我们看看solution 方案的sample_alg_ipm.cpp
首先初始化读取pstParams文件
cnS32_t sampleAlgIpmInit()
{
cnS32_t s32Ret;
s32Ret = sampleCommDspCreateImage(&g_stIpmParamsImage, CNVE_IMAGE_TYPE_U8C1, sampleCommSysStatFile((cnChar_t*)SAMPLE_IPM_PARAM_FILE), 1);
SAMPLE_DSP_CHECK_EXPR_GOTO(CN_SUCCESS != s32Ret, ERR_EXIT, SAMPLE_DSP_ERR_LEVEL_ERROR, "Error(%#x):sampleCommDspCreateImage failed!\n", s32Ret);
s32Ret = sampleAlgIpmReadBin(&g_stIpmParamsImage);
SAMPLE_DSP_CHECK_EXPR_GOTO(CN_SUCCESS != s32Ret, IMAGE_EXIT, SAMPLE_DSP_ERR_LEVEL_ERROR, "Error(%#x):sampleAlgIpmReadBin failed!\n", s32Ret);
SAMPLE_TRACE("sampleAlgIpmInit success\n");
return CN_SUCCESS;
}
然后执行拼接
cnS32_t sampleAlgIpmProcess(cncveImage_t astRGBInFrame[], cnS32_t s32ChnNum, cncveImage_t *pstRGBOutFrame)
{
static cnS32_t s32Cnt = 0;
cnveImage_t astSrcImage[4];
cnveImage_t astDstImage;
cnveIpmCtrl_t stCtrl;
//0-front 1-left 2-right 3-rear
for (cnS32_t i = 0; i < 4; i++)
{
sampleAlgBGRFrameConvert2Image(&astRGBInFrame[i], &astSrcImage[i]);
}
sampleAlgBGRFrameConvert2Image(pstRGBOutFrame, &astDstImage);
cnS32_t s32Ret = cnveIpm(&astSrcImage[0], &astSrcImage[1], &astSrcImage[2], &astSrcImage[3],
&g_stIpmParamsImage, &astDstImage, &stCtrl, CNVE_DSP_ID_0, CNVE_DSP_PRI_0, CN_FALSE);
...
//sampleCommSysWriteFile((cnChar_t*)"ipm_out.bgr", pstRGBOutFrame->au64VirtAddr[0], 3 * pstRGBOutFrame->u32Width * pstRGBOutFrame->u32Height);
...
}
1、标准化
2、连通域标记操作
3、Crop 抠图
4、Csc 的图像空间转换
5、高斯模糊计算
6、IPM拼接
7、生成图像的边界
8、完成ransac 的多线段拟合
9、图像缩放
(正文完)