将 GDIPlus::Bitmap 转换为 cv::Mat(OpenCV C++ 接口)

发布于 2024-11-29 21:37:40 字数 1011 浏览 1 评论 0原文

我正在尝试将 GDIPlus::Bitmap 转换为 openCV Mat 对象,但我遇到了访问冲突问题,这意味着我没有做正确的事情,但我一遍又一遍地查看代码,我认为它应该有效。

有人看到一个明显的问题吗?

cv::Mat ConvertToOpenCV(Gdiplus::Bitmap &image) {
    cv::Mat *retval = new cv::Mat(
        image.GetWidth(), image.GetHeight(), CV_8UC3
    );

    Gdiplus::BitmapData source;

    Gdiplus::Rect rect( 0, 0, image.GetWidth(), image.GetHeight() );
    Gdiplus::Status status =
        image.LockBits( &rect, Gdiplus::ImageLockModeRead, PixelFormat24bppRGB, &source );
    if ( status != Gdiplus::Ok ) {
        // Some error condition
        return retval; // No image copied
    }

    BYTE *destination = (BYTE *)retval->data;

    for ( int y = 0; y != source.Height; ++y ) {
        BYTE *src = (BYTE *) source.Scan0 + y * source.Stride;
        BYTE *dst = (BYTE *)(destination + y * retval->step);
        memcpy( dst, src, 3 * source.Width );  // Access Violation happens here
    }

    image.UnlockBits(&source);

    return retval;
}

I'm trying to convert a GDIPlus::Bitmap into an openCV Mat object, but I'm running into problems with Access Violations, which means I'm not doing something right, but I've looked over the code over and over, and I think it should work.

Does anyone see an obvious problem?

cv::Mat ConvertToOpenCV(Gdiplus::Bitmap &image) {
    cv::Mat *retval = new cv::Mat(
        image.GetWidth(), image.GetHeight(), CV_8UC3
    );

    Gdiplus::BitmapData source;

    Gdiplus::Rect rect( 0, 0, image.GetWidth(), image.GetHeight() );
    Gdiplus::Status status =
        image.LockBits( &rect, Gdiplus::ImageLockModeRead, PixelFormat24bppRGB, &source );
    if ( status != Gdiplus::Ok ) {
        // Some error condition
        return retval; // No image copied
    }

    BYTE *destination = (BYTE *)retval->data;

    for ( int y = 0; y != source.Height; ++y ) {
        BYTE *src = (BYTE *) source.Scan0 + y * source.Stride;
        BYTE *dst = (BYTE *)(destination + y * retval->step);
        memcpy( dst, src, 3 * source.Width );  // Access Violation happens here
    }

    image.UnlockBits(&source);

    return retval;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

顾挽 2024-12-06 21:37:40

这里有一个问题:

cv::Mat *retval = new cv::Mat(
    image.GetWidth(), image.GetHeight(), CV_8UC3
);

Mat 构造函数的第一个参数是行,第二个参数是列。所以你应该这样做:

cv::Mat *retval = new cv::Mat(
    image.GetHeight(), image.GetWidth(), CV_8UC3
);

这可能会导致访问冲突。

编辑

另外,OpenCV 图像默认为 BGR,而不是 RGB。因此,如果您完成此操作,然后使用 imshow 显示图像,您的蓝色和红色值将向后。您可以通过在 return 语句之前调用 cv::cvtColor(retval, retval, CV_RGB2BGR) 来解决此问题。

Here's one problem:

cv::Mat *retval = new cv::Mat(
    image.GetWidth(), image.GetHeight(), CV_8UC3
);

The Mat constructor's first argument is rows, second is columns. So you should be doing this:

cv::Mat *retval = new cv::Mat(
    image.GetHeight(), image.GetWidth(), CV_8UC3
);

That could cause an access violation.

Edit

Also, OpenCV images are by default BGR, not RGB. So if you get this working and then display the image with imshow, your blue and red values will be backward. You can fix this with the call cv::cvtColor(retval, retval, CV_RGB2BGR) before your return statement.

三生殊途 2024-12-06 21:37:40

您可以使用我的即用类 CGdiPlus 来自动从 cv::Mat 转换为 Gdiplus::Bitmap,反之亦然:

OpenCV / Tesseract:如何替换 libpng、libtiff 等GDI+ 位图(通过 GDI+ 加载到 cv::Mat)

You can use my ready to use class CGdiPlus that does automatic conversion from cv::Mat to Gdiplus::Bitmap and vice versa:

OpenCV / Tesseract: How to replace libpng, libtiff etc with GDI+ Bitmap (Load into cv::Mat via GDI+)

℡寂寞咖啡 2024-12-06 21:37:40

正如 SSteve 所指出的,Mat 构造函数先行后列,因此先使用高度,然后使用宽度。但是,您无需亲自进行实际复制。您可以使用 Mat 构造函数之一来包装现有数据而不进行复制,然后通过调用 clone 成员函数强制其进行复制。

唯一的麻烦是 Gdiplus::Bitmap 理论上支持像素布局的负载;然而,其中大多数都非常具有异国情调。您可以按如下方式处理简单的情况:

cv::Mat GdiPlusBitmapToOpenCvMat(Gdiplus::Bitmap* bmp)
{
    auto format = bmp->GetPixelFormat();
    if (format != PixelFormat24bppRGB)
        return cv::Mat();

    int wd = bmp->GetWidth();
    int hgt = bmp->GetHeight();
    Gdiplus::Rect rcLock(0, 0, wd, hgt);
    Gdiplus::BitmapData bmpData;

    if (!bmp->LockBits(&rcLock, Gdiplus::ImageLockModeRead, format, &bmpData) == Gdiplus::Ok)
        return cv::Mat();

    cv::Mat mat = cv::Mat(hgt, wd, CV_8UC3, static_cast<unsigned char*>(bmpData.Scan0), bmpData.Stride).clone();

    bmp->UnlockBits(&bmpData);
    return mat;
}

As SSteve notes the Mat constructors go rows then columns, so use height then width. However there is no need to do the actual copy yourself. You can use one of the Mat constructors that will wrap existing data without copying and then force it to copy by calling the clone member function.

The only other trouble is that Gdiplus::Bitmap in theory supports loads of pixel layouts; however, most of these are pretty exotic. You can handle the simple case as follows:

cv::Mat GdiPlusBitmapToOpenCvMat(Gdiplus::Bitmap* bmp)
{
    auto format = bmp->GetPixelFormat();
    if (format != PixelFormat24bppRGB)
        return cv::Mat();

    int wd = bmp->GetWidth();
    int hgt = bmp->GetHeight();
    Gdiplus::Rect rcLock(0, 0, wd, hgt);
    Gdiplus::BitmapData bmpData;

    if (!bmp->LockBits(&rcLock, Gdiplus::ImageLockModeRead, format, &bmpData) == Gdiplus::Ok)
        return cv::Mat();

    cv::Mat mat = cv::Mat(hgt, wd, CV_8UC3, static_cast<unsigned char*>(bmpData.Scan0), bmpData.Stride).clone();

    bmp->UnlockBits(&bmpData);
    return mat;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文