Exclamación C# - XNA- Como Convertir Una Imagen a Escala de Grises
Hola, nuevamente les comparto uno de los artículos de mi blog!
Este articulo es una copia cruzada de mi blog original, visítalo en:
http://juank.black-byte.com/xna-convertir-imagen-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 liego 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 percepció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:
PHP:
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:
PHP:
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.
PHP:
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.
PHP:
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
PHP:
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.3f + data[i + 1] * 0.59f + data[i + 2] * 0.11f);
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.
PHP:
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.3f + data[i + 1] * 0.59f + data[i + 2] * 0.11f);
data[i + 2] = data[i + 1] = data[i] = y;
}
target.SetData<byte>(data);
return target;
}
La implementación definitiva puede quedar así:
PHP:
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.3f + data[i + 1] * 0.59f + data[i + 2] * 0.11f);
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!!