c++ - 如何将屏幕截图位图转换为 cv::Mat
问题描述
我目前正在尝试截取屏幕截图,然后将其转换为 OpenCV 可编辑的格式。我使用的代码来自微软网站,https://docs.microsoft.com/en-gb/windows/win32/gdi/capturing-an-image。该代码使用“Windows.h”库。最简单的方法显然是将位图保存为 .bmp,然后使用 opencv 打开它。但是,我希望它比这更有效,但我不知道该怎么做。当我使用代码时,它输出了一个 char 指针,我不知道如何将其转换为 cv::Mat。代码如下:
cv::Mat * Capture::GetMat()
{
cv::Mat * mat1;
MemoryHandle = NULL;
BitmapHandle = NULL;
// Find the handle for the device context of the entire screen, and the specific window specified.
ScreenHandle = GetDC(NULL);
WindowHandle = GetDC(hwnd);
//Make the compatible DC (Device Context) for storing the data in memory.
MemoryHandle = CreateCompatibleDC(WindowHandle);
//Make a compatible DC for the bitmap to be stored in.
BitmapHandle = CreateCompatibleBitmap(WindowHandle, width, height);
//Select the correct bitmap, and put it into memory using the memory handle.
SelectObject(MemoryHandle, BitmapHandle);
//Transfer the actual bitmap into the compatible memory DC.
BitBlt(MemoryHandle, 0, 0, 1920, 1080, WindowHandle, 0, 0, SRCCOPY);
//Get the bitmap from the handle, and ready it to be filed.
GetObject(BitmapHandle, sizeof(BITMAP), &Bitmap);
//Cofinguring INFO details.
bmpInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
bmpInfoHeader.biWidth = Bitmap.bmWidth;
bmpInfoHeader.biHeight = Bitmap.bmHeight;
bmpInfoHeader.biPlanes = 1;
bmpInfoHeader.biBitCount = 32;
bmpInfoHeader.biCompression = BI_RGB;
bmpInfoHeader.biSizeImage = 0;
bmpInfoHeader.biXPelsPerMeter = 0;
bmpInfoHeader.biYPelsPerMeter = 0;
bmpInfoHeader.biClrUsed = 0;
bmpInfoHeader.biClrImportant = 0;
bmpSize = ((Bitmap.bmWidth * bmpInfoHeader.biBitCount + 31) / 32) * 4 * Bitmap.bmHeight;
memhnd = GlobalAlloc(GHND, bmpSize);
mat1 = (cv::Mat *)GlobalLock(memhnd);
std::cout << GetLastError() << std::endl;
return mat1;
}
int Capture::save_mat(cv::Mat * mat)
{
std::string FileName("P:/Photos/capture");
FileName += std::to_string(image_count_mat);
FileName += (const char*)(".jpg");
cv::Mat mat2 = *mat;
cv::imwrite(FileName.c_str(), mat2);
image_count_mat++;
return 0;
}
该类具有以下属性:
private:
HWND hwnd;
HDC hdc;
int image_count_bitmap = 0;
int image_count_mat = 0;
int height;
int width;
HDC ScreenHandle;
HDC WindowHandle;
HDC MemoryHandle = NULL;
HBITMAP BitmapHandle = NULL;
BITMAP Bitmap;
BITMAPFILEHEADER bmpFileHeader;
BITMAPINFOHEADER bmpInfoHeader;
DWORD bmpSize;
HANDLE memhnd;
GetMat() 函数工作正常并且不输出错误,尽管我不知道如何检查输出的 cv::Mat 是否正确。但是,当我运行 save_mat() 函数时,程序崩溃了。
解决方案
不建议存储设备上下文句柄。例如,一旦你完成了句柄,就GetDC
应该调用 to。ReleaseDC
您可以存储位图句柄和内存 dc,但在大多数情况下没有必要。
将图像复制到位图中后,使用GetDIBits
将位复制到cv::Mat
,如下例所示。
请注意,您的应用程序需要 DPI 兼容性,例如SetProcessDPIAware
找到正确的桌面大小。
此示例使用 32 位位图CV_8UC4
,但这些 GDI 函数是 24 位的。您还可以将 24 位位图与CV_8UC3
.
void screenshot()
{
auto w = GetSystemMetrics(SM_CXFULLSCREEN);
auto h = GetSystemMetrics(SM_CYFULLSCREEN);
auto hdc = GetDC(HWND_DESKTOP);
auto hbitmap = CreateCompatibleBitmap(hdc, w, h);
auto memdc = CreateCompatibleDC(hdc);
auto oldbmp = SelectObject(memdc, hbitmap);
BitBlt(memdc, 0, 0, w, h, hdc, 0, 0, SRCCOPY);
cv::Mat mat(h, w, CV_8UC4);
BITMAPINFOHEADER bi = { sizeof(bi), w, -h, 1, 32, BI_RGB };
GetDIBits(hdc, hbitmap, 0, h, mat.data, (BITMAPINFO*)&bi, DIB_RGB_COLORS);
cv::imwrite("screenshot.png", mat);
SelectObject(memdc, oldbmp);
DeleteDC(memdc);
DeleteObject(hbitmap);
ReleaseDC(HWND_DESKTOP, hdc);
}
推荐阅读
- javascript - 如何随机创建滴数?
- php - JSON 循环使用 Steam API 获取特定游戏的 playtime_forever
- html - 用css悬停时显示一个图标
- powerapps - 如何将数据从 PowerApp 发送到 Flow Automate (POST)?
- java - Android - 即使方法完成后如何不销毁监听器对象?
- sql - 在 postgres 中创建具有差异列的视图
- kubernetes - prometheus 查询以按标签拉取 kubernetes 节点并仅列出某些字段
- c++ - C++20 的 std::vector 如何分配 constexpr?
- excel - 获取图表 Y 轴的中点
- c++ - 如何让“cin”读取原始模式终端