opencv 将%{v_扩展名}转换为cv::Mat

5sxhfpxr  于 2023-05-23  发布在  其他
关注(0)|答案(4)|浏览(147)

我似乎不知道如何做到这一点,我在谷歌上搜索,找到了两个代码示例,一个来自github屏幕截图库,另一个来自一个帖子组,他们似乎都不工作。
我有一个struct:

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++时,我仍然是相当n 00 b,所以可能有一些简单的东西,我只是没有摸索。

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;
}

当我 Package 一个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 %s\n",e.what());
}

我得到输出:
没有图像数据?异常错误分配
当我尝试运行高图形用户界面,它是一样的,以前,问题作物都.jpg和.bmps写入磁盘,这是可见的图像查看器和MS油漆罚款。
我尝试了一个完全不同的图像,一个来自网站的.png,同样的问题。
我到底做错了什么?

dba5bblo

dba5bblo1#

来自OpenCV文档
data -指向用户数据的指针。接受数据和步骤参数的矩阵构造函数不分配矩阵数据。相反,它们只是初始化指向指定数据的矩阵头,即不复制数据。此操作非常高效,可用于使用OpenCV函数处理外部数据。外部数据不会自动释放,用户应该注意它。
我认为这就是关键所在,使用这个构造函数不会复制你传递给构造函数的缓冲区,所以你不应该释放这些数据,直到你不再需要cv::Mat。我也对这些东西几乎一无所知,但是为什么你要把BITMAPINFOHEADER和BITMAPFILEHEADER传递给你的cv::Mat对象,这似乎一点也不正确。

aij0ehis

aij0ehis2#

我写了自己的函数来做类似的事情,希望它能帮助你:如何在OpenCV中捕获桌面(即把位图变成垫子)?

9lowa7mx

9lowa7mx3#

我不知道那是什么,但当我问一个问题的时候,我就被找到答案迷住了。不管怎样,这个问题的一部分实际上是通过这个问题的答案来解决的:
OpenCV 2.0 C++ API using imshow: returns unhandled exception and "bad-flag"
在Visual C++中:
转到项目->属性(或Alt-F7)配置属性->链接器->输入->其他依赖项
将通常的“cv210.lib cxcore210.lib highgui210.lib”替换为“cv210d.lib cxcore210d.lib highgui210d.lib”-这是调试库。
highgui仍然显示灰色并且不工作,但是使用上面的方法从HBITMAP阅读现在可以工作了。而且我也不需要highgui,反正只是测试用的。

gstyhher

gstyhher4#

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

#include <opencv2/opencv.hpp>

#include <windows.h>
#include <gdiplus.h>

using namespace Gdiplus;
using namespace std;

cv::Mat LoadHBITMAPOpenCV(HBITMAP& HBitmap, Gdiplus::Rect rect = Gdiplus::Rect())
{
    // Create a Bitmap object from the HBITMAP
    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");
    }

    // 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
        int rectWidth = rect.Width - rect.X;
        int 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(rect.Height, rect.Width, CV_8UC4);

    BitmapData bitmapData;
    Rect rect_cut(rect.X, rect.Y, rect.Width, rect.Height);
    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);

    return mat;
}

int main()
{
    GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR gdiplusToken;

    // Initialize GDI+
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

    // Load an image using OpenCV
    cv::Mat cvImage = cv::imread("image.png");
    if (cvImage.empty())
    {
        std::cout << "Failed to load image." << std::endl;
        GdiplusShutdown(gdiplusToken);
        return -1;
    }

    // Show the original image
    cv::imshow("Original Image", cvImage);

    // Convert OpenCV image to HBITMAP
    BITMAPINFO bmpInfo;
    bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    bmpInfo.bmiHeader.biWidth = cvImage.cols;
    bmpInfo.bmiHeader.biHeight = -cvImage.rows; // Order the image from top to bottom
    bmpInfo.bmiHeader.biPlanes = 1;
    bmpInfo.bmiHeader.biBitCount = cvImage.channels() * 8;
    bmpInfo.bmiHeader.biCompression = BI_RGB;

    HBITMAP hBitmap = CreateDIBitmap(GetDC(NULL), &bmpInfo.bmiHeader, CBM_INIT, cvImage.data, &bmpInfo, DIB_RGB_COLORS);
    if (hBitmap == NULL)
    {
        std::cout << "Failed to create HBITMAP." << std::endl;
        GdiplusShutdown(gdiplusToken);
        return -1;
    }

    // Define the cropping rectangle
    // Gdiplus::Rect rect(110, 110, 300, 300); // Crop the entire image

    try
    {
        // Load the HBITMAP into cv::Mat
        // cv::Mat croppedImage = LoadHBITMAPOpenCV(hBitmap, rect);
        cv::Mat croppedImage = LoadHBITMAPOpenCV(hBitmap);

        // Display the cropped image
        cv::imshow("HBITMAP to Mat Image", croppedImage);
        cv::waitKey(0);
    }
    catch (const std::exception& e)
    {
        // Handle any exceptions that occur during loading
        std::cout << "Error: " << e.what() << std::endl;
    }

    // Free resources
    DeleteObject(hBitmap);
    GdiplusShutdown(gdiplusToken);

    return 0;
}

相关问题