视差图转换为深度图的实现

视差图转换为深度图

网上关于视差图转换为深度图的博客较多,但缺少具体实现。我根据原理和网上的一些参考自己实现了一个版本,做个记录,也希望能供大家参考

1 原理

根据视差图得到深度的原理很简单,示意如下:视差图转换为深度图的实现_第1张图片
图中b为双目相机的基线,一般需要自己测量(如果只需要相对深度的话可以自己取值)。f表示相机焦距(通常相机焦距有fx,fy,但由于视差只在x方向,因此直接取fx为f即可)。可以得到深度Z与视差d的关系为:
在这里插入图片描述
具体推导可以参考推导过程。需要注意的是,d的单位是像素pixel,而其他几个变量的单位是mm,这看似单位不匹配(也是我一开始的困惑所在),但实际上是没有问题的。可以参考《视觉slam十四讲》的相机模型构建,相机成像过程如下:
视差图转换为深度图的实现_第2张图片
主要分为:
(1)实际空间点到物理成像平面上的投影;
(2)物理成像平面转换到像素平面
这两个过程。(1)中物理成像平面上对应实际空间点(X,Y,Z)的点(X’,Y’)是具有物理尺度的,只不过进行了缩放:

X ′ = f ∗ X / Z X' = f*X/Z X=fX/Z
Y ′ = f ∗ Y / Z Y' = f*Y/Z Y=fY/Z

在次之后,(2)将物理成像平面上的坐标进一步转换到像素平面得到坐标(u,v),也就是我们使用的图像像素坐标,这个过程为:

u = α X ′ + c x u = \alpha X' +cx u=αX+cx
v = β Y ′ + c y v = \beta Y' +cy v=βY+cy

f x = α f fx = \alpha f fx=αf, f y = β f fy = \beta f fy=βf, 这样就将像素坐标与三维空间点建立了联系。相当于 f x fx fx的单位是 pixel/mm,这样就解释了视差单位是pixel的问题。

搞清楚原理后,下面就可以进行实现了。

2 代码实现

这里假定大家已经有了深度图,只进行深度图的求解。话不多说,直接上代码。main函数如下:

int main(int argc, char* argv[])
{
	//视差图 D1, 深度图 depth1
	cv::Mat D1,depth1, draw ,draw1;
	//
	//对视差图的计算,此处省略
	//
	sgm.disp2Depth(D1,depth1);//由视差图计算深度图
	std::cout << "depth map is ok " << std::endl;
	//转换成彩色图便于观察
	depth1.convertTo(draw1, CV_8U, 1./256);
	cv::applyColorMap(draw1, draw1, cv::COLORMAP_JET);
	
	//视差图也转换成彩色图
	D1.convertTo(draw, CV_8U, 255. / (SemiGlobalMatching::DISP_SCALE * param.numDisparities));
	cv::applyColorMap(draw, draw, cv::COLORMAP_JET);
	
	cv::imshow("image", I1);
	cv::imshow("disparity1", draw);
	cv::imshow("depth1", draw1);
	cv::waitKey(0);
}

下面是disp2Depth()函数的实现,

void SemiGlobalMatching::disp2Depth(cv::Mat dispMap, cv::Mat &depthMap)
{
    	float fx = 4.9250945790423793e+02;//取fx为f,fx由标定内参得到
    	float baseline = 600; //基线距离b,根据标定的相机外参计算。如果只需要相对深度取1即可

        int height = dispMap.rows;
        int width = dispMap.cols;
		depthMap.create(height, width, CV_16U);
		
		//这里深度图的数据类型可能会不一样,大家根据自己的情况进行修改
        short* dispData = (short*)dispMap.data;
        ushort* depthData = (ushort*)depthMap.data;
        for (int i = 0; i < height; i++)
        {
            for (int j = 0; j < width; j++)
            {
				int id = i*width + j;
                if (!dispData[id])  continue;  //防止0除
                depthData[id] = ushort( fx*baseline / dispData[id]  );
            }
        }
}

结论

相比于视差图的计算,深度计算只能算是饭后甜点了,不过还是需要把原理和思路理清。

最后,欢迎点赞和评论^_ ^

你可能感兴趣的:(计算机视觉,slam,opencv)