当前位置:   article > 正文

【opencv】示例-asift.cpp 对两张图片之间进行仿射特征比对

【opencv】示例-asift.cpp 对两张图片之间进行仿射特征比对

ffde010e5d8d369c5d53747b8c02cf7e.png

7306a586d8985056a8b34a7970e25b58.png

  1. #include <opencv2/core.hpp> // 包含OpenCV核心功能的头文件
  2. #include <opencv2/imgproc.hpp> // 包含OpenCV图像处理功能的头文件
  3. #include <opencv2/features2d.hpp> // 包含OpenCV特征检测相关功能的头文件
  4. #include <opencv2/highgui.hpp> // 包含OpenCV的GUI功能,如窗口显示的头文件
  5. #include <opencv2/calib3d.hpp> // 包含OpenCV进行相机标定和三维重建功能的头文件
  6. #include <iostream> // 包含标准输入输出流库的头文件
  7. #include <iomanip> // 包含输入输出流格式设置的头文件
  8. using namespace std; // 使用标准命名空间
  9. using namespace cv; // 使用OpenCV命名空间
  10. // 声明帮助函数,该函数会输出使用本程序的方式
  11. static void help(char** argv)
  12. {
  13. cout
  14. << "This is a sample usage of AffineFeature detector/extractor.\n"
  15. << "And this is a C++ version of samples/python/asift.py\n"
  16. << "Usage: " << argv[0] << "\n"
  17. // 以下是该程序的参数说明
  18. << " [ --feature=<sift|orb|brisk> ] # Feature to use.\n"
  19. << " [ --flann ] # use Flann-based matcher instead of bruteforce.\n"
  20. << " [ --maxlines=<number(50 as default)> ] # The maximum number of lines in visualizing the matching result.\n"
  21. << " [ --image1=<image1(aero1.jpg as default)> ]\n"
  22. << " [ --image2=<image2(aero3.jpg as default)> ] # Path to images to compare."
  23. << endl;
  24. }
  25. // 声明计时器函数,用于计算操作的耗时
  26. static double timer()
  27. {
  28. return getTickCount() / getTickFrequency();
  29. }
  30. // 程序的主函数,argc是参数数量,argv是参数列表
  31. int main(int argc, char** argv)
  32. {
  33. vector<String> fileName; // 存储文件名的字符串向量
  34. // 使用OpenCV的命令行解析器解析输入的命令行参数
  35. cv::CommandLineParser parser(argc, argv,
  36. "{help h ||}"
  37. "{feature|brisk|}"
  38. "{flann||}"
  39. "{maxlines|50|}"
  40. "{image1|aero1.jpg|}{image2|aero3.jpg|}");
  41. // 如果用户请求帮助,调用help函数并退出程序
  42. if (parser.has("help"))
  43. {
  44. help(argv);
  45. return 0;
  46. }
  47. // 从解析器中获取输入的参数
  48. string feature = parser.get<string>("feature");
  49. bool useFlann = parser.has("flann");
  50. int maxlines = parser.get<int>("maxlines");
  51. // 查找并存储输入的图像文件路径
  52. fileName.push_back(samples::findFile(parser.get<string>("image1")));
  53. fileName.push_back(samples::findFile(parser.get<string>("image2")));
  54. // 检查参数是否有误
  55. if (!parser.check())
  56. {
  57. parser.printErrors();
  58. cout << "See --help (or missing '=' between argument name and value?)" << endl;
  59. return 1;
  60. }
  61. // 读取图像,并将其转换为灰度图
  62. Mat img1 = imread(fileName[0], IMREAD_GRAYSCALE);
  63. Mat img2 = imread(fileName[1], IMREAD_GRAYSCALE);
  64. // 确保图像成功加载
  65. if (img1.empty())
  66. {
  67. cerr << "Image " << fileName[0] << " is empty or cannot be found" << endl;
  68. return 1;
  69. }
  70. if (img2.empty())
  71. {
  72. cerr << "Image " << fileName[1] << " is empty or cannot be found" << endl;
  73. return 1;
  74. }
  75. // 声明特征检测器和描述符匹配器的指针
  76. Ptr<Feature2D> backend;
  77. Ptr<DescriptorMatcher> matcher;
  78. // 根据用户选择初始化特征检测器和匹配器
  79. if (feature == "sift")
  80. {
  81. backend = SIFT::create();
  82. if (useFlann)
  83. matcher = DescriptorMatcher::create("FlannBased");
  84. else
  85. matcher = DescriptorMatcher::create("BruteForce");
  86. }
  87. else if (feature == "orb")
  88. {
  89. backend = ORB::create();
  90. if (useFlann)
  91. matcher = makePtr<FlannBasedMatcher>(makePtr<flann::LshIndexParams>(6, 12, 1));
  92. else
  93. matcher = DescriptorMatcher::create("BruteForce-Hamming");
  94. }
  95. else if (feature == "brisk")
  96. {
  97. backend = BRISK::create();
  98. if (useFlann)
  99. matcher = makePtr<FlannBasedMatcher>(makePtr<flann::LshIndexParams>(6, 12, 1));
  100. else
  101. matcher = DescriptorMatcher::create("BruteForce-Hamming");
  102. }
  103. else
  104. {
  105. cerr << feature << " is not supported. See --help" << endl;
  106. return 1;
  107. }
  108. // 提取特征点和描述符,并进行匹配
  109. cout << "extracting with " << feature << "..." << endl;
  110. Ptr<AffineFeature> ext = AffineFeature::create(backend);
  111. vector<KeyPoint> kp1, kp2;
  112. Mat desc1, desc2;
  113. ext->detectAndCompute(img1, Mat(), kp1, desc1);
  114. ext->detectAndCompute(img2, Mat(), kp2, desc2);
  115. cout << "img1 - " << kp1.size() << " features, "
  116. << "img2 - " << kp2.size() << " features"
  117. << endl;
  118. cout << "matching with " << (useFlann ? "flann" : "bruteforce") << "..." << endl;
  119. double start = timer(); // 开始计时
  120. // 匹配特征点,并筛选出好的匹配
  121. vector< vector<DMatch> > rawMatches;
  122. vector<Point2f> p1, p2;
  123. vector<float> distances;
  124. matcher->knnMatch(desc1, desc2, rawMatches, 2);
  125. // 筛选出好的匹配点
  126. for (size_t i = 0; i < rawMatches.size(); i++)
  127. {
  128. const vector<DMatch>& m = rawMatches[i];
  129. if (m.size() == 2 && m[0].distance < m[1].distance * 0.75)
  130. {
  131. p1.push_back(kp1[m[0].queryIdx].pt);
  132. p2.push_back(kp2[m[0].trainIdx].pt);
  133. distances.push_back(m[0].distance);
  134. }
  135. }
  136. // 利用单应性计算匹配点对的状态
  137. vector<uchar> status; // 创建一个uchar类型的向量status,用来存储每对匹配点是否是内点的状态
  138. vector< pair<Point2f, Point2f> > pointPairs; // 创建一个存储匹配点对(两个图像中匹配的点)的vector
  139. Mat H = findHomography(p1, p2, status, RANSAC); // 利用RANSAC算法计算从图像1到图像2的单应性矩阵H
  140. int inliers = 0; // 初始化内点数量计数器
  141. // 遍历status向量,统计内点数量并存储这些点对
  142. for (size_t i = 0; i < status.size(); i++)
  143. {
  144. // 如果status向量中的元素为true,则表示该匹配点对是内点
  145. if (status[i])
  146. {
  147. pointPairs.push_back(make_pair(p1[i], p2[i])); // 将内点对添加到pointPairs向量中
  148. distances[inliers] = distances[i]; // 将对应内点的距离存储到distances向量中
  149. // CV_Assert(inliers <= (int)i); // 断言inliers的值应小于等于当前索引,通常用于调试
  150. inliers++; // 内点数量加一
  151. }
  152. }
  153. distances.resize(inliers); // 重新调整distances向量的大小以匹配内点的数量
  154. // 输出执行时间
  155. cout << "execution time: " << fixed << setprecision(2) << (timer()-start)*1000 << " ms" << endl;
  156. // 输出内点与匹配点对的比例
  157. cout << inliers << " / " << status.size() << " inliers/matched" << endl;
  158. // 可视化匹配结果前的准备工作
  159. cout << "visualizing..." << endl;
  160. vector<int> indices(inliers); // 创建一个大小等于内点数量的整数型向量indices,用于存储排序后的索引
  161. // 将distances向量中元素的索引按照距离从小到大排序并存入indices向量
  162. cv::sortIdx(distances, indices, SORT_EVERY_ROW+SORT_ASCENDING);
  163. // 创建可视化图像并绘制匹配的特征点
  164. int h1 = img1.size().height;
  165. int w1 = img1.size().width;
  166. int h2 = img2.size().height;
  167. int w2 = img2.size().width;
  168. Mat vis = Mat::zeros(max(h1, h2), w1+w2, CV_8U);
  169. img1.copyTo(Mat(vis, Rect(0, 0, w1, h1)));
  170. img2.copyTo(Mat(vis, Rect(w1, 0, w2, h2)));
  171. cvtColor(vis, vis, COLOR_GRAY2BGR);
  172. vector<Point2f> corners(4);
  173. corners[0] = Point2f(0, 0);
  174. corners[1] = Point2f((float)w1, 0);
  175. corners[2] = Point2f((float)w1, (float)h1);
  176. corners[3] = Point2f(0, (float)h1);
  177. vector<Point2i> icorners;
  178. perspectiveTransform(corners, corners, H); // 对图像1的四个角进行单应性变换
  179.     transform(corners, corners, Matx23f(1,0,(float)w1,0,1,0)); // 将变换后的角点移到图像2的右侧
  180. Mat(corners).convertTo(icorners, CV_32S); // 将角落点的类型转化为整数
  181. polylines(vis, icorners, true, Scalar(255,255,255)); // 在可视化图像中绘制边界线
  182. // 绘制前maxlines个的匹配对
  183. for (int i = 0; i < min(inliers, maxlines); i++)
  184. {
  185. int idx = indices[i];
  186. const Point2f& pi1 = pointPairs[idx].first;
  187. const Point2f& pi2 = pointPairs[idx].second;
  188. circle(vis, pi1, 2, Scalar(0,255,0), -1); // 绘制圆点
  189. circle(vis, pi2 + Point2f((float)w1,0), 2, Scalar(0,255,0), -1); // 在图像2相应的位置绘制圆点
  190. line(vis, pi1, pi2 + Point2f((float)w1,0), Scalar(0,255,0)); // 绘制连线
  191. }
  192. if (inliers > maxlines)
  193. cout << "only " << maxlines << " inliers are visualized" << endl;
  194. imshow("affine find_obj", vis); // 显示最终的可视化结果窗口
  195. // 当存在更多的匹配时,输出提示信息
  196. // Mat vis2 = Mat::zeros(max(h1, h2), w1+w2, CV_8U); // 创建另一个可视化用的空白图像
  197. // Mat warp1; // 存储变换后图像的矩阵
  198. // warpPerspective(img1, warp1, H, Size(w1, h1)); // 对图像1应用单应性变换
  199. // warp1.copyTo(Mat(vis2, Rect(0, 0, w1, h1))); // 将变换后的图像1复制到可视化图像的左半边
  200. // img2.copyTo(Mat(vis2, Rect(w1, 0, w2, h2))); // 将图像2复制到可视化图像的右半边
  201. // imshow("warped", vis2); // 显示变换后图像与图像2的对比窗口
  202. waitKey(); // 等待任意键按下
  203. cout << "done" << endl; // 输出完成提示
  204. return 0; // 程序结束
  205. }

此段C++代码的主要功能是载入两张图像,通过OpenCV库进行特征点检测和匹配,然后通过单应性变换计算两图像之间的匹配关系,并将匹配结果可视化显示出来。用户可以指定不同的特征检测算法(如SIFT、ORB、BRISK等)以及是否使用FLANN库进行近似最邻近搜索而不是暴力匹配。代码结束时还会显示执行时间和匹配对的数量。

终端输出:

  1. img1 - 39607 features, img2 - 24674 features
  2. matching with bruteforce...
  3. execution time: 35513.65 ms
  4. 41 / 105 inliers/matched
  5. visualizing...

Ptr<AffineFeature> ext = AffineFeature::create(backend);

4ca2e08198e7ea9eee447b87043f3a19.png

Mat H = findHomography(p1, p2, status, RANSAC);

685a67c444636176a512ab30f8cc05bb.png

2b0f108afb23babef5cea0de1a4e7823.png

仿射特征匹配有哪些应用场景

17122b21c5fca6eac63ed99b0d01660c.png

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

闽ICP备14008679号