动态规划最佳缝合线查找(Image Stitching 3)

1. 存在的问题

之前最佳缝合线查找存在一定的问题:查找不到最小能量和的缝合线路径。举一个简单的例子如图1.1所示:

图1.1

如果从上至下求和选择最小能量函数,则选择的路径为蓝色,但是蓝色路径不是最小能量和路径。因为从上至下只是考虑了下一步对于上一步的能量和最小,但是没有考虑到全局的能量和最小。上图中的最小能量和路径应该为红色。因此需要有一个新的思路求取最佳缝合线。

2. 新的思路

(也是OpenCV中缝合线查找思想):我们反过来对最小能量做选择,不在从多个像素中选择一个作为缝合线的延伸方向,而是从多个缝合线中选择一条最优的来连接当前点。思路如图2.1所示

图2.1

如果存在3x3的能量矩阵,把第一行能量值作为三条最小能量和路径,那么能量化去向可以表示为如图2.2所示。

图2.2

算法步骤:

     (1) 初始化。第一行作为第一步的最小能量和路径;

     (2) 最小能量和迭代。依次遍历每一行,得到最小能量和路径来源的位置:左上、顶上、右上。在遍历的时候需要记录抵达每一行每一列的最小能量和向量,以及抵达每行每列路径来源的位置。假设左上(1)、顶上(2)、右上(3),供回溯时使用。

     (3) 缝合线回溯。最后通过记录来源的位置矩阵从最后一行回溯缝合线。在X方向上,如果是1则减1,如果是3则加1,如果是2则不变,Y方向上减1。

3. 实验结果

图3.1 重叠区域
图3.2 最佳缝合线与不同终点缝合线
图3.3 缝合线诱导融合

4. C++代码

void OptimalSeam::DP_find_seam(Mat I1, Mat I2, vector<int>& seam_paths)
{
	
	Mat_<float> E;
    // 能量函数的计算,参考image stitching 2
	energy_function(I1, I2, E);
	
	E.convertTo(E, CV_32F);
	Mat_<uchar> control = Mat::zeros(E.size(), CV_8U); 
	Mat_<float> sum_cost = E.rowRange(0, 1); 

	for (int i = 1; i < E.rows; i++) {
		float* E_ptr = E.ptr<float>(i);
		Mat_<float> cost_tmp = sum_cost.clone();
		for (int j = 0; j < E.cols; j++) {
			float min_e = sum_cost(0, j);
			int c_x = 2; 
			if (j > 0 && min_e > sum_cost(0, j - 1)) {
				min_e = sum_cost(0, j - 1);
				c_x = 1;
			}
			if (j < E.cols - 1 && min_e > sum_cost(0, j + 1)) {
				min_e = sum_cost(0, j + 1);
				c_x = 3;
			}
			
			cost_tmp(0, j) = min_e + E_ptr[j];
			control(i, j) = c_x;
		}
		sum_cost = cost_tmp;
	}


	float minE = sum_cost(0, 0);
	int end_x = 0;
	for (int i = 0; i < sum_cost.cols; i++) {
		if (sum_cost(0, i) < minE) {
			end_x = i;
		}
	}
	
	Point current_p(end_x, control.rows - 1), top_p(end_x, control.rows - 1);
	seam_paths.push_back(end_x);
	for (; top_p.y != 0; seam_paths.push_back(top_p.x)) {
		if (control(current_p) == 1) top_p.x--;
		else if (control(current_p) == 3) top_p.x++;
		top_p.y--;
		current_p = top_p;
	}
	reverse(seam_paths.begin(), seam_paths.end());

}

5. 参考

https://github.com/xitu/gold-miner/blob/master/TODO1/real-world-dynamic-programming-seam-carving.md

https://medium.com/swlh/real-world-dynamic-programming-seam-carving-9d11c5b0bfca  原文

### 图像最佳缝合线拼接算法 图像最佳缝合线拼接涉及到多个方面,包括特征匹配、变换估计以及最终的融合过程。为了找到最优的缝合路径并减少可见伪影,在计算过程中通常会采用图割(graph cuts)技术来优化能量函数。 #### 特征检测与描述子提取 在处理两幅或多幅待拼接图片时,首先需要识别出共同区域内的对应点对。常用的方法有SIFT (Scale-Invariant Feature Transform)[^1] 和 ORB (Oriented FAST and Rotated BLOB)[^2] 。这些算子能够稳定地捕捉到不同视角下的不变特性,从而为后续操作提供可靠的依据。 #### 变换矩阵求解 一旦获得了足够的匹配点集之后,则可以通过RANSAC(random sample consensus)随机抽样一致性算法去除异常值,并利用剩余的有效配对拟合单应性矩阵H(homography matrix),该矩阵可以将一幅图像映射至另一坐标系下以便于重叠部分对比分析[^3]。 #### 缝合线搜索策略 对于给定的一组已校正后的输入影像数据而言,如何选取一条理想的分割边界至关重要。一种流行的做法就是基于最小化视觉差异的原则构建代价模型——即沿着候选路径上的每一个像素位置分别评估其左右两侧邻域间的颜色过渡情况;随后借助动态规划(dynamic programming, DP) 或者更高效的A*启发式搜索机制找出全局累积成本最低的那个选项作为实际使用的分界线[^4]。 ```python import cv2 import numpy as np def find_optimal_seamline(img_left_gray, img_right_gray): h, w = img_left_gray.shape[:2] # 计算绝对差值图像 diff_img = cv2.absdiff(img_left_gray[:, :w//2], img_right_gray[:, w//2:]) # 初始化累计能量表 energy_map = np.zeros_like(diff_img).astype(np.float64) # 动态规划填充能量地图 for i in range(1,h): for j in range(w//2): min_energy = float(&#39;inf&#39;) # 查找上一行相邻三个节点中的最小值 for k in [-1,0,+1]: if 0<=j+k<w//2: prev_energy = energy_map[i-1,j+k]+abs(int(diff_img[i,j])-int(diff_img[i-1,j+k])) if prev_energy<min_energy:min_energy=prev_energy energy_map[i,j]=min_energy # 回溯得到最优路径 seam_path=[None]*h; last_col=np.argmin(energy_map[-1)): curr_col=min(max(last_col+np.argmin([energy_map[r,last_col+x]for x in (-1,0,1)]),0),w//2-1)+w//2 seam_path[r]=curr_col last_col-=w//2-curr_col return seam_path ``` 此代码片段展示了通过比较灰度级差异来确定垂直方向上的理想连接轨迹的过程。具体来说,它先创建了一张表示两个视窗间亮度变化程度的地图,再运用简单的DP原理迭代更新每一行各列的能量得分直至最后一行结束为止;最后由底向上逐层回退选出整条链路上消耗最少的位置集合形成最终的结果向量`seam_path[]`.
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值