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 }