您的位置:首页 > 编程语言 > Qt开发

一个显示 OpenCV Mat 图像的自定义 Qt 控件

2013-11-17 23:14 716 查看
今天学习 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 = 0);
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 = 0; y < src.rows; ++y) {
const cv::Vec3b *srcrow = src[y];
QRgb *destrow = (QRgb*)dest.scanLine(y);
for (int x = 0; x < src.cols; ++x) {
destrow[x] = qRgba(srcrow[x][2], srcrow[x][1], srcrow[x][0], 255);
}
}
}

// 在控件上显示图片,使用 opencv 自带的 resize 使其缩放到和控件大小一致
void QCVDisplay::displayImage(const cv::Mat &img)
{
QSize sz = data.size();
if (img.channels() == 3) {
buffer = img.clone();
} else if (img.channels() == 1) {
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 上点右键,选择 “提升为...”,在弹出的对话框中按照下图输入。



输入完提升的类名称,点击添加,提升即可。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: