Преобразование растрового изображения в monoхромное

Я пытаюсь сохранить изображение как monoхромное (черно-белое, 1 бит-глубина), но я начинаю потерять, как это сделать.

Я начинаю с png и конвертирую в bitmap для печати (это термопринтер и поддерживает только черное в любом случае – плюс его медленное, как черное, для больших изображений, если я пытаюсь отправить их как цвет / оттенки серого).

Мой код до сих пор прост, чтобы преобразовать его в bitmap, но он сохраняет исходную глубину цвета.

Image image = Image.FromFile("C:\\test.png"); byte[] bitmapFileData = null; int bitsPerPixel = 1; int bitmapDataLength; using (MemoryStream str = new MemoryStream()) { image.Save(str, ImageFormat.Bmp); bitmapFileData = str.ToArray(); } 

Вот код, который я собрал вместе, который принимает полноцветное (24 бит / пиксель) изображение и преобразует его в bitmap с 1 бит / пикселем, применяя стандартное преобразование RGB в оттенки серого, а затем используя Floyd-Steinberg для преобразования оттенков серого в 1 бит / пиксель.

Обратите внимание, что это никоим образом не следует рассматривать как «идеальную» реализацию, но она действительно работает. Существует ряд улучшений, которые могут быть применены, если вы хотите. Например, он копирует все входное изображение в массив data , тогда как нам действительно нужно сохранить две строки в памяти («текущая» и «следующая» строки) для сбора данных об ошибках. Несмотря на это, производительность кажется приемлемой.

 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; } 

То, что вы хотите, это хороший алгоритм сглаживания, например, Флойд-Штайнберг или Байер . Вы можете либо реализовать бинаризацию самостоятельно, либо использовать библиотеку AForge.NET, чтобы сделать это за вас (загрузите образцы обработки изображений). Здесь вы можете найти документацию по бинаризации.