Aplicar conversión a escala de grises en una imagen y un video usando el promediado RGB y Luma.
Para ahondar un poco en el tema se explicara antes conceptos fundamentales.
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.
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:
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:
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
Adicionalmente se incluyó la conversión usada por los sistemas UHDTV y HDR modernos que corresponde a:
Para lograr la conversión de las imágenes a escala de grises (promedio RGB, Luma) basta con acceder al componente RGB de cada pixel de la imagen, posteriormente realizar el cálculo ya sea del promedio o del coeficiente Luma (fórmulas expuestas anteriormente) para finalmente crear nuevas imágenes que contendrán el valor obtenido en cada uno de los canales del pixel. De esta manera se obtendrán los resultados mostrados continuos al código fuente de la solución.
Se presenta el código fuente:
1link
2linklet imagenOriginal, imagenPromedio, imagenLuma, imagenLuma2;
3link
4linkfunction preload() {
5link imagenOriginal = loadImage('/vc/docs/sketches/assets/exampleImage.jpg');
6link imagenPromedio = createImage(402, 322);
7link imagenLuma = createImage(402, 322);
8link imagenLuma2 = createImage(402, 322);
9link}
10link
11linkfunction setup() {
12link createCanvas(804, 644);
13link imagenOriginal.resize(402, 322);
14link}
15link
16linkfunction draw() {
17link
18link imagenOriginal.loadPixels();
19link imagenPromedio.loadPixels();
20link imagenLuma.loadPixels();
21link imagenLuma2.loadPixels();
22link
23link for (let y = 0; y < imagenOriginal.height; y++) {
24link for (let x = 0; x < imagenOriginal.width; x++) {
25link
26link let index = (x + y * imagenOriginal.width) * 4;
27link
28link let r = imagenOriginal.pixels[index + 0];
29link let g = imagenOriginal.pixels[index + 1];
30link let b = imagenOriginal.pixels[index + 2];
31link
32link let bw = (r + g + b) / 3; // Promedio
33link
34link imagenPromedio.pixels[index + 0] = bw;
35link imagenPromedio.pixels[index + 1] = bw;
36link imagenPromedio.pixels[index + 2] = bw;
37link imagenPromedio.pixels[index + 3] = 255;
38link
39link
40link let luma = r * .299 + g * .587 + b * .114; //SDTV
41link
42link imagenLuma.pixels[index + 0] = luma;
43link imagenLuma.pixels[index + 1] = luma;
44link imagenLuma.pixels[index + 2] = luma;
45link imagenLuma.pixels[index + 3] = 255;
46link
47link
48link let luma2 = r * .263 + g * .678 + b * .059; //UHDTV HDR
49link
50link imagenLuma2.pixels[index + 0] = luma2;
51link imagenLuma2.pixels[index + 1] = luma2;
52link imagenLuma2.pixels[index + 2] = luma2;
53link imagenLuma2.pixels[index + 3] = 255;
54link
55link }
56link }
57link imagenPromedio.updatePixels();
58link imagenLuma.updatePixels();
59link imagenLuma2.updatePixels();
60link
61link textSize(10);
62link stroke(0);
63link fill(30, 178, 166)
64link textStyle(BOLDITALIC);
65link
66link
67link image(imagenOriginal, 0, 0);
68link text('IMAGEN ORIGINAL', 20, 20);
69link image(imagenPromedio, imagenOriginal.width, 0);
70link text('PROMEDIO RGB', imagenOriginal.width + 20, 20);
71link image(imagenLuma, 0, imagenOriginal.height);
72link text('LUMA (SDTV)', 20, imagenOriginal.height + 20);
73link image(imagenLuma2, imagenOriginal.width, imagenOriginal.height);
74link text('LUMA (UHDTV)', imagenOriginal.width + 20, imagenOriginal.height + 20);
75link}
76link
Se presentan los resultados obtenidos:
Para el caso de conversión a escala de grises en videos podemos pensar que será bastante similar al procedimiento con imágenes y es totalmente acertado, el tratamiento sobre los píxeles del video es exactamente el mismo que el empleado al procesar imágenes, solo 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.
Se presenta el código fuente:
1linkvar widthI= 429; // Anchura del lienzo
2linkvar heightI = 322; // Altura del lienzo
3link
4linklet videoOriginal, videoPromedio, videoLuma, videoLuma2;
5link
6linkfunction setup() {
7link videoOriginal = createVideo(['/vc/docs/sketches/assets/sample.mp4']);
8link videoOriginal.hide();
9link createCanvas(804, 644);
10link
11link videoPromedio = createImage(429, 322);
12link videoLuma = createImage(429, 322);
13link videoLuma2 = createImage(429, 322);
14link videoOriginal.volume(0);
15link}
16link
17linkfunction draw() {
18link
19link background(0);
20link videoOriginal.loadPixels();
21link videoPromedio.loadPixels();
22link videoLuma.loadPixels();
23link videoLuma2.loadPixels();
24link
25link for (let x = 0; x < videoOriginal.width; x++) {
26link for (let y = 0; y < videoOriginal.height; y++) {
27link
28link let index = (x + y * videoOriginal.width) * 4;
29link
30link let r = videoOriginal.pixels[index + 0];
31link let g = videoOriginal.pixels[index + 1];
32link let b = videoOriginal.pixels[index + 2];
33link
34link let bw = (r + g + b) / 3; // Promedio
35link
36link videoPromedio.pixels[index + 0] = bw;
37link videoPromedio.pixels[index + 1] = bw;
38link videoPromedio.pixels[index + 2] = bw;
39link videoPromedio.pixels[index + 3] = 255;
40link
41link let luma = r * .299 + g * .587 + b * .114; //SDTV
42link
43link videoLuma.pixels[index + 0] = luma;
44link videoLuma.pixels[index + 1] = luma;
45link videoLuma.pixels[index + 2] = luma;
46link videoLuma.pixels[index + 3] = 255;
47link
48link let luma2 = r * .263 + g * .678 + b * .059; //UHDTV HDR
49link
50link videoLuma2.pixels[index + 0] = luma2;
51link videoLuma2.pixels[index + 1] = luma2;
52link videoLuma2.pixels[index + 2] = luma2;
53link videoLuma2.pixels[index + 3] = 255;
54link }
55link }
56link videoPromedio.updatePixels();
57link videoLuma.updatePixels();
58link videoLuma2.updatePixels();
59link
60link
61link textSize(10);
62link stroke(0);
63link fill(30, 178, 166)
64link textStyle(BOLDITALIC);
65link
66link image(videoOriginal, 0, 0);
67link text('IMAGEN ORIGINAL', 20, 20);
68link image(videoPromedio, widthI, 0);
69link text('PROMEDIO RGB', widthI + 20, 20);
70link image(videoLuma, 0, heightI);
71link text('LUMA (SDTV)', 20, heightI + 20);
72link image(videoLuma2, widthI, heightI);
73link text('LUMA (UHDTV)', widthI + 20, heightI + 20);
74link
75link}
76link
77linkfunction mousePressed() {
78link videoOriginal.loop();
79link}
Se presentan los resultados obtenidos, para visualizarlos por favor dar click en el canvas:
Se espera continuar con la investigación aplicando distintos algoritmos y evaluando los resultados obtenidos.
Bibliografia
- Wikipedia. (23 de Marzo de 2021). Wikipedia. Obtenido de HSL and HSV: https://en.wikipedia.org/wiki/HSL_and_HSV#Disadvantages
- Wikipedia. (16 de Septiembre de 2020). Wikipedia. Obtenido de Luma (video): https://en.wikipedia.org/wiki/Luma_(video)
- Developer Mozilla. (11 de Marzo de 2021). MDN Web Docs. Obtenido de Digital video concepts: https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Video_concepts