加快BITMAP像素访问速度,取代GetPixel

16 篇文章 1 订阅

https://blog.csdn.net/qq_44575789/article/details/127935697像素处理太慢,这里换种方式

#pragma once
#include <QDebug>
#include <QImage>
#include <QTimer>
#include <qwindowdefs.h>
#include <wtypes.h>
// opencv
#include <opencv2/core/core.hpp>
#include <opencv2/opencv.hpp>

template <typename T>
// 限定cv::Mat类型
concept MatType = std::is_same_v<T, cv::Mat>;

template <typename T>
// 限定QImage类型
concept QImageType = std::is_same_v<T, QImage>;

class WindowImageVideo : public QObject {
    Q_OBJECT;

private:
    // 窗口句柄
    WId _wid;
    // 帧定时器
    QTimer* timer = nullptr;
    // 刷新率
    int _fps = 30;
    // 视频编码器
    cv::VideoWriter writer;
    // 写入路径
    QString _path;
    // 图片缓冲区
    uchar* bits = nullptr;

public:
    void openVideo()
    {
        if (writer.isOpened()) {
            return;
        }
        // 视频帧率
        double fps = _fps;
        auto size = getWindowSize();
        // 打开视频文件
        bool s = writer.open(_path.toStdString(), cv::VideoWriter::fourcc('I', '4', '2', '0'), fps, cv::Size(size.width(), size.height()));
        qDebug() << (s ? "打开视频成功" : "打开视频失败");
    }

    // opencv图片写入视频
    void writeVideo(cv::Mat& mat)
    {
        qDebug() << "New Frame";
        // 写入视频
        writer << mat;
    }

    void releaseVideo()
    {
        if (writer.isOpened()) {
            qDebug() << "Close Video";
            // 关闭视频文件
            writer.release();
        }
    }

    void initBuff(size_t size)
    {
        if (bits == nullptr) {
            bits = new uchar[size];
        }
    }

    void releaseBuff()
    {
        if (bits != nullptr) {
            delete[] bits;
            bits = nullptr;
        }
    }

public:
signals:
    void started();
signals:
    void stoped();

public slots:
    void start()
    {
        if (timer != nullptr && timer->isActive()) {
            return;
        }
        openVideo();
        timer = new QTimer(this);
        QObject::connect(timer, &QTimer::timeout, this, [this]() {
            // 计时
            auto start = std::chrono::high_resolution_clock::now();
            cv::Mat mat;
            getGDIBitmap(mat);
            // 结束
            auto end = std::chrono::high_resolution_clock::now();
            auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
            qDebug() << "GetGDIBitmap:" << duration.count() << "ms";
            writeVideo(mat);
        });
        timer->start(1000 / _fps);
    }

    void stop()
    {
        if (timer != nullptr && timer->isActive()) {
            // 停止定时器
            timer->stop();
            releaseVideo();
        }
    }

public:
    // 获取窗口大小
    QSize getWindowSize()
    {
        // 获取窗口大小
        RECT rect;
        GetWindowRect((HWND)_wid, &rect);
        return QSize(rect.right - rect.left, rect.bottom - rect.top);
    }

    template <MatType T>
    T bitmapTo(HBITMAP& hBitmap)
    {
        return BitmapToMat(hBitmap);
    }

    template <QImageType T>
    T bitmapTo(HBITMAP& hBitmap)
    {
        return bitmapToQImage(hBitmap);
    }

    // 获取窗体句柄窗口的图像
    template <typename T>
    void getGDIBitmap(T& image)
    {
        // 获取窗口大小
        RECT rect;
        GetWindowRect((HWND)_wid, &rect);
        int width = rect.right - rect.left;
        int height = rect.bottom - rect.top;
        // 创建设备上下文
        HDC hdc = GetDC((HWND)_wid);
        HDC hdcMem = CreateCompatibleDC(hdc);
        // 创建位图
        HBITMAP hBitmap = CreateCompatibleBitmap(hdc, width, height);
        // 选择位图
        HBITMAP hOldBitmap = (HBITMAP)SelectObject(hdcMem, hBitmap);
        // 拷贝图像
        BitBlt(hdcMem, 0, 0, width, height, hdc, 0, 0, SRCCOPY);
        // 释放资源
        SelectObject(hdcMem, hOldBitmap);
        DeleteDC(hdcMem);
        ReleaseDC((HWND)_wid, hdc);
        // 转换为image
        image = bitmapTo<T>(hBitmap);
        DeleteObject(hBitmap);
    }

    // return: W H
    QPair<size_t, size_t> updateBitmapToBits(HBITMAP& hBitmap)
    {
        BITMAP bitmap {};
        GetObject(hBitmap, sizeof(BITMAP), &bitmap);
        BITMAPINFOHEADER bih {};
        bih.biSize = sizeof(BITMAPINFOHEADER);
        bih.biWidth = bitmap.bmWidth;
        bih.biHeight = bitmap.bmHeight;
        bih.biPlanes = 1;
        bih.biBitCount = 32;
        bih.biCompression = BI_RGB;
        bih.biSizeImage = 0;
        bih.biXPelsPerMeter = 0;
        bih.biYPelsPerMeter = 0;
        bih.biClrUsed = 0;
        bih.biClrImportant = 0;
        if (bits == nullptr) {
            initBuff(static_cast<size_t>(bih.biWidth) * bih.biHeight * 4);
        }
        HDC hdc = GetDC((HWND)_wid);
        GetDIBits(hdc, hBitmap, 0, bih.biHeight, bits, (BITMAPINFO*)&bih, DIB_RGB_COLORS);
        ReleaseDC((HWND)_wid, hdc);
        return { bih.biWidth, bih.biHeight };
    }

    //将HBITMAP转为QImage
    QImage bitmapToQImage(HBITMAP& hBitmap)
    {
        auto&& wh = updateBitmapToBits(hBitmap);
        return QImage(bits, wh.first, wh.second, QImage::Format_ARGB32);
    }

    // Bitmap To Mat
    cv::Mat BitmapToMat(HBITMAP& hBitmap)
    {
        auto&& wh = updateBitmapToBits(hBitmap);
        return cv::Mat(wh.second, wh.first, CV_8UC4, bits);
    }

public:
    WindowImageVideo(QObject* parent = nullptr)
        : QObject(parent)
    {
        QObject::connect(this, &WindowImageVideo::started, this, &WindowImageVideo::start);
        QObject::connect(this, &WindowImageVideo::stoped, this, &WindowImageVideo::stop);
    }
    ~WindowImageVideo()
    {
        stop();
        releaseBuff();
    }
    void setWid(WId wid)
    {
        this->_wid = wid;
    }

    void setFPS(int fps)
    {
        this->_fps = fps;
    }

    WId wid()
    {
        return _wid;
    }

    int fps()
    {
        return _fps;
    }

    void setPath(QString path)
    {
        this->_path = path;
    }

    QString path()
    {
        return _path;
    }
};

  • 6
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值