• Lunes 23 de Diciembre de 2024, 07:17

Autor Tema:  Pasar Una Imagen A Escala De Grises  (Leído 6131 veces)

Ruben3d

  • Miembro HIPER activo
  • ****
  • Mensajes: 710
  • Nacionalidad: es
    • Ver Perfil
    • Web personal
Pasar Una Imagen A Escala De Grises
« en: Viernes 11 de Junio de 2004, 14:49 »
0
Hola.

He realizado una función para pasar imágenes a escala de grises que es lo suficientemente rápida para ser usada en tiempo real. La he probado en un emulador de PocketPC con un display de 240x320 y supera los 100 fps en un ordenador a 1.5GHz con varios trabajos en segundo plano (entre ellos el seti@home). No sé a qué velocidad funcionará ejecutandose nativamente en un PC, pero supongo que será viable a 640x480 en una máquina equivalente.

Para pasar a escala de grises tengo que obtener la luminosidad de cada píxel, multiplicando cada componente de color por un peso y redondeando a un número entero el resultado, de esta manera:
Código: Text
  1. gris = (unsigned char) (0.30*ROJO(pixel) + 0.59*VERDE(pixel) + 0.11*AZUL(pixel));
  2.  
Esto implica tres multiplicaciones de números flotantes por píxel, dos sumas y un redondeo. Estuve meditando y me di cuenta de que podía hacerse sólo con números enteros, que sería más rápido:
Código: Text
  1. gris = (unsigned char) (30*ROJO(pixel) + 59*VERDE(pixel) + 11*AZUL(pixel))/100;
  2.  
Esto elimina el uso de los números flotantes, pero añade una división entera, así que no me convenció. Pensé en usar sólo desplazamientos de bits, aunque de esta manera se pierde exactitud:
Código: Text
  1. gris = ((ROJO(pixel)<<1) + (VERDE(pixel)<<2) + AZUL(pixel))/7;
  2.  
De esta manera, el peso del rojo sería 0.28, el del verde 0.57 y el del azul 0.14. No son iguales a los originales, pero es una aproximación bastante buena. Aún me quedaba solucionar la división. Es fácila darse cuenta de que 7 está muy próximo a 8, que se arregla con un desplazamiento de bits:
Código: Text
  1. gris = ((ROJO(pixel)<<1) + (VERDE(pixel)<<2) + AZUL(pixel))>>3;
  2.  
De esta manera, paso un píxel a escala de grises y sólo realizo desplazamientos de bits y sumas de enteros. Sin embargo, he de pagar el precio de un leve oscurecimiento de la imagen final, causado por dividir entre 8 en vez de entre 7.

Os dejo aqui la función acabada, por si a alguien le parece útil:
Código: Text
  1. void io3d::CGraphics2D::grayscaleArea(int x1, int y1, int x2, int y2)
  2. {
  3.   if (x1>x2)
  4.   {
  5.     switchElems<int>(x1,x2);
  6.   }
  7.   if (y1>y2)
  8.   {
  9.     switchElems<int>(y1,y2);
  10.   }
  11.  
  12.   unsigned char c;
  13.  
  14.   int next_line = -(x2-x1+1)+m_nNormYPitch;
  15.  
  16.   WORD *pPixel = &m_pBackBuffer[x1+y1*m_nNormYPitch];
  17.  
  18.   for (int y=y1; y<=y2; y++)
  19.   {
  20.     for (int x=x1; x<=x2; x++)
  21.     {
  22.       // Los pesos son 0.30, 0.59 y 0.11, pero realizo una aproximación
  23.       // Multiplico por 2, 4 y 1. Va incluido en el despl. el bajar G a 5 bits
  24.       // Debería dividir por 7, pero hacerlo por 8 es mucho más rápido (aunque se
  25.       // oscurece un poco la imagen).
  26.       c = ( (CR5(*pPixel)<<1) + (CG6(*pPixel)<<1) + CB5(*pPixel) ) >> 3;
  27.       *(pPixel++) = C565(c,c<<1,c);
  28.     }
  29.  
  30.     // Reajuste para la siguiente línea
  31.     pPixel += next_line;
  32.   }
  33. }
  34.  

