OpenCV 常用图像拼接方法(三):基于特征匹配拼接


    OpenCV常用图像拼接方法将分为四个部分与大家共享,这里是第三种方法,欢迎关注后续。

    OpenCV的常用图像拼接方法(三):基于特征匹配的图像拼接,本次介绍SIFT特征匹配拼接方法,OpenCV版本为4.4.0。特点和适用范围:图像有足够重合相同特征区域,且待拼接图像之间无明显尺度变换和畸变。

优点:适应部分倾斜变化情况。缺点:需要有足够的相同特征区域进行匹配,速度较慢,拼接较大图片容易崩溃。

    如下是待拼接的两张图片:

 

特征匹配图:

 拼接结果图:

 拼接缝处理后(拼接处过渡更自然):

核心代码:

 1 /********************直接图像拼接函数*************************/
 2 bool ImageOverlap0(Mat &img1, Mat &img2)  3 {  4   Mat g1(img1, Rect(0, 0, img1.cols, img1.rows));  // init roi 
 5   Mat g2(img2, Rect(0, 0, img2.cols, img2.rows));  6  
 7  cvtColor(g1, g1, COLOR_BGR2GRAY);  8  cvtColor(g2, g2, COLOR_BGR2GRAY);  9  
 10   vector<cv::KeyPoint> keypoints_roi, keypoints_img;  /* keypoints found using SIFT */
 11   Mat descriptor_roi, descriptor_img;                           /* Descriptors for SIFT */
 12   FlannBasedMatcher matcher;                                   /* FLANN based matcher to match keypoints */
 13  
 14   vector<cv::DMatch> matches, good_matches;  15   cv::Ptr<cv::SIFT> sift = cv::SIFT::create();  16   int i, dist = 80;  17  
 18   sift->detectAndCompute(g1, cv::Mat(), keypoints_roi, descriptor_roi);      /* get keypoints of ROI image */
 19   sift->detectAndCompute(g2, cv::Mat(), keypoints_img, descriptor_img);         /* get keypoints of the image */
 20   matcher.match(descriptor_roi, descriptor_img, matches);  //实现描述符之间的匹配
 21  
 22   double max_dist = 0; double min_dist = 5000;  23   //-- Quick calculation of max and min distances between keypoints 
 24   for (int i = 0; i < descriptor_roi.rows; i++)  25  {  26     double dist = matches[i].distance;  27     if (dist < min_dist) min_dist = dist;  28     if (dist > max_dist) max_dist = dist;  29  }  30   // 特征点筛选
 31   for (i = 0; i < descriptor_roi.rows; i++)  32  {  33     if (matches[i].distance < 3 * min_dist)  34  {  35  good_matches.push_back(matches[i]);  36  }  37  }  38  
 39   printf("%ld no. of matched keypoints in right image\n", good_matches.size());  40   /* Draw matched keypoints */
 41  
 42  Mat img_matches;  43   //绘制匹配
 44  drawMatches(img1, keypoints_roi, img2, keypoints_img,  45     good_matches, img_matches, Scalar::all(-1),  46     Scalar::all(-1), vector<char>(),  47  DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);  48   imshow("matches", img_matches);  49  
 50   vector<Point2f> keypoints1, keypoints2;  51   for (i = 0; i < good_matches.size(); i++)  52  {  53  keypoints1.push_back(keypoints_img[good_matches[i].trainIdx].pt);  54  keypoints2.push_back(keypoints_roi[good_matches[i].queryIdx].pt);  55  }  56   //计算单应矩阵(仿射变换矩阵) 
 57   Mat H = findHomography(keypoints1, keypoints2, RANSAC);  58   Mat H2 = findHomography(keypoints2, keypoints1, RANSAC);  59  
 60  
 61   Mat stitchedImage;  //定义仿射变换后的图像(也是拼接结果图像)
 62   Mat stitchedImage2;  //定义仿射变换后的图像(也是拼接结果图像)
 63   int mRows = img2.rows;  64   if (img1.rows > img2.rows)  65  {  66     mRows = img1.rows;  67  }  68  
 69   int count = 0;  70   for (int i = 0; i < keypoints2.size(); i++)  71  {  72     if (keypoints2[i].x >= img2.cols / 2)  73       count++;  74  }  75   //判断匹配点位置来决定图片是左还是右
 76   if (count / float(keypoints2.size()) >= 0.5)  //待拼接img2图像在右边
 77  {  78     cout << "img1 should be left" << endl;  79     vector<Point2f>corners(4);  80     vector<Point2f>corners2(4);  81     corners[0] = Point(0, 0);  82     corners[1] = Point(0, img2.rows);  83     corners[2] = Point(img2.cols, img2.rows);  84     corners[3] = Point(img2.cols, 0);  85     stitchedImage = Mat::zeros(img2.cols + img1.cols, mRows, CV_8UC3);  86     warpPerspective(img2, stitchedImage, H, Size(img2.cols + img1.cols, mRows));  87  
 88  perspectiveTransform(corners, corners2, H);  89     /*
 90  circle(stitchedImage, corners2[0], 5, Scalar(0, 255, 0), 2, 8);  91  circle(stitchedImage, corners2[1], 5, Scalar(0, 255, 255), 2, 8);  92  circle(stitchedImage, corners2[2], 5, Scalar(0, 255, 0), 2, 8);  93  circle(stitchedImage, corners2[3], 5, Scalar(0, 255, 0), 2, 8); */
 94     cout << corners2[0].x << ", " << corners2[0].y << endl;  95     cout << corners2[1].x << ", " << corners2[1].y << endl;  96     imshow("temp", stitchedImage);  97     //imwrite("temp.jpg", stitchedImage);
 98  
 99     Mat half(stitchedImage, Rect(0, 0, img1.cols, img1.rows)); 100  img1.copyTo(half); 101     imshow("result", stitchedImage); 102  } 103   else  //待拼接图像img2在左边
104  { 105     cout << "img2 should be left" << endl; 106     stitchedImage = Mat::zeros(img2.cols + img1.cols, mRows, CV_8UC3); 107     warpPerspective(img1, stitchedImage, H2, Size(img1.cols + img2.cols, mRows)); 108     imshow("temp", stitchedImage); 109  
110     //计算仿射变换后的四个端点
111     vector<Point2f>corners(4); 112     vector<Point2f>corners2(4); 113     corners[0] = Point(0, 0); 114     corners[1] = Point(0, img1.rows); 115     corners[2] = Point(img1.cols, img1.rows); 116     corners[3] = Point(img1.cols, 0); 117  
118     perspectiveTransform(corners, corners2, H2);  //仿射变换对应端点
119     /*
120  circle(stitchedImage, corners2[0], 5, Scalar(0, 255, 0), 2, 8); 121  circle(stitchedImage, corners2[1], 5, Scalar(0, 255, 255), 2, 8); 122  circle(stitchedImage, corners2[2], 5, Scalar(0, 255, 0), 2, 8); 123  circle(stitchedImage, corners2[3], 5, Scalar(0, 255, 0), 2, 8); */
124     cout << corners2[0].x << ", " << corners2[0].y << endl; 125     cout << corners2[1].x << ", " << corners2[1].y << endl; 126  
127     Mat half(stitchedImage, Rect(0, 0, img2.cols, img2.rows)); 128  img2.copyTo(half); 129     imshow("result", stitchedImage); 130  
131  } 132   imwrite("result.bmp", stitchedImage); 133   return true; 134 }

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM