本文译自:http://www.robot-home.it/blog/en/software/tutorial-opencv-qt-opengl-widget-per-visualizzare-immagini-da-opencv-in-una-gui-con-qt/

重要术语保持英文不变,如Widget等。原文中rendering意为渲染或绘制。


此教程是关于在Qt图形界面中显示OpenCV图像的问题,我们创建了一个基于QGLWidget的Qt Widget。

这个Widget提供了更好的图像绘制性能,并支持在缩放窗口时固定高宽比。

此教程假定读者掌握关于C++、Qt 5 框架、OpenCV 2库在Qt Creator中开发环境的配置的基础知识。实例代码是在Windows 7下设计的,但也能在不需大幅改动的前提下移植到Linux和MacOS中,只需配置正确的库文件路径即可。

第一部分描述了Widget的创建。

CQtOpenCVViewerGl 类的创建:

在 QtCreator 中:

  • 文件-> 新建文件或工程 -> C++ -> C++ Class -> Choose
  • 类名: CQtOpenCVViewerGl
  • 基类: QGLWidget  [注意字母大小写]

译者注:从Qt 5.4版本开始,QGLWidget已更新为QOpenGLWidget。

到这一步你应该在工程中建立了两个新的文件,cqtopencvviewergl.cpp 和 cqtopencvviewergl.h

打开 cqtopencvviewergl.h 并添加几个私有成员变量:

private:
bool mSceneChanged; /// Indicates when OpenGL view is to be redrawn
QImage mRenderQtImg; /// Qt image to be rendered
cv::Mat mOrigImage; /// original OpenCV image to be shown
QColor mBgColor; /// Background color
int mOutH; /// Resized Image height
int mOutW; /// Resized Image width
float mImgratio; /// height/width ratio
int mPosX; /// Top left X position to render image in the center of widget
int mPosY; /// Top left Y position to render image in the center of widget
  • mSceneChanged: 指示图像是否需要绘制
  • mRenderQtImg: 包含需要绘制的图像 (Qt格式)
  • mOrigImage: 包含需要绘制的图像 (OpenCV格式)
  • mBgColor: 不显示图像的区域的背景色
  • mOutH & mOutW: 图像真实绘制尺寸
  • mImgratio: 包含有关图像大小比例的信息
  • mPosX & mPosY: 左上角开始的图像坐标,允许在窗口部件的中心绘制图像

至此我们还需要一些函数,首先我们添加一个信号函数(signal)和一个槽函数(slot):

signals:
void imageSizeChanged( int outW, int outH ); /// Used to resize the image outside the widget
public slots:
bool showImage( cv::Mat image ); /// Used to set the image to be viewed
  • void imageSizeChanged( int outW, int outH ): 这是一个用来在“Qt世界中"交流"的函数,它能够发出widget大小发生变化的消息,还有变化后的尺寸大小(outW、outH参数)。如果我们想向Widget传递一个缩放过的图像,而不想迫使Widget在每次渲染时都进行缩放操作的话,这个函数很有用。
  • bool showImage( cv::Mat image ): 这个函数向窗口传递需要绘制的图像。这是一个槽函数,是信号函数的响应。

最后,添加五个函数用来绘制图像:

protected:
void initializeGL(); /// OpenGL initialization
void paintGL(); /// OpenGL Rendering
void resizeGL(int width, int height); /// Widget Resize Event
void updateScene(); /// Forces a scene update
void renderImage(); /// Render image on openGL frame
  • void initializeGL(): 初始化openGL
  • void paintGL(): 使用 openGL 绘制
  • void resizeGL(int width, int height): 使用 openGL 调整尺寸
  • void updateScene(): 强制绘制
  • void renderImage(): 被paintGL调用,用来高效地绘制图像

现在,是时候在 cqtopencvviewergl.cpp 中进行变量初始化和函数实现了。

构造函数:

CQtOpenCVViewerGl::CQtOpenCVViewerGl(QWidget *parent) :
QGLWidget(parent)
{
mSceneChanged = false;
mBgColor = QColor::fromRgb(150, 150, 150); mOutH = 0;
mOutW = 0;
mImgratio = 4.0f/3.0f; // Default image ratio mPosX = 0;
mPosY = 0;
}

其他函数:

void CQtOpenCVViewerGl::initializeGL()
{
makeCurrent();
qglClearColor(mBgColor.darker());
}

设定背景色,这个函数如同下一个一样,从 makeCurrent 函数开始,这个函数允许图形界面使用多于一个的OpenGL部件。

