密集匹配之动态规划DP

       动态规划通常应用于最优化问题的求解中,Baker、Ohta 等将动态规划引入立体匹配中来获取视差图。动态规划匹配的过程可以分为两个阶段,建立视差空间和动态规划优化,将立体匹配问题转化为视差空间内寻找最优路径的问题。

       密集匹配通常会充分利用影像间的核线约束条件,对立体像对进行核线纠正,这样同名像点肯定位于对应的同名核线上,降低了匹配的难度。视差空间影像DSI(Disparity Space Image)是一种反映核线立体像对间同一扫描线视差关系的数据结构,将视差分析转化为一种类似的谱分析。其数学表达为:

其中w 为图像的宽度, NaN 表示在该视差值为d 时不可能有对应的同名点,L((* ))  是对应点之间的相似性测度函数。在匹配核线影像时,逐行计算像素的DSI,将每一行的DSI 依次叠加起来构成一个三维空间,称之为视差空间,视差空间包含了每个像素所有可能的视差取值。如下图所示,图中的x、y 代表图像的横纵坐标,d 表示视差搜索范围。

在建立好视差空间后,立体匹配问题就会转化为在视差空间内寻找最佳路径的问题,该路径上的视差累积的能量最小,相似性最大。一般采用从图像左边到右边的顺序,逐行进行。

实现过程如下:

void DynamicProgramMatch(const cv::Mat& img1, const cv::Mat& img2,
	int min_disp, int max_disp, int P1, int P2,
	int m_size, cv::Mat& disp)
{
	if (img1.empty() || img1.type() != CV_8UC1
		|| img2.empty() || img2.type() != CV_8UC1
		|| img1.size() != img2.size() || m_size < 1) return;

	int disp_range = max_disp - min_disp + 1;///视差范围
	disp = cv::Mat(img1.size(), CV_8UC1, cv::Scalar::all(0));///左视差图
	for (int r = 0; r < img1.rows; ++r)
	{
		printf("动态规划...%d%%\r", (int)(r * 100.0 / (img1.rows))); // 方便查看进度
		
		cv::Mat diff(cv::Size(disp_range, img1.cols), CV_64FC1, cv::Scalar::all(0)); //视差空间
		cv::Mat p_mat(cv::Size(disp_range, img1.cols), CV_8UC1, cv::Scalar::all(0)); // 路径走向
		for (int c1 = 0; c1 < img1.cols; ++c1)
		{
			for (int d = 0; d <= disp_range; ++d)
			{
				int c2 = c1 - d - min_disp;///当前视差下左像素对应对的右像素坐标
				if (c2 >= 0)
				{
					diff.ptr<double>(c1)[d] =
						SAD(img1, c1, r, img2, c2, r, m_size);//SAD匹配代价
				}
				else
				{
					diff.ptr<double>(c1)[d] =
						c1 < min_disp ? 0 : diff.ptr<double>(c1)[d - 1]; // 防止边界超限处代价对整体路径的影响
				}
				
			}
		}

		cv::Mat e_mat_cur(cv::Size(1, disp_range), CV_64FC1, cv::Scalar::all(0));
		for (int c = 1; c < img1.cols; ++c)
		{
			cv::Mat e_mat_pre = e_mat_cur.clone(); // 能量空间
			for (int cur = 0; cur < disp_range; ++cur)
			{
				double cost_min = FLT_MAX;//当前状态下路径代价
				int p_min = 0; //路径走向
				double e_cur =  diff.ptr<double>(c)[cur]; //数据项
				for (int pre = 0; pre < disp_range; pre++)
				{
					int deta_d = abs(cur - pre);
					double e_smooth = deta_d > 1 ? P2 : (deta_d == 0 ? 0 : P1); //视差等于1与大于1时候的平滑惩罚不同
					double e_data = e_cur + e_smooth + e_mat_pre.ptr<double>(pre)[0]; // 路径能量值
					if (e_data < cost_min)
					{
						cost_min = e_data;
						p_min = pre;
					}
				}
				e_mat_cur.ptr<double>(cur)[0] = cost_min;
				p_mat.ptr<uchar>(c)[cur] = p_min;
			}
		}

		int p_min = 0;
		double e_min = e_mat_cur.ptr<double>(0)[0];
		for (int i = 0; i < disp_range; ++i)
		{
			if (e_mat_cur.ptr<double>(i)[0] < e_min)
			{
				p_min = i;
				e_min = e_mat_cur.ptr<double>(i)[0];
			}
		}
		//取得视差
		for (int c = img1.cols - 1; c >= 0; --c)
		{
			int d = p_mat.ptr<uchar>(c)[p_min];
			p_min = d;
			disp.ptr<uchar>(r)[c] = d + min_disp; 
		}
	}
}

