Tramado | Dithering

Repo: https://github.com/umarquez/100DaysOfC0D3/tree/master/25-dithering

El tramado es una técnica usada en computación gráfica para crear la ilusión de profundidad de color en imágenes con una paleta de colores limitada (reducción de color). En una imagen tramada, los colores no disponibles en la paleta se aproximan por una difusión de píxeles de color dentro de la gama de colores disponibles. https://es.wikipedia.org/wiki/Tramado

Si, este es un capítulo más de “Jugando con píxeles”; en esta ocasión vamos a agregar esta funcionalidad al proyecto pasado [20/100] Segundo proyecto: Repintado, pero antes, vamos a tratar de explicar en qué consiste el tramado.

Como ya explicamos en capítulos pasados, hacemos uso del Teorema de Pitágoras para calcular el vecino más cercano a un punto dentro de un espacio euclideano definido, la hipotenusa resultante representa la distancia entre el punto inicial y el punto a evaluar; en el caso de la sustitución de colores, esta distancia representa además el error existente entre ambos colores; es decir, que tanto dista uno de otro.

El tramado aprovecha este error para propagarlo a los píxeles cercanos no procesados, de tal forma que conforme se avanza sobre la imagen los píxeles vecinos van acumulando parte del error de los píxeles pasados, y cuando este es procesado, se calcula el sustituto basado en el color original más el error acumulado, pudiendo compensar de cierta manera la falta de colores, para representar un degradado, por ejemplo.

Uno de los métodos más comunes es el algoritmo Floyd-Steinberg, que divide el error en 16 partes iguales, mismas que reparte de la siguiente manera (asumiendo que recorremos la imagen de izquierda a derecha y de arriba a abajo):

Con este método, una paleta blanco y negro por ejemplo, será capaz de representar de una mejor manera los tonos degradados de una imagen.

Vamos a describir este método más a detalle con un ejemplo, como de costumbre.

Ejemplo

Vamos a convertir los siguientes píxeles de una imagen en escala de grises, cada componente (R, G y B) de cada color comparten el mismo valor (0xAAAAAA), por lo que solo vamos a utilizar dos dígitos exadecimales para representar cada tono (0xAA); también contaremos con una paleta de tres colores para realizar la sustitución, los colores de esta paleta serán: 0x00, 0x80 y 0xFF

La matriz de pixeles a procesar será la siguiente:

Paso 1

Comenzamos con el pixel 00-A, calculamos cuál es el color más cercano de la paleta y la diferencia de estos dos la dividimos entre 16.

Paso 2

Distribuimos el error sumando las partes correspondientes a los píxeles cercanos, si estos no estuvieran dentro de la imagen, se deberán omitir.

Obtendremos el siguiente resultado:

Paso 3

Repetimos el mismo procedimiento con el píxel de la derecha 00-B

Y de la misma forma con cada píxel de la fila

Pasos siguientes

Deberemos repetir el mismo procedimiento en cada píxel de la imagen, como alternativa podría alternarse la dirección de las filas, invirtiendo el sentido del filtro para distribuir el error y conseguir de una distribución más uniforme.

Resultado final

Una vez concluido el proceso obtendremos los siguientes valores.

Otros filtros

Además de esta forma de distribuir el error existen otro filtros que son capaces de producir resultados similares, algunos de ellos son:

  • Jarvis, Judice, & Ninke
  • Stucki
  • Atkinson
  • Burkes
  • Sierra

Implementación en Go

En esta ocasión, revisamos el artículo Image Dithering: Eleven Algorithms and Source Code de Tanner Helland para implementar algunos filtros y comparar los resultados utilizando diferentes paletas de color.

OriginalOriginal

Sin tramadoSin tramado

Floyd-SteinbergFloyd-Steinberg

Jarvis, Judice & NinkeJarvis, Judice & Ninke

StuckiStucki

AtkinsonAtkinson

BurkesBurkes

SierraSierra