void CQtOpenCVViewerGl::resizeGL(int width, int height)
{
makeCurrent();
glViewport(0, 0, (GLint)width, (GLint)height); glMatrixMode(GL_PROJECTION);
glLoadIdentity(); glOrtho(0, width, 0, height, 0, 1); // To Draw image in the center of the area glMatrixMode(GL_MODELVIEW); // ---> Scaled Image Sizes
mOutH = width/mImgratio;
mOutW = width; if(mOutH>height)
{
mOutW = height*mImgratio;
mOutH = height;
} emit imageSizeChanged( mOutW, mOutH );
// < --- Scaled Image Sizes mPosX = (width-mOutW)/2;
mPosY = (height-mOutH)/2; mSceneChanged = true; updateScene();
}

这个函数在每次Widget大小变化时被调用:

  • 3-12行: OpenGL 布局初始化
  • 14-21行: 计算图像大小,维持高宽比
  • 23行: 发出 “imageSizeChanged” 信号
  • 26-27行: 计算图像左上角的坐标,使得图像位于窗口中间
  • 29-31行: 使Widget知道有一张新的图像要显示
void CQtOpenCVViewerGl::updateScene()
{
if( mSceneChanged && this->isVisible() )
updateGL();
}

updateScene 函数被用来在图像更新之后 “强制” 绘制图像。 只在确定需要时才调用 updateGL 绘制图像。

void CQtOpenCVViewerGl::paintGL()
{
makeCurrent(); if( !mSceneChanged )
return; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); renderImage(); mSceneChanged = false;
}

paintGL 是当OpenGL Widget等待更新时被调用的一个函数。在这里,这个函数清除现有图像并调用renderImage 来高效地绘制图像。

void CQtOpenCVViewerGl::renderImage()
{
makeCurrent(); glClear(GL_COLOR_BUFFER_BIT); if (!mRenderQtImg.isNull())
{
glLoadIdentity(); QImage image; // the image rendered glPushMatrix();
{
int imW = mRenderQtImg.width();
int imH = mRenderQtImg.height(); // The image is to be resized to fit the widget?
if( imW != this->size().width() &&
imH != this->size().height() )
{ image = mRenderQtImg.scaled( //this->size(),
QSize(mOutW,mOutH),
Qt::IgnoreAspectRatio,
Qt::SmoothTransformation
); //qDebug( QString( "Image size: (%1x%2)").arg(imW).arg(imH).toAscii() );
}
else
image = mRenderQtImg; // --->Centering image in draw area glRasterPos2i( mPosX, mPosY );
// < --- Centering image in draw area imW = image.width();
imH = image.height(); glDrawPixels( imW, imH, GL_RGBA, GL_UNSIGNED_BYTE, image.bits());
}
glPopMatrix(); // end
glFlush();
}
}

renderImage 是widget中一个主要的函数,事实上是它绘制了图像。

  • 19-32行: 这几行用来调整图像大小,并保证此时图像是一直可见的。注意图像大小是否正确,过于繁重的操作是不会被处理的。我在这一段插入了imageSizeChanged 信号,使得其他GUI函数仅在原始图像创建时进行调整大小的操作,而不是在每一次绘制图像时都去处理
  • 36行: 指示图像左上角坐标,使得图像居中显示
  • 42行: 利用OpenGL 的 glDrawPixels 函数进行真正的绘制
bool CQtOpenCVViewerGl::showImage( cv::Mat image )
{
image.copyTo(mOrigImage); mImgratio = (float)image.cols/(float)image.rows; if( mOrigImage.channels() == 3)
mRenderQtImg = QImage((const unsigned char*)(mOrigImage.data),
mOrigImage.cols, mOrigImage.rows,
mOrigImage.step, QImage::Format_RGB888).rgbSwapped();
else if( mOrigImage.channels() == 1)
mRenderQtImg = QImage((const unsigned char*)(mOrigImage.data),
mOrigImage.cols, mOrigImage.rows,
mOrigImage.step, QImage::Format_Indexed8);
else
return false; mRenderQtImg = QGLWidget::convertToGLFormat(mRenderQtImg); mSceneChanged = true; updateScene(); return true;
}

最后的函数也很重要,这个函数需要告诉widget哪个图像要被绘制,showImage 函数工作在单通道或三通道的8 bit OpenCV (cv::Mat) 格式下,并且把图像转换成QGLWidget要用的格式。

这个函数还计算并存储了图像的高宽比(第5行)。


教程第一部分结束。

示例代码可到Github上下载。

*https://github.com/Myzhar/QtOpenCVViewerGl*

