Imagelab-0-QT label显示 opencv 图像
Imagelab-0-QT label显示 opencv 图像
这其实也是opencv 处理图像的系列, 只是想我们在进一步复杂化我们的代码之前, 每次给出代码我们都要给出很多, 然后窗口的显示上也有很多不必要的东西, 我们为了后面进行更好的算法效果以及算法执行, 我们先规划一下程序, 写出来一个界面程序出来, 这样的话, 我们之后的程序部分只需要给出一个函数的部分就好, 我们的程序算法在增加的时候, 将功能做到一个一个的菜单里面来, 这样一边处理算法, 一边写出界面图像..
目录我们主要将图形界面部分使用代码来实现, 这样不需要进行编译便能够大概知道结果..
我们在进行复杂的界面之前, 我们先实现一个简单的工程, 能够使用 opencv 读取图片, 然后显示在 qt 的 label 控件 上面,
QT 图像格式在qt 中提供了几种图像显示的方式,可以看这篇文章关于QPixmap/QImage/QPicture, 详细的介绍了几种格式的使用方法,
QT自带的 QImage
和 QPixmap
, 都是支持读取图像的,可以直接用于显示图像, 但是呢, 我们后续还要进行复杂的算法实现, 所以我们还是要转回到 opencv 的怀抱中来, 那么我们不可避免的需要进行数据图像格式之间的互相转换, 目前大多说使用的方式都是 opencv的 Mat 格式与 QT QPixmap 格式之间的转换, 按后显示到 QT 的label 上面, 我们先来实现一下:
这里稍微提一下 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加载图片 效果 .中介绍了其他的方式显示图像, 我们就不去深究了,
终于终于到了我们这篇文章的重点了, 其实经过上面的铺垫, 我们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 上,
其他