一、概述
使用OpenCV实现一个简单的图搜索的小功能
特点:暴力匹配
实现原理:
1.将图片集合生成特征描述,并存入文件
2.加载目标图像,并生成图像特征描述
3.加载图像特征描述文件列表
4.图像特征描述和集合中的特征描述列表进行匹配
5.如果匹配成功就说明搜索有值,如果没有匹配成功说明没有搜索结果。
二、代码示例
#include "ORBImageMatch.h" ORBImageMatch::ORBImageMatch(QWidget* parent) : QWidget(parent) { this->setWindowTitle("图像匹配(相似度比较)"); this->setFixedSize(QSize(320, 480)); Button* btn1 = new Button(this); btn1->setText("选择第一张图"); Button* btn2 = new Button(this); btn2->setText("选择第二张图"); Button* btn3 = new Button(this); btn3->setText("开始匹配"); Button* btn4 = new Button(this); btn4->setText("生成特征文件"); QVBoxLayout* vLayout = new QVBoxLayout(this); vLayout->setAlignment(Qt::AlignTop); vLayout->addWidget(btn1); vLayout->addWidget(btn2); vLayout->addWidget(btn3); vLayout->addWidget(btn4); connect(btn1, &Button::clicked, [=]() { filePath1 = QFileDialog::getOpenFileName(this, tr("请选择图片"), "C:/Users/DBF-DEV-103/Downloads/", tr("Image Files(*.jpg *.png *.webp *.jpeg)")); searchImage(); }); connect(btn2, &Button::clicked, [=]() { filePath2 = QFileDialog::getOpenFileName(this, tr("请选择图片"), "C:/Users/DBF-DEV-103/Downloads/", tr("Image Files(*.jpg *.png *.webp *.jpeg)")); }); connect(btn3, &Button::clicked, [=]() { matchImage(); }); //选择所有文件并生成特征 connect(btn4, &Button::clicked, [=]() { // 打开文件对话框,选择目录 QString directory = QFileDialog::getExistingDirectory(this, tr("请选择需要是生成特征的文件"), "C:/Users/DBF-DEV-103/Downloads", QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); // 检查是否选择了目录 if (directory.isEmpty()) { // 用户没有选择目录 qDebug() << "请选择目录"; return; } //qDebug() << "directory=" << directory; // 获取目录下所有文件的列表 QStringList fileList = QDir(directory).entryList(QDir::Files); batchGenerateImageCharacteristics(directory, fileList); }); } void ORBImageMatch::matchImage() { // 加载目标图片和仓库图片 cv::Mat targetImage = cv::imread(filePath1.toStdString().c_str(), cv::IMREAD_GRAYSCALE); cv::Mat repositoryImage = cv::imread(filePath2.toStdString().c_str(), cv::IMREAD_GRAYSCALE); // 初始化ORB检测器 cv::Ptr<cv::ORB> orbDetector = cv::ORB::create(); std::vector<cv::KeyPoint> keypointsRepository, keypointsTarget; cv::Mat descriptorsRepository, descriptorsTarget; // 检测特征点并计算描述符 orbDetector->detectAndCompute(repositoryImage, cv::noArray(), keypointsRepository, descriptorsRepository); orbDetector->detectAndCompute(targetImage, cv::noArray(), keypointsTarget, descriptorsTarget); // 创建暴力匹配器对象 cv::BFMatcher matcher; std::vector<cv::DMatch> matches; // 进行匹配 matcher.match(descriptorsTarget, descriptorsRepository, matches);//对特征带你进行匹配然后比较距离 // 根据匹配结果判断是否存在 double minDist = 100; for (auto match : matches) { qDebug() << "distance=" << match.distance; if (match.distance < minDist) minDist = match.distance; } // 设定阈值 if (minDist < 100) { qDebug() << "目标图片在仓库中存在"; } else { qDebug() << "目标图片在仓库中不存在"; } //c/c++ opencv保存特征到本地及加载本地特征并匹配相似度 } //批量生成特征文件 void ORBImageMatch::batchGenerateImageCharacteristics(QString directory, QStringList fileList) { for (QString filePath : fileList) { qDebug() << "filePath=" << directory + "/" + filePath; QString imageFilePath = directory + "/" + filePath; cv::Mat targetImage = cv::imread(imageFilePath.toStdString().c_str(), cv::IMREAD_GRAYSCALE); cv::Ptr<cv::ORB> orbDetector = cv::ORB::create(); std::vector<cv::KeyPoint> keypointsTarget; cv::Mat descriptorsTarget; // 检测特征点并计算描述符 orbDetector->detectAndCompute(targetImage, cv::noArray(), keypointsTarget, descriptorsTarget); //将得到的特征存起来 // 创建FileStorage对象并打开文件 QFileInfo fileInfo(filePath); QString fileName = fileInfo.completeBaseName(); // 返回不带扩展名的文件名 QString mDirectory = directory + "/features/"; QString fileFullName = "C:/Users/DBF-DEV-103/Downloads/features/" + fileName + ".yml"; qDebug() << "fileFullName=" << fileFullName; cv::FileStorage fs(fileFullName.toStdString().c_str(), cv::FileStorage::WRITE); if (!fs.isOpened()) { std::cerr << "Error: 无法打开文件" << std::endl; return; } // 将特征存储到文件 //fs.write(fileName.toStdString(), descriptorsTarget); cv::String mFileName = cv::String(fileName.toStdString()); fs << "feature_" + mFileName << descriptorsTarget; // 关闭文件 fs.release(); std::cout << descriptorsTarget << std::endl; } } //搜索图片 void ORBImageMatch::searchImage() { // 加载目标图片和仓库图片 cv::Mat targetImage = cv::imread(filePath1.toStdString().c_str(), cv::IMREAD_GRAYSCALE); // 初始化ORB检测器 cv::Ptr<cv::ORB> orbDetector = cv::ORB::create(); std::vector<cv::KeyPoint> keypointsTarget; cv::Mat descriptorsTarget; // 检测特征点并计算描述符 orbDetector->detectAndCompute(targetImage, cv::noArray(), keypointsTarget, descriptorsTarget); // 创建暴力匹配器对象 cv::BFMatcher matcher; std::vector<cv::DMatch> matches; //加载特征列表 // 打开文件对话框,选择目录 QString directory = "C:/Users/DBF-DEV-103/Downloads/features/"; // 检查是否选择了目录 if (directory.isEmpty()) { // 用户没有选择目录 qDebug() << "请选择目录"; return; } //qDebug() << "directory=" << directory; // 获取目录下所有文件的列表 QStringList fileList = QDir(directory).entryList(QDir::Files); vector<QString> vectors; for (QString filePath : fileList) { QFileInfo fileInfo(filePath); QString fileName = fileInfo.completeBaseName(); // 返回不带扩展名的文件名 QString fileFullName = directory + filePath; qDebug() << "fileFullName=" << fileFullName; // 初始化FileStorage对象 cv::FileStorage fs(fileFullName.toStdString().c_str(), cv::FileStorage::READ); if (!fs.isOpened()) { qDebug() << "Failed to open " << fileFullName; return; } // 假设特征存储在名为"features"的节点下 cv::Mat descriptors; const String feature_name = "feature_" + fileName.toStdString(); fs[feature_name] >> descriptors; // 进行匹配 matcher.match(descriptorsTarget, descriptors, matches);//对特征带你进行匹配然后比较距离 // 根据匹配结果判断是否存在 double minDist = 100; for (auto match : matches) { qDebug() << "distance=" << match.distance; if (match.distance < minDist) minDist = match.distance; } // 设定阈值 if (minDist < 100) { //qDebug() << "目标图片在仓库中存在"; vectors.push_back(fileFullName); } else { //qDebug() << "目标图片在仓库中不存在"; } // 关闭FileStorage对象 fs.release(); } if (vectors.size() > 0) { qDebug() << "检测到相似图片"; } else { qDebug() << "未检测到相似图片"; } for (auto mPath : vectors) { qDebug() << "mPath=" << mPath; } } ORBImageMatch::~ORBImageMatch() { }
标签:匹配,案例,搜图,Button,descriptorsTarget,OpenCV,QString,orbDetector,cv From: https://www.cnblogs.com/tony-yang-flutter/p/18332687