一、
1.模板匹配是一种最原始、最基本的模式识别方法,研究某一特定对象物的图案位于图像的什么地方,进而识别对象物,这就是一个匹配问题。
它是图像处理中最基本、最常用的匹配方法。模板匹配具有自身的局限性,主要表现在它只能进行平行移动,若原图像中的匹配目标发生旋转或大小变化,该算法无效。
2.painter.drawImage使用QImage::Format_RGB888格式的图像会出错,需要转换成Format_RGB32。
img=image.convertToFormat(QImage::Format_RGB32);
二、
#ifndef MATCHTEMPLATEWIDGET_H #define MATCHTEMPLATEWIDGET_H #include <QObject> #include <QWidget> #include <QLabel> #include <QLineEdit> #include <QPushButton> #include <QComboBox> #include <QHBoxLayout> #include <QVBoxLayout> #include "displayimagewidget.h" #include "opencv2/opencv.hpp" class MatchTemplateWidget : public QWidget { Q_OBJECT public: explicit MatchTemplateWidget(QWidget *parent = nullptr); public: // enum TemplateMatchModes { // TM_SQDIFF = 0, // TM_SQDIFF_NORMED = 1, // TM_CCORR = 2, // TM_CCORR_NORMED = 3, // TM_CCOEFF = 4, // TM_CCOEFF_NORMED = 5 // }; const int MatchMethodeNum=6; const QString MatchMethodeText[6]={"SQDIFF 平方差匹配法","SQDIFF NORMED 归一化平方差匹配法","TM CCORR 相关匹配法", "TM CCORR NORMED 归一化相关匹配法","TM COEFF 系数匹配法","TM COEFF NORMED 归一化相关系数匹配法"}; signals: public slots: private slots: void slotSelectImageSource(); void slotSelectImageTemplate(); void slotStart(); private: DisplayImageWidget *displayImageWidget; QLineEdit *leImageSource; QLineEdit *leImageTemplate; QLabel* lblDisplayImage; QComboBox *cmbMatchMethode; void initUI(); void matchTemplate(cv::TemplateMatchModes matchMethod); QImage matToQImage(cv::Mat mat); }; #endif // MATCHTEMPLATEWIDGET_H
#include "matchtemplatewidget.h" #include <QFileDialog> #include <QDebug> MatchTemplateWidget::MatchTemplateWidget(QWidget *parent) : QWidget(parent) { this->resize(600,400); initUI(); } void MatchTemplateWidget::slotSelectImageSource() { QFileDialog *fileDialog = new QFileDialog(this); fileDialog->setWindowTitle(tr("Select File")); fileDialog->setDirectory("."); if(fileDialog->exec() == QDialog::Accepted) { QString imgPath = fileDialog->selectedFiles()[0]; leImageSource->setText(fileDialog->selectedFiles()[0]); } } void MatchTemplateWidget::slotSelectImageTemplate() { QFileDialog *fileDialog = new QFileDialog(this); fileDialog->setWindowTitle(tr("Select File")); fileDialog->setDirectory("."); if(fileDialog->exec() == QDialog::Accepted) { QString imgPath = fileDialog->selectedFiles()[0]; leImageTemplate->setText(fileDialog->selectedFiles()[0]); } } void MatchTemplateWidget::slotStart() { matchTemplate((cv::TemplateMatchModes)cmbMatchMethode->currentIndex()); } void MatchTemplateWidget::initUI() { displayImageWidget=new DisplayImageWidget(this); QLabel *lblSourceText=new QLabel(this); lblSourceText->setText("Image Source:"); lblSourceText->setMinimumSize(100,30); lblSourceText->setMaximumSize(100,30); leImageSource=new QLineEdit(this); QPushButton *btnSourceDlg=new QPushButton(this); btnSourceDlg->setText("..."); QLabel *lblTemplateText=new QLabel(this); lblTemplateText->setMinimumSize(100,30); lblTemplateText->setMaximumSize(100,30); lblTemplateText->setText("Image Template:"); leImageTemplate=new QLineEdit(this); QPushButton *btnTemplateDlg=new QPushButton(this); btnTemplateDlg->setText("..."); cmbMatchMethode=new QComboBox(this); for(int i=0;i<MatchMethodeNum;i++) { cmbMatchMethode->addItem(MatchMethodeText[i]); } QPushButton *btnStart=new QPushButton("Start",this); // lblDisplayImage=new QLabel(this); QHBoxLayout *h1=new QHBoxLayout(); h1->addWidget(lblSourceText); h1->addWidget(leImageSource); h1->addWidget(btnSourceDlg); QHBoxLayout *h2=new QHBoxLayout(); h2->addWidget(lblTemplateText); h2->addWidget(leImageTemplate); h2->addWidget(btnTemplateDlg); QHBoxLayout *h3=new QHBoxLayout(); h3->addWidget(cmbMatchMethode); h3->addWidget(btnStart); QVBoxLayout *mainLayout=new QVBoxLayout(this); mainLayout->addLayout(h1); mainLayout->addLayout(h2); mainLayout->addLayout(h3); mainLayout->addWidget(displayImageWidget); setLayout(mainLayout); connect(btnSourceDlg,SIGNAL(clicked()),SLOT(slotSelectImageSource())); connect(btnTemplateDlg,SIGNAL(clicked()),SLOT(slotSelectImageTemplate())); connect(btnStart,SIGNAL(clicked()),SLOT(slotStart())); } //TM_SQDIFF,TM_SQDIFF_NORMED匹配数值越低表示匹配效果越好,其它四种反之。 //TM_SQDIFF_NORMED,TM_CCORR_NORMED,TM_CCOEFF_NORMED是标准化的匹配,得到的最大值,最小值范围在0~1之间,其它则需要自己对结果矩阵归一化。 void MatchTemplateWidget::matchTemplate(cv::TemplateMatchModes matchMethod) { cv::Mat imgSource=cv::imread(leImageSource->text().toStdString()); cv::Mat imgTemplate=cv::imread(leImageTemplate->text().toStdString()); if(imgSource.cols==0||imgTemplate.cols==0) { return; } cv::Mat imgDisplay; imgSource.copyTo(imgDisplay); cv::Mat imgResult; int resultRows=imgSource.rows-imgTemplate.rows+1; int resultCols=imgSource.cols-imgTemplate.cols+1; imgResult.create(resultRows, resultCols, CV_32FC1 ); //匹配,最后一个参数为匹配方式,共有6种 cv::matchTemplate(imgSource,imgTemplate,imgResult,matchMethod); normalize(imgResult,imgResult,0,1,cv::NORM_MINMAX,-1,cv::Mat()); //获取最大或最小匹配系数 //首先是从得到的 输出矩阵中得到 最大或最小值(平方差匹配方式是越小越好,所以在这种方式下,找到最小位置) //找矩阵的最小位置的函数是 minMaxLoc函数 double minVal; double maxVal; cv::Point minLoc; cv::Point maxLoc; cv::Point matchLoc; cv::minMaxLoc(imgResult, &minVal, &maxVal, &minLoc, &maxLoc, cv::Mat()); displayImageWidget->setText("minVal:"+QString::number(minVal)+" " +"maxVal:"+QString::number(maxVal)); if( matchMethod == cv::TM_SQDIFF || matchMethod == cv::TM_SQDIFF_NORMED ) { matchLoc = minLoc; } else { matchLoc = maxLoc; } cv::rectangle(imgDisplay,matchLoc,cv::Point(matchLoc.x + imgTemplate.cols , matchLoc.y + imgTemplate.rows), cv::Scalar(0,255,0), 2, 8, 0 ); cv::rectangle(imgResult,matchLoc,cv::Point(matchLoc.x + imgTemplate.cols , matchLoc.y + imgTemplate.rows), cv::Scalar(0,255,0), 2, 8, 0 ); displayImageWidget->setImage(matToQImage(imgDisplay)); //cv::imshow( "imgDisplay", imgDisplay); //cv::imshow( "imgResult", imgResult); } QImage MatchTemplateWidget::matToQImage(cv::Mat mat) { QImage img; int channels=mat.channels(); if(channels==3) { cv::cvtColor(mat,mat,cv::COLOR_BGR2RGB); img = QImage(static_cast<uchar *>(mat.data),mat.cols,mat.rows,QImage::Format_RGB888); } else if(channels==4) { //argb img = QImage(static_cast<uchar *>(mat.data),mat.cols,mat.rows,QImage::Format_ARGB32); } else { //单通道,灰度图 img = QImage(mat.cols, mat.rows,QImage::Format_Indexed8); } return img; }
#ifndef DISPLAYIMAGEWIDGET_H #define DISPLAYIMAGEWIDGET_H #include <QWidget> class DisplayImageWidget : public QWidget { Q_OBJECT public: explicit DisplayImageWidget(QWidget *parent = nullptr); public: void setImage(QImage image); void setText(QString text); signals: public slots: protected: void paintEvent(QPaintEvent *event); private: QImage img; QString txt; }; #endif // DISPLAYIMAGEWIDGET_H
#include "displayimagewidget.h" #include <QPainter> #include <QDebug> DisplayImageWidget::DisplayImageWidget(QWidget *parent) : QWidget(parent) ,txt("") { this->resize(1300,800); } void DisplayImageWidget::setImage(QImage image) { img=image.convertToFormat(QImage::Format_RGB32); update(); } void DisplayImageWidget::setText(QString text) { txt=text; update(); } void DisplayImageWidget::paintEvent(QPaintEvent *event) { QPainter painter(this); if(img.width()>0 && img.height()>0) { painter.drawImage(0,0,img); painter.setPen(QPen(Qt::blue,2)); painter.drawText(QPoint(10,20),txt); } }