Compresión fractal de imágenes

La teoría de fractales es apasionante, y su aplicación a la compresión de imágenes, cuanto menos, interensante

Descripción

Con la aplicación podrás:

  • Comprimir imágenes con este método fractal
  • Descomprimir imágenes previamente comprimidas a otro formato de imagen

Hace uso de las librerías de plataforma, con las siguientes características:

  • Multi-idioma
  • Multi-resolución (zoom configurable)
  • Opción de modo oscuro
  • Notificación de nueva versión


Ventajas:

  • Si se programa bien, es independiente de la resolución de la imagen
  • Es un método de compresión muy exótico
  • Permite aprender en el camino


Desventajas:

  • El proceso de compresión es muy lentooo
  • No se consiguen ratios de compresión muy altos (por lo menos con los parámetros que he usado)

Descripción del código

Es una implementación de este algoritmo de compresión fractal de imágenes publicado en IEEE en mi época universitaria

Se hace uso de la librería de triangulación incremental de Delaunay usada en la aplicación de Morphing


Algoritmo a alto nivel:

  • Compresión:
    • La compresión fractal se caracteriza por que no se guardan valores de píxeles, sino relaciones entre regiones de la imagen (entre triángulos en este caso).
    • Se divide la imagen en una triangulación, con los triángulos de dominio.
    • Se divide la imagen en otra triangulación, con los triángulos (mayores) para el codebook.
    • Las triangulaciones son dinámicas, se usa el algoritmo split and merge en función de la varianza de los píxeles de los triángulos.
    • Una vez se tiene la triangulación para el codebook, se eligen los 2^n triángulos más representativos, que serán el codebook, donde el valor de n incidirá drásticamente en el tiempo de compresión, y un poco en el ratio de compresión y en la calidad obtenida.
    • Luego, para cada triángulo de dominio, se busca el mapeo óptimo con un triángulo del codebook (con un criterio de mínimo error cuadrático medio (MSE))
    • El mapeo óptimo entre triángulos se busca haciendo combinaciones de:
      • Permutaciones de los vértices (6 posibilidades)
      • Buscando un offset para la media
      • Buscando un factor de escala entre 0 y 1 para aplicarlo a la desviación de la media (entre 0 y 1, para evitar la divergencia del proceso iterativo de descompresión)
      Esos parámetros deberán cuantizarse con pocos bits, para que el tiempo y ratio de compresión sean razonables, en compromiso con la calidad obtenida.
  • Información almacenada en el archivo comprimido para cada uno de los canales:
    • La información para reproducir las dos triangulaciones:
      • Triángulos que hacen split en cada iteración
      • Vértices eliminados por el merge (al final de las iteraciones de split)
    • La elección de los 2^n triángulos del codebook
    • El mapeo óptimo de cada triángulo de dominio:
      • Con qué triángulo del codebook mapea (n bits)
      • La permutación de vértices óptima (6 combinaciones, 3 bits)
      • Offset (los autores indican que 6 bits es un buen compromiso)
      • Escala (los autores indican que 6 bits es un buen compromiso)
  • Descompresión (para cada canal):
    • Se obtienen los triángulos de las dos triangulaciones.
    • Se obtienen los triángulos del codebook.
    • Se itera hasta que converja:
      • El mapeo de todos los triángulos de dominio, desde sus mapeos óptimos con los triángulos del codebook


Tras cursar el máster de I.A., me veo en condiciones de innovar en la obtención de los triángulos más representativos para el codebook, aplicando nada más y nada menos que un K-medoids.

La innovación no sé si mejora o si empeora el algoritmo, pero evita que tenga que programarlo yo (que en principio parecía complicado de programar).


Se divide el procesamiento en los canales de la imagen (normalmente RGB).

Se hace uso de multiprocesamiento (un hilo para cada canal).


Con la versión v1.1, se añade un reader compatible con el ImageIO estándar.

Así pues, podrás abrir una imagen comprimida con la aplicación (.dfc) simplemente haciendo:

BufferedImage image = ImageIO.read("image.dfc");

Pantallas

Compresión fractal de imágenes v1.0 (2022-2024)

Ver vídeo
Descargar

Compresión fractal de imágenes v1.1 (2026)

Descargar

Versiones

image

La aplicación es la implementación de un algoritmo de compresión fractal de imágenes explicada en un artículo publicado en IEEE de mi época universitaria, basado en la triangulación de Delaunay y codificación por bloques

Mi primera versión de este algoritmo la programé conjuntamente con un compañero de Universidad en una práctica de la asignatura de último curso de Televisión de Teleco (plan 64 de Barcelona).

Internet todavía estaba en pañales y el desarrollo dependía casi únicamente de uno mismo y de los documentos físicos que pudieras conseguir.

Me acuerdo que programamos una triangulación de Delaunay que no estaba mal del todo, y que llegamos a programar el split and merge, el cálculo de los triángulos más representativos y también el cálculo de los mapeos óptimos durante la codificación, pero no llegamos a terminar la aplicación (después de haberle dedicado tres meses de desarrollo intensivo)

25 años después, llega una nueva implementación del algoritmo, esta vez completo, y en el tiempo récord de dos semanas

Está claro que algo se mejora en 25 años, y además, esta vez con el apoyo de funciones para el manejo de triángulos que ya había programado para la aplicación de efecto de Morphing

Usando esta vez una librería de triangulación de Delaunay programada por profesionales

Está claro que cuando no tienes que construirte los ladrillos, las paredes se levantan más rápido ...


