【问题标题】:How to turn a screenshot bitmap to a cv::Mat如何将屏幕截图位图转换为 cv::Mat
【发布时间】:2019-12-03 14:47:03
【问题描述】:

我目前正在尝试截取屏幕截图,然后将其转换为 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() 函数时,程序崩溃了。

【问题讨论】:

  • 这样不行。您需要以通常的方式创建 Mat,通过使用正确的大小构造或调用 Mat::create()。然后将数据从 Bitmap 复制到 Mat,因为 AFAIK 在 OpenCV 中没有对应于 Bitmap 布局的配色方案。

标签: c++ windows opencv


【解决方案1】:

不建议存储设备上下文句柄。例如,一旦您完成了句柄,就应该在调用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);
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-13
    • 2011-04-18
    • 1970-01-01
    • 1970-01-01
    • 2011-07-20
    • 1970-01-01
    相关资源
    最近更新 更多