¿Cómo obtener por separado el canal RGB de una imagen con HTML5 y la API Canvas?
- Andrés Cruz
El procesamiento digital de imágenes es una labor que tenemos que realizar en nuestras aplicaciones día a día, desde cambiar el tamaño de imágenes para así optimizarlas a nuestras aplicaciones, como recortes como inclusive alterarse el color, brillo, saturación, e inclusive obtener por separado el canal RGB de una imagen empleando el Canvas como veremos en esta entrada.
En este artículo veremos un tutorial de como obtener los tres canales RGB de una imagen y operarlos por separados, es decir, que los tendremos los canales RGB individualmente como vemos en la imagen promocional de esta entrada; al igual que los en los artículos pasados, usaremos HTML5 y javaScript nativo para lograr el objetivo; el cuerpo del documento no difiere mucho de los otros ejercicios que ya hemos realizado:
Definiendo el HTML
El HTML es realmente simple, al igual que hemos empleado en entradas anteriores consisten en un tag Canvas el cual emplearemos para dibujar la imagen base, es decir, la imagen con sus tres canales que es como está por defecto:
<canvas id="canvas">
<p>Tu navegador no soporta Canvas.</p>
</canvas>
Y tres imágenes en donde serán colocados los tres canales RGB mediante un JavaScript que mostraremos en la siguiente sección de este tutorial:
<img id="r"/>
<img id="g"/>
<img id="b"/>
Definiendo el JavaScript para obtener los canales RGB de la imagen
Lo primero que hacemos es definir las variables globales de siempre para obtener el canvas y su contexto, luego obtenemos acceso a las imágenes que por defecto esta vacías (el atributo src que es el que define la imagen no tiene nada) y lo llenaremos un poco más adelante con el procesamiento que haremos mediante la API Canvas para obtener el canal RGB:
var canvas = document.getElementById('canvas');// canvas
var ctx = canvas.getContext('2d'); // contexto
var imgR = document.getElementById('r');// imagen que representa el canal R
var imgG = document.getElementById('g');// imagen que representa el canal G
var imgB = document.getElementById('b');// imagen que representa el canal B
var srcImg = "image.png";// imagen fuente
Creamos un objeto de tipo Image y le asignamos una imagen fuente que es la que especificamos anteriormente:
img = new Image();
img.src = srcImg;
Una vez que la imagen fuera cargada, vamos a redimensionar el Canvas a el tamaño original de la imagen; dibujamos la imagen en el Canvas y colocamos los canales RGB por separados en la imagen invocando al método getRGB()
; son esos los 3 pasos fundamentales que puedes detallar en el siguiente código:
// cargo la imagen
img.onload = function() {
// reescalamos el canvas a las dimenciones de la imagen
canvas.width = img.width;
canvas.height = img.height;
// dibujamos la imagen en el Canvas
ctx.drawImage(this, 0, 0);
getRGB();
};
La siguiente función permite separar los tres canales RGB de una imagen (que en nuestro caso es la que cargamos anteriormente) y la guardamos en variables individuales, como vemos obtenemos es la imageData
que es un string o un texto muy largo con el contenido de la imagen (algo similar a lo que ocurre cuando abres una imagen con un bloc de notas u otro procesador de texto sencillo).
El siguiente paso que hacemos es volcar el contenido de la imageData
de cada uno de los canales que ya tenemos separados desde el paso anterior en el source o fuente de cada una de nuestras imágenes HTML que definimos anteriormente en nuestro HTML y que referenciamos anteriormente también:
// pinta una imagen por canal
function getRGB() {
// obtenemos el ImageData
var imgd = ctx.getImageData(0, 0, canvas.width, canvas.height);
// obtenemos el ImageData para R
var imgdR = ctx.getImageData(0, 0, canvas.width, canvas.height);
// obtenemos el ImageData para G
var imgdG = ctx.getImageData(0, 0, canvas.width, canvas.height);
// obtenemos el ImageData para B
var imgdB = ctx.getImageData(0, 0, canvas.width, canvas.height);
// cada una de estos array va a tener un solo canal
var pixR = imgdR.data;
var pixG = imgdG.data;
var pixB = imgdB.data;
// va a tener los 3 canales; la usaremos
//para reestablecer los colores originales en el canvas
var pix = imgd.data;
// cambiamos el contraste
for (var i = 0, n = pixR.length; i < n; i += 4) {
//Mantengo el canal R
pixR[i + 1] = 0;//G
pixR[i + 2] = 0;//B
//Mantengo el canal G
pixG[i] = 0;//R
pixG[i + 2] = 0;//B
//Mantengo el canal B
pixB[i] = 0;//R
pixB[i + 1] = 0;//B
}
// retornamos la data modificada al Canvas; canal R
ctx.putImageData(imgdR, 0, 0);
dataURL = canvas.toDataURL();
imgR.src = dataURL;
// retornamos la data modificada al Canvas; canal G
ctx.putImageData(imgdG, 0, 0);
dataURL = canvas.toDataURL();
imgG.src = dataURL;
// retornamos la data modificada al Canvas; canal B
ctx.putImageData(imgdB, 0, 0);
dataURL = canvas.toDataURL();
imgB.src = dataURL;
// retornamos la data original al Canvas; canal RGB
ctx.putImageData(imgd, 0, 0);
}
Para colocar cada uno de los canales RGB en imágenes individuales tenemos que procesar la data de la imagen que obtuvimos en un array en donde solo prevalece uno de los canales, los otros dos son establecidos con el valor de cero.
Analizando la función anterior para obtener procesar los canales RGBs de las imágenes
Obtenemos la ImageData
o data sobre toda la imagen dibujada en el Canvas, la cual nos permitirá operar la data que compone a la imagen a nivel de píxel en su escala RGB; puedes ver más sobre el ImageData
en el siguiente enlace:
// obtenemos el ImageData
var imgd = ctx.getImageData(0, 0, canvas.width, canvas.height);
// obtenemos el ImageData para R
var imgdR = ctx.getImageData(0, 0, canvas.width, canvas.height);
// obtenemos el ImageData para G
var imgdG = ctx.getImageData(0, 0, canvas.width, canvas.height);
// obtenemos el ImageData para B
var imgdB = ctx.getImageData(0, 0, canvas.width, canvas.height);
El siguiente paso consiste en obtener todos los píxeles que componen a la imagen; esta información viene almacenado en un Array
:
// cada una de estos array va a tener un solo canal
var pixR = imgdR.data;
var pixG = imgdG.data;
var pixB = imgdB.data;
// va a tener los 3 canales; la usaremos
// para reestablecer los colores originales en el canvas
var pix = imgd.data;
Esta sección de código representa el corazón del ejercicio; se obtiene un canal por vez, estableciendo los restantes en cero:
// cambiamos el contraste
for (var i = 0, n = pixR.length; i < n; i += 4) {
//Mantengo el canal R
pixR[i + 1] = 0;//G
pixR[i + 2] = 0;//B
//Mantengo el canal G
pixG[i] = 0;//R
pixG[i + 2] = 0;//B
//Mantengo el canal B
pixB[i] = 0;//R
pixB[i + 1] = 0;//B
}
Luego de que tengamos divididos los canales; el siguiente paso consiste en guardar los tres canales RGB en imágenes separadas:
// retornamos la data modificada al Canvas; canal R
ctx.putImageData(imgdR, 0, 0);
dataURL = canvas.toDataURL();
imgR.src = dataURL;
// retornamos la data modificada al Canvas; canal G
ctx.putImageData(imgdG, 0, 0);
dataURL = canvas.toDataURL();
imgG.src = dataURL;
// retornamos la data modificada al Canvas; canal B
ctx.putImageData(imgdB, 0, 0);
dataURL = canvas.toDataURL();
imgB.src = dataURL;
// retornamos la data original al Canvas; canal RGB
ctx.putImageData(imgd, 0, 0);
Resultado Final
Por último puedes ver el resultado de nuestro experimento en la siguiente sección y descargar el código fuente:
Como podemos apreciar, con HTML podemos hacer prácticamente todo: juegos, procesadores de texto, de imágenes en donde la imaginación es el límite.
Desarrollo con Laravel, Django, Flask, CodeIgniter, HTML5, CSS3, MySQL, JavaScript, Vue, Android, iOS, Flutter