1

I have a System.Drawing.Bitmap with PixelFormat of Format32bppRgb. I want this image to be converted to a bitmap of 8bit.

The following is the code to convert a 32-bit image to an 8-bit grayscale image:

        public static Bitmap ToGrayscale(Bitmap bmp)
        {
            int rgb;
            System.Drawing.Color c;

            for (int y = 0; y < bmp.Height; y++)
                for (int x = 0; x < bmp.Width; x++)
                {
                    c = bmp.GetPixel(x, y);
                    rgb = (int)((c.R + c.G + c.B) / 3);
                    bmp.SetPixel(x, y, System.Drawing.Color.FromArgb(rgb, rgb, rgb));
                }
            return bmp;
        }

However, the Bitmap I end up with still has the PixelFormat Property of Format32bppRgb.

Also,

  • How can I convert a 32-bit color image into an 8-bit color image?

Thanks for any input!

Related.
- Convert RGB image to RGB 16-bit and 8-bit
- C# - How to convert an Image into an 8-bit color Image?
- C# Convert Bitmap to indexed colour format
- Color Image Quantization in .NET
- quantization (Reduction of colors of image)
- The best way to reduce quantity of colors in bitmap palette

user366312
  • 16,949
  • 65
  • 235
  • 452
tzippy
  • 6,458
  • 30
  • 82
  • 151
  • @anonymous - You have quite a number of links, also https://www.codeproject.com/Articles/66341/A-Simple-Yet-Quite-Powerful-Palette-Quantizer-in-C and https://code.msdn.microsoft.com/Convert-32-bit-PNGs-to-81ef8c81 what do you need now? – Simon Mourier Jul 05 '18 at 16:34
  • @SimonMourier, I solved this problem after throwing away the bounty. So, I don't need anything now. – user366312 Jul 05 '18 at 17:29

1 Answers1

3

You must create (and return) new instance of Bitmap.

PixelFormat is specified in constructor of Bitmap and can not be changed.

Edit (30 March 2022): fixed byte array access expression from x * data.Stride + y to y * data.Stride + x and changed the palette to be grayscale.

EDIT: Sample code based on this answer on MSDN:

    public static Bitmap ToGrayscale(Bitmap bmp) {
        var result = new Bitmap(bmp.Width, bmp.Height, PixelFormat.Format8bppIndexed);

        var resultPalette = result.Palette;

        for (int i = 0; i < 256; i++)
        {
            resultPalette.Entries[i] = Color.FromArgb(255, i, i, i);
        }

        result.Palette = resultPalette;

        BitmapData data = result.LockBits(new Rectangle(0, 0, result.Width, result.Height), ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);

        // Copy the bytes from the image into a byte array
        byte[] bytes = new byte[data.Height * data.Stride];
        Marshal.Copy(data.Scan0, bytes, 0, bytes.Length);

        for (int y = 0; y < bmp.Height; y++) {
            for (int x = 0; x < bmp.Width; x++) {
                var c = bmp.GetPixel(x, y);
                var rgb = (byte)((c.R + c.G + c.B) / 3);

                bytes[y * data.Stride + x] = rgb;
            }
        }

        // Copy the bytes from the byte array into the image
        Marshal.Copy(bytes, 0, data.Scan0, bytes.Length);

        result.UnlockBits(data);

        return result;
    }
Yacoub Massad
  • 27,509
  • 2
  • 36
  • 62
TcKs
  • 25,849
  • 11
  • 66
  • 104
  • Thanks. If I create a new Bitmap with PixelFormat `Format8bppIndexed` I can not use the `SetPixel` Method as it would result in an Exception: `SetPixel is not supported for images with indexed pixel formats.` – tzippy May 10 '17 at 11:57
  • 2
    bytes[x * data.Stride + y] = rgb ... isn't stride the width of a row? I guess x and y have to be swapped, but i didn't test it... so just for clarification. – Florian p.i. Jul 09 '18 at 17:02
  • Should be bytes[x * data.Height + y] = rgb, otherwise will crash with index out of array error. – Edward Zhang Dec 12 '19 at 04:00
  • 1
    Maybe it "depends". `bytes[y * data.Stride + w] = rgb` worked for me. – r2d2 Jul 16 '20 at 10:26