Opencv模板匹配

一、

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);
   }



}

 

上一篇:《Python数据科学手册》学习笔记及书评


下一篇:机器学习笔记(十四)——非线性逻辑回归(sklearn)