OpenCV__005_模板匹配(大图像中查找与给定小图像(模板)最相似区域)

 

运行环境:

vscode 1.112.0

编译环境:

Visual Studio Community 2026 Release - amd64_x86

 

 CMakelists.txt

 1 cmake_minimum_required(VERSION 3.10.0)
 2 
 3 project(opencvtest VERSION 0.1.0 LANGUAGES CXX)
 4 
 5 include(CTest)
 6 enable_testing()
 7 
 8 find_package(OpenCV REQUIRED)
 9 
10 add_executable(opencvtest test04.cpp)
11 
12 target_link_libraries(opencvtest ${OpenCV_LIBS})
13 
14 set(CPACK_PROJECT_NAME ${PROJECT_NAME})

 

  1 #include <opencv2/opencv.hpp>
  2 #include <iostream>
  3 void my_imshow(const std::string& name,cv::Mat& photo){
  4 
  5     cv::namedWindow(name,cv::WINDOW_NORMAL);
  6     cv::resizeWindow(name,600,400);
  7     cv::imshow(name,photo);
  8 }
  9 int main() {
 10     //  读取图像,转化为灰度图
 11     cv::Mat img_object = cv::imread("./photo/object.png");
 12     cv::Mat img_scene = cv::imread("./photo/scene.png");
 13 
 14     if (img_object.empty() || img_scene.empty()) {
 15         std::cout << "The camera cannot be opened!" << std::endl;
 16         return -1;
 17     }
 18         
 19     my_imshow("img_object",img_object);
 20     my_imshow("img_scene",img_scene);
 21     //灰度转换
 22     cv::Mat gray_object,gray_scene;
 23     cv::cvtColor(img_object,gray_object,cv::COLOR_BGR2GRAY);
 24     cv::cvtColor(img_scene,gray_scene,cv::COLOR_BGR2GRAY);
 25 
 26     my_imshow("gray_object",gray_object);
 27     my_imshow("gray_scene",gray_scene);
 28 
 29     //  ORB 特征检测 + 描述
 30     //Ptr是opencv提供的智能指针
 31     //ORB是ORB特征检测的具体类,
 32     //这个类里面包含了所有用于提取 FAST 角点、计算 BRIEF 描述子、构建图像金字塔的代码逻辑
 33     //而这些东西用于后续的检测和计算
 34     //opencv提供了工厂方法初始化ORB,而ORB的构造函数通常是private或protected
 35     //所以不允许使用new进行初始化
 36     //cv::ORB::create(1000)表示最多保留1000个特征点
 37     cv::Ptr<cv::ORB> orb = cv::ORB::create(3000);
 38     
 39     //创建存放坐标的向量变量
 40     std::vector<cv::KeyPoint> keypoints_obj, keypoints_scene;
 41     //Mat是opencv用于存储多维数组的通用容器
 42     cv::Mat descriptors_obj, descriptors_scene;
 43 
 44     // void detectAndCompute(
 45     // InputArray image,           // 输入图像 (通常是灰度图)
 46     // InputArray mask,            // 可选掩码 (指定哪些区域参与检测)
 47     // std::vector<KeyPoint>& keypoints, // 输出:检测到的关键点
 48     // OutputArray descriptors     // 输出:每个关键点对应的描述子
 49     // );
 50     //第二个参数,cv::noArray()表示整个图像都参与检测
 51     //detectAndCompute是ORB内部的方法,类内部实现的函数叫做方法
 52     orb->detectAndCompute(gray_object, cv::noArray(), keypoints_obj, descriptors_obj);
 53     orb->detectAndCompute(gray_scene, cv::noArray(), keypoints_scene, descriptors_scene);
 54
 55     //  OpenCV的KNN 特征匹配
 56     //这里是找出场景中最匹配的两个描述子,后续再跟目标物体的描述子进行比对,看哪个更匹配
 57     //定义使用汉明距离的暴力匹配器
 58     cv::BFMatcher matcher(cv::NORM_HAMMING);
 59     
 60 
 61     //存储描述子的结果,由于每个目标物体的描述子可能有2个最接近的匹配结果(这两个点来自场景图像),
 62     //                                                                  所以是创建二维数组
 63     std::vector<std::vector<cv::DMatch>> knn_matches;
 64 
 65     //void knnMatch(
 66     // InputArray  queryDescriptors,    // 查询描述子(目标物体)
 67     // InputArray  trainDescriptors,    // 训练描述子(场景图像)
 68     // std::vector<std::vector<DMatch>>& matches, // 输出:匹配结果
 69     // int k                            // 返回前 k 个最近邻
 70     // );
 71     matcher.knnMatch(descriptors_obj, descriptors_scene, knn_matches, 2);
 72 
 73     //  Lowe 比率测试(筛选匹配)
 74     //cv::DMatch用于存储特征点匹配结果的数据结构,它记录了两个图像中特征点之间的对应关系
 75     //struct DMatch {
 76     // // 成员变量
 77     // int queryIdx;   // 查询图像中特征点的索引
 78     // int trainIdx;   // 训练图像中特征点的索引  
 79     // int imgIdx;     // 训练图像的索引(用于多图像匹配)
 80     // float distance; // 特征点之间的距离(相似度)
 81     // };
 82     std::vector<cv::DMatch> good_matches;
 83     //设置阈值比
 84     const float ratio_thresh = 0.75f;
 85 
 86     for (size_t i = 0; i < knn_matches.size(); i++) {
 87         if (knn_matches[i].size() < 2) continue;
 88 
 89         const cv::DMatch& m = knn_matches[i][0];
 90         const cv::DMatch& n = knn_matches[i][1];
 91         
 92         //这个判断表达式的意思:如果最近邻明显优于次近邻(距离足够小),则接受该匹配
 93         if (m.distance < ratio_thresh * n.distance) {
 94             good_matches.push_back(m);
 95         }
 96     }
 97     std::cout << "Object keypoints: " << keypoints_obj.size() << std::endl;
 98     std::cout << "Scene keypoints: " << keypoints_scene.size() << std::endl;
 99     std::cout << "KNN matches: " << knn_matches.size() << std::endl;
100     std::cout << "Good matches: " << good_matches.size() << std::endl;
101 
102     //  提取匹配点的坐标
103     std::vector<cv::Point2f> obj_points;
104     std::vector<cv::Point2f> scene_points;
105     //good_matches[i].queryIdx,获取 匹配点的物体图像的秒速子索引,
106     //keypoints_obj[good_matches[i].queryIdx]表示获取 匹配点的物体图像的秒速子索引 对应的坐标
107     //keypoints_obj[].pt,pt是cv::Point2f的成员变量,存储的坐标
108     for (size_t i = 0; i < good_matches.size(); i++) {
109         obj_points.push_back(keypoints_obj[good_matches[i].queryIdx].pt);
110         scene_points.push_back(keypoints_scene[good_matches[i].trainIdx].pt);
111     }
112 
113     // 计算单应性矩阵(RANSAC)
114     //单应性矩阵:是一个 3x3 的变换矩阵,描述两个平面之间的映射关系,如图:
115     // ┌     ┐   ┌             ┐ ┌     ┐
116     // | x'  |   | h11 h12 h13 | | x   |
117     // | y'  | = | h21 h22 h23 | | y   |
118     // | 1   |   | h31 h32 h33 | | 1   |
119     // └     ┘   └             ┘ └     ┘
120 
121     // cv::Mat cv::findHomography(
122     // InputArray srcPoints,    // (目标图像)
123     // InputArray dstPoints,    // (场景图像)
124     // int method = 0,          // 计算方法
125     // double ransacReprojThreshold = 3,  // RANSAC 重投影误差阈值
126     // OutputArray mask = noArray(),      // 输出掩码(可选)
127     // const int maxIters = 2000,         // 最大迭代次数
128     // const double confidence = 0.995    // 置信度
129     // );
130     cv::Mat H;
131     if (obj_points.size() >= 4) {
132         //cv::RANSAC:使用 RANSAC 算法,能有效剔除误匹配
133         // 即使经过 Lowe 比率测试,匹配点中仍可能包含误匹配。RANSAC 可以:
134         //     ①自动识别并剔除误匹配(外点)
135         //     ②只使用正确匹配(内点)计算单应性矩阵
136         H = cv::findHomography(obj_points, scene_points, cv::RANSAC);
137     }
138 
139     //  绘制匹配结果
140     //cv::drawMatches(img_object, keypoints_obj,    // 物体图像像及其特征点
141     // img_scene, keypoints_scene,   // 场景图像及其特征点
142     // good_matches,                 // 匹配点对
143     // img_matches);                 // 输出图像
144     //这里注意:最终输出图像是彩色的
145     //这个函数自动将两个图像并排拼接成一个新图像
146     cv::Mat img_matches;
147     cv::drawMatches(img_object, keypoints_obj,
148                 img_scene, keypoints_scene,
149                 good_matches, img_matches);
150     std::cout << "img_matches.channels() = " <<img_matches.channels() <<std::endl;
151     my_imshow("img_matches",img_matches);
152 
153     //  目标定位(画框)
154     if (!H.empty()) {
155         std::cout << "H matrix:\n" << H << std::endl;
156         std::vector<cv::Point2f> obj_corners(4);
157         //获取四个顶点
158         obj_corners[0] = cv::Point2f(0, 0);
159         obj_corners[1] = cv::Point2f((float)img_object.cols, 0);
160         obj_corners[2] = cv::Point2f((float)img_object.cols, (float)img_object.rows);
161         obj_corners[3] = cv::Point2f(0, (float)img_object.rows);
162 
163         std::vector<cv::Point2f> scene_corners(4);
164         //cv::perspectiveTransform,OpenCV 中用于执行透视变换的函数,
165         //                      将一个点集从源平面映射到目标平面。
166         //void cv::perspectiveTransform(
167         // InputArray src,     // 源点集(输入)
168         // OutputArray dst,    // 变换后的点集(输出)
169         // InputArray m        // 3x3 透视变换矩阵
170         // );
171         cv::perspectiveTransform(obj_corners, scene_corners, H);
172         
173         //打印信息
174         std::cout << "Scene image size: " << img_scene.cols << " x " << img_scene.rows << std::endl;
175         std::cout << "Transformed corners (in scene coordinates):" << std::endl;
176         for (int i = 0; i < 4; ++i) {
177             std::cout << "  Corner " << i << ": (" 
178                     << scene_corners[i].x << ", " << scene_corners[i].y << ")" << std::endl;
179         }
180         // 在匹配图上画框(注意偏移)
181         //因为两个图像显示在一个窗口内,所以需要进行偏移计算,
182         //①(float)img_object.cols,表示x方向发生改变
183         //②0 表示y轴不移动
184         cv::Point2f offset((float)img_object.cols, 0);
185         
186         //在拼接后的匹配图上绘制物体的边界框
187         //cv::line(img_matches,             // 目标图像
188         //  scene_corners[i] + offset,     // 线段起点
189         //  scene_corners[(i + 1) % 4] + offset,  // 线段终点
190         //  cv::Scalar(255, 0, 0),   // 颜色 (B, G, R) = 蓝色
191         //  4);                      // 线宽4像素
192         for (int i = 0; i < 4; i++) {
193             cv::line(img_matches,
194                  scene_corners[i] + offset,
195                  scene_corners[(i + 1) % 4] + offset,
196                  cv::Scalar(255, 0, 0), 4);
197         }
198     } else {
199         std::cout << "Homography calculation failed (too few matching points)" << std::endl;
200     }
201 
202     //  显示结果
203 
204     my_imshow("Good Matches & Object Detection", img_matches);
205 
206     cv::waitKey(0);
207     return 0;
208 }

 

posted @ 2026-03-27 10:45  freeyang8  阅读(0)  评论(0)    收藏  举报