Ecco un codice che ho messo insieme che prende un'immagine a colori (24 bit/pixel) e la converte in una bitmap di uscita a 1 bit/pixel, applicando una conversione da RGB a scala di grigi standard e quindi utilizzando Floyd-Steinberg per convertire la scala di grigi nell'output a 1 bit/pixel.
Si noti che questo non deve essere considerato un'implementazione "ideale", ma funziona. Ci sono una serie di miglioramenti che potrebbero essere applicati se lo si desidera. Ad esempio, copia l'intera immagine di input nell'array data
, mentre abbiamo solo bisogno di tenere due righe in memoria (le righe "corrente" e "successiva") per accumulare i dati di errore. Nonostante questo, le prestazioni sembrano accettabili.
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
possibile duplicato [convertire immagini in nero -White o Seppia in C#] (http://stackoverflow.com/questions/4624998/convert-image-to-black-white-or-sepia-in-c-sharp) – ken2k
In realtà, mi sembra che quelle domande siano riguardo alla conversione in scala di grigi - mentre l'OP vuole convertirlo in 1 monocromio BPP, che implica la soglia/dithering. – Ani