当前位置:   article > 正文

opencv_特征匹配与图像拼接_匹配与拼接opencv

匹配与拼接opencv

opencv中,常用的特征点检测算法有:SURF算法,SIFT算法,ORB算法,FAST算法。

其中检测速度上,在提取一帧图像特征点的实验中,在提取相同数量的特征点情况下,提取SURF点耗时时间大约是提取ORB特征点的14倍,而提取SIFT点耗时更大,大概比提取ORB特征点多三百多倍,所以ORB是计算量最小的算法。

本文使用的SUFR的算法进行的特征点检测以及图像拼接,若想在在opencv中使用SURF的特征点检测算法,需要编译opencv_contrib的模块,具体的流程可见上一篇博客: 

opencv_contrib的编译与运行(windows)_9分钟带帽的博客-CSDN博客

 两张demo的图片如下:

 代码如下:

  1. #include <iostream>
  2. #include<opencv2\opencv.hpp>
  3. #include<opencv2\xfeatures2d.hpp>
  4. #include<vector>
  5. using namespace std;
  6. using namespace cv;
  7. using namespace xfeatures2d;
  8. typedef struct
  9. {
  10. Point2f left_top;
  11. Point2f left_bottom;
  12. Point2f right_top;
  13. Point2f right_bottom;
  14. }four_corners_t;
  15. int clip(int x, int min, int max)
  16. {
  17. if (x > max)
  18. return max;
  19. if (x < min)
  20. return min;
  21. return x;
  22. }
  23. void compute_overlap_mask(Mat& image1, Mat& image2, Mat& lap_mask)
  24. {
  25. int height1 = image1.rows;
  26. int width1 = image1.cols;
  27. int height2 = image2.rows;
  28. int width2 = image2.cols;
  29. int height0 = lap_mask.rows;
  30. int width0 = lap_mask.cols;
  31. if (height0 != height1 || height0 != height2 || width0 != width1 || width0 != width2)
  32. {
  33. cout << "image shape not match!" << endl;
  34. return;
  35. }
  36. for (int i = 0; i < height0; i++)
  37. for (int j = 0; j < width0; j++)
  38. {
  39. Vec3b pix1 = image1.at<Vec3b>(i, j);
  40. Vec3b pix2 = image2.at<Vec3b>(i, j);
  41. Vec3b pix0 = Vec3b(0, 0, 0);
  42. if (pix1 == pix0 || pix2 == pix0)
  43. lap_mask.at<uchar>(i, j) = 0;
  44. }
  45. }
  46. void CalcCorners(const Mat& H, const Mat& src, four_corners_t& corners)
  47. {
  48. double v2[] = { 0, 0, 1 };//左上角
  49. double v1[3];//变换后的坐标值
  50. Mat V2 = Mat(3, 1, CV_64FC1, v2); //列向量
  51. Mat V1 = Mat(3, 1, CV_64FC1, v1); //列向量
  52. V1 = H * V2;
  53. //左上角(0,0,1)
  54. cout << "V2: " << V2 << endl;
  55. cout << "V1: " << V1 << endl;
  56. corners.left_top.x = v1[0] / v1[2];
  57. corners.left_top.y = v1[1] / v1[2];
  58. //左下角(0,src.rows,1)
  59. v2[0] = 0;
  60. v2[1] = src.rows;
  61. v2[2] = 1;
  62. V2 = Mat(3, 1, CV_64FC1, v2); //列向量
  63. V1 = Mat(3, 1, CV_64FC1, v1); //列向量
  64. V1 = H * V2;
  65. corners.left_bottom.x = v1[0] / v1[2];
  66. corners.left_bottom.y = v1[1] / v1[2];
  67. //右上角(src.cols,0,1)
  68. v2[0] = src.cols;
  69. v2[1] = 0;
  70. v2[2] = 1;
  71. V2 = Mat(3, 1, CV_64FC1, v2); //列向量
  72. V1 = Mat(3, 1, CV_64FC1, v1); //列向量
  73. V1 = H * V2;
  74. corners.right_top.x = v1[0] / v1[2];
  75. corners.right_top.y = v1[1] / v1[2];
  76. //右下角(src.cols,src.rows,1)
  77. v2[0] = src.cols;
  78. v2[1] = src.rows;
  79. v2[2] = 1;
  80. V2 = Mat(3, 1, CV_64FC1, v2); //列向量
  81. V1 = Mat(3, 1, CV_64FC1, v1); //列向量
  82. V1 = H * V2;
  83. corners.right_bottom.x = v1[0] / v1[2];
  84. corners.right_bottom.y = v1[1] / v1[2];
  85. }
  86. int merge_image_by_mask1(Mat& lap_mask, Mat& mix_room_01, Mat& mix_room_02, Mat& mix_room)
  87. {
  88. int height1 = mix_room_01.rows;
  89. int width1 = mix_room_01.cols;
  90. int height2 = mix_room_02.rows;
  91. int width2 = mix_room_02.cols;
  92. int height0 = lap_mask.rows;
  93. int width0 = lap_mask.cols;
  94. if (height0 != height1 || height0 != height2 || width0 != width1 || width0 != width2)
  95. {
  96. cout << "image shape not match!" << endl;
  97. return -1;
  98. }
  99. int mix_width = lap_mask.cols;
  100. int mix_height = lap_mask.rows;
  101. //创建遮罩层并根据mask完成权重初始化
  102. Mat mask1 = Mat::ones(Size(mix_width, mix_height), CV_32FC1);
  103. Mat mask2 = Mat::ones(Size(mix_width, mix_height), CV_32FC1);
  104. //printf("%s %d\n", __FUNCTION__, __LINE__);
  105. for (int i = 0; i < height0; i++)
  106. {
  107. vector<int> indexs;
  108. indexs.clear();
  109. for (int j = 0; j < width0; j++)
  110. {
  111. if (lap_mask.at<uchar>(i, j) == 255)
  112. indexs.push_back(j);
  113. }
  114. if (indexs.size() == 0)
  115. continue;
  116. int start = indexs.front();
  117. int end = indexs.back();
  118. //printf("line %d start:%d end:%d\n", i, start, end);
  119. int length = end - start + 1;
  120. float interval = 1.0 / length;
  121. for (int j = start; j < end + 1; j++)
  122. {
  123. mask2.at<float>(i, j) = (j - start + 1) * interval;
  124. mask1.at<float>(i, j) = 1.0 - mask2.at<float>(i, j);
  125. }
  126. }
  127. Mat m1w;
  128. vector<Mat> mvec;
  129. mvec.push_back(mask1);
  130. mvec.push_back(mask1);
  131. mvec.push_back(mask1);
  132. merge(mvec, m1w);
  133. mix_room_01.convertTo(mix_room_01, CV_32F);
  134. multiply(mix_room_01, m1w, mix_room_01);
  135. Mat m2w;
  136. mvec.clear();
  137. mvec.push_back(mask2);
  138. mvec.push_back(mask2);
  139. mvec.push_back(mask2);
  140. merge(mvec, m2w);
  141. mix_room_02.convertTo(mix_room_02, CV_32F);
  142. multiply(mix_room_02, m2w, mix_room_02);
  143. add(mix_room_01, mix_room_02, mix_room);
  144. mix_room.convertTo(mix_room, CV_8U);
  145. return 0;
  146. }
  147. int merge_image_by_mask2(Mat& lap_mask, Mat& mix_room_01, Mat& mix_room_02, Mat& mix_room)
  148. {
  149. int height1 = mix_room_01.rows;
  150. int width1 = mix_room_01.cols;
  151. int height2 = mix_room_02.rows;
  152. int width2 = mix_room_02.cols;
  153. int height0 = lap_mask.rows;
  154. int width0 = lap_mask.cols;
  155. if (height0 != height1 || height0 != height2 || width0 != width1 || width0 != width2)
  156. {
  157. cout << "image shape not match!" << endl;
  158. return -1;
  159. }
  160. int mix_width = lap_mask.cols;
  161. int mix_height = lap_mask.rows;
  162. for (int i = 0; i < height0; i++)
  163. {
  164. uchar* p1 = mix_room_01.ptr<uchar>(i);
  165. uchar* p2 = mix_room_02.ptr<uchar>(i);
  166. uchar* pm = mix_room.ptr<uchar>(i);
  167. vector<int> indexs;
  168. indexs.clear();
  169. for (int j = 0; j < width0; j++)
  170. {
  171. if (lap_mask.at<uchar>(i, j) == 255)
  172. indexs.push_back(j);
  173. }
  174. if (indexs.size() == 0)
  175. continue;
  176. int start = indexs.front();
  177. int end = indexs.back();
  178. int length = end - start + 1;
  179. float interval = 1.0 / length;
  180. for (int j = start; j < end + 1; j++)
  181. {
  182. float w2 = (j - start + 1) * interval;
  183. float w1 = 1.0 - w2;
  184. pm[j * 3] = clip(int(p1[j * 3] * w1 + p2[j * 3] * w2), 0, 255);
  185. pm[j * 3 + 1] = clip(int(p1[j * 3 + 1] * w1 + p2[j * 3 + 1] * w2), 0, 255);
  186. pm[j * 3 + 2] = clip(int(p1[j * 3 + 2] * w1 + p2[j * 3 + 2] * w2), 0, 255);
  187. }
  188. }
  189. return 0;
  190. }
  191. int main()
  192. {
  193. std::string imgpath1 = "../images/room/room1.jpg";
  194. std::string imgpath2 = "../images/room/room2.jpg";
  195. Mat room1 = imread(imgpath1);
  196. Mat room2 = imread(imgpath2);
  197. if (room1.empty() == true || room2.empty() == true) {
  198. cout << "error" << endl;
  199. return -1;
  200. }
  201. Ptr<SURF> surf;
  202. surf = SURF::create(800);
  203. vector<KeyPoint> kpts_room1;
  204. vector<KeyPoint> kpts_room2;
  205. Mat dec_room1, dec_room2;
  206. surf->detectAndCompute(room1, Mat(), kpts_room1, dec_room1);
  207. surf->detectAndCompute(room2, Mat(), kpts_room2, dec_room2);
  208. /*---------暴力 匹配-------------*/
  209. //创建暴力匹配子对象
  210. BFMatcher bf_matcher;
  211. //存放描述子匹配关系
  212. vector<DMatch> matches_bf;
  213. //特征点描述子匹配
  214. bf_matcher.match(dec_room1, dec_room2, matches_bf);
  215. //特征点筛选
  216. float good_rate = 0.15f;
  217. //printf("matches_bf.size:%d\n", matches_bf.size());
  218. int num_good_matchs = std::min(15, int(matches_bf.size() * good_rate));
  219. //printf("num_good_matchs:%d\n", num_good_matchs);
  220. std::sort(matches_bf.begin(), matches_bf.end());
  221. matches_bf.erase(matches_bf.begin() + num_good_matchs, matches_bf.end());
  222. //绘制筛选后匹配结果
  223. Mat result_bf;
  224. drawMatches(room1, kpts_room1, room2, kpts_room2, matches_bf, result_bf);
  225. imshow("result_bf", result_bf);
  226. //获取两张图的特征点
  227. vector<Point2f>room1_points;
  228. vector<Point2f>room2_points;
  229. for (size_t t = 0; t < matches_bf.size(); t++) {
  230. room1_points.push_back(kpts_room1[matches_bf[t].queryIdx].pt);
  231. room2_points.push_back(kpts_room2[matches_bf[t].trainIdx].pt);
  232. }
  233. //根据对应的特征点获取从demo->scene的变换矩阵
  234. Mat homo = findHomography(room2_points, room1_points, RANSAC);
  235. //waitKey(0);
  236. four_corners_t corners;
  237. CalcCorners(homo, room2, corners);
  238. //cout << "left_top:" << corners.left_top << endl;
  239. //cout << "left_bottom:" << corners.left_bottom << endl;
  240. //cout << "right_top:" << corners.right_top << endl;
  241. //cout << "right_bottom:" << corners.right_bottom << endl;
  242. //cout << "room1.width:" << room1.cols << " " << "room1.height:" << room1.rows << endl;
  243. //Mat imageTransform;
  244. int mix_width = int(std::max(corners.right_top.x, corners.right_bottom.x));
  245. int mix_height = int(room1.rows);
  246. Mat mix_room_01 = Mat::zeros(Size(mix_width, mix_height), CV_8UC3);
  247. Rect roi1 = Rect(0, 0, room1.cols, room1.rows);
  248. room1.copyTo(mix_room_01(roi1));
  249. imshow("mix_room_01", mix_room_01);
  250. Mat mix_room_02;
  251. warpPerspective(room2, mix_room_02, homo, Size(mix_width, mix_height));
  252. imshow("mix_room2", mix_room_02);
  253. //计算重合区域mask
  254. Mat lap_mask = Mat::ones(Size(mix_width, mix_height), CV_8UC1) * 255;
  255. //lap_mask.setTo(255);
  256. compute_overlap_mask(mix_room_01, mix_room_02, lap_mask);
  257. //generate_mask(mix_room_02, mask);
  258. imshow("lap_mask", lap_mask);
  259. //method1 计算两张图片分别的mask权重,根据两个mask融合图片
  260. //Mat mix_room;
  261. //merge_image_by_mask1(lap_mask, mix_room_01, mix_room_02, mix_room);
  262. //method2 根据overlap_mask计算重叠区域的像素值,直接融合
  263. Mat mix_room = mix_room_02.clone();
  264. room1.copyTo(mix_room(Rect(0, 0, room1.cols, room1.rows)));
  265. merge_image_by_mask2(lap_mask, mix_room_01, mix_room_02, mix_room);
  266. imshow("mix_room", mix_room);
  267. waitKey(0);
  268. std::string save_imgpath = "result.jpg";
  269. cv::imwrite(save_imgpath, mix_room);
  270. //system("pause");
  271. return 0;
  272. }

其中得到了重叠的mask,进行图像融合的过程中,有两个函数,第一个是merge_image_by_mask1,该函数先计算两张图片对应的mask的权重,方便进行可视化,第二个是merge_image_by_mask2是根据重叠的lap_mask,取两张图片对应坐标的像素,直接计算融合之后的像素值。

参考博客:

opencv ORB特征提取与匹配-实现图像拼接_java opencv 匹配拼接_开阳654的博客-CSDN博客

OpenCV SURF图像拼接、配准和图像融合技术(一)_使用opencv surf算法实现图像的拼接和融合_rjszcb的博客-CSDN博客

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/355986?site
推荐阅读
相关标签
  

闽ICP备14008679号