ImageMagick Ejemplos -- Morfología de las formas
- Operador de morfología
- Núcleos de morfología integrados básicos
- Iterar (repetir) operaciones de morfología
- Salida detallada de los cambios (para monitorización)
- Mostrar el núcleo generado (con fines de depuración)
- Generar una imagen del núcleo (con fines de depuración)
- Morfología plana en escala de grises
- Morfología real en escala de grises o tridimensional
- Variante de intensidad de los métodos básicos
- Granularidad de un conjunto de formas
-
Efectos de núcleos asimétricos (prueba de métodos)
- Thicken: añadir píxeles que coinciden
- Thinning: restar píxeles que coinciden
- Distancia con una forma suavizada (anti-alias)
-
Generar esqueletos a partir de formas. En construcción
- Esqueleto usando Autotrace
La morfología modifica una imagen de diversas maneras según el 'vecindario' cercano de los demás píxeles que la rodean. Esto a su vez permite una enorme variedad de efectos: expansión y contracción de formas (dilate/erode), distancia al borde, adelgazamiento hasta un esqueleto o eje central. Incluso el método más antiguo de las técnicas de 'convolución', que ofrece desenfoque y realce (siguiente sección), es en cierto modo un tipo de método de morfología. En esencia, la morfología sirve para modificar, determinar y descubrir las formas de los objetos presentes en una imagen.
Introducción a la morfología
La morfología se desarrolló originalmente como un método para limpiar y estudiar la estructura de las formas dentro de una imagen. Funciona comparando cada píxel de la imagen con sus vecinos de distintas maneras, para añadir o quitar, aclarar u oscurecer ese píxel. Aplicado sobre toda una imagen, quizá de forma repetida, se pueden encontrar y/o eliminar y modificar formas concretas. Por ejemplo, si un píxel es blanco y está completamente rodeado por otros píxeles blancos, entonces es evidente que ese píxel no está en el borde de la imagen. Podrías querer entonces poner ese píxel en negro, de modo que solo queden activos los píxeles de borde. Ese es un método conocido como '[EdgeIn](#edgein)' (véase más abajo). Todo el proceso depende en realidad de la definición de un 'elemento estructurante' o 'núcleo', que define qué píxeles se clasifican como 'vecinos' para cada método morfológico concreto. El tamaño y la forma exactos de ese 'vecindario' suelen depender de lo que estés tratando de lograr, o de lo que busques específicamente dentro de la imagen. Aquí tienes algunos ejemplos de varios núcleos convertidos en imágenes (usando un script especial "[kernel2image"](../static/img/scripts/kernel2image)") que muestran algunos de los 'vecindarios' alrededor de un píxel central, el 'origen'.
Las imágenes se han escalado para resaltar los elementos individuales del 'núcleo', y como puedes ver los núcleos típicos suelen ser muy pequeños. De hecho, el núcleo 'Disk' que se muestra arriba es en realidad "
", y es uno de los más grandes mostrados arriba. Sin embargo, los 'núcleos' no son realmente imágenes. Son solo una matriz de valores en coma flotante con un elemento designado como el 'origen' del núcleo. Ese elemento especial es la ubicación del píxel que se verá 'afectado' por el vecindario definido, y suele ser, aunque no siempre, el píxel central de un núcleo simétrico. Ten en cuenta que estos son solo algunos ejemplos de vecindarios posibles. Algunos núcleos pueden agrandarse, normalmente aumentando un argumento de 'radio' específico de ese núcleo, mientras que otros usados para fines especiales tienen un tamaño fijo. Para núcleos simples, como los dos primeros, el método morfológico podría repetirse (iterarse) para aumentar el 'tamaño' efectivo del núcleo, de modo que afecte a más píxeles más alejados del 'origen' (marcado). Esto no siempre funciona, sin embargo, y puede producir resultados inesperados; aunque a veces es más rápido que usar directamente un núcleo mayor, pero también esto no siempre es así. El tamaño y la forma finales de un 'elemento estructurante', o 'SE', como se denomina al núcleo en los artículos de investigación sobre morfología, son importantes como medio para localizar y realzar o eliminar elementos de la imagen que sean mayores o menores que esta forma. Esto es lo que hace que la morfología sea extremadamente potente como medio para clasificar diversos elementos dentro de las imágenes. Sin embargo, cuanto mayor sea el núcleo, más tardarán los métodos morfológicos, así que es mejor mantener los núcleos pequeños. Todos los núcleos mostrados, salvo el último, tienen realmente una forma. Las partes que son transparentes no forman parte del 'vecindario' definido del núcleo. Es decir, no tienen ningún valor válido y no participan en ninguno de los cálculos de morfología. Observa cómo el penúltimo núcleo 'Corner #0' no solo tiene valores 'activos', sino también valores 'inactivos', como parte de su 'forma'. Ambos valores, así como los que son transparentes (no parte de la forma), son importantes para los métodos Hit-n-Miss y relacionados (véase más abajo). Este núcleo concreto es solo el primero de una serie de núcleos destinados a localizar los píxeles de 'esquina' de las formas binarias dentro de una imagen. El último 'núcleo' mostrado arriba está completamente definido sobre una amplia área rectangular (cuadrada). Además, a diferencia de los otros núcleos, que solo usan valores de 1 (blanco), 0 (negro) o un valor 'indefinido' especial, los valores de este núcleo van desde casi cero (casi negro) en los bordes hasta un máximo (blanco puro) en el centro. Sin embargo, esos núcleos también pueden usar valores negativos, o incluso muy grandes, mucho más allá del rango normal de otros núcleos. Recuerda que un núcleo es en realidad solo una matriz de valores, y estos podrían tener cualquier valor, no solo un rango de 0 a 1. Este tipo de núcleo es especialmente importante en las 'Operaciones de convolución', un método especial que existe desde mucho antes que la propia morfología. Como resultado, IM tiene un gran número de núcleos integrados, o 'con nombre', de este tipo. Esto se examinará con más detalle en la siguiente sección de los ejemplos de IM, Convolución de imágenes'. Ahora bien, como ya mencioné, los núcleos no son realmente imágenes. Son simplemente una matriz de valores en coma flotante. Examinaremos estos valores reales (que se convirtieron en una imagen para verlos, arriba) más adelante.
Operador de morfología
El operador "[-morphology](https://imagemagick.org/command-line-options/#morphology)" es muy complejo, ya que ofrece al usuario un gran control sobre sus acciones.
-morphology {_method_}[:{_iterations_}] {_kernel_}[:[_k_args_}]
Ten en cuenta que debes proporcionar al menos dos elementos: el 'method ' de morfología, que indica al operador el tipo de operación que quieres aplicar a la imagen, y un 'kernel ' que especifica qué píxeles 'vecinos' deben influir en el resultado final. Ambos son igual de importantes y ambos pueden tener consecuencias de gran alcance. Puedes obtener una lista de los métodos disponibles con "-list morphology ". Una lista de los núcleos integrados que hemos incluido en IM se puede ver con "-list kernel ". Repasaremos los distintos métodos, y los núcleos que esos métodos pueden usar, más adelante. | _El operador "[-morphology](https://imagemagick.org/command-line-options/#morphology)" (métodos básicos) y el conjunto inicial de núcleos fueron añadidos a ImageMagick versión 6.5.9-0 por mí mismo, mientras estaba de vacaciones en China. De diciembre de 2009 a enero de 2010.
Sin embargo, es posible realizar una morfología de núcleo 'cuadrado' simplificada usando el método más antiguo y estrechamente relacionado "[-convolve](https://imagemagick.org/command-line-options/#convolve)". Véase Técnicas alternativas de morfología básica más abajo.
Núcleos de forma integrados básicos
Como el núcleo es común a todos los métodos de morfología, y los resultados de los distintos métodos dependen en gran medida del núcleo concreto seleccionado, primero veremos cómo puedes definir o seleccionar un núcleo para usar. Ya se ha predefinido una buena selección de núcleos para ti y, a menudo, no necesitas buscar más allá de estos. Puedes obtener una lista de los núcleos integrados predefinidos usando "-list kernel". Todos los núcleos tienen un tamaño concreto, normalmente un cuadrado con un número impar de píxeles por lado, cuyo centro es el 'origen' del núcleo. Sin embargo, como verás, el operador "[-morphology](https://imagemagick.org/command-line-options/#morphology)" no está restringido a esta limitación. El k_argument más común usado para los núcleos integrados, y por lo general el primer argumento dado, es un 'radius '. Este define cuán grande será el típico vecindario cuadrado de tamaño impar del núcleo. El tamaño final del núcleo será generalmente dos veces el radio más uno (por el píxel central). Es decir, un 'radius ' de '2' creará un núcleo de 5×5 píxeles cuadrado. Si bien un 'radius ' suele definir el tamaño del núcleo final, y por tanto la velocidad global de la operación morfológica sobre las imágenes, puede no ser el factor más importante, especialmente para los núcleos de convolución, donde los valores tienen mayor efecto en los resultados que el tamaño del núcleo. Si un 'radius ' se fija en 0, o se deja sin definir, el 'radius ' tomará automáticamente algún valor razonable o de uso más común, según el núcleo implicado.
Unity
Este es un núcleo especial que se usa específicamente cuando necesitas un núcleo 'sin operación' (No-Op). La mayoría de los métodos morfológicos que usan este núcleo o bien reproducen la imagen original, o bien generan un resultado en blanco. El núcleo no tiene argumentos. Este mismo núcleo de un solo elemento también se puede generar usando '[Disk:0.5](#disk)', que además permite especificar un argumento de escala como parte de la generación del núcleo.
Diamond
El núcleo más mínimo, aunque quizá no el más simple, es el núcleo integrado 'Diamond '. Una forma sencilla de ver el núcleo básico es usar un método de morfología Dilate sobre una imagen que contiene un único píxel blanco sobre fondo negro. Esto básicamente expande el único píxel hasta la 'forma' del vecindario del núcleo. Aquí está el resultado de usar 'Dilate' con el núcleo integrado mínimo 'Diamond', y escalando el resultado para hacerlo más visible.
magick xc: -bordercolor black -border 5x5 pixel.gif
magick pixel.gif -scale 800% pixel_mag.gif
magick pixel.gif -morphology Dilate Diamond \
-scale 800% k_diamond.gif
| Recuerda que todos los resultados de imágenes de núcleos de esta área de los ejemplos de IM se han ampliado para que puedas ver los píxeles individuales. En realidad, todos los núcleos y los resultados que mostramos son muy pequeños, como deberían ser. En este caso, la imagen que se está dilatando es de solo 11×11 píxeles y se ha escalado 8 veces para su visualización.
---|---
Este es en realidad un núcleo bastante bueno para operaciones morfológicas, y básicamente define el vecindario práctico más mínimo: el píxel original, más los cuatro píxeles en contacto directo. Otro nombre para este tipo de núcleo es elemento estructurante 'Z4'. Se parece bastante a un pequeño signo 'más'. La forma de diamante solo se hace evidente a medida que aumenta el radio. El k_arg opcional de este núcleo puede tomar dos valores, así...
Diamond[:{_radius_}[,{_scale_}]]
Para todos los núcleos de forma, el argumento más importante es radius y, como se mencionó antes, es un entero que representa la distancia desde el 'origen' central hasta el borde más cercano. Por lo tanto, el núcleo 'Diamond' final es un cuadrado (2 veces radius más 1) que contiene la forma de diamante. Aquí están los resultados de usar un radius mayor para generar un núcleo grande.
for r in 1 2 3 4; do
magick pixel.gif -morphology Dilate Diamond:$r -scale 800% k_diamond:$r.gif
done
![[IM Output]](../static/img/morphology/k_diamond_1.gif)
Diamond:1
(predeterminado) | ![[IM Output]](../static/img/morphology/k_diamond_2.gif)
Diamond:2 | ![[IM Output]](../static/img/morphology/k_diamond_3.gif)
Diamond:3 | ![[IM Output]](../static/img/morphology/k_diamond_4.gif)
Diamond:4
---|---|---|---
El otro k_argument es scale, que toma por defecto un valor de 1.0. Normalmente se usa para cambiar los valores reales que el núcleo emplea para formar la forma. Por lo general, esto solo es importante para métodos especiales como Convolve y la morfología en escala de grises.
Square
El 'Square ' es el núcleo más usado para morfología, ya que es el más fácil de aplicar mediante otras técnicas alternativas. Sin embargo, no es el núcleo más mínimo (véase '[Diamond](#diamond)' arriba). Por defecto, el núcleo 'Square' usa un vecindario de 3x3 píxeles alrededor del 'centro'. |
magick pixel.gif -morphology Dilate Square -scale 800% k_square.gif
![[IM Output]](../static/img/morphology/k_square.gif)
Básicamente, esto significa que los 8 vecinos del píxel original se clasificarán como parte del vecindario de ese píxel. Como resultado, es un buen núcleo para promediar píxeles, o para expandir/encoger alguna forma en un píxel. Como todos los núcleos de forma, toma los mismos k_arguments mostrados para el núcleo Diamond de arriba, siendo el primer argumento radius el más importante.
for r in 1 2 3 4; do
magick pixel.gif -morphology Dilate Square:$r -scale 800% k_square:$r.gif
done
![[IM Output]](../static/img/morphology/k_square_1.gif)
Square:1
(predeterminado) | ![[IM Output]](../static/img/morphology/k_square_2.gif)
Square:2 | ![[IM Output]](../static/img/morphology/k_square_3.gif)
Square:3 | ![[IM Output]](../static/img/morphology/k_square_4.gif)
Square:4
---|---|---|---
El valor por defecto (radius=1) de este núcleo, como se mencionó, es un cuadrado de 3×3, y se conoce comúnmente como elemento estructurante 'Z8' (por el número de vecinos inmediatos implicados).
Octagon
El núcleo 'Octagon ' es un núcleo de forma de 8 lados. Y se diseñó específicamente para coincidir con la '[métrica de distancia octagonal](../static/img/morphology/octagonal)'. No confundas ambos, ya que son núcleos muy diferentes. Aquí están los núcleos resultantes para radios pequeños...
Ten en cuenta que con radio 1 obtienes el mismo núcleo que un núcleo "Diamond". Por ello, el tamaño octagonal por defecto es de radio '2'. | _A partir de este punto usaré un script especialkernel2image para generar imágenes de los núcleos, ya que son mucho más claras que usar un método 'dilatar-escalar' en bruto (como arriba). Recuerda, sin embargo, que los núcleos son en general muy pequeños, aunque los núcleos Octagon y Disk (véase a continuación) pueden llegar a ser muy grandes para usos específicos.
---|---
| _El núcleo "Octagon" se añadió en IM v6.6.9-4, junto con el núcleo de distancia "[Octagonal](#octagonal)".
---|---
Disk
El núcleo 'Disk' es, como cabría esperar, una forma circular. Y se usa comúnmente cuando se necesita un núcleo morfológico muy grande. Ten en cuenta, sin embargo, que es un círculo booleano con alias. No obstante, el argumento radius de un disco puede ser un número en coma flotante, lo que te permite producir bastante variedad de formas, usando radios pequeños.
El núcleo 'Disk:4.3' es el predeterminado, y lo que considero la primera forma de disco verdadera. Los discos de este tamaño o mayores son especialmente buenos para redondear y suavizar en general las formas de la imagen. El tamaño final del núcleo que contiene el disco es el valor del 'radius ' redondeado hacia abajo, por 2 más 1. Por tanto, el núcleo 'Disk:4.3' predeterminado tiene un radio de tamaño de núcleo de 4, lo que hace que el tamaño final del núcleo sea 4 por 2 más 1, generando un núcleo de 9×9 para contener la forma de disco. Ten en cuenta que un valor menor que uno (pero no cero) siempre producirá un núcleo de un solo píxel, aunque eso no es muy útil. Después de eso, el núcleo tiende a producir mayormente núcleos que también pueden generarse usando los tipos de núcleo anteriores. Solo a medida que el radio se hace grande empiezan a surgir núcleos con forma de disco verdadera. Lo más importante a tener en cuenta es que un disco con un radio fraccionario funciona mucho mejor que usar un radio entero. Se recomienda en general añadir una fracción de alrededor de 0.3 a 0.5, para evitar generar un píxel solitario de aspecto extraño en los lados del disco.
Plus
El núcleo 'Plus' es en realidad un poco diferente de los demás núcleos morfológicos de forma, en que está diseñado para representar una 'forma' concreta en lugar de un simple 'vecindario' alrededor de un píxel. Usar un 'radius ' mayor con este núcleo no aumenta simplemente el tamaño del núcleo, sino que alarga los brazos del signo más resultante. El grosor de los brazos, sin embargo, no aumenta.
El tamaño por defecto de un núcleo 'Plus' es un radio de 2, produciendo 'brazos' de 2 píxeles alrededor del 'origen' central. Un núcleo 'Plus:1' resulta ser el mismo que el núcleo 'Diamond' por defecto. Ten en cuenta que un núcleo 'Plus' por lo general no se usa para los métodos morfológicos normales, y debe evitarse para tales fines. Sin embargo, es muy útil si quieres encontrar y resaltar puntos aislados en una imagen, como hago más adelante para mostrar la información del esqueleto. Básicamente, ofrece un método para dibujar símbolos, sin necesidad de saber exactamente dónde están situados los 'puntos' individuales en la imagen.
Cross
El núcleo 'Cross' es exactamente como '[Plus](#plus)' pero rotado 45 grados. También es solo una forma de núcleo especial adecuada para expandir píxeles para marcar las ubicaciones de varios puntos
Ring
El núcleo 'Ring', al igual que el núcleo '[Plus](#plus)', también está diseñado como un núcleo de 'forma' especial para marcar píxeles y generar patrones en las imágenes. Sin embargo, no toma solo un radio, puede tomar dos radios y se define de la misma manera que los núcleos Disk...
Ring[:{_radius1_}[,{_radius2_}[,{_scale_}]]]
Lo que hace es activar cualquier píxel que caiga entre los dos radios, independientemente del orden de los dos radios dados. Si no se dan radios, toma por defecto unos radios de '2.5' y '3.5', produciendo un 'Ring:2.5,3.5', que parece un anillo de forma octagonal hueco, ideal para rodear un píxel. Variando los dos radios puedes crear un 'anillo' de cualquier tamaño y grosor. Pequeños cambios en los radios añadirán y quitarán números muy pequeños de píxeles alrededor de los bordes, lo que te permite un control fino del aspecto del anillo. Si los dos radios están dentro de 1 píxel uno del otro, también puedes generar un anillo formado por puntos separados de forma dispersa, lo que puede ser útil como un vecindario para fines especiales. Los radios pequeños también generarán núcleos en forma de caja, que también pueden ser útiles. Si no se da el segundo radio, tomará por defecto un valor de '0.5', que efectivamente define un disco completo, pero sin el píxel 'origen' central. En otras palabras, un núcleo de disco pero excluyendo el píxel 'origen'. Aquí hay ejemplos de muchos de los núcleos 'Ring' que se pueden generar...
Como puedes ver, tienes muchas posibilidades, ajustando cuidadosamente los dos radios, y proporciona una buena manera de mostrar ubicaciones de interés en una imagen.
Rectangle
El núcleo 'Rectangle' está estrechamente relacionado con el núcleo '[Square](#k_square)' de arriba, y por defecto produce el mismo núcleo cuadrado de 3x3. Pero en lugar de un simple argumento de radio, puedes dar un argumento 'geometry ' para especificar el tamaño exacto del núcleo rectangular deseado. Aquí hay algunas especificaciones y una imagen de los núcleos que producen.
Por defecto, el núcleo intentará fijar el 'origen' del vecindario en el 'centro' exacto del núcleo. Pero para un rectángulo de tamaño par, elegirá el punto inmediatamente arriba y/o a la izquierda del centro según corresponda. Sin embargo, también puedes especificar orígenes descentrados. Este núcleo en particular también es bueno para definir líneas horizontales y verticales largas, lo que te permite buscar tales objetos dentro de las imágenes. Más sobre esto más adelante. Por ahora no puedes proporcionar un factor scale para un rectángulo. Todos sus valores de núcleo se fijarán solo en 1.0.
Núcleos caseros definidos por el usuario
No estás restringido solo a los núcleos integrados, sino que también puedes especificar tu propio núcleo, dando los valores exactos que quieres que el núcleo use...
"[{_geometry_}:] {_value_}, {_value_}, {_value_},....."
La especificación 'geometry ' es básicamente exactamente como el argumento del núcleo '[Rectangle](#k_rectangle)' anterior. Indica el tamaño del núcleo y, opcionalmente, el 'desplazamiento' del 'origen' del vecindario. Si solo se proporciona un número, se asumirá que son las dimensiones de un núcleo cuadrado. |
Recuerda que el valor de geometría NO es un argumento de 'radio', sino el tamaño global del núcleo. |
|---|---|
Si no se especifica ninguna 'geometry ' ni ':', estás usando la especificación de estilo 'antiguo'. Se generará un núcleo cuadrado de tamaño impar lo bastante grande para contener todos los valores dados. Esto no se recomienda y solo se ofrece por compatibilidad con versiones anteriores de ImageMagick. Después de los ':' (que son obligatorios tras una especificación 'geometry '), proporcionas entonces width × height valores en coma flotante separados por comas y/o espacios en blanco. Un valor especial de 'NaN' (que significa "Not a Number", no es un número) o un '-' por sí solo se puede usar para especificar que ese punto del núcleo no forma parte del vecindario morfológico. Por ejemplo, aquí hay una especificación para un núcleo cuadrado de anchura 3, que puede usarse para el desenfoque por convolución de la imagen de un solo píxel. |
magick pixel.gif -morphology Convolve \
"3: 0.3,0.6,0.3 0.6,1.0,0.6 0.3,0.6,0.3" \
-scale 800% k_user_3.gif
![[IM Output]](../static/img/morphology/k_user_3.gif)
Con un solo píxel, Convolve funciona casi igual que Dilate; sin embargo, Convolve usa los valores del núcleo, expandiendo y sumando los valores vecinos. Dilate, por otro lado, generalmente funciona usando una forma activado/desactivado (booleana) y el máximo de todos los vecinos. No obstante, cuando se aplica sobre un único píxel aislado con una forma booleana, obtienes el mismo resultado. Observa cómo puedes añadir espaciado extra (o incluso saltos de línea) a la cadena de entrada para separar las filas individuales de la definición del núcleo rectangular. Y aquí definí un área rectangular de 5×3, pero uso los valores especiales 'nan' (no es un número) para recortar las esquinas y hacer un núcleo de forma ovalada... |
magick pixel.gif -morphology Dilate \
"5x3: nan,1,1,1,nan 1,1,1,1,1 nan,1,1,1,nan " \
-scale 800% k_user_5x3.gif
![[IM Output]](../static/img/morphology/k_user_5x3.gif)
Y por último, aquí hay un ejemplo de cómo especificar un vecindario rectangular que forma una 'L' alrededor del 'origen'. Usé '-' en lugar de 'nan' para especificar las partes que no son parte del núcleo. Observa que el origen de este núcleo ni siquiera forma parte de su propio vecindario, puede situarse en cualquier lugar dentro de los límites rectangulares del núcleo. |
magick pixel.gif -morphology Dilate \
"2x3+1+1: 1,- 1,- 1,1 " -scale 800% k_lman.gif
![[IM Output]](../static/img/morphology/k_lman.gif)
Como puedes ver, la especificación de núcleos de usuario es muy flexible, lo que te permite especificar prácticamente cualquier tipo de núcleo que quieras, ya sea un núcleo de convolución con muchas fracciones, o un núcleo con forma con elementos 'no parte del vecindario', para los métodos morfológicos.
Convertir una imagen en un núcleo de usuario
Para facilitar la generación de núcleos caseros puedes usar el script "[image2kernel](../static/img/scripts/image2kernel)" para crear núcleos. Por ejemplo, aquí convierto con magick una pequeña bandera (
) en un archivo de datos de núcleo de usuario ("[flag_kernel.dat](../static/img/morphology/flag_kernel.dat)"), y luego lo uso para dilatar una imagen con un par de píxeles en ella.
magick -size 80x80 xc:black -fill white \
-draw 'point 20,15 point 55,30 point 40,60' points_pixels.gif
image2kernel -qgm flag.gif flag_kernel.dat
magick points_pixels.gif \
-morphology Dilate @flag_kernel.dat \
flagged_points.gif
Véase también Generar una imagen del núcleo más abajo, que puede generar imágenes (o generar versiones ampliadas y bonitas) de un núcleo. Esta técnica también se trata en Alternativas para dibujar símbolos.
Iterar (repetir) operaciones de morfología
Como has visto, puedes generar un núcleo mayor para aplicar una morfología sobre un vecindario más grande. Sin embargo, en la mayoría de los casos, una alternativa más rápida a usar un núcleo mayor es simplemente repetir (iterar o hacer un bucle) el operador de morfología varias veces. Esto significa que el efecto de ese operador se llevará más lejos, teniendo el mismo efecto básico que usar un núcleo mayor, pero sin el coste computacional añadido de usar un núcleo mayor. Por ejemplo, aquí hay una dilatación de un solo píxel usando un núcleo 'Diamond:3'... |
magick pixel.gif -morphology Dilate Diamond:3 -scale 800% k_diamond_x3.gif
![[IM Output]](../static/img/morphology/k_diamond_x3.gif)
Pero también puedes lograr el mismo resultado usando un núcleo 'Diamond' más pequeño (radio 1) tres veces... |
magick pixel.gif -morphology Dilate Diamond \
-morphology Dilate Diamond \
-morphology Dilate Diamond -scale 800% k_diamond_x3.gif
![[IM Output]](../static/img/morphology/k_diamond_x3.gif)
Sigues usando solo un núcleo muy pequeño de 3x3, pero repitiendo la operación morfológica básica tres veces para producir el mismo efecto que si usaras un núcleo mayor. De hecho, repetir núcleos pequeños de este modo es bastante más rápido que usar el núcleo mucho mayor. | _Un núcleo grande 'Diamond:3' tiene 81 elementos que procesar por cada píxel de la imagen. Pero repetir un núcleo 'Diamond' más pequeño 3 veces tiene 3×9, o 27 elementos de núcleo que procesar por píxel de la imagen. En este caso es 3 veces más rápido.
Este aumento de velocidad no es mucho en este caso, pero el ahorro es mucho mayor a medida que aumenta el tamaño de los núcleos._
---|---
Como repetir una operación morfológica es muy común, en lugar de repetir la operación varias veces, puedes simplemente pedirle a IM que haga un bucle o itere la operación, esa cantidad de veces. |
magick pixel.gif -morphology Dilate:3 Diamond -scale 800% k_diamond_3.gif
![[IM Output]](../static/img/morphology/k_diamond_3.gif)
Observa la diferencia entre esto y el primer ejemplo. Todo lo que ha sucedido es que hemos movido el ':3' de ser el radio del núcleo 'Diamond' al número de veces que se va a usar el método 'Dilate'. Usar una 'iteration ' para agrandar el vecindario efectivo funciona para la mayoría de los núcleos 'circulares' o 'convexos', como un 'Square' y un 'Diamond'. Pero no funciona para todos los tipos de núcleo. Por ejemplo, para un núcleo no convexo como un 'Plus' (que no es una forma 'cóncava') producirá resultados muy inusuales. Por ejemplo, esto no es lo mismo que pasar de un 'Plus' (radio 2) a un núcleo 'Plus:4' de doble tamaño... |
magick pixel.gif -morphology Dilate:2 Plus -scale 800% k_plus_2.gif
![[IM Output]](../static/img/morphology/k_plus_2.gif)
Ten en cuenta que si usas un recuento de 'iteration ' de '0', la morfología no hará nada. Esta es una forma útil de 'desactivar' el operador cuando no quieres que haga nada, pero no quieres quitarlo de la línea de comandos. Véase Mostrar salida detallada, más abajo, para otro uso de un recuento de iteración cero. Usar un valor especial de '-1' repetirá la operación hasta que no se vean más cambios en la imagen. Es decir, hasta que la imagen alcance un punto de 'convergencia'. Esto, sin embargo, es peligroso, ya que en algunas situaciones podría conducir a operaciones de muy larga duración. Para una operación como 'Dilate', por ejemplo, simplemente repetiría la dilatación hasta que toda la imagen estuviera completamente llena de blanco. Básicamente produciendo una especie de 'relleno por inundación' descontrolado (véase el siguiente ejemplo más abajo). Iterar un núcleo 'Disk' para producir un efecto de vecindario mayor tampoco se recomienda en general. Esto es porque el núcleo 'Disk' se convierte en una forma de disco más precisa a medida que el radio se hace mayor, mientras que un disco iterado agrandará no solo la forma, sino también los errores (forma no de disco) del núcleo. Por tanto, puede que te convenga más usar un radio mayor (que es más lento) en lugar de iterar la operación (que produce un disco más distorsionado). Sin embargo, cuando un radio de 'Disk' se vuelve realmente grande, una combinación de radio y múltiples iteraciones podría producir un resultado más rápido, pero aún aceptable. Puede que se necesite precaución y algo de experimentación con tu situación concreta.
Salida detallada de los cambios
Si quieres ver los resultados de iterar (repetir) una operación morfológica, puedes establecer la opción "[-define debug=True](https://imagemagick.org/command-line-options/#define)", que activa el control operacional detallado. A medida que el operador de morfología itera, informará de un recuento incremental de la iteración, y de cuántos píxeles de la imagen cambiaron en cada paso iterado. La salida va a la salida de error estándar, de modo que aún puedes canalizar los resultados de imagen. Por ejemplo, vamos a 'Dilate' la imagen de un solo píxel usando el núcleo 'Octagon' más grande hasta que toda la imagen se haya llenado de blanco y no se puedan hacer más cambios en la imagen. Recuerda que un límite de iteración de '-1' significa iterar para siempre, o hasta que no se vean más cambios. | |
MAGICK_THREAD_LIMIT=1 \
magick pixel.gif -define debug=true -morphology Dilate:-1 Octagon \
-scale 800% iterate_infinite.gif
Observa el número de cambios realizados en cada iteración. Inicialmente se convirtieron 20 píxeles de negro a blanco. Luego 48 más en la siguiente iteración, y así sucesivamente. Este número generalmente crece a medida que el borde de la forma resultante se hace más grande, pero luego comenzó a reducirse de nuevo cuando la forma alcanza el límite de la imagen. En la cuarta dilatación se rellenaron los últimos 4 píxeles (en las esquinas de la imagen). En la última dilatación (iteración 5) la imagen ya estaba completamente llena, por lo que no se realizó ningún cambio más a ningún píxel. Como no se hicieron cambios, la morfología se aborta automáticamente, dando un número final de cambios para esta etapa de la operación. Usar una iteración infinita de '-1' sí tiene un límite interno. Actualmente está fijado en la anchura o altura máxima de la imagen. Esto se hace para evitar que ImageMagick entre en un bucle interminable. Sin embargo, normalmente las operaciones terminarán mucho antes de alcanzar ese límite interno. Algunos métodos de morfología están en realidad definidos en términos de métodos más simples y primitivos. Por ejemplo, un método '[Smooth](#smooth)' es uno de esos métodos compuestos. La salida de "[-define](https://imagemagick.org/command-line-options/#debug)" que se genera al usar este método muestra los múltiples pasos internos que componen su procesamiento.
MAGICK_THREAD_LIMIT=1 \
magick man.gif -define debug=true -morphology Smooth:2 Diamond null:
Si te fijas, puedes ver que el '[Smooth](#smooth)' en realidad itera 4 métodos más primitivos, y por tanto procesa internamente la imagen 8 veces para realizar la operación solicitada. Cada línea consta de...
Smooth:_i_._s_esto muestra el método de morfología de alto nivel que se está aplicando a la imagen, y el recuento de iteración 'i ' y la 'etapa' primitiva 's ' que IM está procesando. Para el método 'Smooth', ese primer número siempre es '1', ya que el 'recuento de iteración' dado por el usuario se aplica en el método primitivo de nivel inferior. En otros métodos, la iteración dada por el usuario puede aplicarse en este nivel superior en lugar de en el inferior. El segundo número de 'etapa' es el recuento de 'etapa' primitiva que se está aplicando. 'Smooth' está compuesto de cuatro de esas etapas, ya que implementa los métodos compuestos '[Open](#open)' y '[Close](#close)'.Dilate*:_i_._k_Este es el método primitivo que se está aplicando. El primer número i es de nuevo el recuento de iteración dado por el usuario (si se está aplicando aquí). El segundo número 'k ' es el núcleo que está aplicando el método primitivo de morfología. Como solo hay un núcleo, siempre es cero en este caso. (Véase Manejo de varios núcleos más abajo) El '*' indica que el núcleo se reflejó (o se rotó 180 grados alrededor del origen) antes de ser aplicado por la primitiva morfológica. Esto es necesario para algunos métodos morfológicos compuestos; en este caso, el método '[Close](#close)' siempre usa un núcleo reflejado en su uso de los métodos primitivos '[Dilate](#dilate)' y '[Erode](#erode)'.#6 => Changed 311 Total 637Este es un informe de los resultados de aplicar la primitiva de morfología a la imagen. El número 'hash' es un recuento incremental del número de pasadas primitivas a través de la imagen. Esto te da una buena idea de lo intensivo computacionalmente que es un operador de morfología compuesto. A continuación obtienes el número real de píxeles que se cambiaron de alguna manera durante esa pasada. Si esta es la última de varias iteraciones para esta primitiva y núcleo concretos, también se imprime un recuento total de modificaciones de píxeles. Esto, sin embargo, no refleja el número total de píxeles cambiados en total de principio a fin, solo los cambios causados por la iteración de bajo nivel de la operación de primitiva y núcleo concretos. Algunos píxeles pueden cambiar varias veces por algunas primitivas morfológicas.
De lo anterior puedes deducir que internamente IM puede tener cuatro bucles de procesamiento aplicándose para procesar por completo un método de morfología dado. Sin embargo, normalmente la mayoría de estos bucles se aplican una sola vez. | _Advertencia: ¡el número de píxeles que cambian puede no ser correcto en máquinas que ejecutan un entorno multihilo en máquinas multinúcleo modernas! Solo se garantiza que sea preciso cuando se ejecuta en un entorno de un solo hilo. Yo clasifico esto como un error, pero no uno vital.
Si esto es un problema, asegúrate de establecer la variable de entorno "MAGICK_THREAD_LIMIT" en un valor de '1' para esa ejecución concreta de ImageMagick, como hice en los dos últimos ejemplos de arriba.
A partir de IM v6.8.4 ya no necesitas el ajuste de la variable de entorno "MAGICK_THREAD_LIMIT", ya que los recuentos se manejan correctamente en un entorno multihilo.
Mostrar el núcleo generado (con fines de depuración)
Si quieres ver realmente los valores que se usaron para definir un núcleo concreto que se generó, puedes definir un ajuste especial...
-define morphology:showkernel=1 -define convolve:showkernel=1
Cualquiera de los defines anteriores hace que IM imprima (en la 'salida de error estándar') toda la información sobre un núcleo generado, después de que el núcleo se haya procesado por completo en preparación para su uso. (Véase Escalado de núcleos de convolución). Por ejemplo, aquí están los valores reales del núcleo '[Disk](#disk)' integrado...
magick xc: -define morphology:showkernel=1 -morphology Dilate:0 Disk null:
Ten en cuenta que, como solo quería mostrar el núcleo, en realidad no me importa nada el procesamiento de la imagen. Por eso, fijé la 'iteration ' de la morfología en '0' (no hacer nada), y también descarto cualquier resultado de imagen usando un formato de archivo de salida null:. El valor especial en coma flotante 'nan' de lo anterior tiene el mismo significado que cuando se introduce un núcleo definido por el usuario. Significa 'Not A Number' (no es un número) y marca las partes de un núcleo que no forman parte del vecindario. Esos valores son ignorados por todas las operaciones morfológicas. Aquí hay otro ejemplo. Esta vez de un núcleo de convolución '[Comet](convolve.html#comet)'.
magick xc: -define morphology:showkernel=1 -morphology Dilate:0 Comet:0x2 null:
Esto es en realidad la mitad de una curva gaussiana unidimensional (sigma de 1.0), y puede ser una buena manera de extraer tal curva de ImageMagick. Observa también que el 'origen' de este núcleo concreto (el píxel al que afecta) está descentrado (situado en +0+0), lo cual no es muy común. El tamaño y el espaciado de los valores en la salida se pueden controlar mediante el control operacional de precisión especial. Eso se añadió a IM aproximadamente al mismo tiempo que el operador de morfología. Por ejemplo, aquí hay una repetición del ejemplo anterior pero usando "[-precision](https://imagemagick.org/command-line-options/#precision)" para limitar el número de dígitos significativos del valor por defecto de 6 a 3.
magick xc: -define morphology:showkernel=1 -precision 3 \
-morphology Dilate:0 Comet:0x2 null:
| La opción "[-precision](https://imagemagick.org/command-line-options/#precision)" se añadió a ImageMagick versión 6.5.9-1 durante el ciclo de desarrollo de la morfología. Por tanto, si la morfología está disponible, la precisión también puede considerarse disponible.
---|---
Generar una imagen del núcleo
Para facilitar la visualización de los núcleos, en lugar de usar Dilate o convolución sobre una imagen de un solo píxel para ver qué produce, creé un script especial llamado "[kernel2image](../static/img/scripts/kernel2image)". Este script extrae la salida exacta de Mostrar el núcleo y la convierte en una imagen del núcleo. El script "[kernel2image](../static/img/scripts/kernel2image)" tiene muchas opciones, desde generar la imagen en bruto del núcleo (por defecto) hasta especificar la cantidad de escalado, los espacios entre píxeles, el montaje, el etiquetado e incluso el coloreado de la 'imagen de núcleo' resultante. El script hace que sea mucho más fácil ver y entender los distintos núcleos, y se usa de forma extensiva para generar las imágenes de núcleo mostradas en estas páginas de ejemplos. Por ejemplo, así es como generé la imagen del núcleo "[Octagon](#octagon)". |
kernel2image -10.1 -m "Octagon" kernel_octagon.gif
![[IM Output]](../static/img/morphology/kernel_octagon.gif)
La opción especial '-10.1' significa escalar todos los píxeles a un tamaño de 10 píxeles, pero incluir también un espacio de 1 píxel entre esos píxeles. Si el núcleo se escala lo suficiente, el 'origen' del núcleo se marcará con algunos círculos dibujados. El '-m' especifica entonces que quiero que cree un Montage de la imagen con una etiqueta de identificación del núcleo "[Octagon](#octagon)" extraído, y efectos de sombra. Y aquí genero una 'imagen de núcleo' del núcleo en forma de 'L' definido por el usuario que usé arriba. |
kernel2image -20.2 -ml 'L-Shape' "3: 1,-,- 1,-,- 1,1,- " kernel_lman.gif
![[IM Output]](../static/img/morphology/kernel_lman.gif)
Si quieres crear un núcleo a partir de una imagen existente, se puede usar un script "[image2kernel](../static/img/scripts/image2kernel)" para crear un archivo de datos de núcleo a partir de una imagen. Este script normalmente toma una imagen en escala de grises, pero si se da una imagen multicolor, cada canal de la imagen se convierte en un archivo de datos de núcleo separado. Aquí creo los datos de núcleo de usuario a partir de una pequeña imagen de bandera (
), y luego uso "[kernel2image](../static/img/scripts/kernel2image)" para convertir esos datos de nuevo en una 'imagen de núcleo' ampliada para su visualización. |
image2kernel -qgm flag.gif flag_kernel.dat
kernel2image -6.1 -m -ml "Flag" @flag_kernel.dat kernel_flag.gif
![[IM Output]](../static/img/morphology/kernel_flag.gif)
NOTA AL MARGEN: podría haber generado la versión 'ampliada' de la pequeña imagen más directamente con un script similar "[enlarge_image](../static/img/scripts/enlarge_image), pero eso habría mostrado la imagen, y no los datos del núcleo, "[flag_kernel.dat](../static/img/morphology/flag_kernel.dat)".
Manejo de listas de varios núcleos
Generar varios núcleos
A partir de IM v6.6.2-0 puedes especificar varios núcleos que se aplicarán a la imagen uno a uno. Para especificar varios núcleos, basta con que concatenes cada definición de núcleo, separadas por un punto y coma ';'. Un punto y coma final al término es opcional. Por ejemplo, aquí defino una lista de núcleos especial que contiene una lista que puede usarse para la 'coincidencia de patrones' de píxeles de esquina.
3: 0,0,- 0,1,1 -,1,- ;
3: -,0,0 1,1,1 -,1,- ;
3: -,1,- 1,1,0 -,0,0 ;
3: -,1,- 0,1,1 0,0,- ;
Los puntos y coma adicionales (';') no importan, siempre que se proporcione al menos uno entre las especificaciones de núcleo. Tampoco importa el espacio en blanco adicional (incluidos los saltos de línea) en cualquier especificación de núcleo. Aquí hay una salida de Mostrar el núcleo de esta definición.
magick xc: -define morphology:showkernel=1 -morphology Dilate:0 \
" 3: 0,0,- 0,1,1 -,1,- ;
3: -,1,- 1,1,0 -,0,0 ;
3: -,0,0 1,1,1 -,1,- ;
3: -,1,- 0,1,1 0,0,- ; " null:
Y aquí se muestra una Imagen de núcleo de estos cuatro núcleos usando el script especial «[kernel2image](../static/img/scripts/kernel2image)».
kernel2image -20.2 -ml '' -mt x1 \
" 3: 0,0,- 0,1,1 -,1,- ;
3: -,1,- 1,1,0 -,0,0 ;
3: -,0,0 1,1,0 -,1,- ;
3: -,1,- 0,1,1 0,0,- ; " kernel_multi.gif
En realidad esta definición consiste en un solo núcleo que se ha expandido para formar un conjunto de 4 núcleos, cada uno rotado 90 grados. NOTA: Esta definición es casi equivalente al núcleo especial de coincidencia de patrones '[Corners](#corners)' (ver más abajo), salvo que se limita a las esquinas de la forma real, y no a cualquier esquina, de fondo o de primer plano.
Expansión a una lista de núcleos rotados
A partir de IM v6.2.2-0 se puede pedir a IM que expanda un único núcleo en una lista de núcleos rotados usando una de tres opciones especiales, tanto en núcleos con nombre como definidos por el usuario. Las tres opciones especiales son...
' @'Rota cíclicamente los núcleos 3x3 en incrementos de 45 grados, produciendo una lista de hasta 8 núcleos rotados. (mnemónico: ' @' es circular)' >'Rota (solo núcleos cuadrados o lineales) en incrementos de 90 grados. (mnemónico: el ' >' es un ángulo recto).' <'También produce rotaciones de 90 grados pero en una secuencia 'espejo' (ángulos de rotación de 0, 180, -90, +90 ). Esta forma especial de expansión por rotación funciona mejor con métodos de morfología como ' [Thinning](#thinning)'. (mnemónico: '<' es el espejo de un ángulo recto).
Por ejemplo, ese mismo núcleo anterior se puede especificar de forma más simple así...
' 3>: 0,0,- 0,1,1 -,1,- '
Esto define un núcleo, y la opción '>' le indica a IM que lo expanda en una lista rotada de 90 grados. Y aquí se muestra una imagen de la lista multinúcleo resultante
kernel2image -20.2 -ml '' -mt x1 \
'3>: 0,0,- 0,1,1 -,1,- ' kernel_rotated_list.gif
Y aquí se rota un núcleo 3x3 en una rotación 'cíclica' de 45 grados, expandiéndolo a una lista de 8 núcleos.
kernel2image -20.2 -ml '' -mt x1 \
'3@: -,1,- -,0,- 1,1,1 ' kernel_rotated_list2.gif
También se puede hacer lo mismo con cualquier núcleo 'individual' con nombre incorporado en IM, usando las mismas opciones en la sección de argumentos de esos núcleos. Por ejemplo, aquí se toma un núcleo simétrico '[Blur](convolve.html#blur)' y se expande en una lista rotada de 90 grados usando una opción '>'.
kernel2image -12.1 -n -ml '' "Blur:0x1>" blur_kernels.gif
Nótese que solo se generaron 2 núcleos, ya que un tercer núcleo reproduciría exactamente el primero. Esto se detecta y la generación de núcleos rotados se detiene. Sin embargo, si el 'origen' está descentrado, se habría generado la secuencia completa de 4 núcleos rotados, pues aunque la 'forma' del núcleo coincide, la ubicación del origen no sería la misma. Muchas definiciones de núcleos incorporados generan automáticamente una lista multinúcleo, por lo que no es necesario especificar ninguna opción con ese fin. Es decir, la expansión por rotación también está 'incorporada' en la definición concreta del núcleo. Tales núcleos suelen proporcionar además 'subtipos' de la definición original de núcleo único, de modo que se pueden elegir núcleos concretos para fines concretos.
Combinación de resultados de varios núcleos: reiterar o componer
Cuando se han definido varios núcleos, el método de morfología también necesita saber cómo debe combinar los resultados generados por esos núcleos. Esto se puede controlar mediante un Define global...
-define morphology:compose={_compose_method_}
El valor por defecto para la mayoría de los métodos de morfología es 'None'. Esto significa que, tras aplicar cada núcleo con el método de morfología indicado, la imagen resultante se usa como fuente para el siguiente núcleo. Es decir, simplemente se 'reitera ' o reutiliza la imagen resultante de aplicar un núcleo para el siguiente núcleo. Por ejemplo, si se aplica Convolve usando 2 núcleos '[Blur](convolve.html#blur)' rotados 90 grados, se obtiene lo siguiente.
magick pixel.gif -morphology Convolve "Blur:0x1>" \
-auto-level blur_re-iterate.gif
Como se ve, ambos núcleos se aplicaron a la imagen uno tras otro, de modo que cada núcleo trabaja con el resultado del anterior. Es decir, 'reitera ' el resultado de un núcleo con el siguiente, en secuencia. Esto equivale a realizar los dos pasos así.
magick pixel.gif -morphology Convolve "Blur:0x1" -auto-level blur_1.gif
magick blur_1.gif -morphology Convolve "Blur:0x1+90" \
-auto-level blur_re-iterate.gif
En realidad, así es como funciona el operador Blur para generar desenfoques de imagen más rápidamente. Véase Núcleos Gaussian frente a Blur, que demuestra este uso con más detalle.
Al fijar '{_compose_method_}' en cualquier método distinto de 'None', la operación NO se reitera. En su lugar, cada núcleo se aplica a la imagen original , y las imágenes resultantes se componen juntas usando el método '{_compose_method_}' indicado. Por ejemplo, si se usa un método de morfología '[Lighten](compose.html#lighten)' para generar una unión de los resultados separados, se obtendría..
magick pixel.gif -define morphology:compose=Lighten \
-morphology Convolve "Blur:0x1>" \
-auto-level blur_union.gif
Eso equivalía a hacer...
magick pixel.gif -morphology Convolve "Blur:0x1" -auto-level blur_1.gif
magick pixel.gif -morphology Convolve "Blur:0x1+90" -auto-level blur_2.gif
magick blur_1.gif blur_2.gif -compose Lighten -composite \
-auto-level blur_union.gif
Si no se está seguro de qué hace IM realmente durante una morfología, active la salida detallada de los cambios. Por ejemplo, aquí está la salida detallada de la reiteración con cada núcleo...
magick pixel.gif -define morphology:compose=None \
-define debug=true -morphology Convolve "Blur:0x1>" null:
Y aquí está la salida detallada de una unión (composición Lighten) del resultado de cada núcleo....
magick pixel.gif -define morphology:compose=Lighten \
-define debug=true -morphology Convolve "Blur:0x1>" null:
Ambas muestran claramente lo que ImageMagick hace para generar la imagen final. El número después del punto decimal representa el número de núcleo que se aplica en cada paso. Al final se indica cómo compone las imágenes según la configuración 'morphology:compose'. Muchos de los métodos de composición matemática y sus operaciones equivalentes de tipo teoría de conjuntos también pueden usarse para combinar los resultados de aplicar cada núcleo a la imagen original. En resumen, esta configuración define cómo se aplicarán los núcleos individuales de una lista multinúcleo a la imagen dada. El valor por defecto es 'None', que significa simplemente 'reiterar' los resultados; de lo contrario, combinará todos los resultados según el método de composición indicado.
Métodos básicos de morfología
Los métodos morfológicos son una técnica de procesamiento de imágenes para encontrar y analizar las formas de los objetos dentro de una imagen: expandir, encoger, localizar formas concretas, etc. Originalmente se desarrolló pensando en imágenes binarias (blanco y negro puros) y, por ello, suele aplicarse a imágenes con umbral que contienen formas sencillas en blanco y negro. Por convención, el blanco en una imagen binaria representa el primer plano, mientras que el negro representa el fondo. Los nombres de los métodos se describen, por tanto, según esta convención. Esto no quiere decir que los operadores no funcionen con imágenes en escala de grises o, en algunos casos, con imágenes en color, pero su propósito original era manejar formas binarias. Los núcleos de forma básicos ya vistos arriba son las 'formas' definidoras de vecindario más usadas en los métodos morfológicos. Tales núcleos suelen llamarse 'elementos estructurantes', ya que normalmente se usan para determinar la estructura de las formas dentro de la imagen.
Erode ( )
Como su nombre indica, el método '**Erode**' 'desgasta' la forma blanca desde cualquier píxel de fondo, haciéndola más pequeña. También puede entenderse como una expansión de las áreas negras de la imagen. Por ejemplo, aquí hay una forma binaria sencilla 'con aspecto de hombre' que se ha erosionado usando un núcleo '[Octagon](#octagon)'.
magick man.gif -morphology Erode Octagon erode_man.gif
Su efecto básico es adelgazar las protuberancias o puntos que la imagen pueda tener, o eliminarlos por completo, pero también agranda cualquier agujero presente (como el causado por el 'brazo' de esta imagen). En general, el tamaño del núcleo determina cuántos píxeles se eliminan.
Dilate ( )
El método '**Dilate**' es el dual de 'Erode'. Expande las formas blancas, agrandando una forma según el núcleo (y el número de iteraciones) indicado. Por supuesto, eso también significa que 'erosionará' las áreas negras de la imagen.
magick man.gif -morphology Dilate Octagon dilate_man.gif
Nótese cómo la forma no solo se hace más grande, sino que su contorno se vuelve más suave. El gran hueco entre las 'piernas' se ha rellenado, igual que el pequeño 'agujero' de un solo píxel que contenía la imagen. El tamaño y la forma del núcleo determinan cuántos píxeles se añaden alrededor de los bordes de la imagen. Los métodos 'Dilate' y 'Erode' son duales. Es decir, (al menos con un núcleo simétrico) negando la imagen antes y después de aplicar el método morfológico, se realiza en realidad la otra forma del operador. Por ejemplo, aquí se realiza una erosión usando 'Dilate' sobre las imágenes negadas. |
magick man.gif -negate \
-morphology Dilate Octagon -negate dilate_man_neg.gif
Open ( )
Aquí está el efecto del método '**Open**', pero esta vez usando un núcleo '[Disk](#disk)' mucho más grande.
magick man.gif -morphology Open Disk open_man.gif
Como resultado, se verá que 'Open' suavizó el contorno, redondeando los puntos afilados, y eliminó las partes más pequeñas que la forma usada. También desconectará o 'abrirá' los puentes delgados. Sin embargo, no elimina los 'agujeros' o huecos que pueda haber en la imagen, como el que hay entre las 'piernas' de la forma. Tampoco hace más grande ni más pequeño el tamaño 'central' básico de la forma. En términos reales, lo que hace es '[Erode](#erode)' una imagen y luego '[Dilate](#dilate)' de nuevo usando el mismo núcleo proporcionado
magick man.gif -morphology Erode Disk open_erode.gif
magick open_erode.gif -morphology Dilate Disk open_man_2.gif
Nótese que aplicar un 'Open' a una forma ya abierta, con el mismo núcleo, no producirá ningún cambio adicional en la forma. Por ejemplo...
magick open_man.gif -morphology Open Disk open_man_twice.gif
Es decir, repetir una operación 'Open' con el mismo núcleo no tiene efecto en el resultado. Por ello, cualquier recuento de iteración proporcionado se aplicará a los submétodos individuales de dilatación y erosión, y no al método en su conjunto, de modo que la iteración puede usarse para 'expandir' el núcleo efectivo, en lugar de repetir inútilmente la operación compuesta. Es decir, una iteración 'Open:2 ' se aplicará en realidad como un 'Erode:2, seguido de un 'Dilate:2' a la imagen. Esto tiene el efecto general de agrandar el 'vecindario' efectivo definido por el núcleo. |
magick man.gif -morphology Open:2 Disk open_man_x2.gif
![[IM Output]](../static/img/morphology/open_man_x2.gif)
Aquí se puede ver que el vecindario más grande resultante hizo que se eliminaran tanto la 'cabeza' como los 'pies' del hombre. El cuerpo principal de la forma quedó básicamente intacto, aunque también de aspecto más suave, mientras que el hueco de las piernas permanece intacto. Este es el mismo efecto que duplicar el tamaño del núcleo, aunque su forma exacta puede no ser idéntica a la de un núcleo del doble del radio.
Close ( )
El uso básico del método '**Close**' es reducir o eliminar los 'agujeros' o 'huecos' de un tamaño similar al del 'elemento estructurante' del núcleo. Es decir, 'cerrar' las partes del fondo que tienen aproximadamente ese tamaño.
magick man.gif -morphology Close Disk close_man.gif
El efecto básico de este operador es suavizar el contorno de la forma rellenando (cerrando) los agujeros e indentaciones. También formará 'puentes' de conexión con otras formas que estén lo bastante cerca como para que el núcleo toque ambas a la vez. Pero no hace más grande ni más pequeño el tamaño 'central' básico de la forma. En términos reales, lo que hace es '[Dilate](#dilate)' la imagen y luego '[Erode](#erode)' de nuevo usando el mismo núcleo proporcionado, haciendo que la imagen primero crezca y luego se reduzca. Este es el orden inverso al que sigue '[Open](#open)'.
magick man.gif -morphology Dilate Disk close_dilate.gif
magick close_dilate.gif -morphology Erode Disk close_man_2.gif
El resultado es que los puntos externos de la imagen se dejan tal cual, pero las 'bahías' se suavizan y engrosan, y los 'agujeros' y 'huecos' se cierran. Objetos desconectados que estén muy cerca pueden quedar enlazados entre sí. Como ocurre con '[Open](#open)', repetir el método '[Close](#close)' con el mismo núcleo no produce más cambios en la imagen. No obstante, usar una 'iteración ' con el operador repetirá los submétodos internos, produciendo un efecto de redondeo más fuerte, similar al de usar un núcleo mayor. Y al igual que los métodos '[Dilate](#dilate)' y '[Erode](#erode)', los métodos '[Open](#open)' y '[Close](#close)' son duales. Se puede reproducir el efecto del otro 'dual' negando la imagen antes y después de la operación. |
magick man.gif -negate -morphology Close Disk -negate close_man_neg.gif
Smooth
El método '**Smooth**' aplica un '[Open](#open)' seguido de un '[Close](#close)' de la forma, lo que primero elimina los 'objetos pequeños' y luego rellena los 'agujeros' o 'huecos' de un tamaño similar al del 'elemento estructurante' del núcleo. Aquí se suaviza la imagen usando un núcleo '[Octagon:3](#octagon)' de tamaño intermedio. |
magick man.gif -morphology Smooth Octagon:3 smooth_man.gif
![[IM Output]](../static/img/morphology/smooth_man.gif)
Como se ve, todas las 'indentaciones', 'huecos', 'agujeros' y 'puntos' se han suavizado y redondeado según el tamaño y la forma del núcleo. El operador '[Smooth](#smooth)' también suele repetirse con elementos estructurantes de tamaño lentamente creciente, para eliminar gradualmente el ruido de las imágenes. Si se conservan las partes eliminadas, se obtiene una 'descomposición' morfológica de la imagen que puede usarse para un estudio más detallado. Véase Granularidad más abajo. El método es especialmente bueno para limpiar documentos escaneados. Nótese que en realidad esto aplica 4 operaciones 'primitivas' distintas a la imagen original. Por tanto, es 4 veces más lento que un simple '[Erode](#erode)' o '[Dilate](#dilate)'.
Morfología plana en escala de grises
Aunque esencialmente los cuatro métodos morfológicos básicos, y los posteriores que se definen en términos de estos cuatro, están diseñados específicamente para trabajar con imágenes binarias, pueden aplicarse tanto a imágenes en escala de grises como en color (si bien las imágenes en color pueden generar algunos efectos de color extraños). Se necesita aquí un ejemplo práctico de operación en escala de grises Sin embargo, el núcleo en sí siempre se considera un simple vecindario 'activado' o 'desactivado'. Cualquier valor de núcleo que sea 'nan' o menor que '0.5' se considerará fuera del 'vecindario' que define. En resumen, los operadores anteriores aplican un núcleo 'plano' sin ninguna característica de 'altura' ni '3-dimensional', pero aun así pueden aplicarse a imágenes en escala de grises.
Morfología verdadera en escala de grises o 3-dimensional
La morfología verdadera en escala de grises o 3-dimensional (como la describió una biblioteca) suma o resta los valores del núcleo a los píxeles vecinos de la imagen antes de buscar los valores máximo/mínimo como resultado. Esto significa que trata una imagen en escala de grises como un 'campo de alturas' de un objeto de morfología 3-dimensional, y la forma en escala de grises del núcleo como la forma suavizadora que ajusta ese campo de alturas. Aunque los detalles de implementación de la morfología verdadera en escala de grises están bien documentados, su uso en situaciones prácticas no lo está. Es decir, no he encontrado ningún ejemplo útil de uso de la morfología verdadera en escala de grises más allá de 'núcleos de forma plana', salvo un comentario sobre su uso en el procesamiento 'fotométrico'. Por ello no he implementado la morfología verdadera en escala de grises 3-dimensional. No obstante, si alguien realmente necesita esos operadores morfológicos no planos en escala de grises, hágamelo saber e implementaré los operadores adecuados. Nótese que el método especial '[Distance](#distance)' (ver más abajo) es en realidad similar a cómo funciona la morfología verdadera en escala de grises, en el sentido de que suma el valor del núcleo a cada valor de píxel antes de tomar el valor 'mínimo' más pequeño. Sin embargo, este método no coincide ni con la definición de erosión 3D (restar y tomar el mínimo) ni con la de dilatación (sumar y tomar el máximo). Aun así, está muy estrechamente relacionado y probablemente podría implementarse usando esos métodos.
Variante de intensidad para imágenes en color
Como los cuatro métodos anteriores son métodos de canal en escala de grises, usarlos en imágenes en color puede generar efectos de color desviado, donde un canal se modifica pero otro no. En realidad no están diseñados para usarse con imágenes en color multicanal, solo con imágenes en escala de grises y binarias. El resultado es que, en imágenes en color, los colores se distorsionan, adquiriendo un tono más claro o más oscuro según la operación. Con esto en mente he creado versiones de 'intensidad' de estos métodos: 'ErodeIntensity', 'DilateIntensity', 'OpenIntensity', 'CloseIntensity'. Estas comparan los píxeles dentro del 'vecindario' definido y reemplazan el color del píxel actual según la intensidad de los píxeles. Es decir, se copia el píxel de color completo, y no solo los valores de canal individuales. Como resultado...
Las variantes de intensidad no generan ningún color 'nuevo' en las imágenes.
Por su naturaleza, los métodos de intensidad ignorarán por completo la configuración actual de "[-channel](https://imagemagick.org/command-line-options/#channel)". Por ejemplo, aquí se usan las variantes binaria y de intensidad de la morfología '[Dilate](#dilate)' (expandir áreas brillantes) sobre la imagen incorporada "rose:".
magick rose: -morphology Dilate Octagon:3 rose_dilate.gif
magick rose: -morphology DilateIntensity Octagon:3 rose_dilate_intensity.gif
Como se ve, el método normal '[Dilate](#dilate)' puede generar distintos tonos en cada una de las grandes manchas dilatadas, ya que cada canal se trata por separado. La segunda dilatación de intensidad, en cambio, conserva el color pleno de las manchas más brillantes, expandiéndolas según la forma booleana del núcleo. Los métodos de intensidad también tienen un esquema de nombres abreviado, sustituyendo la palabra 'Intensity' por una simple 'I'. Así, aquí se usa un método 'CloseIntensity' pero con el nombre abreviado 'CloseI'. Por ejemplo, aquí están los resultados de usar cada una de las cuatro variantes de 'intensidad' sobre la imagen rose incorporada. |
magick rose: -morphology ErodeI Octagon:3 rose_erode_intensity.gif
magick rose: -morphology DilateI Octagon:3 rose_dilate_intensity.gif
magick rose: -morphology OpenI Octagon:3 rose_open_intensity.gif
magick rose: -morphology CloseI Octagon:3 rose_close_intensity.gif
![[IM Output]](../static/img/morphology/rose_close_intensity.gif)
Los dos últimos pueden ser especialmente adecuados como operador sustituto del operador Paint. Estos métodos se clasifican como experimentales , y se agradecen comentarios o problemas sobre su uso. Si no recibo comentarios, ¡no se añadirá nada más!
Técnicas alternativas de morfología básica
Para quienes tengan versiones de IM anteriores a la v6.5.9-0, aún se pueden implementar
algunos métodos básicos de morfología.
Se puede generar un núcleo compuesto solo por unos. Por ejemplo, una matriz 7x7 de 1's
(radio=3), usando un sigma extremadamente grande y especificando el radio apropiado,
mediante un desenfoque gaussiano.
Así,
-convolve 1,1,1,1,1,.....
con un total de 49 unos equivale a
-gaussian-blur 3x65535
Esto permite generar un núcleo cuadrado simple para los métodos morfológicos
binarios.
'Dilate' para un núcleo cuadrado 3x3 (radio=1) es entonces
-gaussian-blur 1x65535 -threshold 0
'Erode' es entonces
-gaussian-blur 1x65535 -threshold 99.999%
Como se mostró anteriormente
'Open' es un 'Dilate' seguido de un 'Erode'
'Close' es un 'Erode' seguido de un 'Dilate'
y Smooth es un 'Open' seguido de un 'Close'
Se pueden especificar núcleos cuadrados mayores usando radios mayores.
Lamentablemente, las demás formas de núcleo incorporadas no están disponibles
sin usar el operador convolve para definir manualmente su forma.
Esto además solo funciona realmente para la morfología binaria. Para implementar una
morfología plana en escala de grises, será necesario usar una técnica distinta de
generar una imagen separada para cada píxel del núcleo y desplazarla
según la posición del píxel.
Tanto el método de convolución con umbral como el de composición por desplazamiento se han
implementado en el script "morphology" de Fred Weinhaus, creado mucho
antes de que el operador "-morphology" se añadiera a ImageMagick.
Vea y descargue el script "Morphology" de Fred Weinhaus en
http://www.fmwconcepts.com/imagemagick/morphology/index.php
Métodos de morfología por diferencia
El siguiente nivel de métodos morfológicos es lo que denomino morfología por diferencia. Es decir, el resultado de estos métodos de morfología es la diferencia entre uno de los métodos básicos de morfología anteriores y la imagen original, o algún otro método morfológico. En esencia, devuelven los cambios que uno de los métodos más simples realizó sobre la imagen original, dando los contornos, las adiciones o sustracciones entre las imágenes. Son, en esencia, composiciones de imagen de tipo '[Difference](compose.html#difference)' o '[Minus](compose.html#minus)' de los resultados.
EdgeIn
El método '**EdgeIn**, también llamado 'gradiente interno ', encuentra los píxeles que la erosión elimina del original. Como resultado, se devuelven los píxeles más cercanos al borde pero que formaban parte de la forma original.
magick man.gif -morphology EdgeIn Octagon edgein_man.gif
El borde resultante tiene aproximadamente la mitad del tamaño del núcleo dado, lo que para un núcleo '[Octagon](#octagon)' resulta bastante grueso. Más habitualmente se usaría un núcleo '[Diamond](#diamond)' o '[Square](#square)' mucho más pequeño, para producir un contorno de un solo píxel de la forma. En Color disperso como operador de relleno se muestra un ejemplo de uso de '[EdgeIn](#edgein)' con el canal alfa para extraer los píxeles del borde.
EdgeOut
El método '**EdgeOut**', también llamado 'gradiente externo ', encuentra los píxeles que una dilatación de la imagen añadió al original. Como resultado, se devuelven los píxeles de fondo inmediatamente adyacentes a la forma.
magick man.gif -morphology EdgeOut Octagon edgeout_man.gif
En Transparencia de contorno o halo se muestra un ejemplo de uso de '[EdgeOut](#edgeout)' con el canal alfa.
Edge o gradiente morfológico
El método '**Edge**' devuelve un 'gradiente morfológico ', que puede describirse como la suma de los dos métodos 'edge' anteriores o, más concretamente, como la diferencia entre la forma erosionada y su forma dilatada.
magick man.gif -morphology Edge Octagon edge_man.gif
Como antes, el tamaño y la forma del núcleo definen el grosor de la imagen erosionada. Su grosor es esencialmente equivalente al tamaño de ese núcleo, menos el píxel central. Así, un núcleo de radio 3 producirá en general un 'Edge' de 6 píxeles de grosor (el tamaño del núcleo es de 7 píxeles). Aquí, por ejemplo, está el contorno 'Edge' de la forma usando el núcleo '[Diamond](#diamond)' mínimo. |
magick man.gif -morphology Edge Diamond man_outline.gif
El borde tiene dos píxeles de grosor, ya que contiene los píxeles situados a ambos lados del 'borde de píxel' real de la forma original. La única forma de adelgazar este borde es desplazar la imagen entera en diagonal medio píxel. ![[IM Text]](../static/img/morphology/man_outline.gif)
Para más detalles sobre cómo obtener contornos de formas de varias maneras, véase la sección sobre detección de bordes. Futuro: generar el borde usando una 'línea diagonal'.
Top-Hat
El método '**TopHat**', o más concretamente 'White Top Hat ', devuelve los píxeles que un Opening de la forma eliminó, es decir, los píxeles que se quitaron para redondear los puntos y los puentes de conexión entre formas.
magick man.gif -morphology TopHat Disk tophat_man.gif
Como se ve, los píxeles suelen formar islas pequeñas y muy disjuntas, sin ningún conjunto de píxeles más grueso que el núcleo usado. El nombre del método, 'Top Hat ', se refiere en realidad al uso del operador cuando se aplica con el método de morfología 3-dimensional en escala de grises, y no con imágenes binarias como hemos hecho aquí. Este operador se usa más habitualmente con imágenes en escala de grises. FUTURO: ejemplo de top-hat en escala de grises
Bottom-Hat
El método '**BottomHat**', también conocido como 'Black TopHat ', son los píxeles que un Closing de la forma añade a la imagen. Es decir, los píxeles que se usaron para rellenar los 'agujeros', 'huecos' y 'puentes'.
magick man.gif -morphology BottomHat Disk bottomhat_man.gif
De nuevo se ve que también resulta en 'islas' de píxeles muy disjuntas, ninguna más gruesa que el núcleo usado. No obstante, siempre son un conjunto de islas completamente distinto al del método anterior. FUTURO: ejemplo de bottom-hat en escala de grises
Uso de métodos de morfología de bajo nivel
Morfología básica y canales
Todos los métodos básicos de morfología anteriores son métodos de canal, por lo que se aplican a los canales individuales de una imagen según la configuración actual de "[-channel](https://imagemagick.org/command-line-options/#channel)". Esto significa que se pueden aplicar estos métodos a imágenes en color, siempre que no se sea muy exigente con la 'fuga de color' procedente de áreas transparentes indefinidas. Por ejemplo, apliquemos '[Erode](#erode)' al canal alfa de la imagen original de la 'figura de hombre', sin modificar los canales de color.
magick figure.gif -channel A -morphology Erode Diamond:3 \
+channel figure_erode.gif
Como se ve, funciona bien. Para otros ejemplos, véase Color disperso como operador de relleno, que usa el método '[EdgeIn](#edgein)' para encontrar los píxeles del borde de una imagen. También Transparencia de contorno o halo, que usa '[EdgeOut](#edgeout)' para expandir los bordes de una imagen con un color específico.
Búsqueda de formas concretas
El conocimiento sobre un objeto depende de la forma en que lo sondeamos
(observamos). -- Georges Matheron, el padre de la morfología
Uso de Erode para localizar formas concretas dentro de una gran colección de formas. Llevado
al extremo, esto crea [esqueletos](#skeletons); véase también [Adelgazamiento de esqueletos](#thinning_skeleton).
Restauración de objetos usando Open (resultado suavizado) o [dilatación condicional](#dilate_conditional).
Necesita algún tipo de análisis de componentes conectados (segmentación) para contar
correctamente los objetos encontrados dentro de una imagen.
Granularidad de una colección de formas
Usando una serie de operaciones '[Open](#open)' en una imagen con elementos estructurantes de tamaño lentamente creciente, y midiendo el área resultante, se puede obtener rápidamente un resumen del número de formas de ese tipo que se encuentran en la imagen. Tomando la derivada (pendiente) de ese resultado se obtiene un 'espectro' del número y tamaño de las formas que componen la imagen. Esta gráfica es la 'granularidad ' de la imagen para una forma concreta. Véase Granulometría (morfología), Wikipedia. Las diferencias de un tamaño al siguiente también permiten separar y contar elementos concretos según su tamaño y, a su vez, separar áreas que contienen elementos de distinto tamaño y forma. El resultado es un método de segmentación de texturas. Demostración de cómo determinar el número y tamaño de una colección de formas. Sin embargo, esto requiere un método de 'conteo' (por añadir) para implementarse por completo. Nota histórica... Este uso fue, de hecho, la fuerza impulsora original tras la creación de los métodos de morfología, en una empresa minera de París, en los años 1960. Permitió a sus creadores crear un sistema automatizado para analizar la estructura de grano de fotos microscópicas de muestras minerales y determinar su idoneidad para la minería. Es decir, localizar y contar el tamaño y la cantidad de mineral en las muestras. Por ejemplo: dos minerales pueden tener la misma cantidad del mineral deseado (normalmente en forma de granos o cristales en la roca), pero solo el que tiene granos más grandes podría explotarse eficazmente, ya que permitía separar más fácilmente el mineral puro grande de la roca circundante que lo contiene. Esta era una tarea muy laboriosa que la morfología facilitó enormemente.
Efectos de núcleos asimétricos (pruebas de los métodos básicos)
Veamos cómo funcionan estos métodos básicos al usarse con un núcleo que no es simétrico. Por ejemplo, aquí aplico una forma de 'L' definida por el usuario sobre una imagen de prueba morfológica especial (ampliada para ver los píxeles individuales). |
for method in erode dilate open close; do
magick test_morphology.gif \
-morphology $method '2x3+1+1: 1,- 1,- 1,1 ' test_$method.gif
done
![[IM Text]](../static/img/morphology/test_mag.gif)
Que da los siguientes resultados...
'[Erode](#erode)' convierte cualquier coincidencia exacta de la forma del núcleo en un único píxel blanco en el punto coincidente, el 'origen'. También expande cualquier 'hueco' de un solo píxel a esa misma forma pero 'reflejada' alrededor del 'origen', es decir, como si el núcleo se hubiera rotado 180 grados.
'[Dilate](#dilate)' como cabe esperar, produce ese mismo resultado pero para una forma 'negativa' y 'reflejada' de la imagen o del núcleo. Un único píxel blanco se expande a la forma del núcleo, mientras que cualquier hueco con la forma 'reflejada' coincidente se reduce a un 'hueco' de un solo píxel.
Observa además que la frontera entre las mitades positiva y negativa de la imagen de prueba sí se desplaza como consecuencia de aplicar los métodos morfológicos básicos anteriores. Eso es lo esperable. Esto plantea un punto concreto sobre estos dos métodos. Para convertir un método '[Erode](#erode)' en un '[Dilate](#dilate)' o viceversa, no solo hay que Negar las imágenes antes y después, sino que también hay que rotar o reflejar el núcleo en torno al origen. Normalmente este segundo aspecto puede ignorarse, ya que la mayoría de los núcleos son 'simétricos'. Solo cobra importancia con núcleos asimétricos definidos por el usuario.
'[Open](#open)' como se mencionó antes, en general no elimina ningún 'hueco' de la imagen; sin embargo, una forma que coincida exactamente permanecerá sin cambios. Las formas mayores (como la mitad negativa de la imagen de prueba) también pueden permanecer, aunque quizá ligeramente modificadas.
'[Close](#close)' es un resultado exactamente negativo del anterior, pero está definido de modo que no necesita que el núcleo se refleje (ya que se refleja por su propia definición interna), solo que se niegue la imagen.
Coincidencia de patrones Hit And Miss (HMT)
Hit-And-Miss ( )
El método de morfología 'Hit-And-Miss', también conocido habitualmente como "HMT " en la literatura de informática, es un método morfológico de alto nivel diseñado específicamente para hallar y localizar patrones concretos en imágenes. Lo hace buscando una configuración específica de píxeles de 'primer plano' y de 'fondo' alrededor del 'origen'. |
A partir de IM v6.6.9-4 puedes usar cualquiera de los nombres de método 'HitAndMiss', 'Hit_N_Miss' o simplemente 'HMT', y sus variantes, para especificar este método de morfología. Antes de esta versión solo podía usarse el nombre de método 'HitAndMiss'. |
|---|---|
| Por ejemplo, podríamos buscar un píxel de 'primer plano' que tenga un píxel de 'fondo' inmediatamente a su derecha. |
magick man.gif -morphology Hit-and-Miss '2x1:1,0' hmt_right.gif
Como puedes ver, el pequeño núcleo de 2 elementos solo coincidió con los píxeles que estaban en el lado derecho de la imagen. Es decir, el método solo devolvió un píxel concreto que coincidía con el patrón dado. El 'Núcleo' o 'Elemento estructurante' utilizado solo puede contener un patrón de 3 tipos de elementos: un valor de '1' que significa 'primer plano', un valor de '0' que significa 'fondo', y además un tercer elemento que puede especificarse como 'Nan', '-' o un valor de '0.5', que significa 'No me importa' o 'Cualquier píxel'. El valor que uses para el 'origen' es muy importante, ya que definirá si solo quieres 'acertar' (hit) la forma de primer plano o el patrón de fondo. Pero si fijas específicamente el valor del 'origen' a 'No me importa', entonces podrás coincidir tanto con píxeles de primer plano como de fondo que tengan el vecindario circundante correcto. Por ejemplo, si uso un elemento estructurante como...
magick man.gif -morphology Hit-and-Miss '3x1:1,-,0' hmt_right2.gif
Obtienes los píxeles de cualquier borde derecho, ya estén dentro o fuera. Así, ahora marcas ambos lados de la frontera de la forma y extraes un borde de 2 píxeles de ancho. Sin embargo, no todos los píxeles coinciden con el patrón, de modo que no todos los píxeles se duplican, pero en general eso es lo que obtienes. El uso de un valor 'No me importa' para el 'origen' es en realidad muy común, especialmente cuando más adelante veamos los métodos Thicken y Thinning, que se restringen a añadir o a eliminar píxeles. Al 'No importar', la misma definición de núcleo podría usarse para cualquiera de las dos operaciones, ya que la propia operación define qué tipo de 'aciertos' te interesan.
Aquí hay otro ejemplo, pero esta vez vuelvo a limitar mis 'aciertos' a los píxeles que caen dentro de la forma pero que constituyen una esquina orientada al noroeste.
magick man.gif -morphology HMT "3:0,0,- 0,1,1 -,1,-" hmt_nw_corner.gif
Expandiendo esta única esquina a un conjunto de esquinas rotadas en 90 grados añadiendo un indicador '>', podemos encontrar todas las esquinas que aparecen dentro de la forma. |
magick man.gif -morphology HMT "3>:0,0,- 0,1,1 -,1,-" hmt_corners.gif
![[IM Output]](../static/img/morphology/hmt_corners.gif)
Como puedes ver, el método '[Hit-And-Miss](#hmt)' localiza y devuelve TODAS las posiciones de píxel que coinciden con cualquiera de los patrones de núcleo proporcionados. | _Si examinaras laSalida detallada de la operación "[-morphology](https://imagemagick.org/command-line-options/#morphology)" anterior, descubrirás que '[Hit-And-Miss](#hmt)' usa un método de composición '[Lighten](compose.html#set_theory)' para crear una 'unión' de todos los píxeles que coinciden con cada uno de los núcleos de patrón proporcionados.
Por desgracia, el recuento de píxeles 'modificados' es el de todos los píxeles que se apagan con cada aplicación de núcleo. En otras palabras, el número de píxeles de la forma menos el número de píxeles que coincidieron con cada núcleo.
_
---|---
| _Por la misma razón, repetir elMétodo Hit-And-Miss con sus propios resultados suele ser inútil, ya que la imagen habrá cambiado tanto que probablemente acabes sin ninguna coincidencia después.
Puedes, y como ves, usar los resultados para modificar la imagen original y generar así una imagen ligeramente distinta._
---|---
Puedes usar un conjunto de núcleos más selectivo respecto a lo que te interesa específicamente. Por ejemplo, supongamos que te interesan los puntos donde se encuentran tres líneas. Entonces puedes usar el conjunto de núcleos '[LineJunctions](#linejunctions)', diseñado específicamente para este propósito.
magick lines.gif -morphology HMT LineJunctions hmt_junctions.gif
Como puedes ver, solo un puñado de ubicaciones coincide con alguno de los núcleos de ese conjunto. Sin embargo, los resultados pueden hacer muy difícil ver realmente dónde estaban las ubicaciones coincidentes en la imagen original. Esto es especialmente problemático si trabajas con una imagen en escala de grises. Una solución es expandir las coincidencias usando '[Dilate](#dilate)' con algún Núcleo de forma, como un '[Ring](#ring)'. Por ejemplo... |
magick lines.gif \( +clone \
-morphology HMT LineJunctions \
-morphology Dilate Ring \
-background red -alpha shape \
\) -composite hmt_junctions_rings.gif
![[IM Output]](../static/img/morphology/hmt_junctions_rings.gif)
Ahora puedes ver con claridad las ubicaciones donde este conjunto concreto de núcleos encontró uniones de 3 o más líneas. Cada uno de los núcleos de '[LineJunctions](#linejunctions)' puede coincidir solo con un par de ubicaciones concretas, por lo que la coincidencia de patrones de esta forma puede ser lenta. Aun así, es muy precisa y funciona muy bien. Otro conjunto de núcleos '[Hit-And-Miss](#hitmiss)' similar es el núcleo '[LineEnds](#lineend)', que puede usarse para encontrar los extremos libres de todas las líneas de la imagen. |
magick lines.gif \( +clone \
-morphology HMT LineEnds \
-morphology Dilate Ring \
-background red -alpha shape \
\) -composite hmt_lineends_rings.gif
![[IM Output]](../static/img/morphology/hmt_lineends_rings.gif)
HitandMiss - solo con píxeles de primer plano - > erode HitandMiss - solo con fondo - > dilate negado
Hit And Miss con imágenes en escala de grises
Cuando el método '[Hit-And-Miss](#hitmiss)' se aplica a una imagen en escala de grises, el valor que se devuelve realmente será la diferencia entre el valor mínimo de 'primer plano' y el valor máximo de 'fondo'. Si se produce un resultado negativo (sin sentido matemático), el resultado se 'recorta a cero', ya que los negativos no tienen un significado real. En otras palabras, devuelve la 'separación mínima ' de valores entre los dos conjuntos de píxeles. Para formas booleanas, será '0.0' (negro) o '1.0' (blanco). Pero para imágenes en escala de grises esto equivale al 'gradiente' de los píxeles coincidentes. Puede usarse, por ejemplo, para identificar cuánto contraste hay entre un determinado primer plano y fondo en el patrón coincidente. Si lo que realmente quieres es un resultado booleano (encendido/apagado) de qué píxeles coinciden de verdad con el patrón en una imagen en escala de grises, deberías añadir una opción "[-threshold](https://imagemagick.org/command-line-options/#threshold) 0" después del comando.
Thicken (Añadir píxeles a una forma)
El método 'Thicken' añade píxeles a la forma original en cada ubicación coincidente. Por ejemplo, aquí busco un píxel de fondo que esté a dos píxeles del borde derecho de la forma.
magick man.gif -morphology Thicken '3x1+2+0:1,0,0' thick_right.gif
Como puedes ver, has acabado con una línea de píxeles justo fuera de la frontera original de la forma. Puedes Iterar este método 'Thicken' unas cuantas veces para continuar la secuencia. |
magick man.gif -morphology Thicken:4 '3x1+2+0:1,0,0' thick_right2.gif
![[IM Output]](../static/img/morphology/thick_right2.gif)
Sin embargo, como se están añadiendo píxeles, el origen del núcleo de coincidencia de patrones NO debe coincidir con un píxel de primer plano, o estarás añadiendo un píxel donde ya hay un píxel presente. En el ejemplo anterior se fija el píxel de origen a un patrón de fondo, de modo que solo coincidirán los patrones de fondo. Una alternativa es fijar siempre el origen a un valor de elemento 'No me importa'. Al hacerlo, podrás usar el mismo patrón de núcleo tanto para engrosar '[Thicken](#thicken)' como, según verás más adelante, para '[Thinning](#thinning)'. Así que la mejor regla es fijar el origen a 'No me importa'. | _Otra forma de generar una operación '[Thicken](#thicken)' es generar una Unión de los resultados de '[Hit-And-Miss](#hitmiss)' de este núcleo con el núcleo especial '[Unity](#unity)', para incluir la imagen original en los resultados.
Por ejemplo..._ |
magick man.gif -define morphology:compose=Lighten \
-morphology HitAndMiss 'Unity ; 3x1+2+0:1,0,0' hmt_thicken.gif
_En realidad, elAjuste de composición multinúcleo del ejemplo anterior no es necesario, ya que el método '[Hit-And-Miss](#hitmiss)' establece específicamente este ajuste de composición de forma predeterminada cuando el usuario no lo define.
_
Normalmente '[Thicken](#thicken)' se usa para agrandar formas como líneas, pero sin alargarlas. Un conjunto especial de núcleos conocido como núcleo '[ConvexHull](#convexhull)' permite hacer esto. Por ejemplo...
magick -size 80x80 xc:black -fill none -stroke white \
+antialias -draw 'line 10,20 70,60' man_line.gif
magick man_line.gif -morphology Thicken ConvexHull thick_line.gif
Thicken - Envolvente convexa octagonal
El núcleo '[ConvexHull](#convexhull)' está pensado realmente para trabajar con formas de imagen, y expandirá una forma a una 'Envolvente convexa octagonal '. Es decir, intentará rellenar todos los huecos entre los extremos hasta producir un objeto con 'forma octagonal'.
magick man.gif -morphology Close Diamond \
-morphology Thicken:-1 ConvexHull \
-morphology Close Diamond man_hull_full.gif
![[IM Output]](../static/img/morphology/man_hull_full.gif)
Consulta la definición del núcleo '[ConvexHull](#convexhull)' para más detalles y para entender por qué se necesitan los dos métodos '[Close](#close)'.
Puedes observar cómo se realizan las iteraciones activando el Ajuste de salida detallada. Sin embargo, esto mostrará que lo anterior es muy, muy lento. Cada iteración de '[Thicken](#thicken)' solo añade unos pocos píxeles a la forma en cada paso. Por ello, puede hacer falta una gran cantidad de iteraciones para completar la 'envolvente' completa. En este caso concreto, la imagen requirió 80 iteraciones de '[Thicken](#thicken)', con un '[ConvexHull](#convexhull)' de 8 núcleos. Eso significa que lo anterior requirió en realidad 640 iteraciones primitivas, más otras 4 iteraciones primitivas necesarias para ejecutar los dos métodos '[Close](#close)'. Eso puede tardar bastante tiempo. Básicamente, iterar usando la Coincidencia de patrones Hit And Miss puede ser muy, muy 'lento ', y si puede encontrarse una técnica alternativa, debería usarse en su lugar. Puedes usar esto también para encontrar qué puntos de la imagen original provocaron la creación de esta forma octagonal, obteniendo una intersección (Composición Darken) entre el borde de la envolvente convexa y la forma original.
magick man_hull_full.gif \
-morphology EdgeIn Diamond man_convex_edge.gif
magick man.gif man_convex_edge.gif \
-compose Darken -composite man_extremities.gif
Cualquier forma conectada que quepa dentro de la envolvente convexa, pero que incluya al menos un píxel en cada borde de dicha envolvente, generará la misma envolvente convexa octagonal.
Thicken con imágenes en escala de grises
Al manejar una imagen en escala de grises, '[Thicken](#thicken)' sumará el resultado de separación entre primer plano y fondo de '[Hit-And-Miss](#hitmiss)' al píxel de origen. Esto puede usarse para hacer más brillantes los píxeles coincidentes, incluso cuando el píxel de 'origen' no está en el conjunto de 'fondo'. Por ejemplo, repitamos el ejemplo de búsqueda de esquinas de antes pero con una versión de la forma con un gris del 50%.
magick man.gif -evaluate multiply 0.5 man_grey.gif
magick man_grey.gif -morphology Thicken Corners thick_corners.gif
Al usar una versión HDRI de ImageMagick con '[Thicken](#thicken)', probablemente sea buena idea aplicar "[-clamp](https://imagemagick.org/command-line-options/#clamp)" o "[-auto-level](https://imagemagick.org/command-line-options/#auto-level)" a los resultados para evitar que desborden los límites del rango de valores de píxel de la imagen.
Thinning ( ) (Restar píxeles de una forma)
El método 'Thinning' es el dual de '[Thicken](#thicken)'. En lugar de añadir píxeles, este método los resta de la imagen original. Por ejemplo, eliminemos cualquier píxel que esté a 4 píxeles hacia dentro desde el borde derecho.
magick man.gif -morphology Thinning '5x1+0+0:1,1,1,1,0' thin_right.gif
Para que 'Thinning' funcione correctamente, el núcleo de coincidencia de patrones debe tener un origen que contenga un píxel de primer plano; de lo contrario, el método no tiene ningún píxel coincidente que eliminar de la forma. | _Otra forma de generar una operación '[Thinning](#thinning)' es hacer el Complemento relativo (usando una composición MinusSrc) de los resultados de '[Hit-And-Miss](#hitmiss)' respecto a la imagen original. Puedes incluir esa imagen al principio de la lista de núcleos (para 'restar' de ella) usando un núcleo '[Unity](#unity)'.
Por ejemplo..._ |
magick man.gif -define morphology:compose=MinusSrc \
-morphology HMT 'Unity ; 5x1+0+0:1,1,1,1,0' hmt_thinning.gif
_Este es un estilo de adelgazamiento de tipo 'intersección', que elimina en un solo paso todos los píxeles especificados por todos los núcleos, en lugar de un estilo 'iterativo' que elimina los píxeles de cada núcleo en secuencia. ConsultaEstilo de adelgazamiento para más información.
_
Conectividad de líneas
FUTURO: líneas 4-conectadas frente a 8-conectadas Consulta la discusión en los foros de IM, From 8-connected to 4-connected lines.
Adelgazamiento de la salida de un detector de bordes
Uno de los usos más comunes del adelgazamiento es reducir la salida umbralizada de un Detector de bordes, como la Convolución Sobel, a líneas de un solo píxel de grosor, conservando toda la longitud de esas líneas. Ejemplo con una imagen de gradiente de distancia.
Adelgazamiento hasta un esqueleto
Adelgazar imágenes con '[Thinning](#thinning)' se usa en realidad con más frecuencia que '[Thicken](#thicken)', ya que sirve para reducir las formas a representaciones más manejables, como los Esqueletos. Estos, como se comentará más adelante, pretenden ser la línea central de píxeles entre dos (o más) bordes de la forma. Un esqueleto es importante porque ofrece una descripción muy buena de una forma muy compleja. Por ejemplo, procesar la imagen para hallar el número de bucles, segmentos de línea y cómo están dispuestos te dirá mucho sobre la forma que tienes. Así que produzcamos un 'Esqueleto adelgazado ' adelgazando con '[Thinning](#thinning)' los bordes de la forma del hombre de manera repetida, hasta que solo queden las líneas centrales.
magick man.gif -morphology Thinning:-1 Skeleton man_raw_thinned.gif
Un informe Detallado de lo anterior habría mostrado 18 iteraciones, con 8 núcleos, sumando 144 iteraciones primitivas en total. Esto es en realidad mucho más rápido que hallar su Envolvente convexa (arriba), ya que los núcleos de adelgazamiento eliminan filas y columnas enteras de píxeles en cada iteración, y no solo unos pocos cada vez. Observa cómo el conjunto de núcleos '[Skeleton](#skeleton)' no logró expandir el hueco, por lo que no encontró la línea central entre el hueco y el borde exterior. Este es un fallo grave de este núcleo concreto de adelgazamiento de esqueleto, y se debe a que todos los núcleos requieren al menos píxeles de fondo antes de hacer cualquier coincidencia de adelgazamiento. Puedes usar conjuntos de núcleos de adelgazamiento de esqueleto para resolver este problema. Una solución más sencilla es aplicar Erode ligeramente a la imagen para dar a los núcleos algo con lo que trabajar. Además, solo aplicaré la erosión y el adelgazamiento a los canales 'rojo' y 'verde', para dejar la forma original en azul.
magick man.gif -channel RG -morphology Erode Diamond man_erode.gif
magick man_erode.gif -channel RG \
-morphology Thinning:-1 Skeleton +channel man_skeleton.gif
También puedes ver que cualquier hueco de la imagen se ha expandido ahora para producir un bucle continuo de píxeles más grande a su alrededor. Aquí tienes un primer plano del bucle alrededor del hueco erosionado. |
magick man_skeleton.gif -crop 22x22+47+29 +repage \
-scale 120x120 man_skeleton_zoom.gif
![[IM Output]](../static/img/morphology/man_skeleton_zoom.gif)
Observa que no produjo una línea central exacta entre el hueco y el borde. Además, como la forma fue erosionada, las líneas no llegan hasta el borde de la forma original, sino que se detienen un píxel antes. Es decir, los extremos de las líneas han sido 'podados' ligeramente. Esa es la desventaja de la solución de 'erosión'. El esqueleto también se limita a líneas octagonales, lo que significa que pierde mucho detalle, aunque en este caso esa simplificación puede ser algo bueno. Consulta la sección sobre Esqueletos más abajo. Este es un núcleo '[Skeleton](#skeleton)' tradicional, que como puedes ver produce líneas diagonales 'gruesas', de modo que todas las partes del esqueleto son '4-conectadas' o 'conectadas en diamante'. Hay otras variantes de núcleos '[Skeleton](#skeleton)', que producirán otras variaciones en el 'Esqueleto adelgazado ' resultante. Esqueleto más fino, 8-conectado Este esqueleto 'tradicional', como se ha mencionado, tiene diagonales gruesas. Pero a menudo no es lo bastante 'fino'. En algunas situaciones, lo que quieres es un esqueleto ligeramente más fino. Es decir, quieres un esqueleto '8-conectado' en lugar de '4-conectado'. Una solución es usar una variante distinta de generación de esqueleto, como la generada con un núcleo '[Skeleton:2](#skeleton2)' (encontrado en el sitio web del tutorial gráfico HIPR2). Por ejemplo... |
magick man.gif -channel RG -morphology Erode Diamond \
-morphology Thinning:-1 Skeleton:2 +channel man_skeleton_hipr.gif
![[IM Output]](../static/img/morphology/man_skeleton_hipr.gif)
Y aquí tienes un zoom del área del bucle, que muestra cómo el esqueleto resultante es 8-conectado, con diagonales más finas. |
magick man_skeleton_hipr.gif -crop 22x22+47+29 +repage \
-scale 120x120 man_skeleton_hipr_zoom.gif
![[IM Output]](../static/img/morphology/man_skeleton_hipr_zoom.gif)
Sin embargo, he comprobado que tales esqueletos no son tan precisos como el esqueleto 'tradicional'. Básicamente, en casos de prueba he comprobado que las diagonales se 'adelgazaban' por el lado equivocado. Esto se debe a que el lado de las diagonales que se elimina está controlado únicamente por el orden del núcleo de adelgazamiento de 'esquina' en el conjunto de núcleos, y no por ninguna decisión basada en la naturaleza de la forma.
La alternativa es tomar un esqueleto 'tradicional' y adelgazarlo de modo que las diagonales siempre se adelgacen por el lado 'exterior' de la diagonal, según lo definen los puntos extremos de la diagonal. El núcleo de adelgazamiento especial '[Diagonals](#diagonals)' está diseñado para hacer esto, usando después un núcleo '[Corners](#corners)' para 'rematar'. Así que adelgacemos más el esqueleto 'tradicional' anterior...
magick man_skeleton.gif -channel RG \
-morphology Thinning:-1 Diagonals \
-morphology Thinning Corners man_thin_skeleton.gif
Esta técnica de adelgazar un esqueleto tradicional 4-conectado es algo más lenta que simplemente usar de forma directa la variante '[Skeleton:2](#skeleton2)'. El adelgazamiento adicional requirió 8 iteraciones de adelgazamiento de los 8 núcleos, es decir, 64 iteraciones primitivas. Como alternativa puedes usar solo el núcleo '[Corners](#corners)', aunque eso generará la variante 'HIPR', con una elección 'aleatoria' de qué lado de las diagonales se adelgazó. Sin embargo, solo tomará 1 pasada de los 4 núcleos, por lo que es mucho más rápido que usar '[Diagonals](#diagonals)'. En cualquier caso, partiendo de un esqueleto 'tradicional' 4-conectado, puedes generar muy fácilmente una versión 8-conectada (de algún tipo).
Información del esqueleto
Cuando tienes un esqueleto (quizá incluso una versión 4-conectada y otra 8-conectada), el siguiente paso suele ser averiguar más información sobre el esqueleto. Por ejemplo, cuántos 'extremos libres de líneas', 'uniones de líneas' y 'bucles de líneas' hay. Número de extremos de línea Aquí uso una Búsqueda Hit And Miss de '[LineEnds](#lineends)' sobre el esqueleto que generamos antes (extrayéndolo del canal 'rojo'). Luego aplico Dilate a esos extremos de línea para convertirlos en Anillos y los coloreo antes de fusionarlos con el esqueleto original, para hacer muy visibles sus ubicaciones.
magick man_skeleton.gif -channel R -separate +channel \
-morphology HMT LineEnds man_ends.gif
magick man_ends.gif -morphology Dilate Ring -background Red -alpha Shape \
man_skeleton.gif +swap -composite man_ends_marked.gif
Observa que las líneas que se conectan entre sí, o con el bucle de píxeles, no se detectaron. Solo se indicaron los extremos libres de las líneas. Si hicieras un recuento de píxeles (usando una Salida de histograma) verías que este esqueleto generó 12 extremos de línea. Número de uniones de línea Puedes obtener un recuento aproximado del número de uniones de línea de una imagen usando el núcleo '[LineJunctions](#linejunctions)' con un esqueleto 8-conectado , preferiblemente uno que se haya adelgazado a partir del esqueleto original usado para contar los extremos de línea. No mezcles dos variantes distintas de generación de esqueleto.
magick man_thin_skeleton.gif -channel R -separate +channel \
-morphology HMT LineJunctions man_junctions.gif
magick man_junctions.gif -morphology Dilate Ring \
-background Red -alpha Shape \
man_thin_skeleton.gif +swap -composite man_junctions_marked.gif
Si intentaras usar este núcleo directamente con un esqueleto tradicional 4-conectado, obtendrías múltiples coincidencias para algunas de las uniones en 'T', haciendo el recuento muy impreciso. El resultado, como puedes ver, son 12 uniones de línea, lo cual para esta forma concreta es correcto. Sin embargo, para algunas uniones el núcleo '[LineJunctions](#linejunctions)' es impreciso. Por ejemplo, una unión diagonal en 'X' de 4 líneas solo producirá 1 coincidencia, mientras que una unión ortogonal en '+' producirá 4 coincidencias. Ambas uniones especiales deberían producir 2 coincidencias, para mantener correcto el recuento de uniones de línea. Por ello, para obtener un recuento preciso necesitarás añadir 1 valor más por cada unión en 'X', y restar 2 cuentas por cada unión en '+'.
Para un esqueleto sin bucles, el número de uniones debería ser 2 menos que el número de extremos de línea. Sin embargo, si el número de extremos de línea es igual al número de uniones de línea, significa que tienes uno o más bucles en el esqueleto. Ahora bien, este esqueleto tiene 12 extremos de línea y 12 uniones, así que contiene al menos un bucle continuo de píxeles en algún lugar de la imagen. Número de bucles FUTURO: etiquetado de objetos conectados
Poda de líneas
Así que sabes que esta imagen tiene al menos un bucle. Supongamos que quieres simplificar la forma para quedarte solo con ese bucle (o bucles). La solución es 'Podar ' todos los extremos de línea de manera repetida hasta eliminarlos todos. Para un esqueleto 4-conectado como este, puedes incluso usar un conjunto más reducido de núcleos '[LineEnds](#lineends_subtypes)' para hacer el proceso unas dos veces más rápido.
magick man_skeleton.gif -channel G \
-morphology Thinning:-1 'LineEnds:1>' man_loop.gif
Un informe Detallado de esto habría mostrado que tomó 75 iteraciones con 4 núcleos, dando como resultado 300 integraciones primitivas para 'Podar ' todas las líneas con extremos libres de la imagen. Es decir, alrededor del doble de operaciones que las usadas para encontrar el esqueleto, lo que muestra cuánto más intensiva puede ser esta operación. Usar un conjunto completo de núcleos '[LineEnds](#lineends)' (8 núcleos) también habría tomado 75 iteraciones, pero con el doble de núcleos, lo que da 600 iteraciones primitivas.
Poda rápida de líneas
Técnica de poda completa rápida..
1/ Encontrar los extremos de línea y las uniones de línea.
2/ Borrar las uniones de línea para desconectar por completo todos los segmentos de línea.
3/ Rellenar por inundación, o usar dilatación condicional para eliminar los segmentos de 'extremo de línea'.
4/ Restaurar las uniones de línea.
5/ usar eso como mapa sobre la imagen original para restaurar los 'bucles'.
Ya hemos cubierto el primer paso... que da como resultado... |
magick man_skeleton.gif -channel R -separate +channel \
-morphology HMT LineEnds man_ends.gif
![[IM Output]](../static/img/morphology/man_ends.gif)
Para desconectar (o separar) todos los segmentos de línea puedes usar un núcleo '[LineJunctions](#linejunctions)'. Sin embargo, el conjunto de núcleos predeterminado no desconectará por completo las uniones en 'T' (solo las localizará). Para desconectar correctamente todos los segmentos de línea necesitarás también añadir núcleos ortogonales en 'T' al conjunto de núcleos, y conviene además incluir una unión en '+'. Por ejemplo. |
magick man_skeleton.gif -channel R -separate +channel \
-morphology HMT 'LineJunctions;LineJunctions:3>;LineJunctions:5' \
man_disconnect.gif
![[IM Output]](../static/img/morphology/man_disconnect.gif)
Aplicar adelgazamiento con estas coincidencias desconectará realmente los segmentos, pero debes hacerlo todo en un solo paso (consulta Estilo de adelgazamiento), o no funcionará correctamente. |
magick man_skeleton.gif -channel R -separate +channel \
-define morphology:compose=Darken \
-morphology Thinning 'LineJunctions;LineJunctions:3>;LineJunctions:5' \
man_line_segments.gif
![[IM Output]](../static/img/morphology/man_line_segments.gif)
Aquí tienes un zoom del 'bucle' que muestra los segmentos desconectados. |
magick man_line_segments.gif -crop 22x22+47+29 +repage \
-scale 120x120 man_line_segments_zoom.gif
![[IM Output]](../static/img/morphology/man_line_segments_zoom.gif)
En este punto ya podemos eliminar cualquier segmento de línea que contenga una coincidencia con el 'extremo de línea' descubierto previamente. Esto puede hacerse 'rellenando por inundación' desde esos puntos 'semilla' para borrarlos. Sin embargo, esto solo funciona para un esqueleto 4-conectado, que es lo que asume el relleno por inundación. Ejemplo aquí Como alternativa, podemos usar Dilatación condicional para encontrar todos los puntos simultáneamente y eliminarlos. Ejemplo aquí - cuando estén disponibles la dilatación o erosión condicional. Si ahora restauras las uniones de línea, haces una poda y eliminas cualquier píxel suelto que quede, habrás eliminado todos los segmentos de línea de forma rápida. Sí, parecen muchos pasos, pero créeme, sigue siendo mucho más rápido que tener que 'podar los extremos de las líneas ' 300 veces para obtener el mismo resultado.
Estilo de adelgazamiento - secuencial o simultáneo
Si hicieras una única 'poda' de los extremos de los segmentos de línea y la compararas con la imagen original, descubrirías que, más a menudo que no, un segmento de línea se podó entre 2 y 4 veces, según la forma y orientación exactas de las líneas. Por ejemplo (imagen resultante ampliada), este es un adelgazamiento de extremos de línea predeterminado
magick -size 10x10 xc:black -fill white \
+antialias -draw 'line 1,7 8,3' line.gif
magick line.gif -channel GB \
-morphology Thinning LineEnds line_seqential.gif
Esto se debe a que, de forma predeterminada, cada uno de los núcleos '[Thinning](#thinning)' se aplica sobre los resultados del núcleo anterior, en secuencia. Es decir, elimina todos los píxeles seleccionados por un núcleo antes de aplicar el siguiente núcleo a ese resultado, el cual puede seleccionar (y selecciona) más píxeles del mismo extremo de línea. En otras palabras, de forma predeterminada adelgazará los extremos de las líneas varias veces en una 'iteración' completa a través de todos los núcleos proporcionados. Esto significa que no puedes confiar en la Salida detallada para hacerte una idea exacta de cuánto medían las líneas contando el número de píxeles eliminados por una sola iteración de este operador. Sin embargo, puedes modificar cómo funciona '[Thinning](#thinning)' para que elimine solo el conjunto de píxeles que encontraría una única iteración '[Hit-And-Miss](#hitmiss)' a través de todos los núcleos. En otras palabras, aplicar todos los núcleos a la misma imagen al inicio de la iteración, fusionarlos y luego eliminar solo esos píxeles, una sola vez para todos los núcleos. Es decir, eliminar todos los píxeles seleccionados por HMT simultáneamente. Básicamente, fijas el Ajuste de composición multinúcleo para que use un método de composición '[Darken](compose.html#darken)', que hará exactamente eso. En concreto, fusiona todos los píxeles seleccionados para una única eliminación de los píxeles seleccionados. Por ejemplo... |
magick line.gif -channel GB -define morphology:compose=darken \
-morphology Thinning LineEnds line_simultaneous.gif
![[IM Output]](../static/img/morphology/line_simultaneous_mag.gif)
Lo que ocurre aquí es que cada núcleo de los Núcleos de coincidencia de patrones se aplicará solo a la imagen original. Cualquier píxel que coincida con el original se recopilará después en conjunto. Es decir, solo eliminaremos una 'intersección' de los resultados de todos los núcleos contra el original, usando una composición 'darken'. Pero la eliminación se hace toda en un paso para cualquier iteración a través de todos los núcleos. El resultado es que un extremo de línea solo coincidirá una vez, aunque varios núcleos pudieran coincidir con ese extremo de línea. Y así solo se eliminará el único píxel del extremo, en lugar de 2 o más píxeles por distintos núcleos. En resumen, añadir un Ajuste de composición multinúcleo '[Darken](compose.html#darken)' garantizará que el método '[Thinning](#thinning)' realice un 'Adelgazamiento simultáneo ' (todos los núcleos a la vez), en lugar de un 'Adelgazamiento secuencial ' (un núcleo cada vez, el predeterminado).
Sin embargo, aunque esto hará que la poda de extremos de línea sea más predecible, la hará más lenta y puede cambiar el resultado global de un adelgazamiento. Tomemos el caso de adelgazar con '[Thinning](#thinning)' algunos rectángulos adelgazando simultáneamente los bordes izquierdo y derecho.
magick -size 10x10 xc:black -fill white -draw 'rectangle 4,1 5,7' rect.gif
magick rect.gif -channel GB -define morphology:compose=darken \
-morphology Thinning Edges rect_simultaneous.gif
¡El 'Adelgazamiento simultáneo' en realidad borró por completo el rectángulo central! Lo que ocurre es que la forma se adelgazó hasta un grosor de dos píxeles, y entonces ambos lados del 'grueso' rectángulo central coincidieron con el patrón y ambos lados se 'adelgazaron'. Lo mismo ocurrirá si haces esto al Adelgazar esqueletos. El 'Adelgazamiento secuencial' predeterminado, en cambio, produjo... |
magick rect.gif -channel GB \
-morphology Thinning Edges rect_seqential.gif
![[IM Output]](../static/img/morphology/rect_seqential_mag.gif)
Como puedes ver, conservó uno de los píxeles (a la derecha) como línea central del esqueleto. Esto se debe a que un conjunto de núcleos adelgazó primero un lado de una 'gruesa' línea central, pero los núcleos posteriores no coincidieron con esta línea 'más fina', por lo que no se eliminó. En esencia, hay situaciones en las que el 'Adelgazamiento secuencial ' (el predeterminado) es mejor que el especial 'Adelgazamiento simultáneo ', y viceversa.
Núcleos de coincidencia de patrones
Como se ha mencionado, un núcleo de 'coincidencia de patrones' o '[Hit-And-Miss](#hitmiss)' puede contener 3 tipos distintos de elementos: primer plano, fondo y 'no me importa'. Un valor de '1.0' o (blanco) coincide con píxeles de primer plano. Un valor de '0.0' o (negro) coincide con píxeles de fondo. Puedes usar un valor de '0.5' o el valor especial 'Nan' o '-' para representar elementos de píxel que no forman parte del vecindario y que, por tanto, 'no te importan'. El '[Hit-And-Miss](#hitmiss)' solo coincidirá en lugares donde el menor (mínimo) píxel de primer plano sea mayor que el mayor (máximo) píxel de fondo. Después devolverá la diferencia entre estos dos valores, o cero.
Peaks
El núcleo 'Peaks' es una extensión del núcleo '[Ring](#ring)' mostrado anteriormente. Dos argumentos de radio generarán un 'anillo' de píxeles de fondo que rodea un único píxel de primer plano en el 'origen' central. Aquí tienes algunos ejemplos de los núcleos 'Peak' más útiles...
Los núcleos anteriores pueden usarse para localizar de forma definitiva un valor 'pico' de un solo píxel en un mar de píxeles más oscuros, o para encontrar cualquier forma pequeña que quepa por completo dentro del anillo mayor. Son especialmente útiles para mejorar el contraste de una Búsqueda por coincidencia de patrones de correlación.
Edges
El conjunto de núcleos 'Edges' coincidirá con cualquier píxel situado en un borde plano de una forma. No coincide con píxeles en una esquina aguda de noventa grados, aunque sí coincidirá con un píxel de esquina en una forma octagonal.
Como puedes ver, se generan todas las rotaciones de 90 grados, pero están ordenadas en una disposición especular de 'báscula' (flip-flop) que en general produce mejores resultados. Normalmente este núcleo se usa como un tipo de núcleo de '[Thinning](#thinning)' de imagen; sin embargo, tal como está, no logrará adelgazar los bordes diagonales ni generar un esqueleto adecuado de una imagen. Por ejemplo...
magick man.gif -channel RG \
-morphology Thinning:-1 Edges thin_edges.gif
Consulta los núcleos '[Skeleton](#skeleton)' más abajo.
Corners
Los núcleos 'Corner' localizan cualquier píxel de esquina diagonal en los bordes de una imagen. Consulta '[Hit-And-Miss](#hitmiss)' más arriba para ver un ejemplo de su uso.
Aquí, por ejemplo, lo usé para intentar adelgazar todos los bordes diagonales... |
magick man.gif -channel RG \
-morphology Thinning:-1 Corners thin_corners.gif
Puede combinarse con el núcleo '[Edges](#edges)' para producir un adelgazamiento de esqueleto en un solo método. Consulta los núcleos '[Skeleton](#skeleton)' más abajo para ver un ejemplo de ello. ![[IM Output]](../static/img/morphology/thin_corners.gif)
Diagonals
El núcleo 'Diagonals' es una alternativa a simplemente usar un núcleo '[Corners](#corners)' para adelgazar líneas diagonales 4-conectadas a líneas diagonales 8-conectadas. Puede usarse para adelgazar líneas 4-conectadas, eliminando el conjunto de píxeles exteriores desde una esquina hacia el centro hasta completarse.
Observa que los resultados deberían completarse usando un núcleo '[Corners](#corners)', para localizar y adelgazar las esquinas de 90 grados. Consulta Esqueleto más fino para ver un ejemplo de uso. Subtipos de Diagonals Al proporcionar un argumento 'tipo[,ángulo]' al núcleo puedes seleccionar subtipos específicos que se usaron para componer el conjunto de núcleos anterior.
Esto te permitirá especificar tu propio conjunto concreto de núcleos, para adelgazar las diagonales exactamente como quieras. Por ejemplo, podrías adelgazar por separado cada uno de los cuatro tipos de diagonales (usando los dos núcleos anteriores con el mismo valor de ángulo). Al hacerlo, puedes realizar una reducción iterativa de cada tipo de diagonal de una en una, abortando en cuanto se hayan adelgazado todas esas diagonales concretas, reduciendo así el número total de 'pasos de morfología primitivos ' que se realizan. se necesita un ejemplo Tal como está, el conjunto de núcleos predeterminado simplemente intentará adelgazar todas las diagonales de forma simultánea y repetida hasta que todas se hayan adelgazado. Eso significa que todos los núcleos se aplicarán hasta que todas las diagonales se hayan adelgazado, en lugar de solo las diagonales que necesitan adelgazarse. Eso significa que realiza muchos 'pasos de morfología primitivos ' que ya no son necesarios, con la mayoría de los núcleos sin hacer cambios en la imagen durante cada ciclo. se necesita un ejemplo completo Recuerda que cada una de las cuatro diagonales debe seguir realizándose usando ambos pares de núcleos (para cada ángulo concreto), de modo que ambos extremos de cada diagonal específica se adelgacen juntos, como cuando la diagonal forma parte de un 'arco'. Hay una discusión relacionada sobre este tipo de operación de adelgazamiento/engrosamiento en el foro de IM, From 8-connected to 4-connected lines.
LineEnds
El conjunto de núcleos 'LineEnds', tal como se muestra arriba en Poda de extremos de líneas, está diseñado para localizar el extremo de las líneas. Más concretamente, encuentra los extremos de puntas afiladas.
Como puede ver, solo coincidirá con líneas que tengan al menos dos píxeles, con el píxel coincidente «rematado» o «rodeado» por píxeles de fondo. Por ejemplo, aquí usamos '[Hit-And-Miss](#hitmiss)' para encontrar todos los extremos de líneas.
magick lines.gif -morphology HMT LineEnds hmt_lineends.gif
Sí, hay muchos extremos de líneas en esta imagen. Pero debe tener en cuenta que las líneas que terminan en algún tipo de «bucle» no producirán coincidencia. Observe que si está aplicando '[Thinning](#thinning)' a una imagen con este núcleo en un estilo de «adelgazamiento iterativo» (el predeterminado), núcleos sucesivos podrían coincidir con el mismo extremo de una línea dos o más veces, encogiendo así la línea muchas veces durante una sola iteración del método '[Thinning](#thinning)' completo. Consulte Adelgazamiento - Secuencia frente a Simultáneo para más detalles. Subtipos de extremos de líneas También puede dar a este núcleo argumentos 'type[,angle]', que devolverán una de las definiciones de núcleo individuales que se usaron para generar el conjunto de núcleos '[LineEnds](#lineends)' anterior.
Estos pueden luego expandirse en una lista de núcleos rotados según necesite, o rotarse a un 'angle ' específico, según haga falta. El conjunto '[LineEnds](#lineends)' predeterminado usa en realidad la definición de núcleo.
'LineEnds:1> ; LineEnds:2>'
El 'LineEnds:3' es el equivalente ortogonal del diagonal 'LineEnds:2', que solo encontrará los extremos de líneas bien alejados de cualquier esquina o unión diagonal. El 'LineEnds:4' es un núcleo tradicional de extremo de línea, que se rota de forma cíclica para producir 8 núcleos (por ejemplo 'LineEnds:4@). Sin embargo, no consigue localizar el último píxel de una línea que conecta con una unión 'T' ortogonal. El conjunto '[LineEnds](#lineends)' predeterminado, definido arriba, sí encuentra ese píxel final en las uniones 'T', usando el mismo número de núcleos.
LineJunctions
Donde '[LineEnds](#lineends)' encuentra los extremos de un grupo de líneas, 'LineJunctions' encontrará puntos que forman una unión de 3 o más líneas.
Por ejemplo, aquí usamos '[Hit-And-Miss](#hitmiss)' para encontrar todas las uniones de líneas.
magick lines.gif -morphology HMT LineJunctions hmt_junctions.gif
El núcleo '[LineJunctions](#linejunctions)' se usa generalmente para dos propósitos.
- Contar el número de uniones de líneas en una imagen y, así, avanzar hacia un recuento del número de segmentos de línea del esqueleto.
- Desconectar todos los segmentos de línea entre sí.
Tenga en cuenta, sin embargo, que en las uniones 'T' y '+' de la imagen anterior, el núcleo de unión 'Y' coincide con puntos que están a un píxel de distancia de la intersección real. Por ello, los recuentos de uniones pueden no ser exactamente los esperados, especialmente en el '+', donde se encontraron cuatro coincidencias en lugar de las dos requeridas para contar una unión. Se recomienda precaución. Consulte Información del esqueleto y Poda rápida de líneas para más detalles sobre estos dos aspectos. El núcleo solo define en realidad los píxeles de primer plano como tales, por lo que puede aplicarse simplemente como un método '[Erode](#erode)', en lugar de como un método '[Hit-and-Miss](#hitmiss)'. Subtipos de uniones de líneas Este núcleo también da acceso a los distintos subtipos, especificando argumentos 'type[,angle]'. Esto puede usarse para buscar tipos específicos de uniones de líneas.
Los núcleos 'LineJunctions:2' también pueden especificarse mediante 'LineJunctions:3,45', y de forma similar 'LineJunctions:5' y 'LineJunctions:4,45' son equivalentes. El conjunto de núcleos '[LineJunctions](#linejunctions)' predeterminado solo usa las dos primeras definiciones de unión (las uniones 'Y' y la 'T' diagonal), de la siguiente manera...
'LineJunctions:1@ ; LineJunctions:2>'
Esto es apropiado para uniones de líneas con 8-conectividad. Como se discute en los foros de IM "Kernels used by LineJunctions", si quiere comprobar únicamente uniones de líneas con 4-conectividad, tendría que buscar uniones 'T' ortogonales y uniones '+'.
'LineJunctions:3> ; LineJunctions:5'
Sin embargo, dado que los núcleos 'T' también coinciden con un '+', puede reducir lo anterior a solo...
'LineJunctions:3>'
Si se necesita determinar recuentos de segmentos de línea, puede usarse una prueba de imagen aparte solo para uniones '+' de 4 vías, para separarlas de las uniones 'T' de 3 vías.
Ridges
Los núcleos 'Ridges' se usan para localizar crestas y líneas finas de píxeles, como en una imagen de Gradiente de distancia. Estos núcleos son experimentales y pueden cambiar. El predeterminado está diseñado para localizar líneas de cresta de un solo píxel de grosor.
Ridges:2 Un subtipo expandido especial diseñado para encontrar líneas de cresta de dos píxeles de grosor. La complejidad proviene de la necesidad de localizar y marcar una línea inclinada de este tipo, incluidos los reflejos de esas líneas.
Este conjunto de núcleos es importante porque un «esqueleto morfológico» consiste en realidad tanto en líneas de 1 como de 2 píxeles de grosor.
ConvexHull
El conjunto de núcleos 'ConvexHull' está diseñado para engrosar formas de modo que se produzca una «envolvente convexa octagonal» de la forma. Es decir, la forma octagonal más pequeña que puede contener la totalidad de la forma.
Hay dos conjuntos de núcleos rotados 90 grados, uno la imagen especular del otro. Como el origen es en realidad un elemento de «fondo», realmente solo está pensado para usarse como núcleo de patrón '[Thicken](#thicken)'. Sin embargo, el núcleo fallará en imágenes que contengan «ranuras» horizontales o verticales, como las que tenemos en la forma del 'hombre'.
magick man.gif -channel R \
-morphology Thicken:-1 ConvexHull man_hull.gif
La solución es '[Close](#close)' (cerrar) esas ranuras (y el hueco central) antes de usar '[ConvexHull](#convexhull)'. |
magick man.gif -morphology Close Diamond \
-morphology Thicken:-1 ConvexHull \
-morphology Close Diamond man_hull_full.gif
![[IM Output]](../static/img/morphology/man_hull_full.gif)
Observe que arriba también repetí el '[Close](#close)' después de usar '[ConvexHull](#convexhull)'. La razón es que cualquier «hueco» grande de una imagen también será reducido por el '[Thicken](#thicken)' hasta píxeles aislados o «ranuras» ortogonales. Repetir el '[Close](#close)' elimina esos huecos sin afectar a la forma final. Aquí hay otro ejemplo, donde la forma original (blanca) se expandió usando un engrosamiento de envolvente convexa (rojo). |
magick circles.gif -channel R \
-morphology Thicken:-1 ConvexHull circles_hull.gif
![[IM Output]](../static/img/morphology/circles_hull.gif)
Como puede ver, el resultado es una forma octagonal, mientras que el hueco central se redujo a una ranura de dos píxeles, lista para ser cerrada.
Skeleton
Generar «esqueletos» mediante el adelgazamiento de una forma concreta no es asunto fácil. Incluso con el mismo conjunto de núcleos, reordenar los núcleos puede generar una variación distinta del «esqueleto» final. Por ello, no he implementado un único conjunto de núcleos 'Skeleton', sino varios, que pueden seleccionarse dando un número como argumento 'type '.
Skeleton:1
El primer conjunto y el predeterminado, '**Skeleton:1**', es un núcleo de adelgazamiento tradicional, como el que se usó originalmente. Es básicamente igual que el núcleo '[Edges](#edges)' de arriba, pero rotado cíclicamente en incrementos de 45 grados.
|
magick man.gif -channel RG \
-morphology Thinning:-1 Skeleton thin_skeleton1.gif
![[IM Output]](../static/img/morphology/thin_skeleton1.gif)
El resultado es un «esqueleto adelgazado» razonable de una forma, aunque las diagonales tienden a quedar un poco gruesas por un lado. Básicamente, el esqueleto producido es de 4-conectividad, lo que le permitirá usar una técnica de Poda rápida. Observe también que este conjunto de núcleos no expande correctamente el hueco de un solo píxel de la imagen. En otras palabras, el esqueleto alrededor de ese hueco no se acerca ni de lejos a la línea central entre el hueco y el resto de la imagen. Para más detalles consulte Adelgazamiento hasta un esqueleto.
Skeleton:2
La variante '**Skeleton:2**' es casi exactamente igual que la versión tradicional 'Skeleton:1'. Se encontró en la documentación de HIPR2 Image Processing Resources.
|
magick man.gif -channel RG \
-morphology Thinning:-1 Skeleton:2 thin_skeleton2.gif
![[IM Output]](../static/img/morphology/thin_skeleton2.gif)
Si compara este conjunto con el anterior, notará que se han eliminado los píxeles internos de las esquinas. Esto permite entonces que la operación de adelgazamiento elimine el engrosamiento adicional de las diagonales. Sin embargo, este adelgazamiento diagonal no es simétrico, y depende en gran medida de la forma de la imagen y del orden en que se aplican los núcleos. La variante 'Skeleton:2' está muy estrechamente relacionada con el simple uso de una lista de núcleos combinada '**Edges;Corners**'.
|
magick man.gif -channel RG \
-morphology Thinning:-1 'Edges;Corners' thin_edge-corner.gif
![[IM Output]](../static/img/morphology/thin_edge-corner.gif)
La única diferencia entre esto y lo que usa 'Skeleton:2' es el orden de los núcleos en la lista. Observe cómo el esqueleto resultante también difiere, aunque se usara el mismo conjunto de núcleos. Esto muestra que generar esqueletos mediante adelgazamiento es en realidad bastante frágil, ya que un simple cambio de orden puede producir resultados distintos en el esqueleto conectado.
Skeleton:3
El '**Skeleton:3**' se desarrolló en un estudio formal sobre el uso de núcleos de adelgazamiento (consulte Núcleos ThinSE más abajo), en el artículo de investigación "Connectivity-Preserving Morphological Image Transformations" de Dan S. Bloomberg, 1991. Desarrolló bastantes de estos esqueletos y tabuló los resultados del estudio. El siguiente es el mejor que pudo idear, que genera un esqueleto de 4-conectividad. Sin embargo, a diferencia de los esqueletos anteriores, este requiere el uso de 3 núcleos rotados (12 en total).
|
magick man.gif -channel RG \
-morphology Thinning:-1 Skeleton:3 thin_skeleton3.gif
![[IM Output]](../static/img/morphology/thin_skeleton3.gif)
Cabe señalar que el primer grupo de núcleos rotados contiene un solo píxel de fondo. Eso significa que este esqueleto fue capaz de abrir el hueco de un solo píxel presente en la forma del 'hombre' y producir un esqueleto orientado a la línea central. No genera demasiadas ramas, produce líneas limpias y suaves, y además se adelgaza por completo. En conjunto, este es uno de los mejores núcleos de adelgazamiento para esqueletos.
ThinSE
El mismo artículo de investigación "Connectivity-Preserving Morphological Image Thansformations" de Dan S. Bloomberg desarrolló en realidad, a partir de primeros principios, una gama completa de 'elementos estructurantes de adelgazamiento' 3x3 mínimos, todos ellos diseñados para preservar líneas de 4-conectividad o de 8-conectividad. El conjunto de núcleos '**ThinSE:**{_type_}' es una lista de todos esos elementos estructurantes, y se enumeran abajo ordenados en grupos según su conectividad y su fuerza de preservación. El '{_type_}' es un número basado en los números de superíndice (conectividad) y subíndice de los elementos usados en el artículo de investigación. Así, el núcleo '**ThinSE:41**' es el primero de los elementos que preservan líneas de 4-conectividad. También puede añadir un ángulo de rotación, o generar un conjunto de banderas rotadas o rotadas-reflejadas para la definición de núcleo dada.
El último de todos, el 'núcleo general de adelgazamiento , '**ThinSE:482**', quizá lo reconozca como el mismo núcleo usado para definir el conjunto de núcleos de detección de bordes. Este núcleo general es en realidad el núcleo central a partir del cual se desarrollaron todos los demás núcleos de adelgazamiento mostrados arriba. Es el núcleo predeterminado del conjunto. Tenga en cuenta que ambos núcleos generales 'ThinSE:481' y 'ThinSE:482' son los únicos núcleos relacionados rotacionalmente. Es decir, 'ThinSE:481x45' es equivalente a 'ThinSE:482'. Muchos de los demás conjuntos de núcleos HMT integrados están en realidad definidos internamente en términos de estos núcleos. Por ejemplo, el conjunto de núcleos '**ThinSE:41 ; ThinSE:42 ; ThinSE:43**', y su expansión rotada, producirá los 12 núcleos usados para crear el conjunto '[Skeleton:3](#skeleton3)'. Este esqueleto figuraba en el artículo como el mejor conjunto de núcleos encontrado para producir un buen esqueleto adelgazado. Los demás núcleos de adelgazamiento que generan esqueletos también se definen usando los núcleos anteriores.
Tenga cuidado, sin embargo, porque algunos núcleos, como 'ThinSE:44', que aunque diseñado para preservar la «conectividad», no preserva en realidad los extremos de las líneas, y por ello hará que un esqueleto se pode hasta un único punto, o un conjunto de anillos conectados. Todos los núcleos no definen el valor del origen central, lo que significa que estos «núcleos de adelgazamiento» no solo sirven para adelgazar formas, sino que también pueden usarse para engrosar formas, para generar SKIZ (zonas de influencia). Si mira con atención, probablemente notará que cada uno de los núcleos de 4-conectividad está también presente en forma negada y rotada 180 grados en el conjunto de 8-conectividad, y viceversa. Por ejemplo, 'ThinSE:41' y 'ThinSE:84' son rotaciones negadas el uno del otro. La razón es que la 4 y la 8 conectividad están estrechamente relacionadas entre sí a través de la dualidad de los métodos morfológicos de adelgazamiento y engrosamiento (usando imágenes negadas). En esencia, un «núcleo de adelgazamiento» que preserva la 4-conectividad, usado luego para engrosar una imagen, dará como resultado un esqueleto de fondo de 8-conectividad (SKIZ sin podar) alrededor de la forma, y viceversa. Así, usando las formas negadas (de modo que los métodos de engrosar y adelgazar se intercambian) puede generar la otra forma de conectividad para la misma operación.
Morfología de gradiente de distancia
El método de morfología 'Distance' es el primero de los muchos métodos especializados posibles. Lo que hace es usar un núcleo especializado para medir la distancia de cada píxel de primer plano respecto al 'borde' de las formas. Más concretamente, mide la distancia del píxel a un valor de color 'cero' o 'negro'. Sin embargo, solo funciona con formas puramente binarias (blanco sobre negro), aunque, como verá más adelante, puede modificar una forma suavizada (antialias) para que funcione con el método de distancia. Y solo con núcleos de distancia especialmente diseñados. El núcleo de distancia se aplica a la imagen de modo que a cada píxel se le asigna el menor valor de píxel más el valor del núcleo para esa distancia. Esto se aplica a toda la imagen simultáneamente, usando un algoritmo que no requiere múltiples iteraciones, como vimos en métodos de morfología anteriores. Por ello, es casi tan rápido como una sola operación morfológica primitiva, lo cual es deslumbrantemente rápido en comparación con, por ejemplo, un método morfológico de esqueleto por adelgazamiento. Como se aplica a toda la imagen, no se necesita un argumento 'iteration ', ya que repetir (iterar) la misma operación de núcleo no producirá ningún cambio adicional en el resultado. |
Antes de IM v6.6.9-4 se necesitaba un recuento de iteraciones de '-1', ya que el núcleo se aplicaba con una técnica similar a un Erode normal. Esto ya no es necesario, y cualquier argumento de 'iteración' dado, distinto de cero (ninguna acción), simplemente se ignora ahora. |
|---|---|
Aquí hay un ejemplo de uso del método 'Distance' sobre nuestra forma del 'hombre'. |
magick man.gif -threshold 50% \
-morphology Distance Euclidean:4 \
+depth distance.png
Bueno, eso fue de lo más emocionante, ¡NO! El problema es que el color de las imágenes finales es muy muy oscuro. Pero si tiene un buen monitor y puede mirar de cerca, quizá vea una forma muy oscura, como un 'fantasma', donde estaba el 'hombre'. Lo que sucedió es que, al menos para esta imagen pequeña, todos los píxeles están 'cerca' del borde y, por tanto, no obtienen un valor de 'distancia' muy grande. | _Se recomienda una imagen PNG para cualquier uso del método 'Distance'. Esto es porque puede proporcionar mayor 'profundidad' de valores de salida que, por ejemplo, GIF, sin ninguna pérdida de color como JPEG.
También es la razón por la que se usó el ajuste de profundidad "+depth" para asegurar que la salida se reinicie a 16 bits de profundidad (para mi versión Q16 de IM), aunque leyera una imagen de origen GIF de 8 bits.
Para usuarios con una versión Q8 de IM, sugiero que lean sobre la opción de núcleo de distancia 'scale' en Núcleos de distancia (más abajo) para ajustar los 'valores de escalado' usados (consulte la siguiente sección, más abajo). No se recomienda usar versiones Q8 de IM con núcleos de distancia no enteros (como este núcleo de distancia euclidiana), aunque producirá un resultado menos preciso.
Consulte la sección de IM Examples sobre Calidad y profundidad para entender mejor estos dos aspectos.
---|---
De forma predeterminada, los núcleos de distancia integrados aplicarán un valor de color de "100 × {_distancia_del_píxel}" a cada píxel. Si un píxel es más brillante que esto, se ajusta a ese valor, de modo que se asigna la menor distancia desde un píxel a cualquier borde. El resultado es que a los píxeles a lo largo del mismísimo borde de una forma se les asignará un valor 100 unidades mayor que el color de fondo. Al siguiente píxel más hacia dentro se le darán 100 unidades más. El número exacto de unidades que se asigna lo da el núcleo de distancia que se use. Así que veamos el mayor valor de color que se estableció en la imagen anterior.
magick identify -verbose distance.png | grep max:
Es decir, el mayor valor de color en la imagen resultante fue '1616', lo que hace que el píxel 'más brillante' de la imagen sea un gris muy oscuro al 2,5%, y su distancia al borde más cercano, de 16,16 píxeles. En otras palabras, vemos una imagen muy oscura, pero no del todo negra. Usemos el "[-auto-level](https://imagemagick.org/command-line-options/#auto-level)" matemático para ajustar los valores de color resultantes, de modo que el píxel más brillante, o más distante de un borde, se establezca en blanco. Así podremos ver realmente el efecto completo del 'gradiente de distancia' generado. |
magick distance.png -auto-level distance_man.gif
| Como ya no nos importan los valores exactos de 'distancia' generados para esta imagen, solo el efecto visible de la distancia, ahora puede guardarse y mostrarse usando el formato de archivo de imagen GIF.
---|---
![[IM Output]](../static/img/morphology/distance_man.gif)
Esto es lo que hace el método 'Distance'. Genera un gradiente a lo largo de la forma dada que define la distancia de cada píxel respecto al borde más cercano, según el núcleo de distancia específico que se use. Otra forma de hacer más brillante la imagen de 'distancia' resultante es usar un valor de 'scale ' del núcleo de distancia mayor, por ejemplo un valor de 3000 unidades (los usuarios de Q8 probablemente puedan usar un valor de 20). |
magick man.gif -threshold 50% +depth \
-morphology Distance Euclidean:4,3000 distance_scaled.gif
![[IM Output]](../static/img/morphology/distance_scaled.gif)
Observe que el gradiente de distancia no cubrió de negro a blanco, sino que alcanzó un máximo en cierto valor de escala de grises. Como ya conocemos la 'distancia' del pico, podemos calcular ese pico máximo en 16.16 * 3000 => 48480, o aproximadamente un 74% de gris. También puede usar un factor de escalado en porcentaje, por ejemplo usar un valor de rango de color del 8% por cada distancia de píxel respecto al borde. |
magick man.gif -threshold 50% +depth \
-morphology Distance Euclidean:4,8% distance_scale_percent.gif
![[IM Output]](../static/img/morphology/distance_scale_percent.gif)
Como puede ver, esta vez alcanzamos nuestro límite de valor máximo antes de alcanzar la distancia máxima. Puede calcular que la distancia máxima que el gradiente de distancia puede cubrir es (100% en el rango máximo) / (8% por píxel) => 12.5 distancia_de_píxel. Por supuesto, si estuviera usando una versión HDRI de ImageMagick, los valores completos de distancia se mantendrían en memoria, al menos hasta que acote su valor, o lo guarde en un formato de archivo de imagen no de coma flotante. También puede especificar directamente la distancia máxima de píxel que le interesa usando la bandera especial de escalado de distancia, '!'. Como ya sabemos que nuestra forma tiene una distancia máxima al borde de 16,16, solicitemos al menos un límite de 18 píxeles. |
magick man.gif -threshold 50% +depth \
-morphology Distance Euclidean:4,'18!' distance_range.gif
![[IM Output]](../static/img/morphology/distance_range.gif)
La bandera '!' escalará la distancia de modo que dé 'n' valores de escala de grises antes de alcanzar el límite del rango de color. Así, un valor de 1 solo 'difuminará' (o hará gris) únicamente los píxeles que son vecinos directos del borde de la imagen. Como puede ver, todos los métodos de escalado dependen, sin embargo, en gran medida del tamaño real de la forma sobre la que se realiza el método 'Distance'. Demasiado pequeña y queda muy oscura, y puede no ser precisa para sus necesidades. Demasiado grande y la distancia puede quedar 'recortada' por el máximo valor de color posible de la calidad en tiempo de compilación de su ImageMagick. Para más detalles sobre el factor 'scale ' del núcleo, consulte la sección Núcleo de distancia más abajo. Quisiera hacer una última observación sobre la 'forma' similar a un hombre usada en estos ejemplos. La forma contiene un 'hueco' de un solo píxel que creó una especie de 'pozo de gradiente' a su alrededor. Esto produce un efecto muy fuerte en la mitad superior de la imagen de 'gradiente de distancia' resultante. Una solución es eliminar ese hueco, usando '[Close](#close)', para hacer la forma 'limpia y suave'. Por ejemplo...
magick man.gif -morphology Close Diamond man_clean.gif
magick man_clean.gif -morphology Distance Euclidean \
-auto-level distance_clean.gif
Por supuesto, esto también tiene el efecto de 'cerrar' además el hueco entre las 'piernas' de la forma, lo que afecta a la mitad inferior del resultado final. Una solución alternativa es usar un método de relleno por inundación para extraer el exterior de la imagen y convertir eso en una nueva máscara. El resultado es que cualquier hueco se ha cerrado, pero se ha preservado el límite exterior de la imagen. Por ejemplo...
magick man.gif -gamma 0,1,1 -bordercolor black -border 1x1 \
-fill red -floodfill +0+0 black -shave 1x1 \
-channel R -separate +channel -negate man_floodfill.gif
magick man_floodfill.gif -morphology Distance Euclidean \
-auto-level distance_floodfill.gif
Núcleos de distancia
El núcleo dado es muy especial, ya que se usa para definir las mediciones de distancia reales que se asignarán a cada píxel. Por ejemplo, aquí la salida de Mostrar núcleo de uno de los 'núcleos de distancia' integrados.
magick xc: -define morphology:showkernel=1 -precision 3 \
-morphology Distance:0 Chebyshev:3 null:
Lo importante a tener en cuenta es que el 'origen' (en este caso el centro exacto del núcleo) tiene un valor de cero. Esto es muy importante. Ese 'origen' está luego rodeado por valores mayores, que aumentan linealmente con una mayor distancia respecto a ese 'origen'. Si el núcleo no se define de esta forma específica, pueden producirse efectos inesperados y extraños. El valor dado en el núcleo es el 'valor' real que se sumará a una distancia ya 'conocida', asignado a un píxel si ese valor es menor que el ya asignado. El resultado es que los píxeles 'blancos' se oscurecen cuanto más cerca están de un borde, y se aclaran linealmente (sumándose a los valores previamente asignados) cuanto más se alejan del borde. Todos los núcleos de distancia integrados que se proporcionan pueden tomar dos argumentos_k opcionales...
{_núcleo_de_distancia_}[:{_radio_}[,{_escala_}[%][!]]]
El primer argumento, como en todos los núcleos de forma, es el radio del núcleo, que define cómo de grande hacer el núcleo generado. De forma predeterminada, el radio se establece en '1' para los núcleos de distancia integrados, lo que da un núcleo muy pequeño de 3 por 3, que en la mayoría de los casos funciona bastante bien. El segundo argumento, 'escala ', establece la escala de distancia usada para representar la distancia de un píxel de longitud. Como se muestra en el ejemplo anterior, su valor predeterminado es '100'. Es decir, un píxel al que se da un valor final de píxel o de escala de grises de, por ejemplo, '300' debería estar exactamente a '3 píxeles' del borde. Escalado de distancia Como se mencionó antes, se usa un valor de 'escala ' grande para que pueda usar distancias 'fraccionarias' para mediciones de distancia más 'exactas'. Sin embargo, solo el núcleo de distancia '[Euclidean](#euclidean)' proporcionado usa tales valores 'fraccionarios'. En los ejemplos anteriores, el valor de 'mayor distancia' asignado fue '1700', que desbordaría una versión Q8 de ImageMagick (consulte Calidad, profundidad de bits en memoria). Un IM Q8 solo permite que los valores de color alcancen un máximo de 255 (2Q => 28 => 256 valores de color, en el rango de 0 a 255). Por ello, usar una escala más pequeña como '10' o '20' funcionará mejor para los usuarios que usan la variante Q8 en tiempo de compilación de IM. Aunque es mucho menos preciso cuando se usa con un núcleo '[Euclidean](#euclidean)'. Por ello, se recomienda que los usuarios con versiones Q8 de IM se limiten a usar los otros núcleos de distancia 'enteros', con un factor de escala de '1'. También puede especificar el escalado de distancia como un porcentaje del rango de color completo incluyendo un '%' en el factor de escala. Eso significa que si usa una escala del '12.5%' del rango de valor de color, podrá obtener una métrica de distancia de unos 8 píxeles antes de que la distancia desborde los límites del rango de color de la versión de IM que esté usando. Alternativamente, puede usar en su lugar un '!', que significa que la escala es un divisor del rango de color. Es decir, si especifica una escala de '20!', el escalado de distancia se establecerá de modo que el límite del rango de color se alcance a 20 píxeles del borde de la imagen. Sin embargo, incluso con estas 'banderas de escalado especiales', seguirá teniendo una grave limitación de precisión de rango en las versiones Q8 de IM. Sencillamente, no tiene el rango para los valores de datos necesarios en muchas operaciones de distancia. Por supuesto, cualquier escala (incluida la de coma flotante completa) puede usarse con precisión en las versiones HDRI de IM, ya que los valores de color resultantes también se almacenan como valores de coma flotante. Solo asegúrese de reescalar el rango de color adecuadamente antes de intentar guardar tal imagen en formatos de archivo de imagen no de coma flotante.
Se proporcionan varios núcleos diferentes de medición de distancia, uno de los cuales puede usarse de dos formas distintas. Cada núcleo le proporciona 'métricas de distancia' resultantes distintas para especificar la distancia del píxel respecto al borde, y básicamente define qué se considera el 'borde más cercano'.
Núcleo de distancia Chebyshev (tablero de ajedrez)
El núcleo de distancia '**Chebyshev**' es el más simple, y especifica que todos los píxeles alrededor del 'origen' están simplemente a 1 unidad de distancia de sus vecinos. Es decir, los 8 vecinos están 'junto' los unos a los otros. Por ello, no solo los cuatro vecinos inmediatos están a una distancia de 1 unidad, sino que los vecinos diagonales también están a exactamente 1 unidad. Esto suele compararse con la distancia en casillas que mueve una pieza de ajedrez 'Rey' o 'Reina' en un tablero, y por eso también se conoce a menudo como métrica de distancia de 'tablero de ajedrez '. Tenga en cuenta, sin embargo, que los núcleos de distancia usan un factor {escala} predeterminado de 100 unidades de distancia por píxel de distancia. Así, la distancia es de 100 unidades por cada paso que se aleja del origen. Este fue también el núcleo usado en los ejemplos anteriores. Aquí está el núcleo real que genera...
magick xc: -define morphology:showkernel=1 -precision 4 \
-morphology Distance:0 Chebyshev null:
El nombre de este núcleo proviene del matemático ruso Pafnuty Chebyshev, quien describió matemáticamente por primera vez esta forma de medición de distancia. Puede encontrar más sobre esta medida en Wikipedia, distancia de Chebyshev. Usando una medida de distancia 'Chebyshev', la distancia final de un píxel es el mayor valor X o Y hasta el borde más cercano. Sin embargo, como la distancia diagonal es de solo 1 unidad, la distancia máxima dentro de una imagen suele ser menor de lo que cabría esperar. Generemos un 'gradiente de distancia' usando esta 'métrica' de núcleo. Pero, para que podamos ver qué está pasando, usemos un método de morfología 'Iterative Distance' más lento con un recuento de iteraciones infinito. | |
magick man.gif -threshold 50% +depth \
-define debug=true -morphology IterativeDistance:-1 Chebyshev \
chebyshev_gradient.png
magick identify -format 'Maximum Distance = %[max]' chebyshev_gradient.png
magick chebyshev_gradient.png -auto-level chebyshev_gradient.gif
rm chebyshev_gradient.png
| _El método de morfología 'Iterative Distance' calcula la distancia aplicando el núcleo de distancia repetidamente hasta que no se observan más cambios de valor.
Esto es mucho más lento que el método 'Distance' más habitual, que usa un método de dos pasadas para establecer la distancia en toda la imagen. Sin embargo, la salida detallada del método 'Distance' es mucho menos interesante.
---|---
Activé la bandera Verbose para que el comando muestre cuántos píxeles cambió la operación (todos píxeles blancos) durante cada iteración (pasada) por la imagen. Luego extraje la distancia 'máxima' generada ('1400'), antes de ajustar el resultado (normalizándolo) en una imagen donde puede verse el gradiente resultante. La distancia máxima '1400' es el valor de los píxeles más brillantes de la imagen (en realidad es un grupo de 4 de esos píxeles). Esta información es el resultado más importante de este núcleo (métrica) de distancia, ya que representa el tamaño del mayor cuadrado que cabe dentro de esta forma. En concreto, un radio de 14 píxeles, o un cuadrado de aproximadamente (R-1)*2+1 => 27 píxeles por lado, centrado en esos 4 píxeles máximos. Como todas las unidades de distancia de este núcleo son siempre múltiplos de '100', entonces este valor final de distancia debería ser siempre un múltiplo de '100', y nunca tendrá componente fraccionario alguno. Básicamente, este núcleo producirá una distancia entera, y puede usar una simple _escala de '1 unidad' con este núcleo sin pérdida de información de distancia. Esto se recomienda si está usando una versión Q8 de ImageMagick, o si lo aplica a imágenes muy muy grandes. Aquí hay una ampliación del gradiente entre las 'piernas' de la forma, que resalta las características del gradiente de distancia generado. |
magick chebyshev_gradient.gif -crop 25x20+39+69 +repage \
-scale 500% chebyshev_magnify.gif
![[IM Output]](../static/img/morphology/chebyshev_magnify.gif)
Como puede ver, el núcleo de distancia '[Chebyshev](#chebyshev)' produce un gradiente muy similar a un cuadrado. Esta es una característica específica de esta forma simple de métrica de distancia, y refleja directamente la naturaleza cuadrada del propio núcleo de distancia. Lo anterior también muestra los 4 píxeles de distancia máxima en la 'barriga' de la figura, cerca de la parte superior de la imagen. Centrando un cuadrado en cualquiera de estos 4 puntos puede generar el mayor cuadrado de tamaño impar que está totalmente contenido dentro de la figura. Sin embargo, tenga en cuenta que puede haber varios de estos 'picos'.
Núcleo de distancia Manhattan (taxi)
El núcleo de distancia '**Manhattan**' mide la distancia sumando los valores X e Y hasta el borde más cercano. Es básicamente la distancia que necesita recorrer cuando está restringido únicamente a movimientos en forma de cuadrícula, como un taxi por las calles de grandes ciudades como Manhattan, Nueva York. Por ello, otros nombres más comunes para esta medida son métrica de distancia de 'taxi ' o de 'manzana de ciudad '. Puede encontrar más en Wikipedia, distancia de Manhattan. Aquí está el núcleo real que genera...
magick xc: -define morphology:showkernel=1 -precision 4 \
-morphology Distance:0 Manhattan null:
Observe que las diagonales tienen ahora un valor de '200', o 2 unidades desde el centro. Es decir, para llegar a un píxel diagonal tendría que atravesar dos píxeles con los movimientos en cuadrícula mencionados. Como resultado de esto, las diagonales tienden a ser mayores de lo esperado, por lo que las mediciones de distancia finales también tienden a ser mayores. Volvamos a extraer la distancia máxima y la imagen de 'gradiente de distancia' usando esta 'métrica'. | |
magick man.gif -threshold 50% +depth \
-morphology Distance Manhattan manhattan_gradient.png
magick identify -format 'Maximum Distance = %[max]' manhattan_gradient.png
magick manhattan_gradient.png -auto-level manhattan_gradient.gif
rm manhattan_gradient.png
Esta vez no usé 'Iterative Distance', y aunque lo hubiera hecho, el recuento total de píxeles cambiados no sería preciso. Solo el núcleo '[Chebyshev](#chebyshev)' anterior establece la distancia de un píxel una vez y solo una vez. Observe cómo la distancia máxima final de la imagen es mucho mayor, de '1700' unidades de distancia, lo que sitúa al píxel o píxeles máximos dentro de la forma a 17 píxeles del borde. Este núcleo de distancia es también un núcleo 'entero', por lo que puede establecer la escala en solo '1 unidad' sin pérdida de información. Aquí hay una ampliación del gradiente. |
magick manhattan_gradient.gif -crop 25x20+39+69 +repage \
-scale 500% manhattan_magnify.gif
![[IM Output]](../static/img/morphology/manhattan_magnify.gif)
Como puede ver, el núcleo de distancia '[Manhattan](#manhattan)' generó un gradiente con forma de diamante, que es básicamente lo que representa esta simple métrica de distancia, como reflejan los valores reales del núcleo.
Núcleo de distancia octagonal
El núcleo de distancia '**Octagonal**' es un poco diferente de los otros dos. Se crea generando primero una distancia Manhattan para los píxeles del mismísimo borde, y usando luego Chebyshev para los píxeles que están a 2 unidades de distancia del borde. Luego repite usando la distancia Manhattan para los píxeles a 3 unidades de distancia, y así sucesivamente. El resultado es un 'entrelazado' o 'promedio' de la distancia que resulta de usar los dos núcleos más simples. Como este núcleo se basa en un entrelazado de dos núcleos de distancia enteros, también es un núcleo de distancia entero. Por ello, puede usarse una escala de '1 unidad' para producir valores menores, para versiones de ImageMagick de menor calidad, o para mediciones de distancia muy grandes. La forma de distancia es también una mezcla de los dos núcleos, por lo que produce el equivalente del núcleo con forma de '[Octagon](#octagon)'. Aquí está el núcleo real que genera...
magick xc: -define morphology:showkernel=1 -precision 4 \
-morphology Distance:0 Octagonal null:
Observe que el núcleo tiene un tamaño mínimo y predeterminado de radio 2, formando un núcleo de 5x5 píxeles. Este núcleo ligeramente mayor es necesario para generar el 'entrelazado' de los núcleos. La distancia global será generalmente algo menor que una distancia verdadera. Aquí volvemos a calcular la distancia máxima... | |
magick man.gif -threshold 50% +depth \
-morphology Distance Octagonal octagonal_gradient.png
magick identify -format 'Maximum Distance = %[max]' octagonal_gradient.png
magick octagonal_gradient.png -auto-level octagonal_gradient.gif
rm octagonal_gradient.png
El resultado de '1500' es una distancia 'entera', y en realidad cae entre la distancia Chebyshev, demasiado pequeña, y la distancia Manhattan, demasiado grande. Sin embargo, en general debería estar razonablemente cerca de la distancia real al centro de la forma, manteniéndose como un valor 'entero'. Aquí hay una ampliación del gradiente. |
magick octagonal_gradient.gif -crop 25x20+39+69 +repage \
-scale 500% octagonal_magnify.gif
![[IM Output]](../static/img/morphology/octagonal_magnify.gif)
Puede ver claramente las distancias 'octagonales' que se han desarrollado alrededor de la parte superior del hueco de las piernas. También puede ver que la diagonal se generó usando un entrelazado de líneas diagonales gruesas y finas.
Núcleo de distancia de octágono fraccionario
No se ha proporcionado un núcleo de distancia con nombre. Pero encaja bien en la secuencia de núcleos de distancia que estamos estudiando en este punto. Puede generar otro tipo de núcleo de distancia entero usando una forma octagonal. Sin embargo, la distancia entera en este caso usa un valor unitario de 2 por píxel, por lo que en realidad los valores de distancia generados necesitan dividirse a la mitad, generando un valor fraccionario a partir de los pequeños enteros que se generan. De ahí el nombre "octágono fraccionario". Para hacer esto usamos una distancia entera de 2 entre píxeles vecinos, y de 3 para la diagonal.
'3: 3,2,3
2,0,2
3,2,3'
Como pueden generarse 'medios enteros', la escala mínima más pequeña que puede usarse es '2 unidades', y aunque no es tan preciso como un 'movimiento de caballo', funciona bien. El octágono de este núcleo tiene 'puntas' en la dirección ortogonal, en lugar de 'caras planas' como genera el núcleo anterior. Está relacionado, aunque no es exactamente igual, con el siguiente núcleo de 'movimiento de caballo', y podría considerarse una especie de forma 'casi entera' del núcleo 'knights'. Si quiere escalar esto igual que los núcleos de distancia anteriores de IM, puede usar este núcleo.
'3: 150,100,150
100, 0 ,100
150,100,150'
Aquí hay un ejemplo | |
magick man.gif -threshold 50% +depth \
-morphology Distance '3:3,2,3 2,0,2 3,2,3' \
fractional_gradient.png
magick identify -format 'Maximum Distance = %[max]' fractional_gradient.png
magick fractional_gradient.png -auto-level fractional_gradient.gif
rm fractional_gradient.png
El resultado de '34' es una distancia 'entera', pero necesita dividirse entre 2 para producir un resultado de distancia máxima real de 17. Sin embargo, aunque este también es un entero, podría perfectamente haber salido como una distancia fraccionaria de 16,5. Este aspecto fraccionario de los resultados de distancia es la razón por la que la mayoría de los núcleos se definen usando unidades de 100, y se hará más frecuente en núcleos posteriores a medida que nos alejemos de los núcleos de distancia puramente enteros. Aquí hay una ampliación del gradiente. |
magick fractional_gradient.gif -crop 25x20+39+69 +repage \
-scale 500% fractional_magnify.gif
![[IM Output]](../static/img/morphology/fractional_magnify.gif)
El gradiente (si estudia los resultados con cuidado) tiene una forma octagonal. Pero es difícil ver los píxeles que tienen valores de distancia comunes. Para ver la forma con más claridad, he tomado la imagen anterior y coloreado un conjunto de píxeles con el mismo valor de color, rojo. |
magick fractional_magnify.gif -fill red -opaque gray53 \
fractional_magnify_shape.gif
![[IM Output]](../static/img/morphology/fractional_magnify_shape.gif)
Como puede ver, los píxeles del mismo valor están generalmente separados en un patrón de 'movimiento de caballo', pero forman líneas que generan una forma octagonal. El octágono, sin embargo, está rotado 45 grados respecto al núcleo de distancia octagonal. Es también esta diferencia de forma la que hace que la distancia máxima final sea mayor. En esencia, un octágono mayor rotado de esta manera encaja mejor en la forma, de ahí el mayor resultado de distancia máxima. Otro núcleo de distancia fraccionario 'entero' que puede usarse es este, aunque la distancia está en términos de 3 unidades en lugar de 2. Es decir, los resultados necesitan dividirse entre 3 para obtener la distancia en términos de tamaño de píxel, lo cual no es realmente un buen divisor. Es, sin embargo, otra métrica de distancia de tipo octagonal.
'3: 4,3,4
3,0,3
4,3,4'
El núcleo de distancia euclidiana (movimiento de caballo) de abajo también genera un estilo de forma octagonal (todos los núcleos de distancia 3x3 lo hacen), pero intenta ser lo más preciso posible a lo largo de las diagonales. Esto puede ser o no la mejor idea, pero es el núcleo de distancia octagonal de este tipo más lógico matemáticamente.
Núcleos de distancia Chamfer
| No se ha proporcionado un núcleo de distancia con nombre. Pero encaja bien en la secuencia de núcleos de distancia que estamos estudiando en este punto. Un núcleo de distancia 'Chamfer' (aún no implementado) se define usando solo los números (típicamente enteros) que se usan para rellenar la matriz de distancia. Puede, por ejemplo, darle 2 números para definir cualquier núcleo de distancia 3x3 de tipo 'octagonal', como se describió arriba. Aquí están las definiciones de los núcleos enteros anteriores, Chebyshev | Chamfer:1,1 |
|---|---|
| Manhattan | Chamfer:1,2 |
| Octágono fraccionario | Chamfer:2,3 |
| Octágono fraccionario alternativo | Chamfer:3,4 |
| --- | --- |
| Todos estos núcleos son simples núcleos de radio 1. Los valores dados pueden considerarse como los valores reales de 'escalado de distancia' que debería usar. Tenga en cuenta, sin embargo, que el núcleo entero Octagonal anterior requiere un núcleo Chamfer de radio 2 con 3 números para definirlo. El núcleo Chamfer más conocido es un núcleo de radio 2, 'Chamfer:5,7,11', que genera una distancia muy precisa, y además genera valores de distancia enteros, lo que lo hace muy adecuado para los usuarios de Q8. Tradicionalmente, el núcleo (chamfer 5,7,11) tiene la forma... |
'5: - 11 - 11 -
11 7 5 7 11
- 5 0 5 -
11 7 5 7 11
- 11 - 11 -'
O multiplique lo anterior por 20, para producir el mismo escalado de distancia de píxel (100) usado normalmente por ImageMagick para los núcleos de distancia....
'5: - 220 - 220 -
220 140 100 140 220
- 100 0 100 -
220 140 100 140 220
- 220 - 220 -'
| Observa que el núcleo no rellena en realidad TODAS las distancias del núcleo. Eso se debe a que esos valores obtienen su distancia a partir de los otros valores ya indicados. Es decir, no hace falta rellenar la matriz bidimensional completa para definir por entero un núcleo de distancia, aunque suele hacerse para facilitar el procesamiento. Aquí tienes una lista de otros núcleos Chamfer conocidos (que solo usan valores enteros) que he encontrado en mi investigación. Chamfer:3,4 | /3 |
|---|---|
| Chamfer:5,7,11 | /5 |
| Chamfer:99,141,221 | /100 |
| Chamfer:987,1414,2206 | /1000 |
| Chamfer:12,17,27,38,43 | /12 |
![[diagrama]](../static/img/img_diagrams/chamfor_values.png)
Cómo se colocan 5 valores para definir un núcleo Chamfor de radio 3
El núcleo resaltado en la tabla anterior es el núcleo de distancia Chamfor más conocido y utilizado. Genera la imagen usando solo valores enteros pequeños, de modo que las imágenes de gradiente de distancia con grandes valores pueden almacenarse sin pérdida de precisión. También es muy preciso, al menos lo suficiente para prácticamente cualquier propósito imaginable. Como ventaja adicional, los resultados de distancia no producen decimales recurrentes, sino solo una fracción de un dígito cuando se normalizan. Esta es otra razón por la que muchos paquetes de procesamiento de imágenes lo eligen a menudo. El «Chamfer:5,7,11» debe considerarse el núcleo de distancia «Chamfor» predeterminado.
Núcleo de distancia Euclidean (movimiento de caballo)
El núcleo '**Euclidean**' se genera usando números de distancia exactos en punto flotante. Pero para que esto funcione con las versiones no HDRI de ImageMagick es necesario usar distancias diagonales fraccionarias. Como la diagonal, que tiene un valor igual a la raíz cuadrada de 2, un valor de aproximadamente 1,4142 unidades de distancia. Para que esto funcione, las distancias se escalan por un valor de 100 (como en todos los núcleos anteriores), produciendo una distancia en porcentaje fraccionario. Aquí está el núcleo predeterminado que genera...
magick xc: -define morphology:showkernel=1 -precision 4 \
-morphology Distance:0 Euclidean null:
Ahora bien, usar el radio predeterminado de 1, aunque supone una gran mejora respecto a los núcleos anteriores en cuanto a precisión, sigue teniendo algunas limitaciones. Básicamente, proporciona una distancia en términos solo de diagonales a 45 grados y movimientos ortogonales (X e Y). Es decir, las distancias se asemejan a un 'movimiento del caballo ' en el ajedrez. Aquí están la distancia máxima y la imagen de 'gradiente de distancia' que se creó usando el núcleo 'Euclidean' o 'movimiento de caballo' predeterminado. | |
magick man.gif -threshold 50% +depth \
-morphology Distance Euclidean knight_gradient.png
magick identify -format 'Maximum Distance = %[max]' knight_gradient.png
magick knight_gradient.png -auto-level knight_gradient.gif
rm knight_gradient.png
Como puedes ver, para esta forma concreta también obtienes un valor de distancia de '1700' unidades. Normalmente el resultado sería una distancia fraccionaria, entre la distancia menor '[Chebyshev](#chebyshev)' y la distancia mayor '[Manhattan](#manhattan)'. Es pura casualidad que saliera como un múltiplo simple de '100' y que además coincidiera con la distancia '[Manhattan](#manhattan)'. La distancia real a un píxel es en realidad la suma de la distancia diagonal más la distancia ortogonal (de eje). Es decir, no es exactamente una distancia euclidiana perfecta, pero es lo más cercano posible usando el núcleo de distancia más pequeño posible (de radio 1). Aquí tienes una ampliación del gradiente entre las 'piernas' de la forma. |
magick knight_gradient.gif -crop 25x20+39+69 +repage \
-scale 500% knight_magnify.gif
![[Salida IM]](../static/img/morphology/knight_magnify.gif)
Como puedes ver, el gradiente tiene un aspecto mucho más redondeado, sin el efecto de 'niveles' o 'terrazas' tipo cebolla que se obtiene con un núcleo de distancia 'entero'. Esto se debe a que cada píxel tiene más probabilidad de recibir una distancia fraccionaria individual respecto al borde. Sin embargo, la forma final del gradiente es en realidad aproximadamente 'octagonal', aunque con puntas como una brújula, en lugar de las partes planas arriba y abajo que se obtienen con el núcleo de distancia '[Octagonal](#octagonal)' anterior. Para el trabajo general de distancia (como el 'difuminado'), este núcleo 'Euclidean' o 'movimiento de caballo' predeterminado da un buen resultado. Sin embargo, como no obtienes distancias 'enteras', no puedes usarlo con un factor de escala de distancia de '1', lo que lo hace menos útil para las versiones Q8 de ImageMagick.
Núcleo de distancia Euclidean más grande
Aumentando el 'radio ' del núcleo 'Euclidean' generado produces una métrica de distancia 'pitagórica' o 'euclidiana' verdadera aún más precisa. Cuanto mayor sea el radio, más preciso será el resultado, pero el método morfológico '[Distance](#distance)' tardará más en ejecutarse, aunque se necesitarán menos iteraciones de ese tipo. Más allá de un radio de 4, sin embargo, no obtendrás mucha más precisión, pero sí una pérdida de velocidad mucho mayor. Consulta Distancia con una forma suavizada más abajo para ver algunos ejemplos del uso de núcleos 'Euclidean' muy grandes para mejorar la precisión. Aquí tienes un núcleo 'Euclidean' verdadero usando el radio recomendado de 4, que genera un núcleo más grande de 9×9...
magick xc: -define morphology:showkernel=1 -precision 4 \
-morphology Distance:0 Euclidean:4 null:
La ventaja añadida de usar un radio de 4 es que el núcleo también contiene el triángulo de Pitágoras, que tiene lados 3,4,5, o con la escala de núcleo predeterminada, unidades de 300,400,500. Aunque esto puede reducir el número de componentes fraccionarios en la imagen resultante, en realidad es solo un efecto menor. Aun así, es una elección lógica para obtener más precisión. Aquí está su aplicación... | |
magick man.gif -threshold 50% +depth \
-morphology Distance Euclidean:4 euclidean_gradient.png
magick identify -format 'Maximum Distance = %[max]' euclidean_gradient.png
magick euclidean_gradient.png -auto-level euclidean_gradient.gif
rm euclidean_gradient.png
Como resultado de usar este núcleo 'Euclidean' de radio mayor, la distancia máxima final es la medición de distancia máxima más precisa hasta ahora. También hace improbable que obtengas más de un píxel 'más brillante' en la imagen, a menos que la forma sea muy regular. Aquí tienes una ampliación del gradiente entre las 'piernas' de la forma. |
magick euclidean_gradient.gif -crop 25x20+39+69 +repage \
-scale 500% euclidean_magnify.gif
![[Salida IM]](../static/img/morphology/euclidean_magnify.gif)
Como puedes ver, el gradiente produce un gradiente circular casi perfecto alrededor del extremo del 'hueco entre las piernas'. El coste de usar este núcleo es, como dije antes, un tiempo de ejecución marginalmente más lento.
Comparación de núcleos de distancia
Aquí tienes de nuevo una comparación lado a lado de las ampliaciones. Esto muestra con claridad los gradientes tan distintos que genera cada una de las cuatro métricas de distancia utilizadas.
![[Salida IM]](../static/img/morphology/chebyshev_magnify.gif)
Chebyshev
(tablero de ajedrez) | ![[Salida IM]](../static/img/morphology/manhattan_magnify.gif)
Manhattan
(taxi) | ![[Salida IM]](../static/img/morphology/octagonal_magnify.gif)
Octagonal
(mixta) | ![[Salida IM]](../static/img/morphology/knight_magnify.gif)
Euclidean
(movimiento de caballo) | ![[Salida IM]](../static/img/morphology/euclidean_magnify.gif)
Euclidean
(radio=4)
---|---|---|---|---
Aquí tienes otra comparación, esta vez obteniendo la distancia respecto a un único píxel negro cercano a la esquina inferior izquierda, sin ninguna ampliación de los píxeles.
magick -size 100x100 xc: -draw 'point 20,80' distance_start.png
for kernel in chebyshev manhattan octagonal euclidean euclidean:2 euclidean:4
do
magick distance_start.png -morphology Distance $kernel \
-auto-level point_$kernel.png
done
![[Salida IM]](../static/img/morphology/point_chebyshev.png)
Chebyshev
(tablero de ajedrez) | ![[Salida IM]](../static/img/morphology/point_manhattan.png)
Manhattan
(taxi) | ![[Salida IM]](../static/img/morphology/point_octagonal.png)
Octagonal
(mixta) | ![[Salida IM]](../static/img/morphology/point_euclidean.png)
Euclidean
(movimiento de caballo) | ![[Salida IM]](../static/img/morphology/point_euclidean_2.png)
Euclidean
(radio=2) | ![[Salida IM]](../static/img/morphology/point_euclidean_4.png)
Euclidean
(radio=4)
---|---|---|---|---|---
Estas imágenes muestran con claridad las líneas a lo largo de las cuales se considera que los píxeles están más cerca del píxel inicial de lo que probablemente esperas, y cómo los distintos núcleos se vuelven más suaves a medida que se hacen más complejos. Solo el último no muestra líneas visibles de 'píxeles más cercanos', pero incluso con radio 4 siguen presentes. Para ese núcleo las líneas solo aparecen a gran distancia del origen. Un radio 7 produce resultados aún mejores, pero con un gran coste en velocidad, aunque a veces se necesita tal precisión para evitar artefactos en la imagen resultante. Recuerda que solo los tres primeros núcleos producen distancias enteras, que pueden usarse en una versión Q8 de ImageMagick (con el ajuste de escala apropiado, consulta la descripción de cada núcleo). Y son estos los que normalmente usan muchos paquetes de procesamiento de imágenes. El cálculo de la distancia desde un único punto se retomará más adelante en los ejemplos de Distancia restringida que aparecen más abajo.
Núcleos de distancia especiales definidos por el usuario
No estás limitado a los núcleos de distancia que se te han proporcionado. Mientras te ciñas a las reglas, usando un valor de cero en el 'origen' y un valor de distancia creciente a su alrededor, puedes generar otros efectos de distancia muy interesantes. Por ejemplo, aquí aplico un núcleo definido por el usuario muy pequeño que simplemente indica que cualquier píxel a la derecha tenga un valor mayor. |
magick man.gif -threshold 50% +depth \
-morphology Distance '2x1+0+0: 0,100 ' \
-auto-level distance_linear.gif
![[Salida IM]](../static/img/morphology/distance_linear.gif)
Observa el efecto del hueco entre las piernas, que 'reinicia' el gradiente que crece lentamente y que se genera. Y aquí creo un gradiente de distancia solo desde los dos lados, ¡pero con escalas distintas para cada lado! |
magick man.gif -threshold 50% +depth \
-morphology Distance '3x1: 50,0,100 ' \
-auto-level distance_sides.gif
![[Salida IM]](../static/img/morphology/distance_sides.gif)
Estos son solo algunos ejemplos de las variantes de núcleos de distancia que son posibles. ¿Se te ocurren otras? Por favor, házmelo saber. Ten en cuenta que si tu imagen sale mal, probablemente el ajuste del origen de tu núcleo sea incorrecto, o el origen no tenía un valor 'cero'. La función de distancia morfológica no comprueba esto.
Distancia con una forma suavizada
El método Distance funciona muy bien. Pero la mejor prueba de su funcionalidad es aplicar la función de distancia a un círculo y luego sombrearlo para resaltar incluso el error más pequeño que la función pueda generar.
magick -size 129x129 xc: -draw 'circle 64,64 60,4' \
-negate circle_shape.png
magick circle_shape.png -morphology Distance Euclidean:4 \
-auto-level cone_distance.png
magick cone_distance.png -shade 135x30 -auto-level \
+level 10,90% cone_distance_shade.png
Puedes ver en lo anterior que, aunque obtienes un resultado de aspecto 'cónico', dista mucho de ser un 'cono' suave. Está cubierto por una red de crestas radiales que parten de los bordes. Si observas de cerca el borde del cono sombreado, verás que el cono no tiene una base circular suave como la forma original. Está muy 'aliasado' o 'escalonado', y son estos 'escalones' los que la función de distancia refleja para formar las crestas radiales que se ven. El problema es que el método de distancia no tiene ni idea de los pequeños píxeles 'grises' de suavizado que el círculo usa alrededor del borde para darle ese aspecto liso. De hecho, cualquier píxel 'gris' se considera en general un píxel completo en lugar del píxel de borde parcial suavizado que representa. Lo que necesitamos es incluir de algún modo esos valores grises de borde en el resultado, y esto se hace mediante un paso de preprocesamiento antes de aplicar el método de distancia. |
magick circle_shape.png -gamma 2 +level 0,100 -white-threshold 99 \
-morphology Distance Euclidean:4 -auto-level \
-shade 135x30 -auto-level +level 10,90% cone_antialiased.png
![[Salida IM]](../static/img/morphology/cone_antialiased.png)
Lo que se hizo fue convertir primero todos esos píxeles grises de una representación de cuánto del píxel estaba dentro del límite del círculo a una representación de a qué distancia estaba el píxel del borde del círculo. Como puedes ver en el resultado, casi todos los errores de borde escalonado se han corregido. Los errores finales que quedan visibles solo ocurren lejos del borde, y se hacen más visibles hacia el centro del 'cono'. Los provoca la iteración de la función de distancia, cada 4 píxeles (el tamaño del núcleo de distancia euclidiano) una y otra vez. Por ello, las pequeñas imprecisiones se acentúan a medida que nos alejamos del borde. Esto normalmente no es un problema en la mayoría de las situaciones, pero puedes reducir, o quizás incluso eliminar, esos pequeños errores usando un núcleo euclidiano más grande, para producir un resultado mucho más preciso y suave. Sin embargo, esto tarda más en generarse. |
magick circle_shape.png -gamma 2 +level 0,100 -white-threshold 99 \
-morphology Distance Euclidean:7 -auto-level \
-shade 135x30 -auto-level +level 10,90% cone_improved.png
![[Salida IM]](../static/img/morphology/cone_improved.png)
El resultado es una función de distancia casi perfecta para una forma suavizada o lisa. Aquí tienes otro ejemplo, esta vez usando los dos núcleos de distancia definidos por el usuario de la sección anterior.
magick circle_shape.png -gamma 2 +level 0,100 -white-threshold 99 \
-morphology Distance '2x1+0+0:0,100' -auto-level \
circle_gradient.png
magick circle_shape.png -gamma 2 +level 0,100 -white-threshold 99 \
-morphology Distance '3x1:50,0,100' -auto-level \
circle_ridge.png
Sin el tratamiento especial de los píxeles suavizados, lo anterior no produciría un gradiente tan suave a través de la imagen. Aun así, todavía necesitamos restaurar la 'forma' de la forma original.
Difuminado de formas usando Distance
La técnica anterior puede aplicarse al canal alfa de una forma para 'difuminar' correctamente el objeto. Por ejemplo, aquí tienes un difuminado 'suavizado' de 10 píxeles alrededor de un objeto con forma.
magick rose_orig.png \
\( +clone -fill black -colorize 100% \
-fill white -draw 'circle 114,75 110,2' \
\) -alpha off -compose CopyOpacity -composite \
-trim +repage rose_shape.png
magick rose_shape.png \
\( +clone -alpha extract -virtual-pixel black \
-gamma 2 +level 0,100 -white-threshold 99 \
-morphology Distance Euclidean:4,10! \
-sigmoidal-contrast 3,0% \
\) -compose CopyOpacity -composite \
rose_feathered.png
Esto preservará TODOS los bordes con exactitud, a diferencia de la técnica más sencilla de difuminado por desenfoque. Podría haber aplicado lo anterior directamente sobre el canal alfa, salvo que algunos operadores no tratan el canal de transparencia como 'valores alfa', sino como 'valores de opacidad' (en concreto el operador "[-white-threshold](https://imagemagick.org/command-line-options/#white-threshold)"). Por ello, extraje el canal alfa para manejarlo como una imagen en escala de grises, antes de volver a fusionarlo en la imagen final. El núcleo de distancia especial hará tres iteraciones de un núcleo euclidiano de 4 píxeles, para generar suficiente gradiente de distancia cerca del borde de la forma. El operador "[-level](https://imagemagick.org/command-line-options/#level)" lo convierte entonces en un gradiente lineal desde el borde ('0') hasta 10 píxeles ('1000' unidades) dentro de la forma. El ajuste "[-virtual-pixel](https://imagemagick.org/command-line-options/#virtual-pixel)" también se proporciona para asegurar que cualquier forma que toque el borde del contenedor rectangular de la imagen se considere igualmente rodeada de transparencia. El resultado de la función de distancia en este caso es una 'rampa lineal' o 'bisel' que puede producir efectos de aspecto nítido. Por ello, una pequeña modificación "[-sigmoidal-contrast](https://imagemagick.org/command-line-options/#sigmoidal-contrast)" suavizará esta transición de transparente a opaco. Cuanto mayor sea la intensidad ('3' en el ejemplo anterior), más nítido será el difuminado en el borde. Si prefieres que el difuminado se 'atenúe' más suavemente hacia la transparencia, sustituye el '0%' del código anterior por '50%' para situar el 'hombro' de la curva sigmoidal en el medio del difuminado de 10 píxeles. Difuminado de formas de mapa de bits Si la forma es un mapa de bits, como el de una imagen GIF o una máscara de imagen, entonces puedes simplificar la operación de difuminado anterior. Por ejemplo...
magick figure.gif -channel A -virtual-pixel transparent \
-morphology Distance Euclidean:4,3! boolean_feathered.png
El cambio clave es que tienes mucho menos pre y posprocesamiento de la función de distancia: simplemente puedes especificar una unidad de distancia especial usando '3!'. Esto generará un gradiente lineal de 3 píxeles alrededor del borde de la forma. Otro ejemplo de este tipo de difuminado (un 'difuminado lineal' más grande) puede verse en Miniaturas, bordes suaves. Puedes usar el operador "[-sigmoidal-contrast](https://imagemagick.org/command-line-options/#sigmoidal-contrast)" en lo anterior para suavizar difuminados más grandes, pero ten en cuenta que por ahora procesa la transparencia como valores 'matte' en lugar de valores alfa. Por ello, debería usarse un valor de '100%' en lugar de '05' en la solución anterior. Para formas de mapa de bits, puede ser mejor aplicar un "-blur 1x0.7" al canal alfa, para suavizarlo ligeramente, antes de aplicar el difuminado de distancia anterior, más complejo, a esos resultados.
Morfología condicional o restringida
Aquí vemos técnicas en las que las operaciones morfológicas repetidas se restringen o limitan a un área o región concreta de una imagen. Básicamente, técnicas que pueden usarse para asegurar que no te 'desbordas' ni creces más allá de algún límite o área de interés. Esto generalmente requiere algún tipo de imagen 'máscara', y suele hacerse mediante una máscara de protección contra escritura, para limitar qué píxeles se actualizan.
Dilatación condicional
El método morfológico Dilate, como sabes, expande una forma concreta según un vecindario de núcleo dado. La 'dilatación condicional ' es esencialmente lo mismo, pero establece un límite sobre cuán lejos puede extenderse la dilatación cuando se aplica repetidamente a una imagen. Un relleno por inundación con Draw es, en cierto sentido, la 'dilatación condicional' definitiva. Simplemente rellenará cualquier vecindario ortogonal (núcleo Diamond) que resulte ser del mismo color que el punto de partida. Por ejemplo, podemos seleccionar un único punto en uno de varios discos y dilatar condicionalmente (rellenar por inundación) hasta que ese disco se haya recoloreado por completo, separándolo de las demás formas.
magick disks.gif -fill red -draw 'color 60,60 floodfill' \
cond_dilate_draw.gif
De forma similar, puedes usar el operador Floodfill para hacer lo mismo, pero solo si el punto de partida también coincide con un 'color condicional' proporcionado por el usuario. |
magick disks.gif \
-fill green -floodfill +10+40 white \
-fill blue -floodfill +30+50 white \
cond_dilate_floodfill.gif
![[Salida IM]](../static/img/morphology/cond_dilate_floodfill.gif)
En este caso, el relleno 'verde' 'dio con un disco' (y lo rellenó), mientras que la operación de relleno 'azul' no coincidió, así que no se rellenó ningún disco. El problema de estos métodos de 'relleno por inundación' es que solo puedes dilatar desde un único punto proporcionado por el usuario. No obstante, es muy rápido y funciona sobre la imagen en conjunto. La dilatación repetida o iterada es igual que el relleno por inundación, pero puede tener múltiples puntos de partida, aunque no entiende los límites ni las fronteras de la operación de relleno que proporciona. Para limitar sus efectos necesitamos proporcionar no solo los 'puntos de partida', sino también los 'límites condicionales' del relleno. Para ello creamos una máscara de protección contra escritura (qué partes de la imagen están protegidas contra escritura).
magick disks.gif disks.gif -morphology Erode:7 Diamond disks_big_center.gif
magick disks.gif -negate disks_mask.gif
magick disks_big_center.gif -write-mask disks_mask.gif \
-morphology Dilate:15 Diamond +write-mask disks_big_found.gif
En lo anterior, primero usamos un método Erode para localizar cualquier disco mayor que un radio de 7 (diámetro 7x2+1 => 15 píxeles). Luego 'dilatamos condicionalmente' los puntos descubiertos en la misma cantidad para restaurar 'perfectamente' el objeto encontrado. Ten en cuenta que la 'dilatación condicional' es muy distinta de usar un método Open para restaurar objetos. Ese método habría generado la forma interna de 'diamante' del núcleo, en lugar de la forma exacta del objeto original. Además, no maneja puntos semilla 'de forma irregular' o 'descentrados'. El número de iteraciones '15' no es crítico, pero debería ser lo bastante grande como para restaurar el objeto por completo. | _Recuerda que es mejor usar un núcleo pequeño, como unDiamond o un Square con un recuento de iteraciones al realizar operaciones morfológicas básicas, que usar un núcleo mucho mayor, como un Disk con un radio grande.
Esto adquiere especial importancia al hacer una dilatación condicional, ya que los núcleos grandes podrían en efecto 'saltar' por encima de los huecos que separan varios objetos.
_
---|---
| _No uses un recuento de iteraciones de '-1' o (casi) infinito con una máscara de escritura. La morfología de IMv7 actualmente no se da cuenta de que ciertos píxeles no son escribibles, y por ello no abortará cuando ya no vea cambios en la imagen, ya que siempre ve cambios (que nunca se escriben) alrededor de los bordes de la forma.
Esto se corregirá con IMv7, que aportó una importante reestructuración interna que permitirá al operador ser un poco más inteligente respecto a los píxeles protegidos contra escritura.
_
---|---
Aquí tienes otro ejemplo de la versatilidad de usar una máscara de protección contra escritura. Encontrar los discos que tocará una línea diagonal a través de la imagen.
magick -size 80x80 xc:black -fill white \
-draw 'line 0,0 79,79' disks_line.gif
magick disks_line.gif disks.gif \
-compose Multiply -composite disks_line_find.gif
magick disks_line_find.gif -write-mask disks_mask.gif \
-morphology Dilate:15 Diamond +write-mask disks_line_found.gif
Observa que el paso adicional de enmascarado (usando una composición Multiply) asegura que solo se 'encuentren' los objetos por los que la línea pasa realmente a través. Sin este paso, los objetos que también están cerca de la línea, es decir, dentro del radio del núcleo de los 'puntos semilla' (apenas tocándose en el ejemplo anterior), también se 'dilatarían'. Por supuesto, si quieres incluir 'objetos cercanos', entonces dilata la línea (haciéndola más ancha) con un núcleo de disco del radio apropiado, antes de hacer el paso de enmascarado inicial. Eso te dará más control sobre la 'cercanía' que confiar en el radio del núcleo cuadrado. En resumen, una dilatación condicional podría considerarse un relleno por inundación con Draw de múltiples puntos que, aunque más lento, puede ser más versátil al seleccionar exactamente qué debe rellenarse, o 'descubrirse'.
Distancia restringida
El método morfológico Distance puede usarse fácilmente para averiguar a qué distancia está un punto dentro de un objeto respecto a un borde. Pero también puede usarse para averiguar a qué distancia está cada punto dentro del objeto respecto a otro punto. Por ejemplo, aquí descubro a qué distancia está cada punto de un único punto 'semilla' (en línea recta)...
magick -size 100x100 xc: -draw 'point 20,80' distance_start.png
magick distance_start.png -morphology Distance Euclidean \
-auto-level distance_point.png
magick -font Casual -pointsize 140 label:D \
-trim +repage -gravity center -extent 100x100 \
-threshold 20% distance_bounds.png
magick distance_point.png \( distance_bounds.png -negate \) \
-compose multiply -composite -auto-level distance_direct.png
La distancia desde un punto se enmascara con la forma del objeto, ya que esos son los únicos puntos que nos interesan. El problema es que el gradiente de distancia anterior representa una distancia 'en línea recta' o directa al 'punto de partida'. Simplemente enmascarar ese gradiente con el objeto no cambia eso. Por desgracia, esto normalmente no es lo que un usuario quiere como "distancia desde el punto de partida". Si la forma representara una isla, te mojarías bastante si quisieras tomar la 'ruta directa' de un extremo de esta isla al otro. Lo que realmente quieres es la distancia limitada a caminos 'dentro del objeto '. Es decir, quieres que la distancia esté 'restringida' por el propio objeto, y siga el camino más corto posible dentro del objeto. Para hacer esto podemos configurar una máscara de protección contra escritura de modo que, a medida que se calcula el gradiente de distancia, no cruce (escriba en) las zonas 'prohibidas' o el fondo de la imagen.
magick distance_start.png -write-mask distance_bounds.png \
-morphology IterativeDistance:150 Euclidean \
+write-mask -fill black -opaque white -auto-level \
distance_constrained.png
El resultado, como puedes ver, es un gradiente correcto de 'distancia a través de la imagen', que muestra con claridad que la mayor distancia (blanco) desde el punto de partida es el otro extremo de la isla, que está apenas a 'un tiro de piedra' a través del hueco en el objeto.
Ten en cuenta que no usé simplemente un método Distance normal, sino un método morfológico de más bajo nivel 'Iterative_Distance'. El método Distance normal es un método de distancia RÁPIDO especial de 2 pasadas que se aplica a la imagen en conjunto. Por ello, la máscara de escritura no restringirá sus acciones a lo largo de una 'fila' de píxeles, y por tanto la máscara de escritura tendrá poco efecto. El resultado es que 'Distance' tenderá a 'saltar' por encima de los huecos horizontales, independientemente de la máscara de escritura. Es decir, el método Distance normal no está adecuadamente 'restringido'. El método 'Iterative_Distance', en cambio, funciona más como un método morfológico básico más sencillo y solo se aplica de forma incremental al vecindario local. En realidad se parece más a una forma de gradiente de 'dilatación', y de hecho está estrechamente relacionado con un método de morfología verdadera en escala de grises. Como procesa la imagen en 'pasos incrementales' más pequeños, el método 'Iterative_Distance' estará 'restringido' por la máscara de escritura. Por desgracia, también es mucho más lento. En lugar de 2 pasadas por la imagen, lo anterior realiza 150 pasadas, según el recuento de iteraciones dado al método morfológico. Lo mejor es intentar mantener este recuento de iteraciones tan pequeño como sea posible, pero lo bastante grande como para cubrir la mayor distancia que se encontrará dentro de la imagen. | _No uses un recuento de iteraciones de '-1' o (casi) infinito con una máscara de escritura. La morfología de IMv7 no entiende que algunos píxeles no son escribibles, y por ello no abortará cuando ya no vea cambios en la imagen, ya que siempre ve cambios en los 'píxeles no escribibles' alrededor de los bordes de la forma.
Esto se corregirá con suerte con IMv7, que aporta una importante reestructuración interna que permitirá al operador ser un poco más inteligente, al entender que algunos píxeles no son escribibles, y así evitar calcularlos, o contarlos como 'modificados'.
_
---|---
Por último, notarás que usé un núcleo de distancia que solo tiene un radio de 1, aunque esos núcleos no son tan precisos. Esto es importante, ya que un núcleo más grande podría hacer que el gradiente de distancia salte cualquier hueco que sea menor que su radio. Consulta Dilatación condicional más arriba. Si se necesita más precisión, entonces tendrás que asegurarte de que no haya huecos menores que el núcleo de distancia que quieras usar. Eso incluye los huecos debidos a un giro brusco en el borde de la imagen.
Generación de esqueletos de formas.
En construcción
From HIPR2 Morphology
http://homepages.inf.ed.ac.uk/rbf/HIPR2/morops.htm
The skeleton/MAT can be produced in two main ways. The first is to use some
kind of morphological thinning that successively erodes away pixels from the
boundary (while preserving the end points of line segments) until no more
thinning is possible, at which point what is left approximates the skeleton.
The alternative method is to first calculate the distance transform of the
image. The skeleton then lies along the singularities (i.e. creases or
curvature discontinuities) in the distance transform. This latter approach is
more suited to calculating the MAT since the MAT is the same as the distance
transform but with all points not part of the skeleton suppressed to zero.
Note: The MAT is often described as being the 'locus of local maxima' on the
distance transform. This is not really true in any normal sense of the phrase
'local maximum'. If the distance transform is displayed as a 3-D surface plot
with the third dimension representing the gray-value, the MAT can be imagined
as the ridges on the 3-D surface.
Definition??
Morphological Skeleton (by erosion?), (by thinning)
Skeletons are calculated either by repeated thinning, or by distance
transform, and finding the 'creases', or ridges on the 3d surface (watershed
transform?).
mat.gif -morphology HMT Ridges -threshold 0
mat.gif -morphology HMT LineEnds -threshold 0
mat.gif -morphology HMT Ridges\;LineEnds -threshold 0
mat.gif -morphology HMT Ridges\;Ridges2 -threshold 0
mat.gif -morphology TopHat Diamond -threshold 0
mat.gif -define morphology:compose=Lighten \
-morphology TopHat '3@:-,1,- -,1,- -,-,-' -threshold 0
One definition of medial axis transform (MAT) uses the intensity of each point
to represent the distance ot the boundary. That the skeleton was used as
a mask for the distance transform. The distance transform method is more
suited to this, and it is probably faster to calculate than by thinning.
SKIZ (Skeleton by Influence Zones) is a skeleton of the background, the
negative of the operation. That is, dividing the regions closest to each
foreground object. (generated by thickening)
Generally a SKIZ is pruned down to simple areas, or basins, by also eroding
end of line segments, unless they are attached to an image edge.
Identifying shape by their skeletons.
distance between farthest 'end' points,
number of 'loops' or regions in image,
number of triple points.
Distancia a esqueleto
Una forma rápida y sencilla de generar un 'esqueleto morfológico' en bruto a partir de una imagen es aplicar el método '[TopHat](#tophat)' al gradiente de distancia. Por ejemplo, aquí tienes un esqueleto de la forma después de abrirla un poco para suavizar algo su contorno. |
magick man.gif \
-morphology Open Diamond \
-morphology Distance Chebyshev \
-morphology TopHat Diamond \
-auto-level chebyshev_dist_skel.gif
![[Salida IM]](../static/img/morphology/chebyshev_dist_skel.gif)
Esto es básicamente un esqueleto morfológico. Solo muestra los píxeles donde podría encontrarse un 'cuadrado' maximal (más comúnmente conocido como 'bola maximal'), razón por la cual parece incompleto. Sin embargo, sigue siendo un resultado muy en bruto con muchos píxeles aislados. Sorprendentemente, funciona. Sin el '[Open](#open)' el resultado es muy malo, debido a que la forma tiene un contorno tan irregular. Usar una medida de distancia euclidiana produce un mejor esqueleto de la forma. |
magick man.gif \
-morphology Open Diamond \
-morphology Distance Euclidean:4 \
-morphology TopHat Diamond \
-auto-level euclidean_dist_skel.gif
![[Salida IM]](../static/img/morphology/euclidean_dist_skel.gif)
Pero como puedes ver, también obtienes más ruido y el esqueleto, aunque más completo, también está muy sucio con muchos valores en escala de grises. Aquí tienes una ampliación de la 'cabeza' del esqueleto, que muestra cómo permanece inconexo. |
magick euclidean_dist_skel.gif -crop 35x28+30+13 +repage \
-scale 400% euclidean_dist_skel_mag.gif
![[Salida IM]](../static/img/morphology/euclidean_dist_skel_mag.gif)
Por supuesto, lo anterior podría umbralizarse y usarse como máscara con el gradiente de distancia real que se utilizó para generarlo. Esto te permitirá consultar el tamaño real (radio de distancia) de los discos maximales que componen el esqueleto, lo que te permite recrear la forma original.
Esqueleto usando Autotrace
Otra alternativa para la generación de esqueletos es usar el programa "[AutoTrace](http://autotrace.sourceforge.net/)" y su opción especial de línea central. Ten en cuenta que asume negro sobre blanco para su procesamiento debido a su relación con la impresión y la conversión de fuentes. Por ejemplo...
magick man.gif -negate man_at_prep.pgm
autotrace -centerline -output-format svg man_at_prep.pgm |\
magick SVG:- man_centerline.gif
magick man.gif man_centerline.gif \
-compose multiply -composite man_at_skeleton.gif
Ten en cuenta que la línea central generada tiene forma de curva suave debido a la naturaleza vectorial del proceso. También está desconectada, con huecos en los bucles del esqueleto y ramas inconexas. Sin embargo, no he investigado cómo "autotrace" genera realmente este esqueleto. Para otros ejemplos del uso de "[AutoTrace](http://autotrace.sourceforge.net/)", consulta Manejo de salida SVG y Bordeado de ráster a vector.
![[IM Output]](../static/img/morphology/kernel_corner.gif)
![[IM Output]](../static/img/morphology/kernel_gaussian.gif)
![[IM Output]](../static/img/morphology/k_diamond.gif)
![[IM Output]](../static/img/morphology/kernel_octagon_1.gif)
![[IM Output]](../static/img/morphology/kernel_octagon_2.gif)
![[IM Output]](../static/img/morphology/kernel_octagon_3.gif)
![[IM Output]](../static/img/morphology/kernel_octagon_4.gif)
![[IM Output]](../static/img/morphology/kernel_octagon_5.gif)
![[IM Output]](../static/img/morphology/kernel_disk_01.gif)
![[IM Output]](../static/img/morphology/kernel_disk_02.gif)
![[IM Output]](../static/img/morphology/kernel_disk_03.gif)
![[IM Output]](../static/img/morphology/kernel_disk_04.gif)
![[IM Output]](../static/img/morphology/kernel_disk_05.gif)
![[IM Output]](../static/img/morphology/kernel_disk_06.gif)
![[IM Output]](../static/img/morphology/kernel_disk_07.gif)
![[IM Output]](../static/img/morphology/kernel_disk_08.gif)
![[IM Output]](../static/img/morphology/kernel_disk_09.gif)
![[IM Output]](../static/img/morphology/kernel_disk_10.gif)
![[IM Output]](../static/img/morphology/kernel_disk_11.gif)
![[IM Output]](../static/img/morphology/kernel_plus_1.gif)
![[IM Output]](../static/img/morphology/kernel_plus_2.gif)
![[IM Output]](../static/img/morphology/kernel_plus_3.gif)
![[IM Output]](../static/img/morphology/kernel_plus_4.gif)
![[IM Output]](../static/img/morphology/kernel_cross_1.gif)
![[IM Output]](../static/img/morphology/kernel_cross_2.gif)
![[IM Output]](../static/img/morphology/kernel_cross_3.gif)
![[IM Output]](../static/img/morphology/kernel_cross_4.gif)
![[IM Output]](../static/img/morphology/kernel_ring.gif)
![[IM Output]](../static/img/morphology/kernel_ring_01.gif)
![[IM Output]](../static/img/morphology/kernel_ring_02.gif)
![[IM Output]](../static/img/morphology/kernel_ring_03.gif)
![[IM Output]](../static/img/morphology/kernel_ring_04.gif)
![[IM Output]](../static/img/morphology/kernel_ring_05.gif)
![[IM Output]](../static/img/morphology/kernel_ring_06.gif)
![[IM Output]](../static/img/morphology/kernel_ring_07.gif)
![[IM Output]](../static/img/morphology/kernel_ring_08.gif)
![[IM Output]](../static/img/morphology/kernel_ring_09.gif)
![[IM Output]](../static/img/morphology/kernel_ring_10.gif)
![[IM Output]](../static/img/morphology/kernel_ring_11.gif)
![[IM Output]](../static/img/morphology/kernel_ring_12.gif)
![[IM Output]](../static/img/morphology/kernel_ring_13.gif)
![[IM Output]](../static/img/morphology/kernel_ring_14.gif)
![[IM Output]](../static/img/morphology/kernel_ring_15.gif)
![[IM Output]](../static/img/morphology/kernel_ring_16.gif)
![[IM Output]](../static/img/morphology/kernel_ring_17.gif)
![[IM Output]](../static/img/morphology/kernel_ring_18.gif)
![[IM Output]](../static/img/morphology/kernel_ring_19.gif)
![[IM Output]](../static/img/morphology/kernel_ring_20.gif)
![[IM Output]](../static/img/morphology/kernel_ring_21.gif)
![[IM Output]](../static/img/morphology/kernel_rect_1.gif)
![[IM Output]](../static/img/morphology/kernel_rect_2.gif)
![[IM Output]](../static/img/morphology/kernel_rect_3.gif)
![[IM Output]](../static/img/morphology/kernel_rect_4.gif)
![[IM Output]](../static/img/morphology/kernel_rect_5.gif)
![[IM Output]](../static/img/morphology/kernel_rect_6.gif)
![[IM Output]](../static/img/morphology/kernel_rect_7.gif)
![[IM Output]](../static/img/morphology/kernel_rect_8.gif)
![[IM Output]](../static/img/morphology/flagged_points.gif)
![[IM Output]](../static/img/morphology/iterate_infinite.gif)
![[IM Text]](../static/img/morphology/verbose_iterate.txt.gif)
![[IM Text]](../static/img/morphology/verbose_compound.txt.gif)
![[IM Text]](../static/img/morphology/k_disk.txt.gif)
![[IM Text]](../static/img/morphology/k_comet.txt.gif)
![[IM Text]](../static/img/morphology/k_precision.txt.gif)
![[IM Text]](../static/img/morphology/k_multi.txt.gif)
![[IM Text]](../static/img/morphology/kernel_multi.gif)
![[IM Text]](../static/img/morphology/kernel_rotated_list.gif)
![[IM Text]](../static/img/morphology/kernel_rotated_list2.gif)
![[IM Text]](../static/img/morphology/blur_kernels.gif)
![[IM Output]](../static/img/morphology/blur_kernel.gif)
![[IM Output]](../static/img/morphology/blur_kernel2.gif)
![[IM Output]](../static/img/morphology/blur_re-iterate.gif)
![[IM Output]](../static/img/morphology/blur_1_mag.gif)
![[IM Output]](../static/img/morphology/blur_union_mag.gif)
![[IM Output]](../static/img/morphology/blur_2_mag.gif)
![[IM Text]](../static/img/morphology/blur_re-iterate.txt.gif)
![[IM Text]](../static/img/morphology/blur_union.txt.gif)
![[IM Output]](../static/img/morphology/erode_man.gif)
![[IM Output]](../static/img/morphology/dilate_man.gif)
![[IM Output]](../static/img/morphology/dilate_man_neg.gif)
![[IM Output]](../static/img/morphology/open_man.gif)
![[IM Output]](../static/img/morphology/open_erode.gif)
![[IM Output]](../static/img/morphology/open_man_2.gif)
![[IM Output]](../static/img/morphology/open_man_twice.gif)
![[IM Output]](../static/img/morphology/close_man.gif)
![[IM Output]](../static/img/morphology/close_dilate.gif)
![[IM Output]](../static/img/morphology/close_man_2.gif)
![[IM Output]](../static/img/morphology/close_man_neg.gif)
![[IM Output]](../static/img/images/rose.gif)
![[IM Output]](../static/img/morphology/rose_dilate.gif)
![[IM Output]](../static/img/morphology/rose_dilate_intensity.gif)
![[IM Output]](../static/img/morphology/rose_erode_intensity.gif)
![[IM Output]](../static/img/morphology/rose_open_intensity.gif)
![[IM Output]](../static/img/morphology/edgein_man.gif)
![[IM Output]](../static/img/morphology/edgeout_man.gif)
![[IM Output]](../static/img/morphology/edge_man.gif)
![[IM Output]](../static/img/morphology/tophat_man.gif)
![[IM Output]](../static/img/morphology/bottomhat_man.gif)
![[IM Output]](../static/img/images/figure.gif)
![[IM Output]](../static/img/morphology/kernel_diamond_3.gif)
![[IM Output]](../static/img/morphology/figure_erode.gif)
![[IM Output]](../static/img/morphology/kernel_right.gif)
![[IM Output]](../static/img/morphology/hmt_right.gif)
![[IM Output]](../static/img/morphology/kernel_right2.gif)
![[IM Output]](../static/img/morphology/hmt_right2.gif)
![[IM Output]](../static/img/morphology/kernel_nw_corner.gif)
![[IM Output]](../static/img/morphology/hmt_nw_corner.gif)
![[IM Output]](../static/img/morphology/lines.gif)
![[IM Output]](../static/img/morphology/hmt_junctions.gif)
![[IM Output]](../static/img/morphology/kernel_right_out.gif)
![[IM Output]](../static/img/morphology/thick_right.gif)
![[IM Output]](../static/img/morphology/hmt_thicken.gif)
![[IM Output]](../static/img/morphology/man_line.gif)
![[IM Output]](../static/img/morphology/thick_line.gif)
![[IM Output]](../static/img/morphology/man_convex_edge.gif)
![[IM Output]](../static/img/morphology/man_extremities.gif)
![[IM Output]](../static/img/morphology/man_grey.gif)
![[IM Output]](../static/img/morphology/thick_corners.gif)
![[IM Output]](../static/img/morphology/kernel_right_in.gif)
![[IM Output]](../static/img/morphology/thin_right.gif)
![[IM Output]](../static/img/morphology/hmt_thinning.gif)
![[IM Output]](../static/img/morphology/man_raw_thinned.gif)
![[IM Output]](../static/img/morphology/man_erode.gif)
![[IM Output]](../static/img/morphology/man_skeleton.gif)
![[IM Output]](../static/img/morphology/man_thin_skeleton.gif)
![[IM Output]](../static/img/morphology/man_ends_marked.gif)
![[IM Output]](../static/img/morphology/man_junctions.gif)
![[IM Output]](../static/img/morphology/man_junctions_marked.gif)
![[IM Output]](../static/img/morphology/man_loop.gif)
![[IM Output]](../static/img/morphology/line_mag.gif)
![[IM Output]](../static/img/morphology/line_seqential_mag.gif)
![[IM Output]](../static/img/morphology/rect_mag.gif)
![[IM Output]](../static/img/morphology/rect_simultaneous_mag.gif)
![[IM Output]](../static/img/morphology/kernel_peaks_01.gif)
![[IM Output]](../static/img/morphology/kernel_peaks_02.gif)
![[IM Output]](../static/img/morphology/kernel_peaks_03.gif)
![[IM Output]](../static/img/morphology/kernel_peaks_04.gif)
![[IM Output]](../static/img/morphology/kernel_peaks_05.gif)
![[IM Output]](../static/img/morphology/kernel_peaks_06.gif)
![[IM Output]](../static/img/morphology/kernel_edges.gif)
![[IM Output]](../static/img/morphology/thin_edges.gif)
![[IM Output]](../static/img/morphology/kernel_corners.gif)
![[IM Output]](../static/img/morphology/kernel_diagonals.gif)
![[IM Output]](../static/img/morphology/kernel_diagonals1.gif)
![[IM Output]](../static/img/morphology/kernel_diagonals2.gif)
![[IM Output]](../static/img/morphology/kernel_lineends.gif)
![[IM Output]](../static/img/morphology/hmt_lineends.gif)
![[IM Output]](../static/img/morphology/kernel_lineends1.gif)
![[IM Output]](../static/img/morphology/kernel_lineends2.gif)
![[IM Output]](../static/img/morphology/kernel_lineends3.gif)
![[IM Output]](../static/img/morphology/kernel_lineends4.gif)
![[IM Output]](../static/img/morphology/kernel_linejunctions.gif)
![[IM Output]](../static/img/morphology/kernel_linejunctions1.gif)
![[IM Output]](../static/img/morphology/kernel_linejunctions2.gif)
![[IM Output]](../static/img/morphology/kernel_linejunctions3.gif)
![[IM Output]](../static/img/morphology/kernel_linejunctions4.gif)
![[IM Output]](../static/img/morphology/kernel_linejunctions5.gif)
![[IM Output]](../static/img/morphology/kernel_ridges.gif)
![[IM Output]](../static/img/morphology/kernel_ridges2.gif)
![[IM Output]](../static/img/morphology/kernel_convexhull.gif)
![[IM Output]](../static/img/morphology/man_hull.gif)
![[IM Output]](../static/img/morphology/kernel_thinse_4strong.gif)
![[IM Output]](../static/img/morphology/kernel_thinse_4weak.gif)
![[IM Output]](../static/img/morphology/kernel_thinse_8strong.gif)
![[IM Output]](../static/img/morphology/kernel_thinse_8weak.gif)
![[IM Output]](../static/img/morphology/kernel_thinse_23comb.gif)
![[IM Output]](../static/img/morphology/kernel_thinse_48gen.gif)
![[IM Output]](../static/img/morphology/distance.png)
![[IM Text]](../static/img/morphology/distance_max.txt.gif)
![[IM Output]](../static/img/morphology/man_clean.gif)
![[IM Output]](../static/img/morphology/distance_clean.gif)
![[IM Output]](../static/img/morphology/man_floodfill.gif)
![[IM Output]](../static/img/morphology/distance_floodfill.gif)
![[IM Text]](../static/img/morphology/k_chebyshev_3.txt.gif)
![[IM Text]](../static/img/morphology/k_chebyshev.txt.gif)
![[IM Output]](../static/img/morphology/chebyshev_gradient.gif)
![[IM Text]](../static/img/morphology/chebyshev_gradient.txt.gif)
![[IM Text]](../static/img/morphology/chebyshev_distance.txt.gif)
![[IM Text]](../static/img/morphology/k_manhattan.txt.gif)
![[IM Output]](../static/img/morphology/manhattan_gradient.gif)
![[IM Text]](../static/img/morphology/manhattan_distance.txt.gif)
![[IM Text]](../static/img/morphology/k_octagonal.txt.gif)
![[IM Output]](../static/img/morphology/octagonal_gradient.gif)
![[IM Text]](../static/img/morphology/octagonal_distance.txt.gif)
![[IM Output]](../static/img/morphology/fractional_gradient.gif)
![[IM Text]](../static/img/morphology/fractional_distance.txt.gif)
![[Texto IM]](../static/img/morphology/k_knight.txt.gif)
![[Salida IM]](../static/img/morphology/knight_gradient.gif)
![[Texto IM]](../static/img/morphology/knight_distance.txt.gif)
![[Texto IM]](../static/img/morphology/k_euclidean.txt.gif)
![[Salida IM]](../static/img/morphology/euclidean_gradient.gif)
![[Texto IM]](../static/img/morphology/euclidean_distance.txt.gif)
![[Salida IM]](../static/img/morphology/circle_shape.png)
![[Salida IM]](../static/img/morphology/cone_distance.png)
![[Salida IM]](../static/img/morphology/cone_distance_shade.png)
![[Salida IM]](../static/img/morphology/circle_gradient.png)
![[Salida IM]](../static/img/morphology/circle_ridge.png)
![[Salida IM]](../static/img/morphology/rose_shape.png)
![[Salida IM]](../static/img/morphology/rose_feathered.png)
![[Salida IM]](../static/img/morphology/boolean_feathered.png)
![[Salida IM]](../static/img/images/disks.gif)
![[Salida IM]](../static/img/morphology/cond_dilate_draw.gif)
![[Salida IM]](../static/img/morphology/disks_big_center.gif)
![[Salida IM]](../static/img/morphology/disks_mask.gif)
![[Salida IM]](../static/img/morphology/disks_big_found.gif)
![[Salida IM]](../static/img/morphology/disks_line.gif)
![[Salida IM]](../static/img/morphology/disks_line_find.gif)
![[Salida IM]](../static/img/morphology/disks_line_found.gif)
![[Salida IM]](../static/img/morphology/distance_start.png)
![[Salida IM]](../static/img/morphology/distance_point.png)
![[Salida IM]](../static/img/morphology/distance_bounds.png)
![[Salida IM]](../static/img/morphology/distance_direct.png)
![[Salida IM]](../static/img/morphology/distance_constrained.png)
![[Salida IM]](../static/img/morphology/man_at_prep.png)
![[Salida IM]](../static/img/morphology/man_centerline.gif)
![[Salida IM]](../static/img/morphology/man_at_skeleton.gif)