zoukankan      html  css  js  c++  java
  • 一个显示 OpenCV Mat 图像的自定义 Qt 控件

    今天学习 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 上点右键,选择 “提升为...”,在弹出的对话框中按照下图输入。

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

  • 相关阅读:
    yii模板中常用变量总结
    Yii CDbCriteria的常用方法总结
    Yii框架Yiiapp()的理解
    Yii 中比较常用的rules验证规则记录
    c++,opencv播放视频
    python--输入一组无序的数,排序
    python入门,猜数
    关于爬楼梯问题的斐波那契数列
    获取文件名字,路劲中的某一部分信息
    简单帧差法
  • 原文地址:https://www.cnblogs.com/heleifz/p/qt-opencv-widget.html
Copyright © 2011-2022 走看看