海思DPU(Depth Process Unit)的中文翻译为深度信息处理单元,主要是来实现双目测距和三维重建的硬件加速功能从而提高实时性。海思SDK里面和DPU相关的文档资料很少,就两篇:《HiDPU工具使用指南》和《HiDPUAPI参考》。所以这里有必要结合sample代码来理清其思路。
DPU包括两个子模块。一个叫RECT,注意这里不是矩形框的意思,而是rectify,即校正的意思,后面会进一步解释其原理。另一个模块叫Match,即匹配的意思。这些概念需要有些双目测距/重建的基础才能比较好理解。
此外,在dpu sample代码中有两个独立的运行case。
一个是走VI和VPSS通道,直接对双VPSS输出数据来进行双目测距以此实现实时双目视频流的三维重建。其入口函数为:
SAMPLE_DPU_VI_VPSS_RECT_MATCH();
另一个是基于双目拍摄到的图片来进行双目测距和重建。 其入口函数为:
SAMPLE_DPU_FILE_RECT_MATCH();
两者算法原理都差不多,都是先做校正再做匹配来得到视差图,为后面的测距和重建奠定坚实基础。这里方便起见,就介绍第二个case。
RECT校正是用来将双目同时拍摄到的图片通过对应摄像头的映射参数mapx和mapy进行行对齐。而每个摄像头的映射参数是通过摄像头标定所得到的内参和外参来获取得。这两句话如果没有标定基础得话,理解起来会有些困难。换句话说,每个摄像头都有自己得视角和畸变,它们就是靠各自得映射参数文件mapx,mapy来去畸变并实现行对齐。
在sample代码中,双目摄像头对应得mapx和mapy参数为:
HI_CHAR *apcMapFileName[DPU_RECT_MAX_PIPE_NUM] = {"./data/input/lut/1050x560_LeftMap.dat",
"./data/input/lut/1050x560_RightMap.dat"};
这里1050x560_LeftMap.dat和1050x560_RightMap.dat分别指左和右摄像头得映射参数,其分辨率为1050x560。它把x和y方向得参数融合进一个文件,其里面得排列格式为:
可以看到原始得mapx或y每个坐标值为浮点数,占4个字节,两者融合起来就变成每个坐标值占8个字节了,如果分辨率为1050x560得话,那么融合得到数据文件size为1050x560*8 bytes。
需要注意的是,代码里面得1050x560_LeftMap.dat和1050x560_LeftMap.dat的大小只为 1050*560*4bytes,那是因为使用海思dpu工具dpu_tool_rect.exe(详见HiDPU工具 开发指南)做了一个内部转换来节省内存空间。
映射参数文件又叫查找表LUT。 举个例子,如果想获取左摄像头图片对齐后目标图像(1,0)的像素值,那么得先去1050x560_LeftMap.dat查找表中(1,0)出得x和y方向坐标值,假如是(3.2, 2.8),那么就到源图像中做一个插值,所得结果就赋给目标图像(1,0)。
当然在海思dpu模块中,这个查找表过程由硬件来实现,我们只需要把查找表以及源图像正确得输入进去即可。
/************************************************
step2: start DPU RECT
*************************************************/
for (i = 0; i < DPU_RECT_MAX_PIPE_NUM; i++)
{
s32PipeNum = i;
s32Ret = SAMPLE_COMM_DPU_RECT_LoadLut(apcMapFileName[i],
&s_stSampleDpuConfig.astDpuRectMemInfo[i], &s_stSampleDpuConfig.s32LutId[i]);
if(s32Ret != HI_SUCCESS)
{
SAMPLE_PRT("load lut failed for %#x!\n", s32Ret);
goto END4;
}
}
。。。 。。。
s32Ret = SAMPLE_COMM_DPU_RECT_Start(s_stSampleDpuConfig.DpuRectGrp,
&s_stSampleDpuConfig.stDpuRectGrpAttr, astChnAttr);
if(s32Ret != HI_SUCCESS)
{
SAMPLE_PRT("start dpu rect failed for %#x!\n", s32Ret);
goto END5;
}
双目图像行对齐后,接下来就是行匹配来找到对应点,找到后,就将该两点的x坐标值相减,所得到差就是所谓视差值。这里的难点以及计算量大的工作自然是匹配。
双目立体视觉匹配算法有SAD, BM以及SGBM等。相较而言,SGBM用的比较多,因为它是全局算法,其匹配效果肯定好于局部匹配算法BM,而且它还是半全局的,所以速度方面也有所提升。 目前还不清楚海思dpu里面用的是哪个匹配算法,应该是全局或半全局。
其对应功能启动的代码如下:
s32Ret = SAMPLE_COMM_DPU_MATCH_Start(s_stSampleDpuConfig.DpuMatchGrp,
&s_stSampleDpuConfig.stDpuMatchGrpAttr, &stChnAttr);
需要注意的是,匹配得到的视差图大小为 width*height*2,而不是 width*height*4,这是因为其每个像素数据格式为S10Q6((1bit 符号位+9bit 整数部分+6bit 小数部分),换句话来说,用2个byte来表示float32数了。
RECT和MATCH无疑是双目立体视觉中最耗计算资源的部分。所以用硬件来实现这两步可以大大提高测距和三维重建实时性。得到视差图后就可以通过公式d = (B*fx) / d来计算得到Z轴方向上的值,进而可以其x,y坐标值,这里就不再赘述。