Star

Created With

linkConversión a escala de grises.

linkProblem statement

Aplicar conversión a escala de grises en una imagen y un video usando el promediado RGB y Luma.

linkBackground

Para ahondar un poco en el tema se explicara antes conceptos fundamentales.

linkRGB

RGB (red, green, blue) es la composición del color en términos de la intensidad de los colores primarios de la luz, con el se realiza el tratamiento de la señal de vídeo que trata por separado las señales de los tres colores rojo, verde y azul. Al usarlo independientemente, proporciona mayor calidad y reproducción más fiel del color.

El modelo RGB está formado por los tres componentes de colores primarios aditivos y como mínimo un componente de sincronismo. Los componentes de color son las señales rojo, verde y azul; siendo transmitidos cada uno independiente y aislado del resto.

De esta forma no hay pérdidas en el tratamiento de la imagen puesto que los colores primarios siguen existiendo como tal en su transmisión. Por el contrario, mediante este sistema hay mucha información redundante, con el consiguiente aumento del ancho de banda necesario respecto a otros métodos de transmisión. Por ejemplo, cada color lleva el valor de brillo de toda la imagen, de forma que esta información está por triplicado.

linkEscala de Grises

En computación una escala de grises es la representación de una imagen en la que cada pixel se dibuja usando un valor numérico individual que representa su luminancia, en una escala que se extiende entre blanco y negro.

Algunas formas de transformar una imagen a escala de grises:

linkPromedio RGB

Es sin duda la solución trivial donde por medio del promedio de los 3 canales (rojo,verde y azul) para cada pixel de la imagen obtendremos un valor que sobreescribirá el valor de cada canal para este píxel, de esta manera la imagen que obtenemos tendrá un gris bastante razonable.

Dando una definición un poco más formal esta es simplemente la proyección de un punto sobre el eje neutral, la altura vertical de un punto en nuestro cubo inclinado. La ventaja es que, junto con los cálculos de tonalidad y crominancia de la distancia euclidiana, esta representación conserva las distancias y los ángulos de la geometría del cubo RGB.

Dicho esto se podría calcular con la siguiente fórmula:

I=avg(R,G,B)=13(R+G+B)I = avg(R,G,B) = \frac{1}{3} (R + G + B)1

linkLuma

En una transmisión de señal de vídeo, luma es el componente que codifica la información de luminosidad de la imagen. En términos generales, es algo muy similar a la versión en blanco y negro de la imagen original. Luma y crominancia combinadas proporcionan la señal denominada señal de vídeo compuesto, utilizada en multitud de aplicaciones; pueden enviarse conjuntamente o transmitirse independientemente. Forman parte de la codificación de vídeo en los estándares de TV NTSC y PAL, entre otros.

Es un término comúnmente utilizado en el procesamiento digital de imágenes para caracterizar a cada píxel.

En los formatos digitales que siguen el estándar CCIR_601 que sería equivalente a SDTV , la luma de un píxel se calcula con la fórmula

Y=0,299R+0,587G+0,114B.Y'=0,299 * R + 0,587 * G + 0,114 * B.1

Adicionalmente se incluyó una conversión usada en multiples aplicaciones, corresponde al componente mas grande entre los canales :

Y=max(R,G,B)=M.Y'=max(R,G,B) = M.1

linkCode (solution) & results

linkEscala de grises en imágenes

Para lograr la conversión de las imágenes a escala de grises (promedio RGB, Luma) en esta ocasión se trabajara con WebGL, la idea será mostrar en el canvas un cubo en 3D con la imagen a la que se le hará la transformación posicionada en cada una de sus caras y con la ayuda del Fragment Shader se realizaran dichas transformaciones dependiendo de la entrada que suministre el usuario.

Podremos observar cada uno de los filtros mencionados al dar click sobre el canvas y oprimir una de las teclas mencionadas a continuación:

TeclaResultado
1Promedio RGB
2Componente más grande
3Luma (SDTV)
4Imagen Original

linkResultados

Se presentan los resultados obtenidos:

RGBImagen.js
1link let imgShader;

2link let shaderTexture;

3link let img;

4link

5link let angle=0;

6link let gray = 0;

7link

8link function preload(){

9link // cargamos imagen y shader

10link img = loadImage('/vc/docs/sketches/assets/exampleImage.jpg');

11link imgShader = loadShader('/vc/docs/sketches/scripts/hardware/texture.vert','/vc/docs/sketches/scripts/hardware/texture.frag');

12link }

13link

14link function setup() {

15link

16link pixelDensity(1);

17link createCanvas(windowWidth, 400, WEBGL);

18link noStroke();

19link

20link // inicializar la capa del createGraphics y Quitar bordes

21link shaderTexture = createGraphics(512, 512, WEBGL);

22link shaderTexture.noStroke();

23link }

