Molto spesso ci troviamo a lavorare con le immagini quando programmiamo. Abbiamo già visto come salvare le immagini in formato JPEG e anche come conoscere la loro dimensione prima di salvarle su disco. Vediamo come applicare alle immagini presenti in un Image un effetto particolarmente suggestivo per quanto banale possa risultare: l’effetto bianco e nero (o grayscale, scala di grigi).
Prima di iniziare, occore capire bene come ottenere questo effetto e Wikipedia ci aiuta molto in questo, infatti cercando “grayscale” nella versione inglese dell’enciclopedia troviamo:
To convert any color to a grayscale representation of its luminance, first one must obtain the values of its red, green, and blue (RGB) primaries in linear intensity encoding. Then, add together 30% of the red value, 59% of the green value, and 11% of the blue value.
Ovvero, in italiano:
Per convertire ogni colore in una rappresentazione in scala di grigi, è necessario ottenere i suoi valori di Rosso, Verde e Blu (RGB). A questo punto basta sommare i tre valori rispettivamente al 30% del rosso, 59% del verder e 11% del blu.
In pratica ci dice che per ottenere un effetto che a occhio umano sia correttamente calibrato è necessario eseguire questa operazione su ogni singolo pixel dell’immagine:
int eyeGrayScale = (int)(inputColor.R * .3 + inputColor.G * .59 + inputColor.B * .11);
Adesso non è difficile creare una funzione ad-hoc per eseguire l’operazione:<
private Image TransformToBlackWhite(Image inputImage)
{
Bitmap outputBitmap = new Bitmap(inputImage.Width, inputImage.Height);
for (int x = 0; x < outputBitmap.Width; x++)
{
for (int y = 0; y < outputBitmap.Height; y++)
{
Color currentBWColor = TransformColorToBW((inputImage as Bitmap).GetPixel(x, y));
outputBitmap.SetPixel(x, y, currentBWColor);
}
}
return outputBitmap;
}
In questa funzione si richiede in input un oggetto di tipo Image e in uscita si restituisce un nuovo oggetto sempre di tipo Image ma trasformato in scala di grigi. Si scorre quindi l’immagine pixel per pixel, sia sull’asse x, sia sull’asse y e per ogni pixel viene eseguita la funzione:
private Color TransformColorToBW(Color inputColor)
{
int eyeGrayScale = (int)(inputColor.R * .3 + inputColor.G * .59 + inputColor.B * .11);
Color outputColor = Color.FromArgb(eyeGrayScale, eyeGrayScale, eyeGrayScale);
return outputColor;
}
La funzione TransformColorToBW non fa altro che fare quanto specificato da Wikipedia. Ogni pixel viene quindi scritto per mezzo della funzione SetPixel sull’immagine di uscita nella stessa posizione XY dell’originale.
Per vedere l’algoritmo all’opera, ecco un’immagine di test:

Quello che si nota tuttavia, è una incredibile lentezza nell’esecuzione dell’algoritmo tantochè per l’immagine sopra che ha una risoluzione di 2592×1944 pixel, ovvero 5.038.848 corrispondenti a 5MegaPixel, impiega ben 11,069 secondi.
Per sapere come fare a calcolare questo valore leggi l’articolo che ho scritto sul calcolo delle performance degli algoritmi in C#.
Questo ci lascia intendere che ci sono modalità ben più performanti per ottenere il medesimo risultato, utilizzando le ColorMatrix, vediamo qui un esempio:
private Image FastTransformToBW(Image inputImage)
{
Bitmap outputBitmap = new Bitmap(inputImage.Width, inputImage.Height);
ImageAttributes attributes = new ImageAttributes();
ColorMatrix colorMatrix = new ColorMatrix(new float[][] {
new float[] {.3f, .3f, 0.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}
}
);
attributes.SetColorMatrix(colorMatrix);
Graphics outputGraphics = Graphics.FromImage(outputBitmap);
Rectangle rectInput = new Rectangle(0,0,inputImage.Width,inputImage.Height);
outputGraphics.DrawImage(inputImage, rectInput, 0, 0, inputImage.Width, inputImage.Height, GraphicsUnit.Pixel, attributes);
outputGraphics.Dispose();
return outputBitmap;
}
Calcolando la performance di questo algoritmo, vediamo che abbiamo ottenuto lo stesso risultato in ben 838 millisecondi, ovvero un incremento in velocita pari al 1221%! Per facilitarvi nella comprensione, ho scritto appositamente un articolo per utilizzare al meglio le ColorMatrix. Buon lavoro!
di Denis, postato alle 09:34








