特征检测属于opencv_contrib 库,在编译 OpenCV 时包含了附加模块才能使用,具体如何编译这里就不再作说明了
一、创建检测器
首先读取要建立提取特征的图像与存放特征点的数组
cv::Mat image = cv::imread("hy.jpg", 1);std::vector<cv::KeyPoint>keypoints;
使用特征检测方法之前,要创建检测器,然后调用它的检测方法,例如
1、FAST
// FAST 特征检测器,阈值为 40cv::Ptr<cv::FastFeatureDetector> ptrFAST =cv::FastFeatureDetector::create(40);// 检测关键点ptrFAST->detect(image,keypoints);
2、SURF
// 创建 SURF 特征检测器对象cv::Ptr<cv::xfeatures2d::SurfFeatureDetector> ptrSURF =cv::xfeatures2d::SurfFeatureDetector::create(2000.0);// 检测关键点ptrSURF->detect(image, keypoints);
3、SIFT
// 构建 SIFT 特征检测器实例cv::Ptr<cv::xfeatures2d::SiftFeatureDetector> ptrSIFT =cv::xfeatures2d::SiftFeatureDetector::create();// 检测关键点ptrSIFT->detect(image, keypoints);
4、BRISK
// 构造 BRISK 特征检测器对象cv::Ptr<cv::BRISK> ptrBRISK = cv::BRISK::create();// 检测关键点ptrBRISK->detect(image, keypoints);
5、ORB
// 构造 ORB 特征检测器对象cv::Ptr<cv::ORB> ptrORB =cv::ORB::create(75, // 关键点的总数1.2, // 图层之间的缩放因子8); // 金字塔的图层数量// 检测关键点ptrORB->detect(image, keypoints);
二、特征匹配
五种方法的使用都非常相似,接下来给出一个具体的例子
1、画图函数
cv::drawMatches(image1, keypoints1, // 第一幅图像image2, keypoints2, // 第二幅图像matches, // 匹配项的向量matchImage,//最终图像cv::Scalar(255, 255, 255), // 线条颜色cv::Scalar(255, 255, 255)); // 点的颜色
2、匹配
std::vector<cv::DMatch> matches; //存放匹配关系
这里将主要几条语句拿出来看,建议直接看下边源码注释
//patch1、patch2是两幅图片特征点附近的一块矩形区域,两者进行比较,//结果存放在result中cv::matchTemplate(patch1, patch2, result, cv::TM_SQDIFF);
三、示例
int main(){cv::Mat image = cv::imread("hy.jpg", 1);cv::pyrDown(image, image);cv::Mat image1 = image.clone();cv::Mat image2 = image.clone();cv::Ptr<cv::FeatureDetector> ptrDetector; // 泛型检测器指针ptrDetector = // 这里选用 FAST 检测器cv::FastFeatureDetector::create(80);// 检测关键点std::vector<cv::KeyPoint>keypoints1, keypoints2;ptrDetector->detect(image1, keypoints1);ptrDetector->detect(image2, keypoints2);const int nsize(11); // 邻域的尺寸cv::Rect neighborhood(0, 0, nsize, nsize); // 11×11cv::Mat patch1;cv::Mat patch2;cv::Mat result;std::vector<cv::DMatch> matches;for (int i = 0; i < keypoints1.size(); i++) {// 定义图像块neighborhood.x = keypoints1[i].pt.x - nsize / 2;neighborhood.y = keypoints1[i].pt.y - nsize / 2;if (neighborhood.x < 0 || neighborhood.y < 0 ||neighborhood.x + nsize >= image1.cols ||neighborhood.y + nsize >= image1.rows)continue;patch1 = image1(neighborhood);cv::DMatch bestMatch;for (int j = 0; j < keypoints2.size(); j++) {// 定义图像块neighborhood.x = keypoints2[j].pt.x - nsize / 2;neighborhood.y = keypoints2[j].pt.y - nsize / 2;// 如果邻域超出图像范围,就继续处理下一个点if (neighborhood.x < 0 || neighborhood.y < 0 ||neighborhood.x + nsize >= image2.cols ||neighborhood.y + nsize >= image2.rows)continue;// 第二幅图像的块patch2 = image2(neighborhood);// 匹配两个图像块cv::matchTemplate(patch1, patch2, result, cv::TM_SQDIFF);// 检查是否为最佳匹配if (result.at<float>(0, 0) < bestMatch.distance) {bestMatch.distance = result.at<float>(0, 0);bestMatch.queryIdx = i;bestMatch.trainIdx = j;}}// 添加最佳匹配matches.push_back(bestMatch);}//取25条相关性最强的匹配std::nth_element(matches.begin(), matches.begin() + 25, matches.end());matches.erase(matches.begin() + 25, matches.end());cv::Mat matchImage;cv::drawMatches(image1, keypoints1, // 第一幅图像image2, keypoints2, // 第二幅图像matches, // 匹配项的向量matchImage,//最终图像cv::Scalar(255, 255, 255), // 线条颜色cv::Scalar(255, 255, 255)); // 点的颜色cv::imshow("image", matchImage);cv::waitKey(0);}
下边是匹配结果:(想更换检测器的可以按照第一部分直接将对应代码替换掉)