Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reducing Bitmap bit-size in C#

Tags:

c#

bitmap

I am using C#, and having an image stored in the object Bitmap.

Now I would like to convert this image into 8bit greyscale, then into a 4-bit greyscale image.

Do you have any tips how this can be made?

like image 902
mouthpiec Avatar asked Apr 24 '10 09:04

mouthpiec


People also ask

How do I reduce the size of a bitmap?

JPEG compression can significantly reduce bitmap image file size, but can result in the loss of fine detail for some images.

How many bits in a BMP file?

Bitmap (BMP) BMP is a standard format used by Windows to store device-independent and application-independent images. The number of bits per pixel (1, 4, 8, 15, 24, 32, or 64) for a given BMP file is specified in a file header. BMP files with 24 bits per pixel are common.

What is padding in BMP?

The final part of the BMP file is the image data. The data is stored row by row with padding on the end of each row. The padding ensures the image rows are multiples of four.


1 Answers

In the .NET Bitmap formats, there are no such thing as a 8 or 4 bit grayscale image. The supported formats are enumerated by the PixelFormat enumeration. You can, however, create a 4 or 8 bit image by creating a indexed image (8bppIndexed or 4bppIndexed), where each entry in the palette is a greyscale value.

This code takes a Bitmap and creates a copy as a 8bpp indexed image with greyscale values:

    public static Bitmap BitmapToGrayscale(Bitmap source)
    {
        // Create target image.
        int width = source.Width;
        int height = source.Height;
        Bitmap target = new Bitmap(width,height,PixelFormat.Format8bppIndexed);
        // Set the palette to discrete shades of gray
        ColorPalette palette = target.Palette;            
        for(int i = 0 ; i < palette.Entries.Length ; i++)
        {                
            palette.Entries[i] = Color.FromArgb(0,i,i,i);
        }
        target.Palette = palette;

        // Lock bits so we have direct access to bitmap data
        BitmapData targetData = target.LockBits(new Rectangle(0, 0, width,height),
                                                ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed);
        BitmapData sourceData = source.LockBits(new Rectangle(0, 0, width,height),
                                                ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);

        unsafe
        {
            for(int r = 0 ; r < height ; r++)
            {
                byte* pTarget = (byte*) (targetData.Scan0 + r*targetData.Stride);
                byte* pSource = (byte*) (sourceData.Scan0 + r*sourceData.Stride);
                for(int c = 0 ; c < width ; c++)
                {
                    byte colorIndex = (byte) (((*pSource)*0.3 + *(pSource + 1)*0.59 + *(pSource + 2)*0.11));
                    *pTarget = colorIndex;
                    pTarget++;
                    pSource += 3;
                }
            }
        }

        target.UnlockBits(targetData);
        source.UnlockBits(sourceData);
        return target;
    }

In order to make a 4Bpp image instead, you would need to create the target with PixelFormat.Format4bppIndexed, and then set the ColorPalette to 16 discrete shades of gray. Finally, in the loop you should normalize values 2 be between 0-15 and pack each 2 pixel values into a single byte.

This is the modified code to make a 4bpp greyscale image:

    public static Bitmap BitmapToGrayscale4bpp(Bitmap source)
    {
        // Create target image.
        int width = source.Width;
        int height = source.Height;
        Bitmap target = new Bitmap(width,height,PixelFormat.Format4bppIndexed);
        // Set the palette to discrete shades of gray
        ColorPalette palette = target.Palette;            
        for(int i = 0 ; i < palette.Entries.Length ; i++)
        {
            int cval = 17*i;
            palette.Entries[i] = Color.FromArgb(0,cval,cval,cval);
        }
        target.Palette = palette;

        // Lock bits so we have direct access to bitmap data
        BitmapData targetData = target.LockBits(new Rectangle(0, 0, width,height),
                                                ImageLockMode.ReadWrite, PixelFormat.Format4bppIndexed);
        BitmapData sourceData = source.LockBits(new Rectangle(0, 0, width,height),
                                                ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);

        unsafe
        {
            for(int r = 0 ; r < height ; r++)
            {
                byte* pTarget = (byte*) (targetData.Scan0 + r*targetData.Stride);
                byte* pSource = (byte*) (sourceData.Scan0 + r*sourceData.Stride);
                byte prevValue = 0;
                for(int c = 0 ; c < width ; c++)
                {
                    byte colorIndex = (byte) ((((*pSource)*0.3 + *(pSource + 1)*0.59 + *(pSource + 2)*0.11)) / 16);
                    if (c % 2 == 0)
                        prevValue = colorIndex;
                    else
                        *(pTarget++) = (byte)(prevValue | colorIndex << 4);

                    pSource += 3;
                }
            }
        }

        target.UnlockBits(targetData);
        source.UnlockBits(sourceData);
        return target;
    }
like image 96
driis Avatar answered Oct 02 '22 20:10

driis