opencv中cv::Mat类赋值的坑

2020.9.27

使用使用opencv中模板匹配方法简易地实现对视频中目标的追踪。

Qt5.14.2,MSVC2017 64bit; opencv4.3.0;

 

widget.h头文件

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <opencv.hpp>
#include <core.hpp>
#include <highgui.hpp>
#include <imgproc.hpp>
#include <QPainter>
#include <QImage>
#include <video/tracking.hpp>
#include <core/utility.hpp>
#include <videoio.hpp>


//using namespace cv;

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();


protected:
    void timerEvent(QTimerEvent *);
    void paintEvent(QPaintEvent *);
    void readFrame();
    cv::Point match(cv::Mat, cv::Mat);

private slots:
    void on_OpenButton_clicked();


    void on_PlayButton_clicked();

    void on_PauseButton_clicked();

    void on_StopButton_clicked();

private:
    Ui::Widget *ui;
    cv::Mat frame;
    cv::Mat dealframe;
    cv::VideoCapture capture;
    int timer;
    QPixmap map;
    QPixmap map2;

    QString file;
    bool roi;
    cv::Rect2d targetRect;
    cv::Mat target;
    cv::Mat targetModel;
    cv::Mat grayFrame;

};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QFileDialog>
#include <QDebug>
#include <QTime>
#include <QPainter>
#include <iostream>
#include <QMessageBox>
#include <opencv.hpp>


Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    setWindowTitle(tr("Player"));
    ui->OpenButton->setEnabled(true);
    ui->PlayButton->setEnabled(false);
    ui->PauseButton->setEnabled(false);
    ui->StopButton->setEnabled(false);
    roi = false;


    map = QPixmap(600,400);
    map2 = QPixmap(600,400);
    map.fill(Qt::black);
    map2.fill(Qt::black);
    update();
    //temp = cv::imread("E:\\QtProjectData\\build-videoFrame-Desktop_Qt_5_14_2_MSVC2017_64bit-Debug\\debug\\autograph.jpg");
    //if(temp.empty())
    //{
     //   QMessageBox::warning(this,tr("WARNING"),tr("No autograph.jpg."));
      //  exit(1);
    //}
    //cv::resize(temp, temp, cv::Size(600, 400));

}

Widget::~Widget()
{
    delete ui;
}

void Widget::timerEvent(QTimerEvent *event)
{
    if(event->timerId() == timer)
    {
        readFrame();
        update();
    }
}

void Widget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    painter.drawPixmap(100,30,map);
    painter.drawPixmap(800,30,map2);
}


void Widget::on_OpenButton_clicked()
{
    QString tempfile = QFileDialog::getOpenFileName();
    if(tempfile.isEmpty())
    {
        return ;
    }
    cv::VideoCapture tempcapture;
    tempcapture.open(tempfile.toStdString());
    //qDebug()<< file;
    if(tempcapture.isOpened())
    {
        file = tempfile;
        capture = tempcapture;

        ui->OpenButton->setEnabled(true);
        ui->PlayButton->setEnabled(true);
        ui->PauseButton->setEnabled(false);
        ui->StopButton->setEnabled(false);
        roi = false;

        readFrame();
        update();
    }


}


void Widget::on_PlayButton_clicked()
{
    ui->OpenButton->setEnabled(false);
    ui->PlayButton->setEnabled(false);
    ui->PauseButton->setEnabled(true);
    ui->StopButton->setEnabled(true);

    timer = startTimer(1000);
}

void Widget::readFrame()
{
    capture >> frame;
    if(frame.empty())
    {
        on_StopButton_clicked();
        return ;
    }
    cv::resize(frame, dealframe, cv::Size(600, 400));
    //dealframe = dealframe+temp;

    cv::cvtColor(dealframe,grayFrame,cv::COLOR_BGR2GRAY);
    if(!roi)
    {

        //cv::imshow("srcImage", grayFrame);
        targetRect = cv::selectROI("srcImage",dealframe);
        target = grayFrame(targetRect);
        //targetModel = grayFrame(targetRect);
        //targetModel.copyTo(target);
        
        imshow("tempImage",dealframe(targetRect));
        roi = true;
    }

    //cv::cvtColor(target,target,cv::COLOR_BGR2GRAY);


    imshow("3",target);
    cv::Point targetPoint = match(grayFrame,target);
    imshow("33",target);
    targetRect = cv::Rect2d(targetPoint,cv::Size(target.cols,target.rows));
    std::cout << targetPoint << std::endl;
     std::cout << targetRect << std::endl;
    cv::rectangle(grayFrame,targetRect,cv::Scalar(0,0,255));

    imshow("2",grayFrame);
    imshow("22",target);

    ui->label_2->setText(tr("(%1,%2)").arg(targetPoint.x+floor(target.cols/2)).arg(targetPoint.y+floor(target.rows/2)));

    cv::cvtColor(dealframe, dealframe, 4);   //  4 表示将BGR转为RGB.

    QImage image((const uchar*)dealframe.data, dealframe.cols, dealframe.rows, QImage::Format_RGB888);
    QImage image2((const uchar*)grayFrame.data, grayFrame.cols, grayFrame.rows, QImage::Format_Grayscale8);

    map = map.fromImage(image);
    map2 = map2.fromImage(image2);
}

