qt-opencv图像增强之边缘检测(多线程)

qt-opencv图像增强之边缘检测(多线程)

  • 1 .pro项目文件配置
  • 2 ui设计
  • 3 多线程逻辑
  • 4 Canny边缘检测和Laplace算子
    • 4.1 Canny边缘检测
    • 4.2 L a p l a c e Laplace Laplace算子
  • 5 主要代码
  • 6 运行结果

1 .pro项目文件配置

INCLUDEPATH += /usr/local/include \
                /usr/local/include/opencv
                /usr/local/include/opencv2

LIBS += /usr/local/lib/libopencv_* \

2 ui设计

左边是Canny边缘检测结果,右边是 L a p l a c e Laplace Laplace算子(梯度的散度)结果,分别用QLabel显示图像。下面是两个QPushButton,参考运行结果。

3 多线程逻辑

几个重要的点:

  • 附属于子线程的对象,其函数在子线程中运行。需要使用信号槽使该函数运行,主线程直接调用会在主线程运行。
  • 子线程中重写的void run()方法,在子线程中运行。
  • 通过信号槽机制传递参数,将参数从信号所属的对象,传递到槽所属对象。
  • 更好的设计是,将ui与数据的处理使用不同线程完成。

这里使用第一条,即将对象附属到子线程。
qt-opencv图像增强之边缘检测(多线程)_第1张图片
括号中…点表示携带参数,对于Mat类型这里传递的是指针,传引用时失败了。

  1. 定义两个子类:
    基类–>子类
    QThread–>MyThread,MyThread类中不重写run()函数,也不添加信号和槽等,默认的即可。
    QObject–>Worker,Worker类中写要处理的事务。
  2. mainwindow中,点击“打开摄像头”按钮后的主要流程:
    (1) MyThread线程类对象启动线程start()
    (2) mainwindow对象发送operate(…)信号
    (3) Worker类对象接受到信号后,执行task(…)事务
    (4) MyThread线程类对象wait()阻塞主线程,等待MyThread子线程任务完成。
    (5) 回到(2)

4 Canny边缘检测和Laplace算子

4.1 Canny边缘检测

Canny, J. “A Computational Approach To Edge Detection”(《一种边缘检测的计算方法》). IEEE Trans. Pattern Analysis and Machine Intelligence. 1986, (8): 679–714

4.2 L a p l a c e Laplace Laplace算子

二阶微分算子,定义为梯度的散度。具有各向同性

△ f = ▽ ∙ ▽ f = ∂ 2 f ∂ x 2 + ∂ 2 f ∂ y 2 \bigtriangleup f= \bigtriangledown \bullet \bigtriangledown f= \frac{\partial ^2f}{\partial x^2}+\frac{\partial ^2f}{\partial y^2} f=f=x22f+y22f

 离散后,对应的卷积核为
0 1 0
1 -4 1
0 1 0

5 主要代码

worker.cpp如下

#include "worker.h"
#include
#include

Worker::Worker(QObject *parent) : QObject(parent)
{

}

//flag是一个任务类型标识,0的时候做Canny边缘检测,1的时候做拉普拉斯算子
void Worker::task(cv::Mat* src,cv::Mat* dst, uchar flag)
{
    if (src->data == NULL)
        return;
    if (flag == 0) //Canny边缘检测
    {
        cv::Canny(*src,*dst,20,40,3); //3是直径,应在3~7范围内,否则会报错
//        qDebug()<
    }
    if (flag == 1 ) //拉普拉斯算子,梯度的散度
    {
        this->laplaceGrad(*src,*dst);
    }
    emit threadId((int*)QThread::currentThreadId()); //当前线程id返回
}

//laplace任务
void Worker::laplaceGrad(cv::Mat& src,cv::Mat& dst)
{

    if (src.channels()!=1)
        return;

    int rows = src.rows;
    int cols = src.cols;

    dst = cv::Mat::zeros(rows,cols,CV_32FC1);

    for (int i=1;i<rows-1;i++)
    {
        for (int j=1;j<cols-1;j++)
        {
//            [1,1,1;1,-8,1;1,1,1]
            dst.ptr<float>(i)[j] = 1.0*src.ptr<uchar>(i-1)[j-1]+1.0*src.ptr<uchar>(i-1)[j]+1.0*src.ptr<uchar>(i-1)[j+1]
                    +1.0*src.ptr<uchar>(i)[j-1]-8.0*src.ptr<uchar>(i)[j]+1.0*src.ptr<uchar>(i)[j+1]
                    +1.0*src.ptr<uchar>(i+1)[j-1]+1.0*src.ptr<uchar>(i+1)[j]+1.0*src.ptr<uchar>(i+1)[j+1];
        }
    }
    cv::convertScaleAbs( dst, dst, 1, 0 ); // 从CV_32FC1转换到CV_8UC1
}