La función está pensada para poder ser aplicada a áreas rectangulares arbitrarias. Para que se aplique a la pantalla completa hay que usar como parámetros (0,0, anchura-1, altura-1). Esta función asume que hay un puntero llamado m_pBackBuffer, que apunta a la memoria en donde se pinta y es de tipo WORD*, pues trabaja en modo de 16 bits de color. Las macros CR5, CG6 y CB5 extraen cada componente de color del píxel de tipo WORD con desplazamientos y máscaras, y lo devuelven en el rango [0,31] para el rojo y el azul y en el rango [0,63] para el verde (por tener un bit más). En m_nNormYPitch se guarda cuánto hay que sumarle al puntero que apunta a un píxel para avanzar a la siguiente fila. C565 es una macro que recibe las tres componentes de color en los rangos citados anteriormente y los compacta en un WORD. Por último, hay que tener en cuenta en todo momento que el verde ocupa un bit más y por eso hay que realizar algunos desplazamientos no esperados.

Espero que esto os sirva. Se me ocurren diversas aplicaciones, como hacer que detrás de una pantalla en un escenario de un juego de plataformas se vea en escala de grises (algo así creo que vi en Jazz Jackrabbit, de Apogee?), o cualquier otro efecto (para escenas cinemáticas, junto a ruido añadido para dar impresión de película antigua).

Un saludo.

Ruben3d

Amilius

  • Miembro HIPER activo
  • ****
  • Mensajes: 665
    • Ver Perfil
Re: Pasar Una Imagen A Escala De Grises
« Respuesta #1 en: Viernes 11 de Junio de 2004, 18:24 »
0
El original: (con algo más de precisión)

gris = (unsigned char) (0.30*ROJO(pixel) + 0.59*VERDE(pixel) + 0.11*AZUL(pixel));

Es usado en los tv "blanco y negro", sistema que usan también los tv a color (separan la imagen en un canal de Y luminosidad y otros 2 sólo para el color UV.)
Algun rato pongo las matrices para pasar a YUV y luego a RGB.


Bueno, verifique que esto también va muy bien si quieres velocidad y no exactitud: (Además que no pierdes claridad):

De más rápido a más lento (no pierden claridad):

1.-
gris=(r+g<<1+b )>>2;
Nota: El rojo es tomado como un color tan oscuro como el azul. Bastante rápido sin duda.

2.-
gris=(r*3 + g<<2 + b ) >> 3  ;
Nota: El rojo es tomado como un color mucho más brillante. (Buenos resultados para fotos de personas!)

3.-
gris=(r*5 + g<<3 + b*3) >> 4;  
Nota: Este es intermedio entre 1 y 2, pero más lento

OJO, como saben una multiplicación entera pesa en tiempo mucho menos cuantos menos bits tenga el multiplicador (una suma por cada bit en 1), habría que optimizarlo en ASM para asegurarse que los multiplicadores sean 5(2 bits en 1) y 3(2 bits en 1).  Si el multiplicador sólo tiene 1 bit, se puede reemplazar por desplazamiento. :)


Y finalmente un truco para transparencia, alpha 50% extra rápida (se ve muy bien!) en 16 bits: (lo pongo para pascal, and es &, shr es >>)

Mascara:=$F7DE;//sin los bits menos significativos.

//el calculo se hará en 32 bits, por lo que no existirá desbordamiento. Pixel1 y 2 son enteros de 16 bits.

Resultado:=((Pixel1 and Mascara)+(Pixel2 and Mascara)) shr 1;

Ruben3d

  • Miembro HIPER activo
  • ****
  • Mensajes: 710
  • Nacionalidad: es
    • Ver Perfil
    • Web personal
Re: Pasar Una Imagen A Escala De Grises
« Respuesta #2 en: Viernes 11 de Junio de 2004, 22:35 »
0
Hola.

¡Muchas gracias por tus propuestas! He probado la primera, por ser la única que carece de multiplicaciones. Los resultados, en cuanto a velocidad, son muy buenos. El único inconveniente es que el azul adquiere tanta luz como el rojo, aunque la luminosidad del blanco es muy buena, pues no pierde intensidad, y se aprecian tonos oscuros que antes no aparecían.

No sé cuál de las dos utilizar, tu opción es más rápida y no se oscurece, pero la mia mantiene la intensidad de cada componente de color. Cuando escriba el código del gestor de texturas lo probaré sobre imágenes reales a ver qué resultados da cada técnica. De momento he compuesto una imagen a modo comparativo, con los colores sin aplicar ningún filtro, con mi versión de escala de grises y con la tuya (al final del post).