24link

25link function draw() {

26link shaderTexture.shader(imgShader);

27link

28link //Se defomem valores uniformes para el fragment shader

29link imgShader.setUniform("u_img", img);

30link imgShader.setUniform("u_key", gray);

31link

32link

33link // Se Renderiza el shader

34link shaderTexture.rect(0,0,width,height);

35link

36link background(0);

37link

38link // Efecto de bombilla

39link pointLight(255, 255, 255, 0, 0, 500);

40link let dx= mouseX-width/2;

41link let dy= mouseY-height/2;

42link pointLight(100,250,255,dx,dy,100);

43link

44link translate(0, 0, 0);

45link

46link push();

47link //Se pasa el shader de la imagen como textura

48link texture(shaderTexture);

49link translate(0, 0, 0);

50link rotateZ(angle);

51link rotateX(angle);

52link rotateY(angle*2);

53link box(200);

54link pop();

55link

56link // Rotacion de la caja

57link angle += 0.001;

58link

59link

60link }

61link

62link // declaramos los eventos

63link

64link function keyPressed() {

65link if (key === '4') {

66link gray = 0;

67link } else if (key === '1') {

68link gray = 1;

69link } else if (key === '2') {

70link gray = 2;

71link } else if (key === '3') {

72link gray = 3;

73link }

74link }

75link

76link function windowResized(){

77link residzeCanvas(windowWidth, windowHeight);

78link }

texture.frag
1link #ifdef GL_ES

2link precision mediump float;

3link #endif

4link

5link varying vec2 vTexCoord;

6link uniform sampler2D u_img;

7link uniform int u_key;

8link

9link float grayscale(vec3 color) {

10link float lightness;

11link

12link if (u_key==1){

13link float I=(color.r + color.g + color.b) / 3.0; // Promedio RGB

14link lightness = I;

15link } else if (u_key==2){

16link float V= max(max(color.r,color.g),color.b); // Componente mayot

17link lightness = V;

18link } else if (u_key==3){ // Luma

19link float Y= dot(color, vec3(0.299, 0.587, 0.114)); // SDTV

20link lightness = Y;

21link }

22link return lightness;

23link }

24link

25link void main() {

26link vec2 uv = vTexCoord;

27link

28link uv.y = 1.0 - uv.y;

29link vec4 tex = texture2D(u_img, uv);

30link float gray =grayscale(tex.rgb);

31link

32link float threshR = gray ;

33link float threshG = gray ;

34link float threshB = gray ;

35link

36link if (u_key==0){

37link threshR = tex.r ;

38link threshG = tex.g ;

39link threshB = tex.b ;

40link }

41link vec3 thresh = vec3(threshR, threshG, threshB);

42link gl_FragColor = vec4(thresh, 1.0);

43link }

texture.vert
1link // vert file and comments from adam ferriss

2link // https://github.com/aferriss/p5jsShaderExamples

3link

4link attribute vec3 aPosition;

5link attribute vec2 aTexCoord;

6link // lets get texcoords just for fun!

7link varying vec2 vTexCoord;

8link

9link void main() {

10link

11link // copy the texcoords

12link vTexCoord = aTexCoord;

13link // copy the position data into a vec4, using 1.0 as the w component

14link vec4 positionVec4 = vec4(aPosition, 1.0);

15link

16link // scale the rect by two, and move it to the center of the screen

17link // if we don't do this, it will appear with its bottom left corner in the center of the sketch

18link // try commenting this line out to see what happens

19link positionVec4.xy = positionVec4.xy * 2.0 - 1.0;

20link

21link // send the vertex information on to the fragment shader

22link gl_Position = positionVec4;

23link }

linkEscala de grises en Videos

Para el caso de conversión a escala de grises en videos podemos pensar que será bastante similar al procedimiento aplicado anteriormente en imágenes y es totalmente acertado, con la diferencia de que en este caso lo mostraremos en una superficie 2Dy que debemos tener la consideración de obtener la información del video con los métodos correspondientes de P5, como lo veremos a continuación en la pestaña de código.

Podremos observar cada uno de los filtros mencionados al dar click sobre el canvas y oprimir una de las teclas mencionadas a continuación:

TeclaResultado
1Promedio RGB
2Componente más grande
3Luma (SDTV)
4Imagen Original

linkResultados

Se presentan los resultados obtenidos, para visualizarlos por favor dar click en el canvas:

RGBVideo.js
1link let theShaderVideo;

2link let shaderVideo;

3link let video;

4link

5link let gray = 0;

6link

7link function preload(){

8link // cargamos video y shader

9link video = createVideo(['/vc/docs/sketches/assets/sample.mp4']);

10link video.hide();

11link

12link theShaderVideo = loadShader('/vc/docs/sketches/scripts/hardware/texture.vert','/vc/docs/sketches/scripts/hardware/texture.frag');

13link }

