对图像进行二值化
分析:感觉阈值180-190比较合适,想把机场检测出来,其他都过滤掉。
基于sift特征的模板匹配
首先这是一段GPT给出的代码,但仍需要逐句分析
#include <opencv2/opencv.hpp>
#include <opencv2/xfeatures2d.hpp>
int main() {
// 读取原图像和模板图像
cv::Mat originalImage = cv::imread("original_image.jpg", cv::IMREAD_GRAYSCALE);
cv::Mat templateImage = cv::imread("template_image.jpg", cv::IMREAD_GRAYSCALE);
// 创建SIFT检测器
cv::Ptr<cv::xfeatures2d::SIFT> sift = cv::xfeatures2d::SIFT::create();
// 检测关键点和计算描述符
std::vector<cv::KeyPoint> keypointsOriginal, keypointsTemplate;
cv::Mat descriptorsOriginal, descriptorsTemplate;
sift->detectAndCompute(originalImage, cv::noArray(), keypointsOriginal, descriptorsOriginal);
sift->detectAndCompute(templateImage, cv::noArray(), keypointsTemplate, descriptorsTemplate);
// 使用FLANN匹配器
cv::FlannBasedMatcher matcher;
std::vector<std::vector<cv::DMatch>> knnMatches;
matcher.knnMatch(descriptorsTemplate, descriptorsOriginal, knnMatches, 2);
// 比率测试,以获取良好的匹配
std::vector<cv::DMatch> goodMatches;
for (size_t i = 0; i < knnMatches.size(); ++i) {
if (knnMatches[i][0].distance < 0.7 * knnMatches[i][1].distance) {
goodMatches.push_back(knnMatches[i][0]);
}
}
// 获取匹配点的位置
std::vector<cv::Point2f> matchedPointsTemplate, matchedPointsOriginal;
for (const auto& match : goodMatches) {
matchedPointsTemplate.push_back(keypointsTemplate[match.queryIdx].pt);
matchedPointsOriginal.push_back(keypointsOriginal[match.trainIdx].pt);
}
// 计算透视变换矩阵
cv::Mat homography = cv::findHomography(matchedPointsTemplate, matchedPointsOriginal, cv::RANSAC);
// 获取模板图像的四个角在原图像上的位置
std::vector<cv::Point2f> templateCorners(4);
templateCorners[0] = cv::Point2f(0, 0);
templateCorners[1] = cv::Point2f(static_cast<float>(templateImage.cols), 0);
templateCorners[2] = cv::Point2f(static_cast<float>(templateImage.cols), static_cast<float>(templateImage.rows));
templateCorners[3] = cv::Point2f(0, static_cast<float>(templateImage.rows));
std::vector<cv::Point2f> transformedCorners(4);
cv::perspectiveTransform(templateCorners, transformedCorners, homography);
// 在原图像上绘制矩形框
cv::Mat resultImage = originalImage.clone();
cv::polylines(resultImage, transformedCorners, true, cv::Scalar(0, 255, 0), 2, cv::LINE_AA);
// 显示结果
cv::imshow("SIFT Matches", resultImage);
cv::waitKey(0);
cv::destroyAllWindows();
return 0;
}
对左侧图像进行sift特征点匹配
对右侧图像进行sift特征匹配
SIFT特征具有良好的尺度不变性、旋转不变性
存在的问题
我现在存在的问题就是,给我一个模板,我能识别出来,但是多个目标就不行。
GPT给了我一个在原图上检测出所有与模板相似的目标的的做法,用的是滑动窗口技术。
#include <opencv2/opencv.hpp>
#include <iostream>
int main() {
cv::Mat originalImage = cv::imread("original_image.jpg", cv::IMREAD_GRAYSCALE);
cv::Mat templateImage = cv::imread("template_image.jpg", cv::IMREAD_GRAYSCALE);
// 创建 ORB 检测器
cv::Ptr<cv::ORB> orb = cv::ORB::create();
// 检测关键点和计算描述符
std::vector<cv::KeyPoint> keypointsOriginal, keypointsTemplate;
cv::Mat descriptorsOriginal, descriptorsTemplate;
orb->detectAndCompute(originalImage, cv::noArray(), keypointsOriginal, descriptorsOriginal);
orb->detectAndCompute(templateImage, cv::noArray(), keypointsTemplate, descriptorsTemplate);
// 创建 BFMatcher 进行匹配
cv::BFMatcher matcher(cv::NORM_HAMMING);
// 设置滑动窗口的步长
int stepSize = 20;
// 设置窗口的大小
int windowSize = templateImage.rows;
for (int y = 0; y < originalImage.rows - windowSize; y += stepSize) {
for (int x = 0; x < originalImage.cols - windowSize; x += stepSize) {
// 获取当前窗口
cv::Rect windowRect(x, y, windowSize, windowSize);
cv::Mat window = originalImage(windowRect);
// 检测关键点和计算描述符
std::vector<cv::KeyPoint> keypointsWindow;
cv::Mat descriptorsWindow;
orb->detectAndCompute(window, cv::noArray(), keypointsWindow, descriptorsWindow);
// 进行匹配
std::vector<cv::DMatch> matches;
matcher.match(descriptorsTemplate, descriptorsWindow, matches);
// 判断匹配是否足够
if (matches.size() > /*设置一个阈值*/) {
// 匹配成功,可以在原图上标记目标
cv::rectangle(originalImage, windowRect, cv::Scalar(0, 255, 0), 2);
}
}
}
// 显示结果
cv::imshow("Object Detection", originalImage);
cv::waitKey(0);
cv::destroyAllWindows();
return 0;
}
如果只有一个目标,sift特征可以很好的匹配上,但如果有多个目标,sift特征就不能很好的匹配上
匹配的结果很差,为什么呢?
问题一:多目标匹配困难
解决措施:采用滑动窗口
在用sift进行匹配的时候,如果原始图像里只有一个目标,就能很好的匹配上,原始图像里有多个目标,就不能很好的匹配上
这里是一些做测试的代码
//
std::vector<std::vector<cv::DMatch>> knnMatches;
matcher.knnMatch(descriptorsTemplate, descriptorsOriginal, knnMatches, 2);
//**可视化一下未经比率测试的sift特征匹配
//cv::Mat imgMatches;
// cv::drawMatches(templateImage, keypointsTemplate, originalImage, keypointsOriginal, knnMatches, imgMatches);
//cv::imshow("Resizable Window", imgMatches);
// cv::waitKey(0);
// 比率测试,以获取良好的匹配
std::vector<cv::DMatch> goodMatches;
for (size_t i = 0; i < knnMatches.size(); ++i) {
if (knnMatches[i][0].distance < 0.85 * knnMatches[i][1].distance) {
goodMatches.push_back(knnMatches[i][0]);
}
}
//**可视化一下经过比率测试的sift特征
cv::Mat imgMatches;
cv::drawMatches(templateImage, keypointsTemplate, originalImage, keypointsOriginal, goodMatches, imgMatches);
cv::imshow("Resizable Window", imgMatches);
cv::waitKey(0);
// 为每个目标创建容器来存储匹配点和透视变换矩阵
std::vector<std::vector<cv::Point2f>> allMatchedPoints;
std::vector<cv::Mat> allHomographies;
// 获取匹配点的位置
for (const auto& match : goodMatches) {
std::vector<cv::Point2f> matchedPointsTemplate, matchedPointsOriginal;
matchedPointsTemplate.push_back(keypointsTemplate[match.queryIdx].pt);
matchedPointsOriginal.push_back(keypointsOriginal[match.trainIdx].pt);
//打印匹配点数量
std::cout << "Matched Points:" << std::endl;
for (size_t i = 0; i < matchedPointsTemplate.size(); ++i) {
std::cout << "Template: " << matchedPointsTemplate[i] << " - Original: " << matchedPointsOriginal[i] << std::endl;
}
// 计算透视变换矩阵
cv::Mat homography = cv::findHomography(matchedPointsTemplate, matchedPointsOriginal, cv::RANSAC);
// 存储匹配点和透视变换矩阵
allMatchedPoints.push_back(matchedPointsTemplate);
allHomographies.push_back(homography);
}
//在原图像上绘制矩阵框
cv::Mat resultImage = originalImage.clone();
for (size_t i = 0; i < allHomographies.size(); ++i) {
std::vector<cv::Point2f> templateCorners(4);
templateCorners[0] = cv::Point2f(0, 0);
templateCorners[1] = cv::Point2f(static_cast<float>(templateImage.cols), 0);
templateCorners[2] = cv::Point2f(static_cast<float>(templateImage.cols), static_cast<float>(templateImage.rows));
templateCorners[3] = cv::Point2f(0, static_cast<float>(templateImage.rows));
std::vector<cv::Point2f> transformedCorners(4);
// 应用透视变换
cv::perspectiveTransform(templateCorners, transformedCorners, allHomographies[i]);
// 转换坐标为整数类型
std::vector<cv::Point> transformedCornersInt(4);
for (int j = 0; j < 4; ++j) {
transformedCornersInt[j] = cv::Point(static_cast<int>(transformedCorners[j].x), static_cast<int>(transformedCorners[j].y));
}
// 在原图像上绘制矩形框
cv::polylines(resultImage, transformedCornersInt, true, cv::Scalar(0, 255, 0), 2, cv::LINE_AA);
}
// 显示结果
cv::imshow("Resizable Window", resultImage);
cv::waitKey(0);
cv::destroyAllWindows();
return 0;
问题二: 窗口大小
窗口大小对于多目标的检测至关重要!
问题三:关键点
很好,调整了一下成这样了
参数:
最终算法还是存在问题
最终算法并不能实现多目标的检测
换一种特征试试呢