实现样例如下:

void DynamicProgrammingTest()
{
	cv::Mat img1 = cv::imread("cones\\im2.png");
	cv::Mat img2 = cv::imread("cones\\im6.png");
	cv::imshow("img1", img1);
	cv::imshow("img2", img2);
	cv::Mat gray1, gray2;
	cv::cvtColor(img1, gray1, cv::COLOR_BGR2GRAY);
	cv::cvtColor(img2, gray2, cv::COLOR_BGR2GRAY);
	cv::Mat disp;
	dense_matching::DynamicProgramMatch(gray1, gray2, 0, 100, 1, 4, 3, disp);
	cv::Mat disp_show;
	cv::normalize(disp, disp_show, 0, 255, cv::NORM_MINMAX);
	cv::imshow("disp", disp_show);
	cv::waitKey(0);
}

本人水平有限,如有错误,还望不吝指正,代码有一定删减,没有重新编译,如有错误,请自行调试,有问题请邮箱联系1299771369@qq.com。

SGBM算法的密集匹配SGBM算法介绍 在OpenCV中使用函数StereoSGBM ( ) 实现了SGBM算法。SGBM 算法核心步骤为:选取匹配基元;构建基于多个方向的扫描线的代价能量和函数;求取能量代价和函数的最优解。OpenCV中SGMB算法的实现主要分为以下四个步骤: ①预处理 SGBM采用水平Sobel算子,把图像做处理,然后用一个函数将经过水平Sobel算子处理后的图像上每个像素点(P表示其像素值)映射成一个新的图像,表示新图像上的像素值。映射函数如下: preFilterCap为一个常数参数,openCv默认取15。预处理实际上是得到图像的梯度信息。经预处理的图像保存起来,将会用于计算代价。 ②代价计算 代价有两部分组成:经过预处理得到的图像的梯度信息经过基于采样的方法得到的梯度代价;原图像经过基于采样的方法得到的SAD代价。 ③动态规划 用一维约束近似二维约束。在P的周围,以 45°为间隔设置了8个路径。通过8个路径计算最小代价路径,以此来近似二维约束匹配计算. 其中动态规划很重要两个参数P1,P2是这样设定的: P1 =8*cn*sgbm.SADWindowSize*sgbm.SADWindowSize; P2 = 32*cn*sgbm.SADWindowSize*sgbm.SADWindowSize; cn是图像的通道数, SADWindowSize是SAD窗口大小,数值为奇数。可以看出,当图像通道和SAD窗口确定下来,SGBM的规划参数P1和P2是常数。 ④后处理 openCvSGBM的后处理包含以下几个步骤: Step1:唯一性检测:视差窗口范围内最低代价是次低代价的(1 + uniquenessRatio/100)倍时,最低代价对应的视差值才是该像素点的视差,否则该像素点的视差为0。其中uniquenessRatio是一个常数参数。 Step2:亚像素插值 Step3:左右一致性检测:误差阈值disp12MaxDiff默认为1,可以自己设置。 获得深度信息 经过sgbm->compute(rectifyImageL, rectifyImageR, disp)获得视差映射后,利用式(2),式(3) ,通过简单的矩阵相乘就可提取深度信息。三维坐标就是( X /W,Y/W,Z /W) 。OpenCV中使用reprojectImageTo3D( )函数实现该功能,该函数输入上面得到的视差数据,输出所需的三维点阵,然后提取深度信息。 SGBM参数设置:(在下面的程序中已经标明) MinDisparity设置为0,因为两个摄像头是前向平行放置,相同的物体在左图中一定比在右图中偏右。如果为了追求更大的双目重合区域而将两个摄像头向内偏转的话,这个参数是需要考虑的。 UniquenessRatio主要可以防止误匹配,此参数对于最后的匹配结果是有很大的影响。立体匹配中,宁愿区域无法匹配,也不要误匹配。如果有误匹配的话,碰到障碍检测这种应用,就会很麻烦。该参数不能为负值,一般5-15左右的值比较合适,int型。 BlockSize:SAD窗口大小,容许范围是[5,255],一般应该在 5x5..21x21 之间,参数必须为奇数值, int型。 NumDisparities:视差窗口,即最大视差值与最小视差值之差,窗口大小必须是 16的整数倍,int型。 在SGBM算法的参数中,对视差生成效果影响较大的主要参数是BlockSize、NumDisparities和UniquenessRatio三个,一般只需对这三个参数进行调整,其余参数按默认设置即可。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值