Leer pixeles de imagen con JavaScript (R, G, B y A)

JavaScript (lado del cliente): leer pixeles de imagen

En ocasiones es necesario leer los pixeles y colores de una imagen con JavaScript del lado del cliente para conocer los valores RGBA o red, green, blue y alpha (rojo, verde, azul, nivel alfa) sin necesidad de algún servidor.

Leer pixeles de imagen con JavaScript (R, G, B y A)
Leer pixeles de imagen con JavaScript (R, G, B y A)

A lo largo de este artículo te voy a enseñar cómo recorrer los pixeles de una imagen con JavaScript del lado del cliente usando OffscreenCanvas y getImageData para leer los pixeles de una imagen desde el client-side.

Prueba la demostración ya mismo en el siguiente enlace. Asegúrate de abrir la consola de depuración para que puedas apreciar cómo se imprime el RGBA de cada pixel de la imagen seleccionada: https://parzibyte.github.io/ejemplos-javascript/pixeles-imagen/

El ejemplo que te mostraré va a leer cada pixel de la imagen a partir de un input de tipo file, pero como siempre te digo: la imagen puede venir de cualquier lugar.

Leer los pixeles de la imagen con JS sirve para esconder texto en una imagen con Esteganografía, por poner un ejemplo.

Explicación del algoritmo

En términos simples, vamos a leer cada pixel de una imagen con JS desde el navegador web; sin pintar la imagen en un canvas visible para el usuario, por ello usaremos OffscreenCanvas y funciones asíncronas.

Vamos a:

  1. Colocar un input de tipo file
  2. Escuchar cuando el usuario seleccione un archivo
  3. Convertir ese archivo a bitmap
  4. Dibujar el bitmap en un canvas fuera de pantalla
  5. Obtener los pixeles de la imagen como un arreglo de una dimensión e iterarlo obteniendo los valores R, G, B y A
  6. Imprimir la información de cada pixel, es decir, los colores y también el nivel alfa

Escuchar cambio de archivo seleccionado

Comencemos viendo el input de tipo file con el cual se va a seleccionar la imagen:

<input type="file" id="imagen">

Obtenemos una referencia al mismo con querySelector y escuchamos el evento change:

const $imagen = document.querySelector("#imagen");
$imagen.addEventListener("change", async () => {
// Leer imagen aquí
});

Los archivos están en la propiedad files del input, que es un arreglo que contiene los archivos seleccionados. Validamos que exista al menos un archivo y accedemos al primero de ellos:

const imagenesSeleccionadas = $imagen.files;
if (imagenesSeleccionadas.length <= 0) {
    return;
}
const primerArchivoDeImagen = imagenesSeleccionadas[0];

Fíjate bien en la constante primerArchivoDeImagen. Este es un archivo de tipo File (valga la redundancia), no es una imagen de tipo Image. Vamos a trabajar únicamente con esa constante, convertirla a bitimage y dibujarla en el canvas.

Nota: por simplicidad, el código presentado no hace la validación de que el archivo sea realmente una imagen.

Dibujando imagen en OffscreenCanvas

Sí, es necesario dibujar la imagen en un canvas (fuera de pantalla) para poder acceder a sus pixeles. No te preocupes, esto está totalmente optimizado y no bloquea la interfaz porque es una función asíncrona.

const imagenComoBitmap = await createImageBitmap(primerArchivoDeImagen);
const canvasFueraDePantalla = new OffscreenCanvas(imagenComoBitmap.width, imagenComoBitmap.height);
const contexto = canvasFueraDePantalla.getContext("2d");
contexto.drawImage(imagenComoBitmap, 0, 0, imagenComoBitmap.width, imagenComoBitmap.height);

La imagen ya está dibujada en el lienzo “invisible”, y al ser un canvas podemos hacer casi todas las operaciones que haríamos en un canvas normal.

Obtener pixeles

Ahora que tenemos acceso al contexto del canvas podemos invocar a getImageData que devolverá, entre otros datos, los pixeles en la propiedad data.

Al invocar a getImageData se nos devuelve un arreglo de una dimensión en donde vienen los valores en el orden de R, G, B, A, R, G, B, A… dicho con otras palabras, cada pixel ocupa 4 posiciones y la longitud del array es un número múltiplo de 4.

El código para leer los valores RGBA de una imagen con JS solo usando el navegador queda así:

const posicionesPorPixel = 4;
const datosDeImagen = contexto.getImageData(0, 0, imagenComoBitmap.width, imagenComoBitmap.height);
const pixeles = datosDeImagen.data;
for (let indice = 0; indice < pixeles.length; indice += posicionesPorPixel) {
    const rojo = pixeles[indice];
    const verde = pixeles[indice + 1];
    const azul = pixeles[indice + 2];
    const alpha = pixeles[indice + 3];
    console.log(`rgba(${rojo}, ${verde}, ${azul}, ${alpha})`);
}

En cada paso del ciclo estamos leyendo exitosamente el RGBA de una imagen e imprimiéndolo en la consola de depuración. Por cierto, el orden de recorrido de los pixeles comienza en x=0,y=0 y termina en x=ancho de imagen - 1, y=alto de imagen - 1.

Dicho con otras palabras, el recorrido es desde la esquina superior izquierda hasta la esquina inferior derecha, o de izquierda a derecha y de arriba hacia abajo.

Poniendo todo junto

Leer pixeles de imagen con JavaScript (R, G, B y A)
Leer pixeles de imagen con JavaScript (R, G, B y A)

La demostración está en el siguiente enlace. No te olvides de abrir la consola de depuración. Recomiendo abrir una imagen no tan grande: https://parzibyte.github.io/ejemplos-javascript/pixeles-imagen/

El código completo (HTML y JavaScript) lo encuentras en GitHub: https://github.com/parzibyte/ejemplos-javascript/tree/master/pixeles-imagen

Nota importante: en la captura, la imagen es muy pequeña (de 3×4 pixeles) y se ve como pixel art. Lo he hecho así con fines demostrativos (usando GIMP para hacer ese zoom), pero funciona con cualquier imagen y cualquier formato. He probado con jpg, png y webp.

Por cierto, si te lo preguntas, he usado este código en mi diseñador de recibos ESC POS para impresoras térmicas. El código, no tan ordenado, está en: https://github.com/parzibyte/esc-pos-ticket-designer/blob/main/src/components/Operaciones/DefinirCaracterPersonalizado.vue#L201

Estoy aquí para ayudarte 🤝💻


Estoy aquí para ayudarte en todo lo que necesites. Si requieres alguna modificación en lo presentado en este post, deseas asistencia con tu tarea, proyecto o precisas desarrollar un software a medida, no dudes en contactarme. Estoy comprometido a brindarte el apoyo necesario para que logres tus objetivos. Mi correo es parzibyte(arroba)gmail.com, estoy como@parzibyte en Telegram o en mi página de contacto

No te pierdas ninguno de mis posts 🚀🔔

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.

Dejar un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *