c++ - 如何将屏幕截图位图转换为 cv::Mat

标签 c++ windows opencv

我目前正在尝试截取屏幕截图,然后将其转换为 OpenCV 可编辑的格式。我使用的代码来自微软网站,https://learn.microsoft.com/en-gb/windows/win32/gdi/capturing-an-image .该代码使用“Windows.h”库。最简单的方法显然是将位图保存为 .bmp,然后使用 opencv 打开它。但是,我希望它比这更有效率,但我不知道该怎么做。当我使用代码时,它输出了一个字符指针,我不知道如何将其转换为 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 调用 ReleaseDC。您可以存储位图句柄和内存 dc,但在大多数情况下没有必要。

将图像复制到位图中后,使用 GetDIBits 将位复制到 cv::Mat 中,如下例所示。

请注意,您的应用程序需要 DPI 兼容性,例如使用 SetProcessDPIAware 来找到正确的桌面大小。

此示例使用带有 CV_8UC4 的 32 位位图,但这些 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);
}

关于c++ - 如何将屏幕截图位图转换为 cv::Mat,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57200003/

相关文章:

opencv - knnMatch 要求 k>1 才能得到好的结果?

C++ - 比较多个 int 数组并返回具有最小跨度的数组

python - 如何使用 Python 在 Windows 上检索附近无线局域网的信号强度?

windows - 为什么此 QString 到 const char* 的转换会在 Windows 上生成 Debian 标识符?

android - 将CameraControl示例OpenCv4Android分为两个线程

android - 特征检测后OpenCV Android提取匹配Mat

c++ - C语言有前置自增和后置自增的历史原因是什么?

c++ - 来自 Visual Studio 的 std 文件中的错误

c++ - 为什么子窗口可能收不到鼠标事件?

windows - 可以安全地在操作系统之间共享本地 git 存储库