14link

15link function setup() {

16link pixelDensity(1);

17link createCanvas(windowWidth, 400, WEBGL);

18link noStroke();

19link

20link // inicializar la capa del createGraphics y Quitar bordes

21link shaderVideo = createGraphics(windowWidth, windowHeight, WEBGL);

22link shaderVideo.noStroke();

23link

24link video.volume(0);

25link }

26link

27link function draw() {

28link shaderVideo.shader(theShaderVideo);

29link

30link //Se defomem valores uniformes para el fragment shader

31link

32link theShaderVideo.setUniform('u_img', video);

33link theShaderVideo.setUniform('u_key', gray);

34link

35link // Se Renderiza el shader

36link shaderVideo.rect(0,0,width,height);

37link

38link background(0);

39link

40link //Se pasa el shader del video como textura

41link push();

42link texture(shaderVideo);

43link translate(0, 0, -100);

44link plane(width,height);

45link pop();

46link }

47link

48link // declaramos los eventos

49link function keyPressed() {

50link if (key === '4') {

51link gray = 0;

52link } else if (key === '1') {

53link gray = 1;

54link } else if (key === '2') {

55link gray = 2;

56link } else if (key === '3') {

57link gray = 3;

58link }

59link }

60link

61link function windowResized(){

62link resizeCanvas(windowWidth, windowHeight);

63link }

64link

65link function mousePressed() {

66link video.loop();

67link

68link }

texture.frag
1link #ifdef GL_ES

2link precision mediump float;

3link #endif

4link

5link varying vec2 vTexCoord;

6link uniform sampler2D u_img;

7link uniform int u_key;

8link

9link float grayscale(vec3 color) {

10link float lightness;

11link

12link if (u_key==1){

13link float I=(color.r + color.g + color.b) / 3.0; // Promedio RGB

14link lightness = I;

15link } else if (u_key==2){

16link float V= max(max(color.r,color.g),color.b); // Componente mayot

17link lightness = V;

18link } else if (u_key==3){ // Luma

19link float Y= dot(color, vec3(0.299, 0.587, 0.114)); // SDTV

20link lightness = Y;

21link }

22link return lightness;

23link }

24link

25link void main() {

26link vec2 uv = vTexCoord;

27link

28link uv.y = 1.0 - uv.y;

29link vec4 tex = texture2D(u_img, uv);

30link float gray =grayscale(tex.rgb);

31link

32link float threshR = gray ;

33link float threshG = gray ;

34link float threshB = gray ;

35link

36link if (u_key==0){

37link threshR = tex.r ;

38link threshG = tex.g ;

39link threshB = tex.b ;

40link }

41link vec3 thresh = vec3(threshR, threshG, threshB);

42link gl_FragColor = vec4(thresh, 1.0);

43link }

texture.vert
1link // vert file and comments from adam ferriss

2link // https://github.com/aferriss/p5jsShaderExamples

3link

4link attribute vec3 aPosition;

5link attribute vec2 aTexCoord;

6link // lets get texcoords just for fun!

7link varying vec2 vTexCoord;

8link

9link void main() {

10link

11link // copy the texcoords

12link vTexCoord = aTexCoord;

13link // copy the position data into a vec4, using 1.0 as the w component

14link vec4 positionVec4 = vec4(aPosition, 1.0);

15link

16link // scale the rect by two, and move it to the center of the screen

17link // if we don't do this, it will appear with its bottom left corner in the center of the sketch

18link // try commenting this line out to see what happens

19link positionVec4.xy = positionVec4.xy * 2.0 - 1.0;

20link

21link // send the vertex information on to the fragment shader

22link gl_Position = positionVec4;

23link }

linkConclusions & future work

Se espera continuar con la investigación aplicando distintos algoritmos y evaluando los resultados obtenidos.

Bibliografia

  • Wikipedia. Obtenido de HSL and HSV: https://en.wikipedia.org/wiki/HSL_and_HSV#Disadvantages
  • Wikipedia. Obtenido de Luma (video): https://en.wikipedia.org/wiki/Luma_(video)
  • Processing. Shaders: https://processing.org/tutorials/pshader/
  • Welcome to p5.js shaders: https://itp-xstory.github.io/p5js-shaders/#/
Conversión a escala de grises.Problem statementBackgroundRGBEscala de GrisesPromedio RGBLumaCode (solution) & resultsEscala de grises en imágenesResultadosEscala de grises en VideosResultadosConclusions & future work

Home

Workshopschevron_right
Imaging & Videochevron_right
Softwarechevron_right
Hardwarechevron_right
Renderingchevron_right
Algovischevron_right
Computer Graphicschevron_right
HCIchevron_right
P5 Code Snippetschevron_right
Memberschevron_right