Aquí hay un código que reúno para obtener una imagen a todo color (24 bits/píxel), convertirlo en un mapa de bits de salida de 1 bit/píxel, aplicar una conversión RGB estándar a escala de grises y luego usar Floyd-Steinberg para convierte la escala de grises en la salida de 1 bit/píxel.
Tenga en cuenta que esto de ninguna manera debe considerarse una implementación "ideal", pero funciona. Hay una serie de mejoras que podrían aplicarse si quisiera. Por ejemplo, copia toda la imagen de entrada en la matriz data
, mientras que realmente solo necesitamos mantener dos líneas en la memoria (las líneas "actual" y "siguiente") para acumular los datos de error. A pesar de esto, el rendimiento parece aceptable.
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;
}
http://stackoverflow.com/questions/4669317/how-to-convert-a-bitmap-image-to-black-and-white-in-c – Dmitriy
posible duplicado de [convertir una imagen a Negro -Blanco o Sepia en C#] (http://stackoverflow.com/questions/4624998/convert-image-to-black-white-or-sepia-inc-c-sharp) – ken2k
En realidad, me parece que esas preguntas son sobre la conversión a escala de grises, mientras que el OP quiere convertirlo a 1 monocromo BPP, que implica el umbral/dithering. – Ani