Vídeo de demostración

image

La novedad de la versión v1.1 es que añade un reader de imágenes comprimidas fractalmente con la aplicación que es compatible con ImageIO.

Así pues se puede leer una imagen comprimida con la aplicación (.dfc) de esta manera:


BufferedImage image = ImageIO.read("image.dfc");


En la comparativa de la imagen en .jpg y en el formato de la aplicación .dfc, se puede observar que la calidad es algo peor que la de .jpg y que ocupa algo más (como era de esperar).

Pero el resultado no está mal del todo.

Aunque dado el alto tiempo de procesamiento tomado para comprimir, este tipo de compresión y su implementación en la aplicación solo tienen cierto interés académico.


05/04/2026 - 12/04/2026. Tras haber recuperado la evolución de esta aplicación, me propongo hacer pruebas con imágenes de verdad, y la pruebo con una de "4000 x 3000" pixels.

Me encuentro con muchos problemas:

  • El proceso se eterniza en las iteraciones de split (debido a las extensas triangulaciones de hasta alrededor de 400.000 triángulos, y que se repetía la comprobación de la condición de split para todos los triángulos en cada iteración)
  • La memoria se agota tras alcanzar el límite de 23 GB que tenía en mi sistema. Debido a que:
    • El resultado de las iteraciones de split y merge se almacenaban en una lista de boolean (alrededor de 16 iteraciones x 2 tipos de triangulaciones x 3 canales x 400.000 elementos !!!!). Eso hace un total de alrededor de 40.000.000 de elementos, cosa todavía asumible)
    • Además, la elección del book de triángulos más representativos (configurado a fuego a 256), se realizaba con un K-medoids (sobre 400.000 elementos) (creo que la memoria usada por ese algoritmo es O(n^2). Ahora sí que ya se nos va de las manos ...)
    • Y, para más INRI, se realizaban esos cálculos en paralelo para los tres canales (rgb) !!
  • Una vez solucionados esos problemas, consigo que se comprima la imagen de (4000 x 3000), pero veo que tarda entre 6 y 8 horas en realizar el trabajo.

Y me propongo solucionarlos:

  • Para la eternización de los split. Solución:
    • Aumento el tamaño mínimo de los triángulos para hacer split, de manera escalonadamente proporcional al número de píxels de la imagen. Resultando en una rebaja del número de triángulos de las triangulaciones. En la imagen usada de (4000 x 3000) se divide el número de triángulos por más de 4 (pasamos de unos 400.000 triángulos a unos 100.000 triángulos).
    • Además, creo un Set con los triángulos que no cumplen con el criterio de split (relativos a la varianza de los valores de sus píxeles), para no repetirlos en cada iteración. Ahora vuela !!
  • Se agota la memoria del sistema. Solución:
    • Resultado de splits y merge guardados en listas de booleanos. Solución:
      • Como a medida que avanzan las iteraciones de split, el número de triángulos que hacen split es radicalmente menor, se ahorra memoria usada almacenando los índices de los triángulos que hacen split en un Set.
    • Memoria de K-medoids (presumiblemente O(n^2)). Solución:
      • Como hemos dividido el número de triángulos por 4 ..., la memoria necesaria para ese algoritmo, esta reducción de triángulos implica dividir la memoria utilizada por un factor de alrededor de 16.
    • Tres K-medoids en paralelo (uno para cada canal). Solución:
      • Secuencializamos los cálculos de las triangulaciones y de la elección de los triángulos más representativos para el code-book (con K-medoids).
    • Hemos dividido la memoria necesaria para el K-medoids por 48!!
  • El tiempo de procesamiento con imágenes "de verdad", se eterniza (la compresión de la imagen de ejemplo tarda entre 6 y 8 horas) con 3 hilos, uno para cada canal. Solución:
    • Hacer configurable el número de hilos. (Configurándolo a 23 hilos (mi sistema tiene 24 cores), el tiempo de procesamiento se reduce a tres horas y media).
  • Efectos de la solución:
    • El efecto de dividir el número de triángulos por 4 se traduce, evidentemente, en una pérdida notable de la calidad de la imagen comprimida. Pero al tener tanta resolución, pues no es tan importante.
    • Al dividir el número de triángulos por 4, también produce que se divida el tamaño del archivo comprimido, supuestamente también del orden de por 4.

También me pongo como objetivo tratar de minimizar el tamaño del archivo comprimido, reorganizando la información a almacenar.

La idea es indicir sobre todo en la codificación de las listas de índices de los triángulos que hacen split en cada una de las iteraciones, cuya codificación original trivial era usar un bit por triángulo, indicando si hacía split o no.

Ideo un algoritmo para tratar de minimizar el tamaño necesario para almacenar cada uno de esos conjuntos de índices.

Creo que no tiene desperdicio ... si tienes curiosidad en este sitio web cuento los puntos clave en los que me he basado.

Aunque quizás hubiera valido más la pena buscar un compresor general sin pérdidas tipo zip o 7z y aplicarlo a la globalidad del archivo comprimido ... Quizás para la próxima.


En la última versión de la entrega se incluye una aplicación de interfaz de comandos, que traduce un archivo creado con versiones del codificador menos optimizadas, a la última versión del codificador, que presumiblemente ocupa menos.

Usando esa pequeña nueva aplicación sobre un par de archivos con la codificación original (uno de 143x143 y otro de 4000x3000), se ve que el tamaño del archivo comprimido se reduce alrededor de un 4% en ellos.

Vídeos

Descargas