worker.h

#ifndef WORKER_H
#define WORKER_H

#include 
#include
#include
using namespace cv;

class Worker : public QObject
{
    Q_OBJECT
public:
    explicit Worker(QObject *parent = nullptr);
    void laplaceGrad(cv::Mat& src,cv::Mat& dst);


signals:
    void threadId(int*); //这个信号用来返回线程id标识
public slots:
	//槽函数,子线程任务在这个函数中处理
    void task(cv::Mat* src,cv::Mat* dst, uchar flag);
};

#endif // WORKER_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include
#include
#include "mythread.h"
#include"worker.h"
#include

void mat2qimg(Mat &src,QImage &qimg);

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

    //打开摄像头
    connect(ui->openCamBtn,&QPushButton::clicked,this,[=](){
       VideoCapture capture(0);
       Mat frame,canny,laplace,bi; //bi表示双边滤波结果

       int rows = ui->cLabel->height();
       int cols = ui->cLabel->width();

//       laplace = Mat::zeros(rows,cols,CV_32FC1);

       QImage qimgc,qimgla;
	   //建立线程1和线程2
       MyThread* thread1 = new MyThread;
       MyThread* thread2 = new MyThread;
	   //建立两个worker
       Worker* worker1 = new Worker;
       Worker* worker2 = new Worker;
	   //将两个worker移动到线程1和线程2中
       worker1->moveToThread(thread1);
       worker2->moveToThread(thread2);
	   //将主线程的信号与worker连接起来,注意传递的参数类型要一致
       connect(this,&MainWindow::operate1,worker1,&Worker::task); //信号1绑定线程1中任务
       connect(this,&MainWindow::operate2,worker2,&Worker::task); //信号2绑定线程2中任务
	   //显示线程标识
       connect(worker1,&Worker::threadId,[=](int* id1){
           QString id = QString::number(*id1,16); //16进制表示
           ui->t1Label->setText(QString("线程标识:%1").arg(id));  //+QString::number(*id1)
       });
       connect(worker2,&Worker::threadId,[=](int* id2){
           QString id = QString::number(*id2,16); //16进制表示
           ui->t2Label->setText(QString("线程标识:%1").arg(id));
       });
	   //启动两个线程
       thread1->start();
       thread2->start();

       while (1)
       {
           capture >> frame;
           if (frame.empty())
                   continue;
           //一些预处理操作,获得双边滤波后图像
           cv::resize(frame,frame,Size(cols,rows));
           cv::cvtColor(frame,frame,CV_BGR2GRAY);
           cv::bilateralFilter( frame, bi, 15, 21,21);

           //向线程1和线程2发送信号,分别处理数据。
           //参数传递的是指针,传递引用时出错。这里两个线程没有同时修改同一个内存地址。应当注意避免出现加锁的情况
           emit operate1(&bi,&canny,0);
           emit operate2(&bi,&laplace,1);
		   //主线程阻塞,等待子线程消息队列中没有任务
           thread1->wait(20);
           thread2->wait(20);

//           qDebug()<<"before qimg";
           //转成QImage
           mat2qimg(canny,qimgc);
           mat2qimg(laplace,qimgla);

           //用QLabel显示
           ui->cLabel->setPixmap(QPixmap::fromImage(qimgc));
           ui->lapLabel->setPixmap(QPixmap::fromImage(qimgla));
           waitKey(5);
       }
    });
    //退出
    connect(ui->closeBtn,&QPushButton::clicked,this,[=](){
        exit(0);
    });
}

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

void mat2qimg(Mat &src,QImage &qimg)
{
    if (src.channels()==3)
    {
        cvtColor(src,src,CV_BGR2RGB);
        qimg = QImage((const unsigned char*)(src.data), src.cols, src.rows, QImage::Format_RGB888 );
    }
    else
    {
        qimg = QImage((const unsigned char*)(src.data), src.cols, src.rows, QImage::Format_Grayscale8 );
    }
    return;
}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include 
#include
#include
using namespace cv;

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

private:
    Ui::MainWindow *ui;

signals:
	 //两个信号,operate1 发送给线程1,operate2发送给线程2
     void operate1(cv::Mat* src,cv::Mat* dst, uchar flag);
     void operate2(cv::Mat* src,cv::Mat* dst, uchar flag);
};
#endif // MAINWINDOW_H

6 运行结果

qt-opencv图像增强之边缘检测(多线程)_第2张图片

你可能感兴趣的:(opencv,qt,多线程)