开发者

Converting GDIPlus::Bitmap to cv::Mat (OpenCV C++ interface)

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 ( s开发者_运维问答tatus != 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;
}


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.


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+)


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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