Convierte imagenes JPEG en XHTML

Si, se que el título es un poco raro, pero es la forma mas rápida de describir el último lío en el que me he metido.

La historia comienza hace un año mas o menos, cuando alguien me propuso como se podría crear un captcha en PHP sin usar GD ni Imagemagick o similares, y como por aquel entonces yo ya estaba trabajando en eyeOS, tenía muy presente el tema del DHTML, y de jugar con elementos DIV con posición absoluta por todas partes, etc, por lo que pensé, que si una imagen es un conjunto de pixels, se podría pintar una imagen, con un conjunto de divs con width:1px y height:1px, y posicionándolos todos con position absolute, en su sitio.

Me di cuenta enseguida de que era viable, pero nunca me puse a hacerlo (tenía demasiado trabajo en eyeOS por aquel entonces), el caso es que hace 2 días, tras el artículo que hice sobre un programa que habíamos hecho mi novia y yo, para romper el viejo captcha de meneame, y como tenía tan reciente el uso de libjpeg, decidí empezar un nuevo mini-proyecto con ella…y ahí nació img2xhtml.

La idea es sencilla, img2xhtml es un programa que usando libjpeg, lee una imagen jpeg linea a linea, y genera un archivo xhtml de salida, que pinta exactamente la misma imagen, pero utilizando anchors (<a>) de un pixel de alto por un pixel de ancho, con el mismo background-color que tenía el pixel en la imagen original, y position absolute en el sitio que le pertoca.

Dicho así, suena muy simple, pero ya me imaginaba que no iba a ser tan fácil, nunca lo es.

En primer lugar, hay una linea que se va a repetir dentro del xhtml muchísimas veces: el pixel, por lo que si podemos reducir esa linea en 1 caracter, reduciremos considerablemente el peso total del archivo html resultante, por ello se usan anchors (<a>) en lugar de div (<div>) por que los primeros se escriben con 2 letras menos. Veamos un pixel:

<a class=w style=top:74px;left:62px;background:rgb(52,52,52) />

Un pixel como vemos, es un anchor (<a>) un poco guarro para pesar lo mínimo, que omite las comillas en los atributos html por que no contienen espacios, etc.

w es una clase css, tal como:

.w{position:absolute;height:1px;width:1px;}

De esta forma, todo lo que no es variable en un pixel, está en la clase CSS, lo que nos ahorra tamaño por pixel.

Vale, con todo esto en mente, hicimos el programa, el cual funciona bastante bien, por ejemplo, esta imagen:

Se puede ver pasada a xhtml aquí.

Lo cual, al menos en mi Firefox3, se renderiza así:

El resultado es bastante bueno, sin embargo, uno enseguida se da cuenta de que esta imagen debe tener MUCHOS elementos xhtml para pintarse así, pesa 277k, frente al original, que pesa 2,8k, además, dentro tiene:

jcarlosn@linux-wnp3:~/img2xhtml> cat tux.html | sed “s/<a/\n/g” | wc -l
4343
jcarlosn@linux-wnp3:~/img2xhtml>

4343 anchors (<a>).

Además, el problema que surge es que la CPU se dispara al abrir imágenes medianamente grandes.

Con el problema de la cpu y del tamaño en mente, estaba claro que teníamos que empezar a trabajar en ideas para reducir los elementos xhtml, le conté la idea a SirKeldon,y me picó un poco para que siguiese reduciendo el tamaño de los pixeles al mínimo, pero eso no servia para intentar reducir el número de elementos xhtml que disparan la cpu.

El segundo paso fue un poco mas complejo, hablando con mi novia, enseguida nos dimos cuenta de que había una manera fácil de reducir el número de <a> en pantalla: uniendo horizontalmente los pixels contiguos del mismo color, en un mismo elemento xhtml, alargado.

Tras hacerlo, obtuvimos una mejora considerable, sin embargo, no era suficiente para imágenes grandes, por lo que decidimos agregar un nuevo concepto: tolerancia.

La tolerancia es el tope de diferencia entre dos colores RGB, para considerarlos “iguales” y unirlos bajo un mismo elemento xhtml, de esta forma, cuanta mas tolerancia, menor calidad y mayor velocidad. Vamos a hacer una prueba con tolerancia 15: (R+-15, G+-15, B+-15):

img2xhtml -f tux.jpg -t 15 > tux_15.html

Lo cual genera esto.

Como vemos, la calidad ha bajado un poco, en mi firefox3 se renderiza así:

Vale, no es perfecto, pero, ahora el tamaño es de 89k, y la cantidad de elementos xhtml es de:

jcarlosn@linux-wnp3:~/img2xhtml> cat tux_15.html | sed “s/<a/\n/g” | wc -l
1313
jcarlosn@linux-wnp3:~/img2xhtml>

Con solo 1313 elementos xhtml, se dibuja esa imagen, si la analizamos un poco, veremos que está hecha con elementos xhtml alargados, no con pixels sueltos.

Lo mejor, es que lo probéis vosotros mismos, y jugueis un poco, no os recomiendo probar con imagenes mayores de 250×250. El programa lo podéis bajar de aquí:

Descargar img2xhtml

Compilarlo e instalarlo es como siempre, primero lo extraemos y ejecutamos ./configure:

jcarlosn@linux-wnp3:~/downloads> tar -xzf img2xhtml-1.0.tar.gz
jcarlosn@linux-wnp3:~/downloads> cd img2xhtml-1.0/
jcarlosn@linux-wnp3:~/downloads/img2xhtml-1.0> ./configure
checking for a BSD-compatible install… /usr/bin/install -c
checking whether build environment is sane… yes
checking for a thread-safe mkdir -p… /bin/mkdir -p
checking for gawk… gawk
checking whether make sets $(MAKE)… yes
checking for gcc… gcc
checking for C compiler default output file name… a.out
checking whether the C compiler works… yes
checking whether we are cross compiling… no
checking for suffix of executables…
checking for suffix of object files… o
checking whether we are using the GNU C compiler… yes
checking whether gcc accepts -g… yes
checking for gcc option to accept ISO C89… none needed
checking for style of include used by make… GNU
checking dependency style of gcc… gcc3
checking for a BSD-compatible install… /usr/bin/install -c
configure: creating ./config.status
config.status: creating Makefile
config.status: creating config.h
config.status: config.h is unchanged
config.status: executing depfiles commands
checking for jpeg_start_compress in -ljpeg… yes
checking for zlibVersion in -lz… yes

Y luego lo compilamos:

jcarlosn@linux-wnp3:~/downloads/img2xhtml-1.0> make
make all-am
make[1]: se ingresa al directorio `/home/jcarlosn/downloads/img2xhtml-1.0′
gcc -DHAVE_CONFIG_H -I. -g -O2 -MT main.o -MD -MP -MF .deps/main.Tpo -c -o main.o main.c
mv -f .deps/main.Tpo .deps/main.Po
gcc -DHAVE_CONFIG_H -I. -g -O2 -MT libjpg.o -MD -MP -MF .deps/libjpg.Tpo -c -o libjpg.o libjpg.c
mv -f .deps/libjpg.Tpo .deps/libjpg.Po
gcc -DHAVE_CONFIG_H -I. -g -O2 -MT libhtml.o -MD -MP -MF .deps/libhtml.Tpo -c -o libhtml.o libhtml.c
mv -f .deps/libhtml.Tpo .deps/libhtml.Po
gcc -g -O2 -o img2xhtml main.o libjpg.o libhtml.o -ljpeg
make[1]: se sale del directorio `/home/jcarlosn/downloads/img2xhtml-1.0′

Finalmente lo instalamos con:

sudo make install

Ahora para ver la ayuda del programa:

jcarlosn@linux-wnp3:~> img2xhtml -h
img2xhtml -f file [-m maxLine] [-t color tolerance]
Convert images to xhtml files

-f FILENAME Filename to convert to xhtml
-m MAXLINE Maximum number of pixels to join with tolerance
-t TOLERANCE Color tolerance to join two pixels with similar colors
into a single xhtml element (between 0-127)

img2xhtml Copyright (C) 2008 Noemi Blazquez & Jose Carlos Norte
This program comes with ABSOLUTELY NO WARRANTY.
This is free software, and you are welcome to redistribute it
under certain conditions.

Please send bugs to jose@eyeos.org

El parametro -m (maxline) especifica el máximo largo que puede tomar un pixel estirado tragandose a su pixel de al lado, sirve para jugar con tolerancias altas sin obtener imagenes que parecen códigos de barras.

Si alguien me pregunta para que sirve todo esto, la verdad es que no lo tengo muy claro, pero…¿y lo bien que se lo pasa uno haciendo el friki y programando en C estas extravagancias?

La dirección del proyecto en sourceforge es:

