OpenCV手拉手01 — 读取图像并显示
OpenCV,开源计算机视觉库,集成了众多优秀的图像处理算法,是从事机器视觉、计算机视觉相关领域开发及研究工作必备技能。
本文使用的开发工具为QT6.4.2、OpenCV4.7.0;
目标:使用OpenCV读取一张图片,并在QT界面上显示。
1、打开QT ,文件–>新建工程–>Application(Qt)–>Qt Widgets Application–>选择
2、输入项目名称—>下一步
3、选择qmake—>下一步
4填写类名,选择base class ,勾选Generate form —>下一步
5、选择语言(可直接点击下一步,或选择自己喜欢的语言)
6、选择套件(推荐使用MSVC64bit)—>下一步
7、点击完成
8、设计UI :双击ui文件,进入设计界面,拖拽控件至相关位置,并给控件命名(命名需具备一定规范,养成良好习惯便于高效开发)
注意:右键单击GraphicView,提升为ImageView类
9、编写ImageView.h文件
#ifndef IMAGEVIEWER_H
#define IMAGEVIEWER_H
#include
#include
#include
#include
#include
#include
#include
#include "opencv2/opencv.hpp"
class ImageViewer : public QGraphicsView
{
Q_OBJECT
public:
// 构造函数 explicit关键字为禁止隐式转换,可参考《Effective C++》
explicit ImageViewer(QWidget *parent = nullptr);
public: // 公共成员函数
void setQImage(QImage); //显示QImage
void setMatImage(cv::Mat ); //显示Mat
void setPixmap(QPixmap); // 显示QPixmap
void resetImage(); //清楚图像
QImage getQImage(); //获取QImage
cv::Mat getMatImage(); // 获取矩阵
QPixmap getPixmap(); // 获取QPixmap
protected: // 保护成员函数
// 重载父类QGraphicsView的成员函数
virtual void wheelEvent(QWheelEvent *event) override; // virtual 虚函数;override 重载
virtual void keyPressEvent(QKeyEvent *event) override;
virtual void mousePressEvent(QMouseEvent *event) override;
virtual void mouseMoveEvent(QMouseEvent *event) override;
virtual void mouseReleaseEvent(QMouseEvent *event) override;
void zoom(QPoint factor); // 缩放
void togglePan(bool pan, const QPoint &startPos = QPoint());
void pan(const QPoint &panTo);
void initShow(); //
private: // 私有成员变量
QPixmap m_image;
bool m_isPan;
QPoint m_prevPan;
QGraphicsScene *scene;
};
#endif // IMAGEVIEWER_H
10、编写ImageView.cpp 文件
#include "imageviewer.h"
#include
ImageViewer::ImageViewer(QWidget *parent) // 构造函数
: QGraphicsView{parent}, // 父类
m_isPan(false), // 私有变量初始化
m_prevPan(0,0),
scene(nullptr)
{
scene = new QGraphicsScene(this);
this->setScene(scene);
setDragMode(QGraphicsView::DragMode::NoDrag); // 拖拽模式
setInteractive(false); // 交互
setEnabled(false);
}
// Mat转QImage
QImage cvMatToQImage(cv::Mat &src)
{
if(src.channels() == 1) { // if grayscale image
return QImage((uchar*)src.data, src.cols, src.rows, static_cast(src.step), QImage::Format_Grayscale8).copy();
}
if(src.channels() == 3) { // if 3 channel color image
cv::Mat rgbMat;
cv::cvtColor(src, rgbMat, cv::COLOR_BGR2RGB); // invert BGR to RGB
return QImage((uchar*)rgbMat.data, src.cols, src.rows, static_cast(src.step), QImage::Format_RGB888).copy();
}
return QImage();
}
//将QImage转化为Mat
cv::Mat qImageToCvMat( const QImage &inImage)
{
switch (inImage.format())
{
case QImage::Format_RGB32:
case QImage::Format_RGB888:
{
QImage swapped = inImage;
if ( inImage.format() == QImage::Format_RGB32 )
{
swapped = swapped.convertToFormat( QImage::Format_RGB888 );
}
cv::Mat matImg = cv::Mat(swapped.height(), swapped.width(),
CV_8UC3,
const_cast(swapped.bits()),
static_cast(swapped.bytesPerLine())
);
cv::cvtColor(matImg,matImg,cv::COLOR_RGB2BGR);
return matImg.clone();
}
case QImage::Format_Indexed8:
{
cv::Mat mat( inImage.height(), inImage.width(),
CV_8UC1,
const_cast(inImage.bits()),
static_cast(inImage.bytesPerLine())
);
return mat.clone();
}
default:
break;
}
return cv::Mat();
}
void ImageViewer::setQImage(QImage image)
{ if(image.isNull())
return;
m_image = QPixmap::fromImage(image);
initShow();
}
void ImageViewer::setMatImage(cv::Mat src)
{
if(src.empty())
return;
m_image = QPixmap::fromImage(cvMatToQImage(src));
initShow();
}
void ImageViewer::setPixmap(QPixmap pixmap)
{
m_image = pixmap.copy();
initShow();
}
void ImageViewer::resetImage()
{
if(m_image.isNull())
return;
scene->clear();
setEnabled(false);
}
QImage ImageViewer::getQImage()
{
return m_image.toImage();
}
cv::Mat ImageViewer::getMatImage()
{
return qImageToCvMat(m_image.toImage());
}
QPixmap ImageViewer::getPixmap()
{
return m_image;
}
void ImageViewer::initShow()
{
setEnabled(true);
setMouseTracking(true);
scene->clear();
scene->addPixmap(m_image);
scene->update();
this->resetTransform();
this->setSceneRect(m_image.rect());
this->fitInView(QRect(0, 0, m_image.width(), m_image.height()), Qt::KeepAspectRatio);
}
// 鼠标点击事件
void ImageViewer::mousePressEvent(QMouseEvent *event)
{
if(event->button() == Qt::LeftButton){
togglePan(true, event->pos());
event->accept();
return;
}
event->ignore();
}
// 鼠标移动事件
void ImageViewer::mouseMoveEvent(QMouseEvent *event)
{
if(m_isPan) {
pan(event->pos());
event->accept();
return;
}
event->ignore();
}
// 鼠标释放事件
void ImageViewer::mouseReleaseEvent(QMouseEvent *event)
{
if(event->button() == Qt::LeftButton){
togglePan(false);
event->accept();
return;
}
event->ignore();
}
// 缩放
void ImageViewer::zoom(QPoint factor)
{
QRectF FOV = this->mapToScene(this->rect()).boundingRect();
QRectF FOVImage = QRectF(FOV.left(), FOV.top(), FOV.width(), FOV.height());
float scaleX = static_cast(m_image.width()) / FOVImage.width();
float scaleY = static_cast(m_image.height()) / FOVImage.height();
float minScale = scaleX > scaleY ? scaleY : scaleX;
float maxScale = scaleX > scaleY ? scaleX : scaleY;
if ((factor.y() > 0 && minScale > 100) || (factor.y() < 0 && maxScale 0)
scale(1.2, 1.2);
else
scale(0.8, 0.8);
}
//键盘按键事件
void ImageViewer::keyPressEvent(QKeyEvent *event)
{
if(event->key() == Qt::Key_O) {
this->resetTransform();
this->setSceneRect(m_image.rect());
this->fitInView(QRect(0, 0, m_image.width(), m_image.height()), Qt::KeepAspectRatio);
}
}
void ImageViewer::pan(const QPoint &panTo)
{
auto hBar = horizontalScrollBar();
auto vBar = verticalScrollBar();
auto delta = panTo - m_prevPan;
m_prevPan = panTo;
hBar->setValue(hBar->value() - delta.x());
vBar->setValue(vBar->value() - delta.y());
}
// 滚轮事件
void ImageViewer::wheelEvent(QWheelEvent *event)
{
if(m_image.isNull())
return;
QPoint numDegrees = event->angleDelta() / 8;
if (!numDegrees.isNull()) {
QPoint numSteps = numDegrees / 15;
zoom(numSteps);
}
event->accept();
}
void ImageViewer::togglePan(bool pan, const QPoint &startPos)
{
if(pan){
if(m_isPan) {
return;
}
m_isPan = true;
m_prevPan = startPos;
setCursor(Qt::ClosedHandCursor);
} else {
if(!m_isPan) {
return;
}
m_isPan = false;
m_prevPan = QPoint(0,0);
setCursor(Qt::ArrowCursor);
}
}
11、编写read_and_show_img.h文件
#ifndef READANDSHOWIMG_H
#define READANDSHOWIMG_H
#include
#include "ImageViewer.h"
QT_BEGIN_NAMESPACE
namespace Ui { class ReadAndShowImg; }
QT_END_NAMESPACE
class ReadAndShowImg : public QMainWindow
{
Q_OBJECT
public:
ReadAndShowImg(QWidget *parent = nullptr);
~ReadAndShowImg();
private slots:
void on_btn_filePath_clicked();
void on_btn_readAndShow_clicked();
private:
Ui::ReadAndShowImg *ui;
QString filePath;
// ImageViewer * m_view;
};
#endif // READANDSHOWIMG_H
12、编写read_and_show_img.cpp文件
#include "read_and_show_img.h"
#include "ui_read_and_show_img.h"
#include
#include "opencv2/opencv.hpp"
ReadAndShowImg::ReadAndShowImg(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::ReadAndShowImg)
{
ui->setupUi(this);
setWindowTitle("读取并显示图片"); // 注意这一句要在setupUi之后,否则不起效果
}
ReadAndShowImg::~ReadAndShowImg()
{
delete ui;
}
void ReadAndShowImg::on_btn_filePath_clicked()
{
filePath = QFileDialog::getOpenFileName(this,"open file dialog","E:\Img\","JPG(*.jpg);;PNG(*.png);;BMP(*.jpeg)");
ui->let_filePath->setText(filePath);
}
void ReadAndShowImg::on_btn_readAndShow_clicked()
{
cv::Mat src = cv::imread(filePath.toStdString(),1);
ui->gpv_img->setMatImage(src);
}
13、main文件保持原样
#include "read_and_show_img.h"
#include
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
ReadAndShowImg w;
w.show();
return a.exec();
}
完成以上文件的编写,即可运行程序。ImageView控件支持图像的缩放,拖拽等操作。
运行效果:
注意,为图省事,本文中的ImageView类摘自互联网,原文链接为:
【[Qt]基于QGraphicsView的图像显示控件,支持放大、缩小、鼠标拖动】_qt 图像控件_xiaohuihuihuige的博客-CSDN博客