[OpenCV Qt教程] 在Qt图形界面中显示OpenCV图像的OpenGL Widget (第一部分)的更多相关文章

  1. [OpenCV Qt教程] 在Qt图形界面中显示OpenCV图像的OpenGL Widget(第二部分)

    本文译自:http://www.robot-home.it/blog/en/software/tutorial-opencv-qt-opengl-widget-per-visualizzare-imm ...

  2. 【OpenCV新手教程之十五】水漫金山:OpenCV漫水填充算法(Floodfill)

    本系列文章由@浅墨_毛星云 出品,转载请注明出处.    文章链接: http://blog.csdn.net/poem_qianmo/article/details/28261997 作者:毛星云( ...

  3. 布局(layout)文件图形界面不能显示:An error has occurred. See error log for more details. java.lang.NullPointe

    #问题解析# Android工程中Layout文件夹下的布局文件图形界面无法显示,一般发生这种情况在导入工程操作后极易出现,因为可能eclipse使用的sdk版本不同,target类型不同,所用And ...

  4. mysql数据库导出模型到powerdesigner,PDM图形窗口中显示数据列的中文注释

    1,mysql数据库导出模型到powerdesigner 2,CRL+Shift+X 3,复制以下内容,执行 '******************************************** ...

  5. 转 在PowerDesigner的PDM图形窗口中显示数据列的中文注释

    Name是名称(字段描述),Code是字段名称,Comment是注释名称,ER图中显示的是Name.一般设计时,Name跟comment都设计成描述, 而设计时候常把comment写成中文,name保 ...

  6. PyQt(Python+Qt)实现的GUI图形界面应用程序的事件捕获方法大全及对比分析

    一. 概述 PyQt的图形界面应用中,事件处理类似于Windows系统的消息处理.一个带图形界面的应用程序启动后,事件处理就是应用的主循环,事件处理负责接收事件.分发事件.接收应用处理事件的返回结果, ...

  7. OpenCV - Linux(Ubuntu 16.04)中安装OpenCV + OpenCV_Contrib

    近两个月来接触了Linux系统,在老板的建议下翻了Ubuntu的牌子,我安装的版本是16.04,用习惯之后感觉蛮好的,比Windows要强.好啦,废话不说啦,下面开始说在Ubuntu中安装OpemCV ...

  8. 将图片在指定窗口中显示-OpenCV应用学习笔记一

    1.OpenCV模块划分 OpenCV其实就是一堆用C和C++语言来实现计算机视觉算法的源代码文件:例如C接口函数cvCany()实现了Canny边缘提取算法,我们可以直接将这些源代码添加到自己的软件 ...

  9. 关于web2py外网访问,图形界面不显示等问题的解决办法

    首先系统版本是ubuntu 15.04,系统默认安装了两个版本的python, sudo python web2py.py 默认会调用python2.7版本来执行 会提示 pydo@planpls:/ ...

随机推荐

  1. python 多维list声明时的小问题

    a=[[]]*3 a Out[18]: [[], [], []] a[0].append(1) a Out[20]: [[1], [1], [1]] b=[[] for _ in range(3)] ...

  2. Spring之基本关键策略

    目的 为了简化Java开发. 策略 基于POJO(普通Java类)的轻量级和最小侵入性编程: 通过依赖注入(DI)和面向接口实现松耦合: 基于切面和惯例进行声明式编程: 通过切面和模板减少样板式代码. ...

  3. sync-settings(vscode)

    vscode插件以及设置 sync-download e45c6db33cd91d661e0cc545efb6817c

  4. linux自学(三)之开启虚拟机

    上一篇:linux自学(二)之centos7镜像安装 前面都已经铺垫好了,下面就是开始安装镜像了,真正的开启一台linux机器. 点击开启此虚拟机,如图: 之后就是等待了,然后就是开始配置,首先如下图 ...

  5. 使用Session防止表单重复提交(不考虑多服务器)

    在平时开发中,如果网速比较慢的情况下,用户提交表单后,发现服务器半天都没有响应,那么用户可能会以为是自己没有提交表单,就会再点击提交按钮重复提交表单,我们在开发中必须防止表单重复提交. 原理:  1, ...

  6. callback回调函数-python

    链接:http://www.zhihu.com/question/19801131/answer/27459821来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 编程分 ...

  7. nginx 获取请求头,URL参数

    获取url参数 在 ngx_lua 中访问 Nginx 内置变量 ngx.var.arg_PARAMETER 即可获得GET参数PARAMETER的内容. 在 nginx配置中,通过$arg_PARA ...

  8. [LeetCode系列]卡特兰数(Catalan Number) 在求解独特二叉搜寻树(Unique Binary Search Tree)中的应用分析

    本文原题: LeetCode. 给定 n, 求解独特二叉搜寻树 (binary search trees) 的个数. 什么是二叉搜寻树? 二叉查找树(Binary Search Tree),或者是一棵 ...

  9. Sqlite基础简介

    1.什么是SQLite ? -> SQLite简介 SQLite 是一个进程内的库,是一种轻量级的.自给自足的.无服务器的.无需配置的,事务性的SQL数据库引擎.和他其他的数据库一样,SQLit ...

  10. bzoj1040(ZJOI2008)骑士——基环树

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1040 基环树的模板. 套路就是把环断开,先把一端作为根节点,强制不选:再把另一端作为根节点, ...