今天学习 Qt 的时候顺手写了一个,包含一个头文件 qcvdisplay.h 和一个源文件 qcvdisplay.cpp,因为这是 qt 默认的文件命名方式,在 Qt Designer 中提升控件时会省去输入文件名的步骤,所以最好不要改名。

qcvdisplay.h :

#ifndef QCVDISPLAY_H
#define QCVDISPLAY_H #include <QWidget>
#include <QException>
#include <opencv2/core/core.hpp> // 当图片不是灰度图或者 BGR 图像时抛出此异常
class UnsupportedFormatError : public QException
{
public:
void raise() const { throw *this; }
UnsupportedFormatError *clone() const { return new UnsupportedFormatError(*this); }
}; // 显示 opencv 图片的自定义控件
class QCVDisplay : public QWidget
{
Q_OBJECT
public:
explicit QCVDisplay(QWidget *parent = );
cv::Mat getBuffer(); public slots:
void displayImage(const cv::Mat& img); protected:
void matToQImage(const cv::Mat3b &src, QImage &dest);
void paintEvent(QPaintEvent *event);
void resizeEvent(QResizeEvent *event);
QImage data;
cv::Mat buffer;
}; #endif // QCVDISPLAY_H

qcvdisplay.cpp

#include "qcvdisplay.h"

#include <QResizeEvent>
#include <QPaintEvent>
#include <QPainter> #include <opencv2/imgproc/imgproc.hpp> QCVDisplay::QCVDisplay(QWidget *parent) :
QWidget(parent)
{
} cv::Mat QCVDisplay::getBuffer()
{
return buffer;
} // 将 Mat 转换为 QImage,由于 QImage 的每一行有多余对齐字节
// 故采用 RBG32 来消除多余字节
void QCVDisplay::matToQImage(const cv::Mat3b &src, QImage& dest)
{
for (int y = ; y < src.rows; ++y) {
const cv::Vec3b *srcrow = src[y];
QRgb *destrow = (QRgb*)dest.scanLine(y);
for (int x = ; x < src.cols; ++x) {
destrow[x] = qRgba(srcrow[x][], srcrow[x][], srcrow[x][], );
}
}
} // 在控件上显示图片,使用 opencv 自带的 resize 使其缩放到和控件大小一致
void QCVDisplay::displayImage(const cv::Mat &img)
{
QSize sz = data.size();
if (img.channels() == ) {
buffer = img.clone();
} else if (img.channels() == ) {
cv::cvtColor(img, buffer, CV_GRAY2RGB);
} else {
throw UnsupportedFormatError();
}
cv::Mat resized;
if (!sz.isEmpty()) {
cv::resize(buffer, resized, cv::Size(sz.width(), sz.height()));
matToQImage(resized, data);
update();
}
} // 绘图事件处理函数
void QCVDisplay::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.drawImage(event->rect(), data, event->rect());
} // 缩放事件也采用 opencv 自带的 resize
void QCVDisplay::resizeEvent(QResizeEvent *event)
{
if (data.size() != event->size()) {
cv::Mat resized;
data = QImage(event->size(), QImage::Format_RGB32);
if (!buffer.empty() && !event->size().isEmpty()) {
cv::resize(buffer, resized, cv::Size(event->size().width(),
event->size().height()));
matToQImage(resized, data);
}
}
QWidget::resizeEvent(event);
}

实现的思路很直接,重写 paintEvent 和 resizeEvent 两个事件处理函数来进行控件的自定义显示,用一个 Mat 作为原始图片的缓存(buffer),将其调整到与空间大小一致后再显示。QImage 的格式选择 RGB32 (第一个字节为 0xFF),使得图像每一行的像素个数全部都是 4 的倍数,消除多余的对齐像素,避免图片在显示时变形。(OpenCV 2.0 以后的图片已经不存在对齐像素了,即图像数据在内存中是连续的)

使用时将两个文件加入当前项目,在 Qt Designer 中拖入一个 widget 基类,在 widget 上点右键,选择 “提升为...”,在弹出的对话框中按照下图输入。

输入完提升的类名称,点击添加,提升即可。