cv::Point Widget::match(cv::Mat src, cv::Mat temp)
{
    qDebug() << "qdebug:1";
    cv::Mat result = cv::Mat::zeros(src.cols-temp.cols+1,src.rows-temp.rows+1,CV_32FC1);
    cv::matchTemplate(src,temp,result,cv::TM_CCOEFF_NORMED);

    double minVal,maxVal;
    cv::Point minPos,maxPos;
    cv::minMaxLoc(result,&minVal,&maxVal,&minPos,&maxPos);
    std::cout << maxVal << '\n' << maxPos << std::endl;
    qDebug() << "qdebug:2";

    return maxPos;
}

void Widget::on_PauseButton_clicked()
{
    ui->OpenButton->setEnabled(false);
    ui->PlayButton->setEnabled(true);
    ui->PauseButton->setEnabled(false);
    ui->StopButton->setEnabled(true);

    killTimer(timer);
}

void Widget::on_StopButton_clicked()
{
    ui->OpenButton->setEnabled(true);
    ui->PlayButton->setEnabled(true);
    ui->PauseButton->setEnabled(false);
    ui->StopButton->setEnabled(false);
    ui->label_2->setText(tr("(...,...)"));

    killTimer(timer);
    capture.open(file.toStdString());
    //qDebug()<< file;
    readFrame();
    update();

}

运行代码后,出现问题:几个imshow()对target进行输出显示发现问题,target为匹配的模板图像,只在第一次selectROI()选择时进行赋值,此后代码段中从未进行修改操作,但运行后发现每次target都在变化,且均为整幅图像中固定位置。

imshow("3",target)结果:

imshow("2",grayFrame)结果:

imshow("22",target)结果:

可以看到"22"显示的图像中有明显的的我用于定位的矩形框,但整个代码中,我只在if()判断条件中对target进行了一次赋值,当时还未进行定位操作,图像还没有矩形的定位框。因此猜测target在进行模板匹配以及向原图像中添加定位矩形框后进行过自动的修改。

继续播放视频,观察后面帧的图像。

imshow("3",target)结果:

imshow("2",grayFrame)结果:

imshow("22",target)结果:

果然target始终是图像中定位框里面的区域。为进一步印证,观察定位框坐标:

定位框坐标始终相同,即target始终为图像固定区域。

猜测是语句 target = grayFrame(targetRect);的问题,或许是在cv::Mat类中运用=进行赋值时,target始终为grayFrame(targetRect)的引用,这就会导致每次读取新的grayFrame时,target对应进行变化,又由于targetRect不变,因此反映在图像输出上就是target不断更新为新的一帧图像的固定的矩形区域。

解决办法为:换用copyTo()进行cv::Mat类的赋值。

//target = grayFrame(targetRect);
targetModel = grayFrame(targetRect);
targetModel.copyTo(target);

由于此语句是在if()条件中,因此只在第一次selectROI()后执行,完成target的赋值,此后由于不再进入if(),即使targetModel作为grayFrame(targetRect)的引用不断进行了修改,但copyTo()不再执行,因此target不会进行修改。

opencv中cv::Mat类赋值问题解决。

但进行目标追踪的效果不太好,当目标被遮挡后,丢失,匹配到另外的区域。考虑对模板匹配过程进行优化,限定最佳匹配位置的搜索范围在前一匹配位置的周围。下次进行一下尝试。

©️2020 CSDN 皮肤主题: 深蓝海洋 设计师:CSDN官方博客 返回首页