小言_互联网的博客

图像配准系列之“Sift特征点+薄板样条变换+FFD变换”配准方法

297人阅读  评论(0)

上篇文章中我们讲了“Sift+TPS”的配准方法:

图像配准系列之“Sift特征点+薄板样条变换”配准方法

我们知道,TPS薄板样条变换(简称TPS变换)与FFD变换均具有一定的局部形变特性。不过对于TPS变换,只要其输入的匹配点对中有一组发生变化,其整体的形变可能会发生很大改变,因此有时候TPS变换并不能很好地矫正局部形变。而对于FFD变换,每个点的形变坐标偏移只与其周围4*4个控制点的控制参数有关,与其它控制点无关,因此FFD变换的局部形变特性比TPS变换更好,然而“FFD+梯度下降”配准方法的输入参数通常很多,也即要优化的参数很多,优化参数时较容易陷入局部极值,且优化参数的过程很耗时:

图像配准系列之基于FFD形变与梯度下降法的图像配准

基于此原因,有研究员提出了层次FFD的方法,我们前面已经讲过:

图像配准系列之层次FFD形变配准

相比来说“Sift+TPS”的配准方法更稳定更快,为了使配准效果更好、更快、更稳定,因此后来又有人提出进一步把“层次FFD”与“Sift+TPS”结合起来,使配准效果更好、更快、更稳定。如下图所示:

下面上代码:

(1) 层次FFD代码


   
  1. void level_ffd_match(Mat img1, Mat img2, Mat &outffd)
  2. {
  3. float min = -0.01;
  4. float max = 0.01;
  5. //第一层
  6. int row_block_num = 8;
  7. int col_block_num = 8;
  8. Mat grid_points;
  9. init_bpline_para(img1, row_block_num, col_block_num, grid_points, min, max);
  10. Mat out;
  11. bpline_match(img1, img2, out, row_block_num, col_block_num, grid_points);
  12. //第二层
  13. row_block_num = 16;
  14. col_block_num = 16;
  15. init_bpline_para(img1, row_block_num, col_block_num, grid_points, min, max);
  16. Mat out1;
  17. bpline_match(img1, out, out1, row_block_num, col_block_num, grid_points);
  18. //第三层
  19. row_block_num = 30;
  20. col_block_num = 30;
  21. init_bpline_para(img1, row_block_num, col_block_num, grid_points, min, max);
  22. Mat out2;
  23. bpline_match(img1, out1, out2, row_block_num, col_block_num, grid_points);
  24.   out2.copyTo(outffd);
  25. }

(2) “Sift+TPS+层次FFD”代码


   
  1. void Sift_Tps_test(void)
  2. {
  3. Mat img1 = imread( "lena.jpg", CV_LOAD_IMAGE_GRAYSCALE);
  4.   Mat img2 = imread( "lena_ffd.jpg", CV_LOAD_IMAGE_GRAYSCALE);
  5. imshow( "image before", img1);
  6. imshow( "image2 before", img2);
  7.    // SIFT - 检测关键点并在原图中绘制
  8. vector<KeyPoint> kp1, kp2;
  9. kp1 = detect_sift_block(img1, 50, 5, 5);
  10. kp2 = detect_sift_block(img2, 50, 5, 5);
  11. Mat outimg1, outimg2;
  12. drawKeypoints(img1, kp1, outimg1);
  13. drawKeypoints(img2, kp2, outimg2);
  14. imshow( "image1 keypoints", outimg1);
  15.   imshow( "image2 keypoints", outimg2);
  16. // SIFT - 特征向量提取
  17. Ptr<SiftDescriptorExtractor> extractor = SiftDescriptorExtractor::create();
  18. Mat descriptor1, descriptor2;
  19. extractor->compute(img1, kp1, descriptor1);
  20. extractor->compute(img2, kp2, descriptor2);
  21. // 两张图像的特征匹配
  22. Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create( "BruteForce");
  23. vector<DMatch> matches;
  24. Mat img_matches;
  25.   matcher->match(descriptor1, descriptor2, matches);
  26. //计算匹配结果中距离最大和距离最小值
  27. double min_dist = matches[ 0].distance, max_dist = matches[ 0].distance;
  28. for ( int m = 0; m < matches.size(); m++)
  29. {
  30. if (matches[m].distance < min_dist)
  31. {
  32. min_dist = matches[m].distance;
  33. }
  34. if (matches[m].distance > max_dist)
  35. {
  36. max_dist = matches[m].distance;
  37. }
  38. }
  39. cout << "min dist=" << min_dist << endl;
  40. cout << "max dist=" << max_dist << endl;
  41. vector<DMatch> goodMatches;
  42. for ( int i = 0; i < matches.size(); i++) //筛选出较好的匹配点
  43. {
  44. if (matches[i].distance < 0.35*max_dist)
  45. {
  46. goodMatches.push_back(matches[i]);
  47. }
  48. }
  49. cout << "The number of good matches:" << goodMatches.size() << endl;
  50. //坐标转换为float类型
  51. vector <KeyPoint> good_kp1, good_kp2;
  52. for ( int i = 0; i < goodMatches.size(); i++)
  53. {
  54. good_kp1.push_back(kp1[goodMatches[i].queryIdx]);
  55. good_kp2.push_back(kp2[goodMatches[i].trainIdx]);
  56. }
  57. //坐标变换
  58. vector <Point2f> p01, p02;
  59. for ( int i = 0; i < goodMatches.size(); i++)
  60. {
  61. p01.push_back(good_kp1[i].pt);
  62. p02.push_back(good_kp2[i].pt);
  63. }
  64. vector<uchar> RANSACStatus; //用以标记每一个匹配点的状态,等于0则为外点,等于1则为内点。
  65. findFundamentalMat(p01, p02, RANSACStatus, CV_FM_RANSAC, 4.5); //p1 p2必须为float型
  66. vector<Point2f> f1_features_ok;
  67. vector<Point2f> f2_features_ok;
  68. for ( int i = 0; i < p01.size(); i++) //剔除跟踪失败点
  69. {
  70. if (RANSACStatus[i])
  71. {
  72. f1_features_ok.push_back(p01[i]); //基准图特征点
  73. f2_features_ok.push_back(p02[i]); //流动图特征点
  74. }
  75. }
  76. Mat Tx, Ty;
  77. Tps_TxTy(f1_features_ok, f2_features_ok, img2.rows, img2.cols, Tx, Ty);
  78. Mat tps_out;
  79. remap(img2, tps_out, Tx, Ty, INTER_CUBIC); //Sift+TPS粗配准结果
  80. Mat ffd_out;
  81.   level_ffd_match(img1, tps_out, ffd_out); //层次FFD细配准结果
  82. imshow( "img2-img1", abs(img2-img1));
  83. imshow( "tps_out-img1", abs(tps_out - img1));
  84. imshow( "ffd_out-img1", abs(ffd_out - img1));
  85. imshow( "tps_out", tps_out);
  86.   imshow( "ffd_out", ffd_out);
  87. cv::waitKey( 0);
  88. }

运行上述代码,对扭曲的Lena图像进行配准,结果如下。由以下结果可知,细配准图像比粗配准图像的形变矫正效果好多了。因此结合两种方法的配准方法的配准效果更好。

原图

浮动图像

粗配准图像

细配准图像

浮动图像与参考图像的差值图

粗配准图像与参考图像的差值图

细配准图像与参考图像的差值图

欢迎扫码关注以下微信公众号,接下来会不定时更新更加精彩的内容噢~


转载:https://blog.csdn.net/shandianfengfan/article/details/114422615
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场