Ejemplos de ImageMagick -- Antialiasing
- Prefacio e índice de los Ejemplos de ImageMagick
- Introducción al antialiasing
- Dibujar solo con colores específicos
- Dibujar círculos finos de mapa de bits
- Antialiasing y problemas del relleno por difusión
El antialiasing es una parte fundamental de todas las operaciones de dibujo dentro de ImageMagick. Por desgracia, también puede causar muchos problemas. Esta página intenta abordar dichos problemas y presentar soluciones para ellos.
Introducción al antialiasing Cuando ImageMagick dibuja imágenes, lo hace de una manera muy particular. Las dibuja con una operación llamada «antialiasing». Para demostrarlo, dibujaré una imagen sobre un fondo transparente y luego ampliaré una pequeña parte de la imagen para que se pueda ver lo que ocurre.
magick -size 80x80 xc:none \
-fill white -draw "circle 40,40 15,20" \
-fill black -draw "line 5,30 78,2" drawn.png
magick drawn.png -crop 10x10+50+5 +repage -scale 80x80 drawn_mag.png
Ahora bien, uno pensaría que la imagen anterior tendría solo tres colores, «white», «black» y «transparent», ya que es todo lo que le pedimos usar a IM. Pero, como se puede ver al ampliar la imagen, tiene toda una gama de colores. Al hacer esto, ImageMagick logra que la imagen luzca más suave y con mejor aspecto, mediante una técnica llamada «antialiasing». Es un término rimbombante que significa que rellena los píxeles del borde del objeto con una mezcla de colores e incluso transparencias, para que el objeto se vea más suave. Si no se aplicara antialiasing, los bordes de todos los objetos dibujados presentarían un efecto en forma de escalera llamado «aliasing», aunque se le conoce más comúnmente como «dientes de sierra». Aquí dibujamos la imagen de nuevo, pero esta vez le pedimos a IM que desactive sus operaciones automáticas de antialiasing, usando "[+antialias](https://imagemagick.org/command-line-options/#antialias)".
magick -size 80x80 xc:none +antialias \
-fill white -draw "circle 40,40 15,20" \
-fill black -draw "line 5,30 78,2" drawn_jaggies.png
magick drawn_jaggies.png -crop 10x10+50+5 +repage -scale 80x80 \
drawn_jaggies_mag.png
Esta vez la imagen realmente solo tiene tres colores. Pero el resultado no es nada agradable. En las versiones más recientes de IM se dibuja una única línea de píxeles, en forma de escalera. En versiones antiguas de IM la línea también habría tenido una apariencia bastante gruesa, haciendo que se viera aún peor. Básicamente, esto no es algo que normalmente quieras hacer. La escalera del efecto «aliasing», comúnmente llamada también «dientes de sierra», es precisamente lo que IM trataba de evitar. Pero si quieres colores específicos, tendrás que aceptar esto, o usar otras técnicas (como la cuantización de color) para asegurarte de usar solo ciertos colores. Ten en cuenta que en realidad están ocurriendo dos formas de antialiasing. La primera es una mezcla de los colores blanco y negro de la imagen, que produce varios matices de color, gris en este caso. La otra forma es una mezcla de color y transparencia para generar píxeles semitransparentes en la imagen. Esta última es algo que deberás tener presente, ya que muchos formatos de imagen (como GIF) no pueden manejar píxeles semitransparentes, y los volverán completamente opacos o completamente transparentes. Los ejemplos de Transparencia booleana en GIF muestran métodos para controlar el tratamiento de los píxeles semitransparentes al guardar en tales formatos.
Resumen
El antialiasing es muy importante en cualquier tipo de dibujo de imágenes, y algo que deberías tener presente. Sin considerar los colores mezclados y los píxeles semitransparentes que genera el antialiasing de IM, tus propias creaciones de imágenes pueden quedar con muy mal aspecto en algunos formatos. Esto cobra aún más importancia cuando creas imágenes en un formato que no admite píxeles semitransparentes, como el muy extendido formato "GIF". Consulta Transparencia booleana en GIF para conocer formas de manejar este problema. IM es muy bueno aplicando antialiasing a colores y transparencias, pero en realidad es bastante malo para dibujar únicamente píxeles puros con «aliasing» (por ejemplo, para coincidir con un mapa de colores específico). Me han dicho que esto será el foco de una versión posterior de IM.
Dibujar usando solo colores específicos
En construcción
Mejores formas de dibujar sin antialiasing, para generar imágenes con colores exactos. Es decir, para «imágenes indexadas». En concreto, dibujar sobre un lienzo transparente, aplicar un umbral al canal alfa y luego superponer, de modo que solo se dibujen píxeles completamente opacos.
Dibujar círculos finos de mapa de bits
Aquí veremos cómo intentar dibujar círculos de «línea fina» de mapa de bits con IM. Normalmente esto se hace mediante un algoritmo de dibujo de círculos de mapa de bits, conocido típicamente como algoritmo del círculo de Bresenham, pero más correctamente como algoritmo del círculo del punto medio. Por desgracia, esto no está disponible en ImageMagick, y puede que nunca lo esté, ya que no es necesario en un entorno de dibujo totalmente con antialiasing. Otra alternativa para dibujar círculos, que veremos en un momento, es usar Morfología para «Dilatar» un único píxel, usando el especial núcleo en anillo.
Por ejemplo, la forma normal en que IM dibuja un círculo produce muchos colores grises de antialiasing para darle al círculo una apariencia suave. |
magick -size 15x15 xc: -fill none -stroke black \
-draw 'translate 7,7 circle 0,0 5,0' \
-scale 500% circle_antialiased.gif
![[IM Output]](../static/img/antialiasing/circle_antialiased.gif)
Sin embargo, simplemente desactivar el antialiasing produce círculos y líneas que no son una bonita línea fina de «mapa de bits». |
magick -size 15x15 xc: -fill none -stroke black +antialias \
-draw 'translate 7,7 circle 0,0 5,0' \
-scale 500% circle_aliased.gif
![[IM Output]](../static/img/antialiasing/circle_aliased.gif)
Lo que tienes que hacer es ajustar además el "[-strokewidth](https://imagemagick.org/command-line-options/#strokewidth)", que por defecto es de 1 píxel de ancho, a algo más pequeño, como 0,5 píxeles de ancho. |
magick -size 15x15 xc: -fill none -stroke black +antialias \
-strokewidth 0.5 -draw 'translate 7,7 circle 0,0 5,0' \
-scale 500% circle_thin_stroke.gif
![[IM Output]](../static/img/antialiasing/circle_thin_stroke.gif)
Mejor, aunque no del todo correcto. Pero también puedes hacer el ancho del trazo demasiado pequeño, en especial con radios de tamaño impar. |
magick -size 15x15 xc: -fill none -stroke black +antialias \
-strokewidth 0 -draw 'translate 7,7 circle 0,0 5,0' \
-scale 500% circle_zero_stroke.gif
![[IM Output]](../static/img/antialiasing/circle_zero_stroke.gif)
Y aquí hay una buena solución para un círculo de 5 píxeles centrado en una ubicación de píxel real con coordenadas enteras. |
magick -size 15x15 xc: -fill none -stroke black +antialias \
-strokewidth 0.4 -draw 'translate 7,7 circle 0,0 5,0' \
-scale 500% circle_perfect.gif
![[IM Output]](../static/img/antialiasing/circle_perfect.gif)
Sin embargo, tras muchos experimentos no pude encontrar ningún "[-strokewidth](https://imagemagick.org/command-line-options/#strokewidth)" que funcione para todos los radios y centros. Especialmente para un círculo ligeramente descentrado.
No existe una solución ideal para todas las situaciones
Por ejemplo, este círculo, que no está centrado en un píxel ni en un límite de píxel, no solo tiene huecos en la parte superior, ¡sino que además es demasiado grueso en la parte inferior! ¡Qué horror! |
magick -size 15x15 xc: -fill none -stroke black +antialias \
-strokewidth 0.47 -draw 'translate 7,7.3 circle 0,0 5,0' \
-scale 500% circle_bad_stroke.gif
![[IM Output]](../static/img/antialiasing/circle_bad_stroke.gif)
Aquí tienes una tabla de buenos valores de "[-strokewidth](https://imagemagick.org/command-line-options/#strokewidth)" para generar un círculo fino de un solo píxel de ancho con un radio específico. Ten en cuenta que el mejor valor a usar varía según si el círculo está centrado en un píxel real (como ' 5 , 5 ') o en un límite de medio píxel (como ' 5.5 , 5.5 ') Radio del círculo | SW real | SW medio
---|---|---
1 | 0.3 | 0.3 ¶
1.5 | 0.5 ¶ | 0.3
2 | 0.3 | 0.3 §
2.5 | 0.5 ¶ | 0.3 ¤
3 | 0.3 ¤ | 0.3
3.5 | 0.5 | 0.3 ¤
4 | 0.5 § | 0.3
4.5 | 0.5 | 0.3
5 | 0.4 | 0.3
5.5 | 0.5 ¶ | 0.3
6 | 0.3 | 0.5 §
6.5 | 0.5 | 0.43
7 | 0.5 | 0.434
7.5 | 0.5 § | 0.5 §
8 | 0.4 | 0.5
¤ Círculo pequeño muy bueno
§ no se encontró un ancho ideal
¶ el círculo es muy malo
ASIDE: Para centrar un círculo en una imagen, en coordenadas de dibujo (coordenadas de píxel), es (size-1)/2
Antialiasing y problemas del relleno por difusión Debido a las características de antialiasing de IM, el relleno por difusión ("[-draw](https://imagemagick.org/command-line-options/#threshold) color floodfill") presenta problemas cuando se usa en imágenes con efectos de antialiasing. También tiene problemas similares con imágenes leídas desde el formato de imagen "JPG". Básicamente, como la mayoría de los objetos en IM están con antialiasing (o se leen desde un archivo de imagen en formato "JPG"), los colores cercanos a los bordes de los objetos dibujados rara vez son el color específico que estás reemplazando con el relleno por difusión. Esto significa que el relleno por difusión no rellenará los bordes mismos de las áreas que intentas rellenar, a menos que estés evitando por completo el antialiasing. En esencia, el relleno por difusión, o incluso el reemplazo de color, no entiende el antialiasing, ni emplea técnicas de antialiasing por sí mismo. En consecuencia, el relleno por difusión generalmente omitirá los píxeles del borde mismo del área que estás rellenando. Por ejemplo, aquí realizamos una operación típica de relleno por difusión. Dibujamos un círculo y luego intentamos rellenarlo con un patrón... |
magick -size 60x60 xc:lightblue -strokewidth 2 \
-fill none -stroke red -draw "circle 30,30 5,30" \
-tile tile_weave.gif -draw "color 30,30 floodfill" \
tile_fill_1.gif
magick tile_fill_1.gif -crop 10x10+35+4 +repage -scale 80x80 \
tile_fill_1_mag.gif
![[IM Output]](../static/img/antialiasing/tile_fill_1_mag.gif)
Como puedes ver en la parte ampliada de la imagen, el relleno por difusión omitió por completo una línea de píxeles «de color distinto», ya que el color de esos píxeles no era exactamente el mismo que el del área que estabas rellenando. Una forma de mejorar esto es prerellenar las áreas que pretendes rellenar con un color que coincida con el patrón que estás usando. El patrón seguirá sin rellenar el área por completo, pero al menos no se verá tan mal. |
magick -size 60x60 xc:lightblue -strokewidth 2 \
-fill black -stroke red -draw "circle 30,30 5,30" \
-tile tile_weave.gif -draw "color 30,30 floodfill" \
tile_fill_2.gif
magick tile_fill_2.gif -crop 10x10+35+4 +repage -scale 60x60 \
tile_fill_2_mag.gif
![[IM Output]](../static/img/antialiasing/tile_fill_2_mag.gif)
Otra manera de hacerlo es rellenar el área con tu patrón usando un alto factor de difusión, para forzar al patrón a rellenar el área por completo, hasta el borde mismo, sin omitir los píxeles del borde. |
magick -size 60x60 xc:lightblue -strokewidth 2 \
-fill none -stroke red -draw "circle 30,30 5,30" \
-fuzz 35% -tile tile_weave.gif -draw "color 30,30 floodfill" \
tile_fill_3.gif
magick tile_fill_3.gif -crop 10x10+35+4 +repage -scale 60x60 \
tile_fill_3_mag.gif
![[IM Output]](../static/img/antialiasing/tile_fill_3_mag.gif)
| Ten en cuenta que un alto «factor de difusión» como este, o un borde demasiado fino, puede provocar que el patrón de relleno se «escape» del área definida. Siempre se necesita cierto cuidado al usar una operación de relleno por difusión. En realidad no la recomiendo como solución general, precisamente por esto.
---|---
El problema con esto es que, como el relleno por difusión por su propia naturaleza NO usa antialiasing, los bordes del área rellenada padecen los «dientes de sierra» o efectos de aliasing. Puedes mejorar esa situación separando el dibujo de la imagen en pasos distintos. Crea un círculo de color, rellénalo y luego dibuja el borde. |
magick -size 60x60 xc:lightblue -fill black -draw "circle 30,30 5,30" \
-tile tile_weave.gif -draw "color 30,30 floodfill" +tile \
-fill none -stroke red -strokewidth 2 -draw "circle 30,30 5,30" \
tile_fill_4.gif
magick tile_fill_4.gif -crop 10x10+35+4 +repage -scale 60x60 \
tile_fill_4_mag.gif
![[IM Output]](../static/img/antialiasing/tile_fill_4_mag.gif)
Esta es una manera sencilla de mejorar el relleno por difusión. Otras consisten en usar una superposición con forma, pero ese puede ser un método complicado de resolver. Más adelante veré modificaciones similares en imágenes existentes. Por supuesto, si tú mismo dibujas el área que se va a rellenar por difusión y no usas una imagen existente, la solución ideal sería evitar el relleno por difusión especificando el patrón de relleno en la operación de dibujo original. |
magick -size 60x60 xc:lightblue -strokewidth 2 \
-tile tile_weave.gif -stroke red -draw "circle 30,30 5,30" \
tile_fill_5.gif
magick tile_fill_5.gif -crop 10x10+35+4 +repage -scale 60x60 \
tile_fill_5_mag.gif
FUTURO: problemas de antialiasing en imágenes preexistentes (en especial el formato JPG).
Por ejemplo, recolorear y superponer una imagen de texto o de un diagrama sobre un color
o un fondo.
También volver a añadir transparencia a archivos GIF, y a JPEG reescalados para uso como iconos.
**Suavizar o aplicar antialiasing a imágenes con un conjunto de colores limitado**
En concreto, imágenes de mapa de bits (blanco y negro puros).
Primero, el antialiasing no funciona en imágenes de mapa de bits.
El antialiasing implica usar una mezcla de colores y transparencias para tratar de
suavizar el efecto de 'escalera' o 'dientes de sierra' de las líneas inclinadas y los
límites de color. Si solo hay dos colores disponibles, ¡el antialiasing no puede ocurrir!
Como mínimo, la imagen debe convertirse desde blanco y negro o escala de grises antes
de poder usar el antialiasing.
Una manera sencilla de suavizar los bordes es usar una pequeña cantidad de desenfoque tras
leer una imagen en blanco y negro o una imagen con un tamaño de paleta ínfimo.
EG: magick image.xbm -blur 0x.3 smoothed_image.png
![[IM Output]](../static/img/antialiasing/drawn.png)
![[IM Output]](../static/img/antialiasing/drawn_mag.png)
![[IM Output]](../static/img/antialiasing/drawn_jaggies.png)
![[IM Output]](../static/img/antialiasing/drawn_jaggies_mag.png)
![[IM Output]](../static/img/antialiasing/tile_fill_5.gif)
![[IM Output]](../static/img/antialiasing/tile_fill_5_mag.gif)