Un saludo.

Ruben3d

Nota: La imagen está en formato PNG. Internet Explorer no trata bien los PNG y no tiene en cuenta la correción gamma de la imagen, por lo que podría aparecer más oscura de lo que en verdad es. Mejor abrirla con algún programa de dibujo (la he creado con Gimp2).


EDIT: No he guardado el gamma de la imagen y debería verse bien (me da esta opción la última versión del Gimp :D)

Geo

  • Miembro de PLATA
  • *****
  • Mensajes: 1011
  • Nacionalidad: mx
    • Ver Perfil
    • http://hsblog.mexchip.com
Re: Pasar Una Imagen A Escala De Grises
« Respuesta #3 en: Sábado 12 de Junio de 2004, 09:01 »
0
Qué tal, hace poco estuve haciendo una función para convertir un mapa de bits a escala de grises, en aquella ocasión no empleé los porcentajes que están utilizando para cada componente RGB, ¿cómo es que se definen estos porcentajes?

Saludos,
José Jorge (Geo).
La imaginación es el límite.
Hardware & Software | Mexchip

Amilius

  • Miembro HIPER activo
  • ****
  • Mensajes: 665
    • Ver Perfil
Re: Pasar Una Imagen A Escala De Grises
« Respuesta #4 en: Lunes 14 de Junio de 2004, 01:05 »
0
Ecuación 2 sin multiplicación: (reemplazando por una suma y un desplazamiento)

gris=( r+r<<1+g<<2+b ) >> 3 ;

Nota: r=3/8  g=4/8 y b=1/8

Ecuación 3 sin las 2 multiplicaciónes: (reemplazando por 2 sumas y 2 desplazamientos)

gris=(r+r<<2 + g<<3 + b+ b<<1) >> 4;

Nota: r=5/16  g=8/16 y b=3/16
----
Para GEO:

Los porcentajes representan que tan luminosos quieras (o creas) que sean los colores, unos respecto de otros.

A continuación adjunto los valores para el YIQ, un estándar bastante usado.
----------

Aqui va el famoso YIQ:

El modelo de color YIQ aplicado a compresión de imágenes:

En esta sección es descrito el modelo de color YIQ y cómo permite optimizar la compresión de imágenes. También es mostrado el proceso que se lleva a cabo para cambiar una imagen del modelo de color RGB al modelo YIQ y viceversa.

El modelo de color YIQ permite separar la información de color (IQ) de la información de luminosidad (Y), en un pixel de una imagen. El modelo YIQ fue especificado por la NTSC (National Television System Committee) para permitir la televisión en colores y hacerla compatible con la televisión en blanco y negro. Las ecuaciones 3.1 a la 3.3 muestran el proceso de cambio de modelo de color RGB a YIQ. [FOL96]

Y   =   0.299 R + 0.587 G + 0.114 B            (Ec. 3.1)
I   =   0.500 R  - 0.231 G  - 0.269 B            (Ec. 3.2)
Q   =   0.203 R  - 0.500 G + 0.297 B            (Ec. 3.3)

Las cantidades en la ecuación 3.1 fueron definidas a partir de las características del sistema visual humano. Se puede apreciar que mientras el color verde (G) tiene gran importancia al momento de determinar la cantidad de luz existente en un pixel, los colores rojo ® y azul (B) no influyen a tal grado. La inversa de la matriz constituida por los coeficientes de las ecuaciones 3.1, 3.2 y 3.3 es usada para el cambio de modelo YIQ a RGB, resultando las ecuaciones 3.4, 3.5 y 3.6.
 
R   =   Y + 1.1390 I + 0.6478 Q            (Ec. 3.4)
G   =   Y  - 0.3233 I -  0.6766 Q            (Ec. 3.5)
B   =   Y  - 1.3228 I + 1.7851 Q            (Ec. 3.6)

En la retina del ojo humano existen alrededor de 130 millones de células (bastoncillos) dedicadas a percibir la luminosidad. En contraste cerca de 7 millones de células (conos) perciben el color. La habilidad de discriminar información espacial de color es mucho más débil que la habilidad de discriminar información espacial monocromática. Esto permite que se pueda asignar menor número de bits a los componentes I y Q. Así mismo, los objetos que cubren una reducida parte del campo visual producen una sensación limitada de color. Para especificar esa sensación limitada de color basta un componente (I o Q), lo que significa que es posible asignar menor número de bits a uno de los componentes de color sin pérdida perceptible de calidad. El resultado obtenido es una imagen con mayor calidad visual de la que se hubiera obtenido al asignar anchos de banda iguales a cada componente.

