.netbitmap64-bit16-bitimage-load

How can I save/load a 16 bit image in .net x64?


Before, when I was using win32, I used FreeImage in order to load and save bitmaps of bit depth greater than 8 bits. That's every image I work with, since I'm doing medical imaging, and before anyone says anything, yes, me and my customers have spent a lot of money on high-brightness, high-contrast monitors with 11 or 12 bits of dynamic range. In fact, if you're curious, requirements by the ACR for running mammography incldue a monitor with at least 10 bits of dynamic range.

I just switched to x64 for the memory overheads and to get all of my development onto one platform and compiling mode. I'd rather not to back to win32, and my customers are right there with me (and really forcing the change). FreeImage does not compile on 64 bit windows; it has a _asm directive in the code that the compiler just can't handle.

I thought I'd try the native .NET support in the Microsoft classes. Long story short: They don't work, and fail with very limited error messages. I suspect it's because Microsoft still doesn't support the Format16bppGrayScale class.

Maybe there's a problem in my code. Here's my code for writing:

Bitmap theBitmap = new Bitmap(inImage.XSize, inImage.YSize, PixelFormat.Format16bppGrayScale);

//have to go with lockbits
Rectangle rect = new Rectangle(0, 0, theBitmap.Width, theBitmap.Height);
System.Drawing.Imaging.BitmapData bmpData =
    theBitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,
    PixelFormat.Format16bppGrayScale);
IntPtr ptr = bmpData.Scan0;
int theByteSize = theBitmap.Width * theBitmap.Height *2;
byte[] theByteBuffer = new byte[theByteSize];
System.Buffer.BlockCopy(inImage.Data, 0, theByteBuffer, 0, theByteSize);
System.Runtime.InteropServices.Marshal.Copy(theByteBuffer, 0, ptr, theByteSize);
theBitmap.UnlockBits(bmpData);

theBitmap.Save(inDirectory + "\\" + inName);
theBitmap.Dispose();

This code crashes the program with

An unhandled exception of type 
 'System.Runtime.InteropServices.ExternalException' occurred in 
 System.Drawing.dll

Additional information: A generic error occurred in GDI+.

Interesting, especially since I never want to draw this image to the screen like this (although it would be nice!), but just want to use the save/load functionality. The image does get written to disk (even though the program crashes), and the following reading code also crashes the program:

Bitmap theBitmap = new Bitmap(theCompleteName, false);
ushort[] theData = new ushort[theBitmap.Width * theBitmap.Height];
int x, y;

switch (theBitmap.PixelFormat)
{
    case PixelFormat.Format16bppGrayScale:
        //have to go with lockbits
        {           
            Rectangle rect = new Rectangle(0, 0, theBitmap.Width, theBitmap.Height);
            System.Drawing.Imaging.BitmapData bmpData = 
                theBitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly,
                PixelFormat.Format16bppGrayScale);
            IntPtr ptr = bmpData.Scan0;//scanline approach left over from FreeImage

            for (y = 0; y < theBitmap.Height; ++y){
                byte[] scanline = new byte[theBitmap.Width*2];
                System.Runtime.InteropServices.Marshal.Copy(ptr, scanline, y * theBitmap.Width * 2, theBitmap.Width * 2);
                System.Buffer.BlockCopy(scanline, 0, theData, y * theBitmap.Width * 2, theBitmap.Width * 2);
            }
            theBitmap.UnlockBits(bmpData);
        }
        break;
    //for colors, just take the red and call it a day


    case PixelFormat.Format24bppRgb:
    case PixelFormat.Format32bppArgb://really stupid reading code, always works
        for (y = 0; y < theBitmap.Height; ++y) {
            for (x = 0; x < theBitmap.Width; ++x) {
                theData[y * theBitmap.Width + x] = (byte)(theBitmap.GetPixel(x, y).R);
            }
        }
        break;
}
theNewImage = new ImageContainer(theData, theBitmap.Width, theBitmap.Height, inName, inAssessmentID);
theBitmap.Dispose();//not needed, anymore

This code crashes the program with the error:

An unhandled exception of type 'System.ArgumentException' occurred in System.Drawing.dll

Additional information: Parameter is not valid.

These results tell me that Microsoft still hasn't fixed the Format16bppGrayScale portion of the PixelFormat enumeration. That's a shame.

So what can I use to load and save 16 bit grayscale images on x64 with .NET?

(EDIT: I should add that while I can save out DICOM images, I need to run experiments on non-patient data to verify that the algorithms are sound, and so forth. DICOM requires a set of UIDs and other required fields that are overkill for what I need; I just need images, and not patient data, at the moment).


Solution

  • FreeImage can be compiled to x64. Following the instructions here you can get around the _asm directive . There also is a compiled 64-bit dll at the bottom of the page.

    The latest version (3.15.1) already contains this fix. I've picked up the source distribution to try (I'm being curious to use FreeImage in my own project) and the x64 platform compiles fine right away.