整个程序主要包含三个类:UI相关的类,名为four;opencv打开和读取摄像头的类,名为video;qt多线程的类,名为videothread。
ui_four.h
/********************************************************************************
** Form generated from reading UI file 'four.ui'
**
** Created by: Qt User Interface Compiler version 5.10.1
**
** WARNING! All changes made in this file will be lost when recompiling UI file!
********************************************************************************/
#ifndef UI_FOUR_H
#define UI_FOUR_H
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
QT_BEGIN_NAMESPACE
class Ui_fourClass
{
public:
QWidget *centralWidget;
QGridLayout *gridLayout_2;
QGridLayout *gridLayout;
QGraphicsView *graphicsView;
QGraphicsView *graphicsView_2;
QGraphicsView *graphicsView_3;
QGraphicsView *graphicsView_4;
QLabel *label;
QSlider *horizontalSlider;
QMenuBar *menuBar;
QToolBar *mainToolBar;
QStatusBar *statusBar;
void setupUi(QMainWindow *fourClass)
{
if (fourClass->objectName().isEmpty())
fourClass->setObjectName(QStringLiteral("fourClass"));
fourClass->resize(648, 556);
centralWidget = new QWidget(fourClass);
centralWidget->setObjectName(QStringLiteral("centralWidget"));
gridLayout_2 = new QGridLayout(centralWidget);
gridLayout_2->setSpacing(6);
gridLayout_2->setContentsMargins(11, 11, 11, 11);
gridLayout_2->setObjectName(QStringLiteral("gridLayout_2"));
gridLayout = new QGridLayout();
gridLayout->setSpacing(6);
gridLayout->setObjectName(QStringLiteral("gridLayout"));
graphicsView = new QGraphicsView(centralWidget);
graphicsView->setObjectName(QStringLiteral("graphicsView"));
gridLayout->addWidget(graphicsView, 0, 0, 1, 1);
graphicsView_2 = new QGraphicsView(centralWidget);
graphicsView_2->setObjectName(QStringLiteral("graphicsView_2"));
gridLayout->addWidget(graphicsView_2, 0, 1, 1, 1);
graphicsView_3 = new QGraphicsView(centralWidget);
graphicsView_3->setObjectName(QStringLiteral("graphicsView_3"));
gridLayout->addWidget(graphicsView_3, 1, 0, 1, 1);
graphicsView_4 = new QGraphicsView(centralWidget);
graphicsView_4->setObjectName(QStringLiteral("graphicsView_4"));
gridLayout->addWidget(graphicsView_4, 1, 1, 1, 1);
label = new QLabel(centralWidget);
label->setObjectName(QStringLiteral("label"));
gridLayout->addWidget(label, 2, 1, 1, 1);
horizontalSlider = new QSlider(centralWidget);
horizontalSlider->setObjectName(QStringLiteral("horizontalSlider"));
horizontalSlider->setOrientation(Qt::Horizontal);
gridLayout->addWidget(horizontalSlider, 3, 0, 1, 1);
gridLayout_2->addLayout(gridLayout, 0, 0, 1, 1);
fourClass->setCentralWidget(centralWidget);
menuBar = new QMenuBar(fourClass);
menuBar->setObjectName(QStringLiteral("menuBar"));
menuBar->setGeometry(QRect(0, 0, 648, 23));
fourClass->setMenuBar(menuBar);
mainToolBar = new QToolBar(fourClass);
mainToolBar->setObjectName(QStringLiteral("mainToolBar"));
fourClass->addToolBar(Qt::TopToolBarArea, mainToolBar);
statusBar = new QStatusBar(fourClass);
statusBar->setObjectName(QStringLiteral("statusBar"));
fourClass->setStatusBar(statusBar);
retranslateUi(fourClass);
QObject::connect(horizontalSlider, SIGNAL(valueChanged(int)), fourClass, SLOT(setslide()));
QMetaObject::connectSlotsByName(fourClass);
} // setupUi
void retranslateUi(QMainWindow *fourClass)
{
fourClass->setWindowTitle(QApplication::translate("fourClass", "four", nullptr));
label->setText(QApplication::translate("fourClass", "TextLabel", nullptr));
} // retranslateUi
};
namespace Ui {
class fourClass: public Ui_fourClass {};
} // namespace Ui
QT_END_NAMESPACE
#endif // UI_FOUR_H
four.h
#pragma once
#include
#include
#include
#include "ui_four.h"
#include "video.h"
#include
#include
using namespace std;
using namespace cv;
class videothread;
class four;
class four : public QMainWindow
{
Q_OBJECT
public:
four(QWidget *parent = Q_NULLPTR);
private:
Ui::fourClass ui;
int slide = 0;
/* 为了在graphview中显示图片,需要创建QGraphicsScene变量
* 这里只显示两个摄像头的视频,因此只创建两个QGraphicsScene变量;多个graphview控件不能使用同一个QGraphicsScene变量,否则会显示不正常
* 网上有些示例代码是在成员方法中创建这个变量,但是由于在显示视频时,需要循环执行显示图像的代码,不断地new会导致程序崩溃
*/
QGraphicsScene *scene_1 = new QGraphicsScene;
QGraphicsScene *scene_2 = new QGraphicsScene;
public slots:
int setslide(); // 这是滑动条的槽函数,不必理会
void show_image_1(QImage dstImage); //分别在两个graphview中显示图像
void show_image_2(QImage dstImage);
};
class videothread : public QThread
{
Q_OBJECT
private:
int cam; //打开摄像头的索引
int order; //对应摄像头要在第几个graphview中进行显示
public:
videothread(int cam, int order);
~videothread();
void run(); //qt多线程类需要重载run函数
/*
* 多线程类中的run函数类似于主线程的main函数,
* 例如创建多线程类对象:videothread th1; th1.start()之后便自动开始执行run函数
*/
signals:
/* 使用多线程可以同时读取两个摄像头的图像
* 但是在进行显示的时候需要操控UI界面中的控件,然而这一操作必须在主线程里执行
* 换言之,子线程不能对UI界面的控件进行修改
* 解决办法是当子线程读取图像以后,向主线程发送信号,并且将图像数据传送过去,然后由主线程中的槽函数执行显示图像的函数
*/
int singal_tomainthread(QImage dstImage);
};
QImage cvMat2QImage(const Mat& mat); //opencv的Mat数据类型转换为qt的QImage数据类型
four.cpp
#include "four.h"
#include
using namespace std;
QImage cvMat2QImage(const cv::Mat& mat)
{
// 8-bits unsigned, NO. OF CHANNELS = 1
if (mat.type() == CV_8UC1)
{
QImage image(mat.cols, mat.rows, QImage::Format_Indexed8);
// Set the color table (used to translate colour indexes to qRgb values)
image.setColorCount(256);
for (int i = 0; i < 256; i++)
{
image.setColor(i, qRgb(i, i, i));
}
// Copy input Mat
uchar *pSrc = mat.data;
for (int row = 0; row < mat.rows; row++)
{
uchar *pDest = image.scanLine(row);
memcpy(pDest, pSrc, mat.cols);
pSrc += mat.step;
}
pSrc = 0;
return image;
}
// 8-bits unsigned, NO. OF CHANNELS = 3
else if (mat.type() == CV_8UC3)
{
// Copy input Mat
const uchar *pSrc = (const uchar*)mat.data;
// Create QImage with same dimensions as input Mat
QImage image(pSrc, mat.cols, mat.rows, mat.step, QImage::Format_RGB888);
pSrc = 0;
return image.rgbSwapped();
}
else if (mat.type() == CV_8UC4)
{
// Copy input Mat
const uchar *pSrc = (const uchar*)mat.data;
// Create QImage with same dimensions as input Mat
QImage image(pSrc, mat.cols, mat.rows, mat.step, QImage::Format_ARGB32);
pSrc = 0;
return image.copy();
}
else
{
return QImage();
}
}
four::four(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
/* 在UI的类的构造函数中创建多线程对象,每一个对象表示一个线程;
* 之后连接多线程对象的信号和UI类中的槽函数
* 最后start()启动子线程
*/
videothread *th1 = new videothread(0, 1); //这里必须用指针,否则构造函数执行完毕以后就会把局部变量删除
QObject::connect(th1, SIGNAL(singal_tomainthread(QImage)),
this, SLOT(show_image_1(QImage)));
th1->start();
videothread *th2 = new videothread(1, 2); //这里必须用指针,否则构造函数执行完毕以后就会把局部变量删除
QObject::connect(th2, SIGNAL(singal_tomainthread(QImage)),
this, SLOT(show_image_2(QImage)));
th2->start();
}
int four::setslide()
{
slide = ui.horizontalSlider->value();
cout << slide;
ui.label->setText(QString::number(slide, 10));
return slide;
}
void four::show_image_1(QImage dstImage)
{
scene_1->clear(); //清空scene_1的内容,否则addPixmap会导致scene_1占用的内存越来越大
scene_1->addPixmap(QPixmap::fromImage(dstImage));
ui.graphicsView->setScene(scene_1);
ui.graphicsView->show();
}
void four::show_image_2(QImage dstImage)
{
scene_2->clear();
scene_2->addPixmap(QPixmap::fromImage(dstImage));
ui.graphicsView_2->setScene(scene_2);
ui.graphicsView_2->show();
}
videothread::videothread(int cam, int order)
{
this->cam = cam;
this->order = order;
}
videothread::~videothread()
{
}
void videothread::run()
{
Camera frame;
frame.setcam(cam); //设置要打开的摄像头的索引
frame.open_camera(); //打开摄像头
if (!frame.isopened()) //如果没有打开摄像头,则退出线程
{
qDebug() << "defeat";
this->quit();
waitKey(1000);
}
while(1) //子线程进入死循环,不断地读取图像并发送信号
{
Mat srcImage = frame.read_frame();
QImage dstImage = cvMat2QImage(srcImage);
qDebug() << srcImage.rows << srcImage.cols << cam << order;
emit singal_tomainthread(dstImage);
msleep(50);
}
}
video.h
#ifndef VIDEO_H
#define VIDEO_H
#include
using namespace cv;
class Camera
{
private:
int camera_num = 0;
Mat img;
VideoCapture capture;
public:
Camera();
Camera(int cam);
void setcam(int cam);
void open_camera();
Mat& read_frame();
bool isopened();
};
#endif
video.cpp
#include "video.h"
#include
Camera::Camera()
{
}
Camera::Camera(int cam)
{
camera_num = cam;
}
void Camera::setcam(int cam)
{
camera_num = cam;
}
void Camera::open_camera()
{
if(!capture.open(camera_num))
std::cout << "open camera defeat." << std::endl;
}
Mat& Camera::read_frame()
{
capture >> img;
return img;
}
bool Camera::isopened()
{
if (capture.isOpened())
return true;
else
return false;
}
main.cpp
#include "four.h"
#include
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
four w;
w.show();
return a.exec();
}