Este articulo es una copia cruzada de mi blog original, visítalo en:
http://juank.black-byte.com/xna-conv...escala-grises/
-------------------
Hola, este artículo abordara un problema relativamente sencillo, aplicar a una imagen un efecto para verla en escala de grises.
Este tema, aunque sencillo, es muy necesario para
adentrarse un poco más en el procesamiento digital de imágenes ,lo cual
desde luego estaré trabajando dentro de poco en el blog.
Para este artículo he decidido utilizar XNA Framework, pero bien habría podido utilizar un PictureBox o un objeto Bitmap o
cualquier otro conjunto de objetos existentes en el .Net Framework,
incluso podría haber escogido simplemente leer un archivo analizarlo,
convertirlo y luego escribirlo de nuevo. He escogido XNA simplemente
porque es la tecnología más adecuada para exponer este tipo de temas y
es donde sin duda muchos de ustedes querrán aplicar estos temas.
Cómo Convertir una Imagen a Escala de Grises
Básicamente para que una imagen sea vea en tonos de
gris se requiere que los tres componentes básicos del color ( en el
computador: rojo, verde, azul – RGB por sus siglas en ingles ) tengan
más o menos la misma intensidad, podemos decir que si queremos
convertir un pixel a su equivalente en escala de grises bastaría con
hacer algo como esto:
- Sumar los valores de los componentes de color del pixel, es decir sumar R + G + B
- Sacar el promedio de esa suma
- El valor hallado se debe asignar a R, G y B
Con estos tres pasos ya logramos que el pixel sea de color gris ya que cada uno de sus componentes tiene el mismo valor.
Hay muchas otras formas de hacerlo, incluso alguien
que haya trabajado previamente con imágenes puede tener su propia
versión de como implementarlo de acuerdo a lo que necesite o al tiempo
que tenga. Pero existe una manera ampliamente conocida y aceptada en el
gremio de las personas que trabajan con imágenes y visión por
computador esa manera es la que aprenderemos a efectuar.
El ojo humano y su sensibilidad
Bien, resulta que el ojo humano es mucho más sensible
a los colores verdes y rojos que al azul, por lo que en cuanto a
precepción de iluminación se trata nuestro ojo reconoce los patrones de
iluminación en color en las siguientes proporciones para cada
componente:
- Rojo:30%
- Verde:59%
- Azul:11%
Así que lo más adecuado es calcular el valor de cada
componente de color con base a esta proporción y de este modo se
obtiene el pixel de color gris con la iluminación adecuada para que
nuestro ojo lo perciba como un mejor equivalente a su versión en color.
Manos A La Obra, Tiempo de Programar
Bueno, la idea de este artículo no es mostrar como poner imágenes en pantalla con XNA pero un super repaso no sienta nada mal.
Para utilizar la técnica que he planteado lo primero que se debe hacer es cargar la imagen, en XNA esto se hace con un Objeto Texture2D, al cual le asignaremos una imagen traída desde el Content:
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
// TODO: use this.Content to load your game content here
conejo = Content.Load<Texture2D>("Imagenes/conejo");
}
Y para dibujar esta imagen basta con hacer esto:
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
// TODO: Add your drawing code here
spriteBatch.Begin(SpriteBlendMode.AlphaBlend);
spriteBatch.Draw(imagenEfectiva, Vector2.Zero, Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
Este es el resultado en pantalla:

Ahora comienza el verdadero trabajo. Implementaré un
método que nos cree una copia de la imagen, pero en escala de grises,
este método recibe como parámetro nuestra textura original (es decir la
imagen).
Lo primero que se debe hacer es crear una nueva Texture2D de las mismas dimensiones que la Texture2D original, para utilizarla como lienzo, donde se dibujara la nueva imagen en escala de grises.
public Texture2D GrayScale(Texture2D source)
{
Texture2D target = new Texture2D(source.GraphicsDevice, source.Width, source.Height);
return target;
}
Para poder dibujar en esta textura es necesario
acceder a su información (sus pixeles) esto se hace con el método
genérico GetData el cual se puede utilizar para obtener un array de
bytes con la información de cada punto color.
El formato de color que utiliza Texture2D
es por defecto color de 32 bit RGBA (la A es de Alpha para las
transparencias… no nos importa ahora) así que GetData nos devuelve un
array de bytes utilizando 4 bytes para cada punto de color de la
imagen, por lo cual la capacidad de el array de bytes debe ser 4 *
ancho * alto de la imagen.
public Texture2D GrayScale(Texture2D source)
{
Texture2D target = new Texture2D(source.GraphicsDevice, source.Width, source.Height);
byte[] data = new byte[source.Width * source.Height * 4];
source.GetData<byte>(data);
return target;
}
Ahora se debe recorrer al array de bytes de pixel en
pixel ( es decir en saltos de a 4 bytes ) sabiendo que los 3 primeros
bytes de cada grupo de 4 son respectivamente R, G y B, de tal forma que
multiplicamos el valor de cada byte por el valor de la proporción de
iluminación, este es el valor de la intensidad asi que asignamos a cada
uno de los tres bytes el valor de iluminación que hemos hallado
public Texture2D GrayScale(Texture2D source)
{
byte y = 0;
Texture2D target = new Texture2D(source.GraphicsDevice, source.Width, source.Height);
byte[] data = new byte[source.Width * source.Height * XNA_COLOR_DEPTH];
source.GetData<byte>(data);
for (int i = 0; i < data.Length; i += 4)
{
// Y = R * 0.3 + G * 0.59 + B * 0.11
y = (byte)(data[i] * 0.11f + data[i + 1] * 0.59f + data[i + 2] * 0.3f);
data[i + 2] = data[i + 1] = data[i] = y;
}
return target;
}
Finalmente se utiliza el método genérico SetData de la Texture2D para asignar a la textura el array de bytes modificado.
public Texture2D GrayScale(Texture2D source)
{
byte y = 0;
Texture2D target = new Texture2D(source.GraphicsDevice, source.Width, source.Height);
byte[] data = new byte[source.Width * source.Height * XNA_COLOR_DEPTH];
source.GetData<byte>(data);
for (int i = 0; i < data.Length; i += 4)
{
// Y = R * 0.3 + G * 0.59 + B * 0.11
y = (byte)(data[i] * 0.11f + data[i + 1] * 0.59f + data[i + 2] * 0.3f);
data[i + 2] = data[i + 1] = data[i] = y;
}
target.SetData<byte>(data);
return target;
}
La implementación definitiva puede quedar así:
public Texture2D GrayScale(Texture2D source)
{
Texture2D target = new Texture2D(source.GraphicsDevice, source.Width, source.Height);
byte[] data = new byte[source.Width * source.Height * XNA_COLOR_DEPTH];
source.GetData<byte>(data);
for (int i = 0; i < data.Length; i += 4)
data[i + 2] = data[i + 1] = data[i] = (byte)(data[i] * 0.11f + data[i + 1] * 0.59f + data[i + 2] * 0.3);
target.SetData<byte>(data);
return target;
}
Utilizando este método podemos entonces crear una
textura que sea la representación en escala de grises de otra, lo cual
al ponerlo en pantalla sería así:

Eso es todo!!
En este otro artículo muestro como hacer lo mismo pero con Bitmap y PictureBox:
http://juank.black-byte.com/c-bitmap-convertir-imagen-escala-grises/