Para obtener resultados más eficientes en tiempo, por lo general se modifican los coeficientes de las ecuaciones 3.4 a 3.6. En el trabajo desarrollado se han tomado los coeficientes indicados en las ecuaciones 3.7 a la 3.9.

Y   =   (5R +8G + 3B)/16               (Ec. 3.7)
I   =   (512 + 2R - G - B)/4               (Ec. 3.8)
Q   =   (1280 + 2R  - 5G + 3B)/10             (Ec. 3.9)

Estas modificaciones permiten optimizar el cambio de modelo de color, evitando el uso de operaciones en punto flotante y permiten usar desplazamientos binarios en lugar de divisiones casi en todos los cálculos. Las sumas adicionales, al calcular los elementos I y Q, evitan que se trabaje con números negativos y sus valores no salgan del intervalo 0 a 255.

Finalmente, el cambio de modelo de color RGB a YIQ permite la compresión de imágenes en escala de grises, tomando sólo el componente Y. Además YIQ logra aprovechar las limitaciones del sistema visual humano y así asignar mejor el espacio de almacenamiento para obtener mejores resultados visuales con la misma cantidad de recursos.

Ruben3d

  • Miembro HIPER activo
  • ****
  • Mensajes: 710
  • Nacionalidad: es
    • Ver Perfil
    • Web personal
Re: Pasar Una Imagen A Escala De Grises
« Respuesta #5 en: Lunes 14 de Junio de 2004, 16:50 »
0
Gracias por todas las ecuaciones. Cuando cargue y muestre imágenes las probaré todas para estudiar la calidad visual y la velocidad.

Un saludo.

Ruben3d

JuanK

  • Miembro de ORO
  • ******
  • Mensajes: 5393
  • Nacionalidad: co
    • Ver Perfil
    • http://juank.io
Re: Pasar Una Imagen A Escala De Grises
« Respuesta #6 en: Lunes 14 de Junio de 2004, 20:18 »
0
hola,
siempre he visto que enconocimientos amilus y el ruben
son bastante avanzados,
los invito a que tambien participen en esta comunidad,
aunque no todos los que estamos alla somos tan expertos como ustedes:

http://www.teleportmedia.com/foro
[size=109]Juan Carlos Ruiz Pacheco
[/size]
Microsoft Technical Evangelist
@JuanKRuiz
http://juank.io

Ruben3d

  • Miembro HIPER activo
  • ****
  • Mensajes: 710
  • Nacionalidad: es
    • Ver Perfil
    • Web personal
Re: Pasar Una Imagen A Escala De Grises
« Respuesta #7 en: Lunes 14 de Junio de 2004, 20:36 »
0
Hola.

Cita de: "JuanK"
tan expertos como ustedes

Ojalá fuera experto. Si así me considerase ya habría echado el currículum en Pyro :lol:. Anda que no me queda por aprender :wacko:. De todas formas me pasaré a ver que tal. Gracias por la invitación.

Un saludo.

Ruben3d

JuanK

  • Miembro de ORO
  • ******
  • Mensajes: 5393
  • Nacionalidad: co
    • Ver Perfil
    • http://juank.io
Re: Pasar Una Imagen A Escala De Grises
« Respuesta #8 en: Lunes 14 de Junio de 2004, 20:56 »
0
Citar
ya habría echado el currículum en Pyro
Que quiere decir eso?
[size=109]Juan Carlos Ruiz Pacheco
[/size]
Microsoft Technical Evangelist
@JuanKRuiz
http://juank.io

Ruben3d

  • Miembro HIPER activo
  • ****
  • Mensajes: 710
  • Nacionalidad: es
    • Ver Perfil
    • Web personal
Re: Pasar Una Imagen A Escala De Grises
« Respuesta #9 en: Lunes 14 de Junio de 2004, 21:00 »
0
Citar
Que quiere decir eso?
Mandar el curriculum aqui: Pyro Studios - Anuncios de empleo

Un saludo.

Ruben3d