将HBITMAP转换为cv::Mat

Convert HBITMAP to cv::Mat

本文关键字:Mat cv HBITMAP 转换      更新时间:2023-10-16

我似乎无法弄清楚如何做到这一点,我在谷歌上搜索并找到了两个代码示例,一个来自github屏幕捕获库,另一个来自张贴组,它们似乎都没有工作。

我有一个结构体:
struct ClacksScreen
{
    HWND hDesktopWnd; 
    int width, height;
    RECT wr, cr;
    HDC hdcClacksScreen; // hardware ClacksScreen
    HDC hdcMemDC; // ClacksScreen in memory
    HBITMAP hbmClacksScreen; //hbitmap of the ClacksScreen
    BITMAP bmpClacksScreen;
    BITMAPINFOHEADER bi;
};

已更新。我定义了一些函数,包括一个将位图写入磁盘的函数,这很好,屏幕被捕获,bmp被写入磁盘,这就是我想要的。

现在我想将屏幕上的HBITMAP直接转换为OpenCV2.1的cv::Mat。

它的工作,除了图像是纯灰色的,它崩溃。很明显,我对c++还是个新手,所以可能有一些简单的东西是我没有注意到的。

static cv::Mat copyToCVMat(const ClacksScreen * s)
{
    cv::Mat image;
    image.create(s->bmpClacksScreen.bmWidth, s->bmpClacksScreen.bmHeight, CV_8UC4);
    GetDIBits(s->hdcMemDC, s->hbmClacksScreen, 0,
            (UINT)s->bmpClacksScreen.bmHeight,
            image.data,
            (BITMAPINFO *)&s->bi, DIB_RGB_COLORS);
    return image;
}

当我换行cv::imwrite(image);在try catch中,我得到了一个严重的分配错误。显然,在这一点上,我们已经确定,我没有任何线索如何做到这一点,所以任何帮助将不胜感激。

如果我运行以下代码:

try {
    cv::Mat screen = cv::imread("captureqwsx.jpg");
    if (!screen.data) {
        printf("no image data?");
    }
    cv::imwrite("out.jpg",screen);
} catch(std::exception e) {
    printf("Exception %sn",e.what());
}

我得到了输出:

没有图像数据?异常分配错误

当我尝试运行高图形界面时,它和以前一样,写入磁盘的。jpg和。bmp都出现了问题,这些都可以在图像查看器和MS Paint中查看。

我尝试了一个完全不同的图像,一个来自网站的。png,同样的问题。

那么我在这一点上做错了什么?

来自OpenCV文档

data -指向用户数据的指针。获取数据的矩阵构造函数步进参数不分配矩阵数据。相反,他们只是初始化指向指定数据的矩阵头,即不复制数据。这种操作效率很高,可以用来使用OpenCV函数处理外部数据。外部数据则不是自动回收,请用户自行处理

我认为这就是重点,使用这个构造函数不会复制你传递给构造函数的缓冲区,所以你不应该释放这个数据,直到你不再需要cv::Mat。另外,我对这个东西几乎一无所知,但是为什么你要传递BITMAPINFOHEADER和BITMAPFILEHEADER到你的cv::Mat对象,这似乎根本不对。

我写了自己的函数来做类似的事情,希望它能帮助你:如何在OpenCV中捕获桌面(即。将位图转换为Mat)?

我不知道它是什么,但我一问问题,我就着迷于寻找答案。无论如何,这个问题的部分答案实际上是通过这个问题的答案解决的:

OpenCV 2.0 c++ API使用imshow:返回未处理的异常和"bad-flag"

在Visual c++中:

转到Project->Properties(或Alt-F7) Configuration属性->链接器->输入->附加依赖项

替换通常的"cv210"。自由cxcore210。自由highgui210。Lib " by "cv210d。自由cxcore210d。自由highgui210d。Lib"-这是调试库。

highgui仍然显示灰色并且不起作用,但是使用上述方法从HBITMAP读取现在可以工作了。我并不真的需要highgui,它只是用于测试。

此代码甚至比相同的OpenCV库更可靠。在OpenCV中有一个特定的函数来加载HBITMAPS,但它有一些问题,例如向后加载图像,我更喜欢控制我的代码。

cv::Mat LoadHBITMAPOpenCV(HBITMAP& HBitmap, Gdiplus::Rect rect = Gdiplus::Rect())
{
    // Create a Bitmap object from the HBITMAP, avoid memory leaks
    std::unique_ptr<Bitmap> bitmap(Bitmap::FromHBITMAP(HBitmap, NULL));
    if (!bitmap)
    {
        // Error loading the HBITMAP
        throw std::runtime_error("[LoadHBITMAPOpenCV] Error loading HBITMAP");
    }
    // Get the bitmap dimensions
    int bitmapWidth = bitmap->GetWidth();
    int bitmapHeight = bitmap->GetHeight();
    // Check if the bitmap is valid
    if (bitmapWidth <= 0 || bitmapHeight <= 0)
    {
        throw std::runtime_error("[LoadHBITMAPOpenCV] Corrupt HBITMAP");
    }
    int rectWidth = bitmapWidth;
    int rectHeight = bitmapHeight;
    // Check if a rect is provided, otherwise use the entire image as the rect
    if (rect.IsEmptyArea())
    {
        rect = Gdiplus::Rect(0, 0, bitmapWidth, bitmapHeight);
    }
    else
    {
        // Check if the cropping rectangle is valid
        rectWidth = rect.Width - rect.X;
        rectHeight = rect.Height - rect.Y;
        if (rectWidth <= 0 || rectHeight <= 0 || rect.X < 0 || rect.Y < 0 || rect.Width > bitmapWidth || rect.Height > bitmapHeight)
        {
            throw std::runtime_error("[LoadHBITMAPOpenCV] Invalid rect");
        }
    }
    // Load the HBITMAP into cv::Mat using GDI+
    cv::Mat mat(rectHeight, rectWidth, CV_8UC4);
    BitmapData bitmapData;
    Rect rect_cut(rect.X, rect.Y, rectWidth, rectHeight);
    bitmap->LockBits(&rect_cut, ImageLockModeRead, PixelFormat32bppARGB, &bitmapData);
    if (bitmapData.Stride < 0)
    {
        bitmap->UnlockBits(&bitmapData);
        throw std::runtime_error("[LoadHBITMAPOpenCV] Corrupt HBITMAP stride");
    }
    BYTE* data = reinterpret_cast<BYTE*>(bitmapData.Scan0);
    memcpy(mat.data, data, mat.total() * mat.elemSize());
    bitmap->UnlockBits(&bitmapData);
    cv::cvtColor(mat, mat, EXTERNAL_CTV_COLOR);
    // cv::cvtColor(mat, mat, cv::COLOR_RGB2Lab);
    return mat;
}