在前面的博文中,我们分别讲了使用FFD形变与梯度下降法、LM算法、粒子群算法来实现图像的非刚性配准:
图像配准系列之基于FFD形变与梯度下降法的图像配准
图像配准系列之基于FFD形变与LM算法的图像配准
图像配准系列之基于FFD形变与粒子群算法的图像配准
以上三篇博文所讲的配准方法中,有一个明显的共同点是:FFD形变的网格控制点数都是保持不变的。我们知道,理论上控制点越少,配准越快,但配准效果越差,控制点越多则配准越慢且配准效果越好。为了让配准效果好一点,我们往往一开始就把FFD形变的控制点设置得多一些,但是这同时导致了配准很耗时,而且,一开始控制点参数多还会有容易陷入局部极值的问题。为了加快配准速度,且减小陷入局部极值的概率,有研究人员提出了层次配准的方法:多次配准(通常3~5次就ok了),前一次配准的结果作为后一次配准的输入,第一次配准时设置较少的控制点,随后逐渐增加控制点。如下图所示:
下面我们使用C++与Opencv实现三次“FFD+梯度下降”的层次配准,在配准过程中逐渐增加控制点:8*8——>16*16——>30*30。
绝大多数代码在前面的文章(上方的超链接)中都有贴出来过,此处不再重复,在此只给出实现层次配准的代码:
void ffd_match_test(void)
{
Mat img1 = imread("wangge.png", CV_LOAD_IMAGE_GRAYSCALE);
Mat img2 = imread("wangge1.png", CV_LOAD_IMAGE_GRAYSCALE);
//第一层
int row_block_num = 8;
int col_block_num = 8;
Mat grid_points;
init_bpline_para(img1, row_block_num, col_block_num, grid_points, -0.001, 0.001);
Mat out;
bpline_match(img1, img2, out, row_block_num, col_block_num, grid_points);
//第二层
row_block_num = 16;
col_block_num = 16;
init_bpline_para(img1, row_block_num, col_block_num, grid_points, -0.001, 0.001);
Mat out1;
bpline_match(img1, out, out1, row_block_num, col_block_num, grid_points);
//第三层
row_block_num = 30;
col_block_num = 30;
init_bpline_para(img1, row_block_num, col_block_num, grid_points, -0.001, 0.001);
Mat out2;
bpline_match(img1, out1, out2, row_block_num, col_block_num, grid_points);
imshow("img1", img1);
imshow("img2", img2);
imshow("out", out2);
imshow("img1-img2", abs(img1-img2));
imshow("img1-out", abs(img1-out2));
waitKey();
}
运行上述代码,对扭曲的网格图像进行配准,结果如下。为了区分开来,我们称之前的一次配准为“单次配准”,本文讲的方法为“层次配准”。
参考图像
浮动图像
单次配准图像
层次配准图像
浮动图像与参考图像的差值图
单次配准图像与参考图像的差值图
层次配准图像与参考图像的差值图
由以上配准结果可知,单次配准陷入了局部极值,也即个别区域没能配准好,相比来说,层次配准的效果就好多了,不过由于层次配准经过了多次插值,导致图像变模糊了。从目标函数值的下降过程也可以看出来后者效果更好:
目标函数值下降过程