Freigeben über


Converting an Image (well, Bitmap really) to Grayscale

First thought – convert the picture to greyscale and increase the contrast to help any kind of edge-detection work.  Some searching found me Bob Powell's handy guide to increasing contrast and converting to grayscale via ColorMatrix.  With Wikipedia’s explanation of “naive” matrix multiplication in hand I cobbled up a converter that takes a bitmap and returns a grayscaled version.

     public class GreyscaleBitmap
    {
        // Magic conversion to greyscale matrix
        static float[][] _grayScaleMatrix = new float[][] {
            new float[] {.3f, .3f, .3f, 0, 0},
            new float[] {.59f, .59f, .59f, 0, 0},
            new float[] {.11f, .11f, .11f, 0, 0},
            new float[] {0, 0, 0, 1, 0},
            new float[] {0, 0, 0, 0, 1}
        };

        // Doubles contrast
        static float[][] _increaseContrast = new float[][] {
            new float[] {2, 0, 0, 0, 0},
            new float[] {0, 2, 0, 0, 0},
            new float[] {0, 0, 2, 0, 0},
            new float[] {0, 0, 0, 1, 0},
            new float[] {0, 0, 0, 0, 1}
        };

        // Dumb and slow.
        static float[][] MatrixMult2(float[][] first, float[][] second)
        {
            float[][] result = new float[5][];

            for (int i = 0; i < 5; i++)
                result[i] = new float[5];

            for (int j = 0; j < 5; j++)
                for (int k = 0; k < 5; k++)
                    for (int i = 0; i < 5; i++)
                        result[i][j] += first[i][k] * second[k][j];

            return result;
        }

        // Only bother making one of these
        static ColorMatrix _matrix = new ColorMatrix(MatrixMult2(_grayScaleMatrix, _increaseContrast));

        // Cache this as well?
        static ImageAttributes GetAttributes()
        {
            ImageAttributes result = new ImageAttributes();
            result.SetColorMatrix(_matrix);
            return result;
        }

        public static Bitmap Convert(Bitmap bmp)
        {
            Bitmap newBitmap = new Bitmap(bmp.Width, bmp.Height, PixelFormat.Format24bppRgb);

            using (Graphics g = Graphics.FromImage(newBitmap))
            {
                Rectangle r = new Rectangle(0, 0, newBitmap.Width, newBitmap.Height);
                g.DrawImage(bmp, r, 0, 0, bmp.Width, bmp.Height, GraphicsUnit.Pixel, GetAttributes());
            }

            return newBitmap;
        }
    }

Now, in another world the output image format would be 16bpp grayscale directly, instead of being re-encoded in 24bpp RGB.  As a result, my eventual scanner will have to convert back from RGB to a grayscale value again.  Not a great start, but hey – conversion to grayscale!