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 }

浙公网安备 33010602011771号