Converting a bitmap to monochrome

后端 未结 2 1357
走了就别回头了
走了就别回头了 2020-12-29 13:14

I am trying to save an image as monochrome (black&white, 1 bit-depth) but I\'m coming up lost how to do it.

I am starting with a png and converting to a bitmap f

相关标签:
2条回答
  • 2020-12-29 13:51

    Here's some code I put together that takes a full colour (24 bits/pixel) image, and converts it to a 1 bit/pixel output bitmap, applying a standard RGB to greyscale conversion, and then using Floyd-Steinberg to convert greyscale to the 1 bit/pixel output.

    Note that this should by no means be considered an "ideal" implementation, but it does work. There are a number of improvements that could be applied if you wanted. For example, it copies the entire input image into the data array, whereas we really only need to keep two lines in memory (the "current" and "next" lines) for accumulating the error data. Despite this, performance seems acceptable.

    public static Bitmap ConvertTo1Bit(Bitmap input)
    {
        var masks = new byte[] { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
        var output = new Bitmap(input.Width, input.Height, PixelFormat.Format1bppIndexed);
        var data = new sbyte[input.Width, input.Height];
        var inputData = input.LockBits(new Rectangle(0, 0, input.Width, input.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
        try
        {
            var scanLine = inputData.Scan0;
            var line = new byte[inputData.Stride];
            for (var y = 0; y < inputData.Height; y++, scanLine += inputData.Stride)
            {
                Marshal.Copy(scanLine, line, 0, line.Length);
                for (var x = 0; x < input.Width; x++)
                {
                    data[x, y] = (sbyte)(64 * (GetGreyLevel(line[x * 3 + 2], line[x * 3 + 1], line[x * 3 + 0]) - 0.5));
                }
            }
        }
        finally
        {
            input.UnlockBits(inputData);
        }
        var outputData = output.LockBits(new Rectangle(0, 0, output.Width, output.Height), ImageLockMode.WriteOnly, PixelFormat.Format1bppIndexed);
        try
        {
            var scanLine = outputData.Scan0;
            for (var y = 0; y < outputData.Height; y++, scanLine += outputData.Stride)
            {
                var line = new byte[outputData.Stride];
                for (var x = 0; x < input.Width; x++)
                {
                    var j = data[x, y] > 0;
                    if (j) line[x / 8] |= masks[x % 8];
                    var error = (sbyte)(data[x, y] - (j ? 32 : -32));
                    if (x < input.Width - 1) data[x + 1, y] += (sbyte)(7 * error / 16);
                    if (y < input.Height - 1)
                    {
                        if (x > 0) data[x - 1, y + 1] += (sbyte)(3 * error / 16);
                        data[x, y + 1] += (sbyte)(5 * error / 16);
                        if (x < input.Width - 1) data[x + 1, y + 1] += (sbyte)(1 * error / 16);
                    }
                }
                Marshal.Copy(line, 0, scanLine, outputData.Stride);
            }
        }
        finally
        {
            output.UnlockBits(outputData);
        }
        return output;
    }
    
    public static double GetGreyLevel(byte r, byte g, byte b)
    {
        return (r * 0.299 + g * 0.587 + b * 0.114) / 255;
    }
    
    0 讨论(0)
  • 2020-12-29 14:09

    What you want is a good dithering algorithm like Floyd-Steinberg or Bayer ordered. You can either implement the binarization yourself or use a library like AForge.NET to do it for you (download the image processing samples). You can find the binarization documentation here.

    0 讨论(0)
提交回复
热议问题