http://sourceforge.net/projects/img2xhtml/

Pero aún no nos ha dado tiempo de subir los paquetes y todo eso.

17 Responses to “Convierte imagenes JPEG en XHTML”


  1. 1 maeghith septiembre 22, 2008 a las 8:43 pm

    Me ha recordado al Homer en CSS que hizo Román Cortés no hace mucho.

    Más tarde hizo un jpeg2css que sólo iba con b/n: http://www.romancortes.com/blog/jpeg2css/

    Enlazo este ya que tiene enlaces al Homer, y a un G.W.Bush también en CSS. Aunque él no publicó el código (alegaba vergüenza).

  2. 2 rooibo septiembre 22, 2008 a las 8:48 pm

    Yo considero, maeghith, que publicar el código es importantísimo en estos artículos de curiosidades, pues puede ayudar a cualquiera que busque como usar estas librerías, o como hacer cosas similares.

    Aunque finalmente, cada uno decide que publicar, y que no.

    El concepto aquí es un poco distinto al Homer en CSS.

    Gracias por comentar, y no conocía el caso de Román, es interesante.

  3. 3 Ruben Cantón septiembre 23, 2008 a las 7:32 am

    Buenas,

    En alguna ocasión también se me ocurrió cometer la locura que tu has cometido cuando trabajé con divs en pos absolute (aunque mi trabajo era aun mas friki todavía), pero nunca llegué a hacerlo -entre otras cosas, porqué pensé que generaría tantos divs que la página no se cargaría-.

    Sólo una idea, si en vez de usar rgb(255,255,255) utilizas #FFFFFF te ocupará aún menos espacio, ya que el hexadecimal utiliza menos caracteres, la idea de usar etiquetas ha sido muy buena.

    Un saludo!

  4. 4 Ruben Cantón septiembre 23, 2008 a las 7:36 am

    Vaya, escribir la etiqueta <a> sin el “& lt;” ha sido un grave error, espero que puedas editar el mensaje y corregirlo… jajajaj

  5. 5 rooibo septiembre 23, 2008 a las 8:55 am

    Hola Ruben, no puedo editarlo, pero con tu mensaje siguiente, se entiende bien!

    Si, el color hexa ocuparía menos, es una buena idea, haré una versión 1.1 con las ideas que me estáis dando los lectores del artículo (por mail y en los comentarios)

    Gracias!

  6. 7 Ruben Cantón septiembre 23, 2008 a las 11:31 am

    Ah, por si te sirve de algo, se puede escribir el hexadecimal con sólo 3 números, aunque se pierde calidad porque tiene menos definición: #FFF = blanco, #F00 = rojo, #000 = negro, …

    Nos vemos!

  7. 8 rooibo septiembre 23, 2008 a las 3:29 pm

    Lo siento oskarloko, por culpa de que ciertos caracteres se consideran especiales de xhtml, el filtro de wordpress ha roto tu comentario, y no entiendo lo que propones.

    Lo había pensado Ruben, quizá sería bueno que el programase calculase si el color puede ser expresado en 3 bytes en lugar de 6, ejemplo: #0d0d0d, que quedaría #ddd, y usara esa notación.

    Muy buena idea, gracias!

  8. 9 Jose septiembre 23, 2008 a las 11:59 pm

    Muy bueno el porgrama o como minimo didactico.
    Otra idea que se me ocurre (no se si sera muy complicada de relazar) seria aplicar el css sobre el tag A (position:absolute;height:1px;width:1px;) de esta forma tendrias todos los tags A con posicion absoluta de 1×1.

    Si algun pixel fuera extendido (que ocupara mas de uno de ancho) y en el css figura de 1 de ancho, se podria cambiar el ancho del pixel desde el propio tag a indicando el ancho de ese tag, ya que el style del tag se aplica despues que el style del head.
    De esta forma nos ahorrariamos todos los ” class=x” lo cual son 8 bytes por tag, no es muhco peor algo es algo jeje.

    Por otra parte se podria poner un control sobre la localizacion del pixel ya que todos los top y left que llevan como localizacion “0px” no requieren la medida “px” ya que el 0 no tiene medida en CSS, es lo mismo 0px, 0em, 0%….

    Nose si me he hecho entender, pero espero ver esa version 1.1

    Saludos.

  9. 10 rooibo septiembre 24, 2008 a las 5:56 pm

    Me han encantado tus ideas Jose, muy buenas todas, prometo que este fin de semana hacemos la 1.1, y citamos a todo el mundo que ha propuesto ideas en un archivo llamado THANKS y en el README.

    Gracias!

    Por cierto, en cuanto tenga la 1.1 que ya será mas definitiva, la subo a sourceforge.
    🙂

  10. 11 Alex Barros septiembre 26, 2008 a las 11:12 pm

    Hey, me encanta la idea! Últimamente ando muy viciado con aplicaciones de tratamiento de imagen, sobretodo con GD, pero esto me ha llamado mucho la atención.

    Aparte de las ideas que ya te han dado, que son muy buenas, voy a tratar de aportar un poco.

    La idea de hacer colores en Hexadecimal es ya definitiva, además como ha dicho Rubén existe la posibilidad de hacerlo con tres caracteres (aunque el rango de colores se reduce mucho, y se pierde calidad)(#ABC = #AABBCC). También lo de aplicar el estilo con el tag a, y evitar la propiedad class, es vital.

    Lo que se me ha ocurrido a mi es que la imagen completa esté dentro de un div con el ancho y alto de la imágen, y todos los píxeles tengan la propiedad float:left. De esta forma los píxeles irían ocupando sus lugares uno al lado del otro, hasta llegar al ancho de la imágen donde pasarían a la línea siguiente. Esto evitaría todas las etiquetas de posición (la cosa está en que los navegadores no hagan de las suyas).

    Y otra idea es que los píxeles por defecto sean blancos, así cuando haya colores blancos (que es un color muy típico) no hay que darle estilo al color.

    Al final con todas las ideas los píxeles serian algo como:

    (excepto cuando pueda obviarse la información)

    y el CSS algo como:

    #jpg2xhtml {width:000px;height:000px;}
    #jpg2xhtml {width:1px;height;1px;float:left;color:#FFF}

    Mucha suerte con la versión 1.1, lo seguiré de cerca!

  11. 12 Alex Barros septiembre 26, 2008 a las 11:15 pm

    ***

    Al final con todas las ideas lox píxeles serian algo como:

    {a style=”color:#XXX;width:0px”}

    (excepto cuando pueda obviarse la información)

    y el CSS algo como:

    #jpg2xhtml {width:000px;height:000px;}
    #jpg2xhtml {width:1px;height;1px;float:left;color:#FFF}

    ***

  12. 13 Alex Barros septiembre 26, 2008 a las 11:16 pm

    Perdón, en el CSS me doy cuenta que la propiedad es background, y no color.

  13. 14 rooibo septiembre 26, 2008 a las 11:25 pm

    Muy buenas ideas Alex Barros!

    Tengo muchas ganas de ponerme este fin de semana con la 1.1, a ver cuando puedo enseñar algo.

    Este sábado estaré en la php conference, por lo que no podré empezar la 1.1, tendrá que ser el viernes.

    Gracias a todos🙂

  14. 15 Zerial octubre 18, 2008 a las 1:35 am

    Habia visto algunas utilidades como esta, pero generaban una gran cantidad de caracteres, haciendo que el fichero sea muy pesado …

  15. 16 olorinj noviembre 12, 2008 a las 12:22 pm

    Creo que para el ancho de los colores podrías utilizar estilos combinados como el siguiente (Puesto por separado para favorecer su lectura):

    .o,.w{position:absolute;height:1px;}
    .w{width:1px;}
    .l0{top:0;}
    .l1{top:1;}
    .c2{left:2px;}
    .c29{left:29px;}
    .w3{width:3px;}
    .j{color:#AAA;}
    .k{color:#BBB;}
    Y podrías utilizar código como:
    en lugar de:

    Y podría quedar como el siguiente:

    Si utilizaras left para posicionamiento podrías obviar las clases .c

  16. 17 olorinj noviembre 12, 2008 a las 12:23 pm

    Creo que para el ancho de los colores podrías utilizar estilos combinados como el siguiente:

    .o,.w{position:absolute;height:1px;}
    .w{width:1px;}
    .l0{top:0;}
    .l1{top:1;}
    .c2{left:2px;}
    .c29{left:29px;}
    .w3{width:3px;}
    .j{color:#AAA;}
    .k{color:#BBB;}
    Y podrías utilizar código como:
    en lugar de:

    Y podría quedar como el siguiente:

    Si utilizaras left para posicionamiento podrías obviar las clases .c


Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s





A %d blogueros les gusta esto: