1

I am new to using GDI+ and I'm doing a proof of concept for a personal project of mine.

For this code segment, I essentially want to convert a cv::Mat into a bitmap and save the file to an arbitrary path.

My conversion process is adapted from this StackOverflow answer:

How to convert an opencv mat image to gdi bitmap

My problem is that when I call bitmap.Save(), it returns an error code of 2 (InvalidParameter).

I don't understand why one or more of my input parameters is wrong.

cv::Mat initialization

cv::Mat mat = cv::Mat::ones(768, 1366, CV_8UC3);
show_image_from_mat(mat);

The show_image_from_mat function

int show_image_from_mat(cv::Mat image) 
{
    cv::Size size = image.size();
    GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR           gdiplusToken;
    // Initialize GDI+.
    printf("================================================================");
    printf("\n%i StarupStatus ",GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL));
    // gdiplus
    Gdiplus::Bitmap bitmap(size.width, size.height, image.step1(), PixelFormat24bppRGB, image.data);
    CLSID bmpClsid;
    printf("\n%p result", CLSIDFromString(L"{557cf400-1a04-11d3-9a73-0000f81ef32e}",&bmpClsid));
    printf("\n%p clsid", bmpClsid);
    const WCHAR* filename = L"C:\\output.bmp";
    char shortfilename[15];//You will need to malloc if you need to handle arbitrary filenames.

    printf("\n%i ErrorCode",bitmap.Save(filename, &bmpClsid,NULL));
    wcstombs(shortfilename, filename, 15);
    show_image_from_file(shortfilename);
    GdiplusShutdown(gdiplusToken);
    return 9001;
}

I got the CLSID abomination from Does GDI+ have standard image encoder CLSIDs?.

And I am writing this in Visual Studio (I'm also new to this).

If the image doesn't load, my output is this:

image

Community
  • 1
  • 1
DNS_Jeezus
  • 289
  • 4
  • 17
  • I'm not sure what the point of printing a CLSID as a pointer is. And you haven't shown the code where you actually try to save the image. But my best guess is, you are trying to write a file to the root of C: and (unless you have UAC disabled) this is not possible unless your program is elevated. – Jonathan Potter Mar 23 '17 at 20:11
  • I just wanted to make sure the CLSID actually contained data. I tried switching the path to some folder on my desktop, but bitmap.Save() still returns an error code of 2. Bitmap.save is what writes the file to disk. Bitmap inherits the Save method from gdiplus' Image class – DNS_Jeezus Mar 23 '17 at 20:22
  • 1
    You can't pass a `CLSID` directly to `printf()`, you have to print the individual fields instead: `printf("\n%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X clsid", bmpClsid.Data1, bmpClsid.Data2, bmpClsid.Data3, bmpClsid.Data4[0], bmpClsid.Data4[1], bmpClsid.Data4[2], bmpClsid.Data4[3], bmpClsid.Data4[4], bmpClsid.Data4[5], bmpClsid.Data4[6], bmpClsid.Data4[7]);` Otherwise, pass the `CLSID` to `StringFromCLSID()` or similar function, and then print the resulting string. – Remy Lebeau Mar 23 '17 at 23:26
  • 1
    The *correct* way to get the `CLSID` for a given image format is to query it dynamically (see [Retrieving the Class Identifier for an Encoder](https://msdn.microsoft.com/en-us/library/windows/desktop/ms533843.aspx) on MSDN. The [other question](http://stackoverflow.com/questions/5345803/) also touches on that). Don't rely on hard-coded CLSIDs, in case it does actually change someday. `GetGdiplusEncoderClsid(L"image/bmp", &bmpClsid);` – Remy Lebeau Mar 23 '17 at 23:31
  • What are the values of the arguments you pass to the `Gdiplus::Bitmap` constructor (i. e. size.width, size.height, image.step1())? Call `Bitmap::GetLastStatus()` immediately after the constructor to see if there were any errors during construction. Try another output path (e. g. `c:\Users\\Documents`) because the root of the system drive is not writable without administrator permissions by default. – zett42 Mar 24 '17 at 18:25
  • The answer was a combination of all of your answers. one of my octets was wrong in my CLSID, The output path wasn't within reach of the current user. And the input parameters to my Bitmap Constructor required that the Stride parameter(The one I shove image.step1() into) is divisible by 4. The status error code I recieved of 2 was carried over from my Bitmap Constructor – DNS_Jeezus Mar 27 '17 at 19:16

1 Answers1

1

The answer was a combination of all of your answers. one of my octets was wrong in my CLSID, The output path wasn't within reach of the current user. And the input parameters to my Bitmap Constructor required that the Stride parameter(The one I shove image.step1() into) is divisible by 4. The status error code I recieved of 2 was carried over from my Bitmap Constructor

DNS_Jeezus
  • 289
  • 4
  • 17