Imagelab-0-QT label显示 opencv 图像
开始之前
这其实也是opencv 处理图像的系列, 只是想我们在进一步复杂化我们的代码之前, 每次给出代码我们都要给出很多, 然后窗口的显示上也有很多不必要的东西, 我们为了后面进行更好的算法效果以及算法执行, 我们先规划一下程序, 写出来一个界面程序出来, 这样的话, 我们之后的程序部分只需要给出一个函数的部分就好, 我们的程序算法在增加的时候, 将功能做到一个一个的菜单里面来, 这样一边处理算法, 一边写出界面图像..
目录
正文
我们主要将图形界面部分使用代码来实现, 这样不需要进行编译便能够大概知道结果..
我们在进行复杂的界面之前, 我们先实现一个简单的工程, 能够使用 opencv 读取图片, 然后显示在 qt 的 label 控件 上面,
QT 图像格式
在qt 中提供了几种图像显示的方式,可以看这篇文章关于QPixmap/QImage/QPicture, 详细的介绍了几种格式的使用方法,
QT自带的 QImage
和 QPixmap
, 都是支持读取图像的,可以直接用于显示图像, 但是呢, 我们后续还要进行复杂的算法实现, 所以我们还是要转回到 opencv 的怀抱中来, 那么我们不可避免的需要进行数据图像格式之间的互相转换, 目前大多说使用的方式都是 opencv的 Mat 格式与 QT QPixmap 格式之间的转换, 按后显示到 QT 的label 上面, 我们先来实现一下:
UI 界面设计
这里稍微提一下 QT Designer, 我们可以通过托拽的方式实现界面的设计, 也提供了很多组件让我们选择, 我们先暂时使用这种比较简单的方式进行, 后面逐渐介绍更为复杂的操作.
这里我们使用数字 1,2,3,4, 标记了四个区域, 就是我们常用的区域了
-
- 编辑区域, 可以编辑与托拽, 能够预览
-
- 控件结构树, 各个控件的从属结构, 名称就是
ObjectName
能够在程序使用控件名进行操控
- 控件结构树, 各个控件的从属结构, 名称就是
-
- 属性区域,能够直接调整相关的参数, 也可以在程序中进行调整各种属性
-
- 控件区域, 不同种类的控件, 可以用于托拽, 直接显示在窗口中..
具体的实现方式不用去深究, 且通过托拽改变 .ui
文件, 实际上就是 一个 xml 格式的文件, QT 通过 uic
会将 xxx.ui
转换成 ui_xxx.h
文件, 我们通过引用即可直接操控控件了,
如果我们改变了ui, 但是运行之后没有更新, 在工程山强制 qmake 一下就能解决了
在我们这个工程中, 我们托拽了两个 QLabel
组件和两个 QPushButton
组件, 相应的可以在上图的2 区域看到对象名称..
- MainWindow:
- geometry: 0,0,960,540 : 我们运行的窗口尺寸
- windowsTitle: "ImageLab"
- lb_src:
- geometry: 20,30,400,400 用于指定控件的左上角位置和 尺寸宽高, 我们使用这个参数指定即可
- frameShape: WinPanel
- lb_dst:
- geometry: 470,30,400,400 用于指定控件的左上角位置和 尺寸宽高, 我们使用这个参数指定即可
- frameShape: WinPanel
- btn_test1,btn_test2: 都是默认托拽的 , 尺寸默认, 位置 随意就好, 后面用于我们进行一下测试算法 暂时忽略
- pt_log: 多行文本, 用于显示一些结果信息, 测试过程中的一些输出
我们这个界面也没有布局, 就是很简单的把东西给显示出来, 在编辑之后 按 Shift+Alt+R
能够预览界面,
如果有布局之类的需要及时查看, 我们这里就是简单的ui , 布局什么样 得到的就是什么样子
我们后面的测试可能就是左边显示原始图像, 右边显示运算之后 的图像, 我们来实现一下
这里关于 ui界面的设计 只是稍微提一下, 你们可以直接查看其他的文章介绍的使用方法, 简单点的可以看使用Qt Designer来设计界面和使用Qt Designer创建界面
信号与槽 实现 UI点击事件
在我们进行显示图像之前, 我们稍微介绍一下 QT 的信号与槽的实现方式, QT 最NB 的地方实现了信号与槽 , 简单理解就是, 我们提前将信号与一个槽(函数)声明连接, 然后我们点击一个按钮 会发射一个信号, 然后经过QT的信号处理机制 就能够调用我们提前设定的函数了,
PS: 只是粗略 的这么看就行, 具体还要复杂很多, 后面再说
我们简单实现一下 这个功能, 点击输出我们点击可哪个按钮..
我们点击 测试按钮1: btn_test1
调用一个函数 testFunc1
, 然后在结果框输出点击了按钮1
,
,我们只看 核心的代码部分
// mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
// 设定信号与槽 连接
connect(ui->btn_test1,&QPushButton::clicked,this,&MainWindow::testFunc1);
connect(ui->btn_test2,&QPushButton::clicked,this,&MainWindow::testFunc2);
// 初始化 ui
ui->pt_log->clear(); // 清除框内输出
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::testFunc1(void)
{
ui->pt_log->appendPlainText("你点击了 测试按钮 1 ");
}
void MainWindow::testFunc2(void)
{
ui->pt_log->appendPlainText("你点击了 测试按钮 2");
}
// mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public slots:
void testFunc1(void);
void testFunc2(void);
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
// main.cpp
#include "mainwindow.h"
#include <QApplication>
// 运行主窗口 用于显示界面 ui
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
这里可以看 代码仓库 SChen1024/ImageLab V0.1.0
我们的程序一直是同步提交到 github和gitee 的, 有什么问题可以去看代码
QImage 和 QPixmap 显示图像
上面就是在简单的测试一下, 那我们 现在就开始正式的工作 首先看下直接读取文件的方式,
我们将上一节中 输出语句的函数部分换成加载图片, 能够得到下面的函数部分, 进而运行就能够得到结果图
// 图片路径
QString lena_img = "../testimages/lena.png";
void MainWindow::testFunc1(void)
{
QPixmap pixmap;
pixmap.load(lena_img);
ui->lb_src->setPixmap(pixmap);
ui->pt_log->appendPlainText("左侧使用 QPixmap load 图像数据1 ");
}
void MainWindow::testFunc2(void)
{
QImage image(lena_img);
ui->lb_dst->setPixmap(QPixmap::fromImage(image));
ui->pt_log->appendPlainText("右侧使用 QImage 转换成 QPixmap 进行显示2 ");
}
其实 label 只能显示 pixmap 图像, 而且十分简单操作, 而 QImage 也是转换成 QPixmap 之后才做的显示, 不过在可以去看QImage与QPixmap加载图片 效果 .中介绍了其他的方式显示图像, 我们就不去深究了,
QT显示 opencv mat 图像
终于终于到了我们这篇文章的重点了, 其实经过上面的铺垫, 我们opencv 读取图像之后要做的就是 将mat 图像转换成 QImage或者 QPixmap 图像就好了, 多一步转换过程, 目前没有找到 mat 直接转换成 QPixmap 的方式 , 目前的实现都是 转换成 QImage 然后再转换的方式,
直接搜索 opencv Mat 转 QImage 能找到很多结果, 其实呢 原理都很简单, 根据原始图像的通道数目将图像转换成相应的 QImage 格式, 比如3通道的 rgb 图像转换 QImage image(mat.data, mat.cols, mat.rows,static_cast<int>(mat.step),QImage::Format_RGB888);
我们能够得到这样的结果, 很简单就能实现, 获取图像的宽度, 高度, 以及最重要的 data
也就是图像的数据指针, 然后依次转换成我们需要的 QImage 图像即可, 值得注意的是, opencv 是 BGR图像的顺序, 所以最后要进行颜色通道的转换, 转换成 rgb 不然颜色会有点奇怪..
具体的参数可以参考我之前的博文, 关于 mat 的step 属性可以参考OpenCV中Mat属性step,size,step1,elemSize,elemSize1
这里附上 opencv Mat 与QImage 的互相转换, 这里没有使用 if 为了更好看
/**
* @fn QImage CvMat2QImage(const cv::Mat & mat)
*
* @brief 将opencv mat 转换成 QT image
*
* @author IRIS_Chen
* @date 2019/12/19
*
* @param mat The matrix
*
* @return A QImage
*/
QImage CvMat2QImage(const cv::Mat &mat)
{
// 图像的通道
int channel = mat.channels();
// 设立一个表 直接查询 其中 0 2 是无效值 1 3 4 对应的转换值
const std::map<int, QImage::Format> img_cvt_map {
{ 1, QImage::Format_Grayscale8 },
{ 3, QImage::Format_RGB888 },
{ 4, QImage::Format_ARGB32 }
};
QImage image(mat.data, mat.cols, mat.rows,
static_cast<int>(mat.step),
img_cvt_map.at(channel));
// 三通道图像 值做 通道转换
return channel == 3 ? image.rgbSwapped() : image;
}
/**
* @fn static cv::Mat QImage2CvMat(const QImage &image);
*
* @brief QT Image 转换成 cv Mat 结构
*
* @author IRIS_Chen
* @date 2019/12/19
*
* @param image The image
*
* @return A cv::Mat
*/
cv::Mat QImage2CvMat(const QImage &image)
{
cv::Mat mat;
const std::map<QImage::Format, int> img_cvt_map{
{ QImage::Format_Grayscale8, 1 },
{ QImage::Format_RGB888, 3 },
{ QImage::Format_ARGB32, 4}
};
return cv::Mat(image.height(), image.width(),img_cvt_map.at(image.format()));
}
为了便于区分, 我们在处理图像的时候, 在图上分别显示一个字符串,
// 图片路径
QString lena_img = "../testimages/lena.png";
void MainWindow::testFunc1(void)
{
QPixmap pixmap;
pixmap.load(lena_img);
// 在图上绘制文字
QPainter painter(&pixmap);
painter.setPen(QColor(Qt::yellow));
painter.drawText(100,100,"QT QPixmap");
ui->lb_src->setPixmap(pixmap);
ui->pt_log->appendPlainText("左侧使用 QPixmap load 图像数据1 ");
}
void MainWindow::testFunc2(void)
{
cv::Mat mat = cv::imread("../testimages/lena.png");
// 在图上显示文字
cv::putText(mat,"OpenCV Mat",cv::Point(100,100),cv::FONT_HERSHEY_COMPLEX,1.0, cv::Scalar(0, 255, 255));
QImage image = CvMat2QImage(mat);
ui->lb_dst->setPixmap(QPixmap::fromImage(image));
ui->pt_log->appendPlainText("右侧使用 Mat --> QImage --> QPixmap 进行显示2 ");
}
运行得到的结果图片
opencv 就是 使用 mat 读取图像, 然后 转换成 QImage, 转换通道 ,再转换成 QPixmap 最后显示在 QLabel 上,