加快BITMAP像素访问速度,取代GetPixel
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;
}
};