Posts

Save 8 bit uncompressed windows bitmap file (.BMP) in c# / .Net

It took me about 3 years to figure this out. In .Net when you save an 8 bit bitmap from software (PixelFormat.Format8bppIndexed) via Save() it is saved by default in a compressed format (as is allowed by the .bmp pseudo-standard).

Now some Machine Vision libraries can’t load compressed 8 bit bitmaps (poor old Cognex, bless) so I had to figure out how to get .Net to save the .bmp file in an uncompressed format.

The following code save the bitmap as 8bit compressed (if the bitmap’s format is PixelFormat.Format8bppIndexed)

 bitmap.Save("it.bmp");

Now Microsoft has not documented this whole area very well, but it is quite easy, you have to manually specify an image encoder and set its ‘Compression’ parameters as in the following code:

  // Make sure that the blighter will be saved
  // uncompressed.
  var enc = GetEncoderInfo("image/bmp");
            
  var parms = new EncoderParameters(1);
         
  var parm = new EncoderParameter(Encoder.Compression, (long)EncoderValue.CompressionNone);
  parms.Param[0] = parm;
  // Save the bitmap.
  bitmap.Save("it.bmp", enc, parms);

Where GetEncoderInfo() is defined as:

private static ImageCodecInfo GetEncoderInfo(String mimeType)
{
    int j;
    ImageCodecInfo[] encoders;
    encoders = ImageCodecInfo.GetImageEncoders();
    for (j = 0; j < encoders.Length; ++j)
    {
        if (encoders[j].MimeType == mimeType)
            return encoders[j];
    }
    return null;
}

Here we set the Encoder.Compression parameter to EncoderValue.CompressionNone and this should ensure that the bitmap is saved uncompressed.

See this post for details of how to save an 8 bit greyscale image to disk from a byte array.

Reference:

http://msdn.microsoft.com/en-us/library/ytz20d80(v=vs.110).aspx

C# Save Grayscale byte array Image (byte[]) as Bitmap file (.bmp) example

Here is a quick, dirty and inefficient example of how to save an 8bit Grey scale image stored in a C# byte array as a 32bit bitmap file (.bmp). Saving bitmaps can be quite suprisingly difficult in .NET so I am posting this for future reference!

This code copies each byte (8 bit pixel) in the 8bit image into an array of 32bit pixels (4 bytes per pixel) and then saves it to disk. Note that you have to build your project with the ‘Allow unsafe code’ checkbox checked (go to project properties / Build and you will see the ‘Allow unsafe code’ checkbox.)

public void SaveAsBitmap(string fileName, int width, int height, byte[] imageData)
{
    // Need to copy our 8 bit greyscale image into a 32bit layout.
    // Choosing 32bit rather than 24 bit as its easier to calculate stride etc.
    // This will be slow enough and isn't the most efficient method.
    var data = new byte[width * height * 4];
    int o = 0;
    for (var i = 0; i < width * height; i++)
    {
        var value = imageData[i];
        // Greyscale image so r, g, b, get the same
        // intensity value.
        data[o++] = value;
        data[o++] = value;
        data[o++] = value;
        data[o++] = 0; // Alpha isn't actually used
    }
    unsafe
    {
        fixed (byte* ptr = data)
        {
            // Create a bitmap wit a raw pointer to the data
            using (Bitmap image = new Bitmap(width, height, width * 4,
            PixelFormat.Format32bppRgb, new IntPtr(ptr)))
            {
                // And save it.
                image.Save(Path.ChangeExtension(fileName, ".bmp"));
            }
        }
    }
}

Thanks to all on this thread for the pointers!

Update, 02/2014:

This code will save the 8 bit bitmap in a compressed (but perfectly valid) format, to have your software save it in an uncompressed format take a look at this post.

.NET, WPF – BitmapImage File Locking

Q: How can I stop BitmapImage, in .NET Windows Presentation Foundation (WPF) from locking the source image file?

 

A: By default BitmapInfo seems to lock the source image file so that you can use the bitmapinfo object and modify or delete the source file at the same (or similar) time. For example, this C# will probably yield a locked image file:

 


var bitmap = new BitmapImage(new Uri(imageFilePath));
// use bitmap...

 

You can get around this locking problem as follows, it’s a little bit more long-winded but it worked for us….

 


var bitmap = new BitmapImage();
var stream = File.OpenRead(imageFilePath);
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.StreamSource = stream;
bitmap.EndInit();
stream.Close();
stream.Dispose();
// Use bitmap.....