一个显示 OpenCV Mat 图像的自定义 Qt 控件的更多相关文章

  1. 【Android开发日记】之入门篇(十四)——Button控件+自定义Button控件

        好久不见,又是一个新的学期开始了,为什么我感觉好惆怅啊!这一周也发生了不少事情,节假日放了三天的假(好久没有这么悠闲过了),实习公司那边被组长半强制性的要求去解决一个后台登陆的问题,结果就是把 ...

  2. 自定义SWT控件一之自定义单选下拉框

    一.自定义下拉控件 自定义的下拉框,是自定义样式的,其中的下拉框使用的是独立的window,非复选框的下拉框双击单机其它区域或选择完之后,独立window构成的下拉框会自动消失. package co ...

  3. Android自定义控件之自定义组合控件

    前言: 前两篇介绍了自定义控件的基础原理Android自定义控件之基本原理(一).自定义属性Android自定义控件之自定义属性(二).今天重点介绍一下如何通过自定义组合控件来提高布局的复用,降低开发 ...

  4. C# DataGridView自定义分页控件

    好些日子不仔细写C#代码了,现在主要是Java项目,C#.Net相关项目不多了,有点手生了,以下代码不足之处望各位提出建议和批评. 近日闲来无事想研究一下自定义控件,虽然之前也看过,那也仅限于皮毛,粗 ...

  5. C# 自定义FileUpload控件

    摘要:ASP.NET自带的FileUpload控件会随着浏览器的不同,显示的样式也会发生改变,很不美观,为了提高用户体验度,所以我们会去自定义FileUpload控件 实现思路:用两个Button和T ...

  6. (九)ASP.NET自定义用户控件(2)

    http://www.cnblogs.com/SkySoot/archive/2012/09/04/2670678.html 用户控件 在 .NET 里,可以通过两种方式把自己的控件插入到 Web 窗 ...

  7. (八)ASP.NET自定义用户控件(1)

    http://blog.csdn.net/laodao1/article/details/5897366 ASP.NET自定义控件组件开发 第一章:从一个简单的控件谈起 起始开发ASP.NET自定义控 ...

  8. 【转】带checkbox的ListView实现(二)——自定义Checkable控件的实现方法

    原文网址:http://blog.csdn.net/harvic880925/article/details/40475367 前言:前一篇文章给大家展示了传统的Listview的写法,但有的时候我们 ...

  9. Qt控件精讲一:按钮

    原地址:http://blog.csdn.net/yuxikuo_1/article/details/17397109 Qt Creater提供6种Button控件.如图1. Button控件介绍 控 ...

随机推荐

  1. Servlet请求参数的方式

    今天整理了以下几种常用的Servlet请求参数的方式,下面简单地介绍 1)getParameter(String key)返回一个字符串,获得name和key 一样的表单控件的数据,如果有重复的nam ...

  2. 关于ArrayList的越界问题?

    大家都知道 ArrayList是自动扩容的. 那为什么会存在越界问题? 话不多说 上代码 package test; import java.util.ArrayList; public class ...

  3. 精简版 Selenium PageFactory, Annotation 实例

    精简版 Selenium  PageFactory, Annotation 实例. 先是类: HomePage package com.test;import org.openqa.selenium. ...

  4. RN的win7开发环境部署和问题解决

    1安装node,配置环境变量 2.安装Android studioe,配置环境变量 3.安装python2 注意 Node 的版本必须高于 8.3,Python 的版本必须为 2.x(不支持 3.x) ...

  5. Xmanager power suit 6 最新版注册激活附注册机

    转自:https://blog.csdn.net/the_liang/article/details/82708907 Xmanager Power Suit 6.0.0109 最新版注册激活,长期更 ...

  6. KNN原理小结

    K近邻法(K-nearest neighbors,KNN)既可以分类,也可以回归. KNN做回归和分类的区别在于最后预测时的决策方式.KNN做分类时,一般用多数表决法:KNN做回归时,一般用平均法. ...

  7. TCP/IP,Http,Socket的区别

    大学学习网络基础的时候老师讲过,网络由下往上分为物理层.数据链路层.网络层.传输层.会话层.表示层和应用层.通过初步的了解,我知道IP协议对应于网络层,TCP协议对应于传输层,而HTTP协议对应于应用 ...

  8. Jmeter(八)乱码处理

    发贴的内容和标题在进行参数化之后, 由于包含中文, 情理之中地出现了乱码(得意地笑, 坐等你跳坑中.)

  9. Selenium 2自动化测试实战15(多表单切换)

    一.多表单切换 在web应用中经常会遇到frame/iframe表单嵌套页面的应用,WebDriver只能在一个页面上对元素识别与定位,对于frame/iframe表单内嵌页面上的元素无法直接定位.这 ...

  10. TensorFlow基础总结

    1.基础概念 Tensor:类型化的多维数组,图的边:Tensor所引用的并不持有具体的值,而是保持一个计算过程,可以使用session.run()或者t.eval()对tensor的值进行计算. O ...