Tutorial y ejercicios
Los descriptores son medidas numéricas que resumen propiedades de un objeto o de una región de la imagen. En lugar de comparar directamente todos los píxeles, se calculan valores como el área, el perímetro, el número de agujeros o la forma general. Esto facilita comparar objetos, clasificarlos o utilizarlos como entrada en sistemas de aprendizaje automático.
Un descriptor es útil si conserva la información importante para la tarea e ignora variaciones que no deberían cambiar la decisión. Por ejemplo, si queremos reconocer una forma, puede interesar que el descriptor no cambie demasiado cuando el objeto se desplaza, gira o cambia ligeramente de tamaño. Esta propiedad se denomina robustez o invariancia, según el caso.
Cargamos las imágenes que utilizaremos en esta sesión práctica
monedas = imread("Imagenes\monedas_color.jpg");
monedas = imresize(monedas,[400,400]);
monedas = rgb2gray(monedas);
montage(monedas)

Segmentamos la imagen. Queremos fondo negro, así que tomamos la imagen negativa.
monedas = 255-monedas;
Y binarizamos usando el metodo de OTSU
En este punto, Otsu sirve para obtener automáticamente una máscara inicial del objeto. Esta máscara no es solo un resultado final: es la base sobre la que se calcularán descriptores. Por eso, si la binarización es mala, los descriptores también pueden ser poco fiables.
T_otsu = graythresh(monedas);
monedas_bin = imbinarize(monedas,T_otsu);
montage(monedas_bin)

Mejoramos la segmentación mediante métodos morfológicos para imágenes binarias
La calidad de los descriptores depende mucho de la segmentación previa. Si la máscara binaria contiene ruido, agujeros no deseados o partes de objeto perdidas, las medidas obtenidas pueden ser incorrectas. Por eso es habitual aplicar operaciones morfológicas antes de calcular descriptores: se busca una representación más limpia y coherente del objeto.
monedas_bin = imfill(monedas_bin,"holes");
SE = strel("disk",5);
monedas_bin = imclose(monedas_bin,SE);
montage(monedas_bin)

Descriptores topológicos
Los descriptores topológicos describen propiedades relacionadas con la conectividad. No miden exactamente el tamaño o la forma, sino cuántas componentes hay, cuántos agujeros aparecen o cómo se organiza la estructura del objeto. Este tipo de descriptor es bastante estable ante deformaciones suaves, siempre que no cambie la conexión entre partes del objeto.
Número de componentes conexas.
Contar componentes conexas significa contar cuántos objetos separados hay en la máscara binaria. El resultado depende de la conectividad escogida y de la calidad de la segmentación. El ruido pequeño puede aparecer como componentes adicionales si no se ha eliminado antes.
numero_componentes_conexas = bwconncomp(monedas_bin).NumObjects
numero_componentes_conexas = 8
comp_conexas = bwlabel(monedas_bin);
Otra forma de contar componentes conexas
numero_componentes_conexas_2 = max(comp_conexas(:))
numero_componentes_conexas_2 = 8
montage({label2rgb(comp_conexas,"jet","k","shuffle")})

Número de agujeros. Matlab extrae los bordes de cada componente conexa, si hay dos bordes, es porque uno de los bordes es interior y envuelve a una agujero.
El número de agujeros es un descriptor topológico porque describe la estructura interna del objeto. No depende directamente del tamaño, sino de si hay regiones de fondo completamente rodeadas por el objeto. Puede ser muy útil para diferenciar formas con apariencia semejante pero topología distinta.
Definimos un vector vacio que llevará la cuenta de los agujeros de cada componente conexa.
agujeros = [];
Iteramos a través de cada objeto etiquetado o componente conexa para ver si tiene agujeros
for i = 1:numero_componentes_conexas
comp_i = (comp_conexas == i);
borde = bwboundaries(comp_i);
if length(borde) > 1
agujeros = [agujeros,length(borde)-1];
end
end
agujeros
agujeros =
[]
Característica de Euler. Recordar que la característica de Euler es el número de componentes conexas que no son negras (fondo) menos el número de agujeros.
La característica de Euler combina en un solo valor el número de componentes conexas y el número de agujeros. Es una medida simple, pero puede ser muy informativa cuando los objetos tienen topologías diferentes. Por ejemplo, puede ayudar a diferenciar objetos sin agujeros de objetos con una o más cavidades. Aun así, no describe el tamaño ni la forma exacta del objeto.
Definimos un vector vacio que llevará la cuenta de la característica de Euler cada componente conexa.
caracteristica_euler = [];
Iteramos a través de cada objeto etiquetado o componente conexa para ver cuál es su característica de Euler
for i = 1:numero_componentes_conexas
comp_i = (comp_conexas == i);
borde = bwboundaries(comp_i);
caracteristica_euler = [caracteristica_euler,1-(length(borde)-1)];
end
caracteristica_euler
caracteristica_euler = 1x8
1 1 1 1 1 1 1 1
Otro ejemplo
figuras_agujeros = imread("Imagenes\figuras_agujeros.png");
figuras_agujeros = rgb2gray(figuras_agujeros);
T_otsu_figuras = graythresh(figuras_agujeros);
figuras_agujeros_bin = imbinarize(figuras_agujeros,T_otsu_figuras);
montage(figuras_agujeros_bin)

numero_comps_conexas = bwconncomp(figuras_agujeros_bin).NumObjects
numero_comps_conexas = 6
comp_conexas_figuras = bwlabel(figuras_agujeros_bin);
montage({label2rgb(comp_conexas_figuras,"jet","k","shuffle")})

agujeros_2 = [];
caracteristica_euler_2 = [];
for i = 1:numero_comps_conexas
comp_i = (comp_conexas_figuras == i);
borde = bwboundaries(comp_i);
if length(borde) > 1
agujeros_2 = [agujeros_2,length(borde)-1];
caracteristica_euler_2 = [caracteristica_euler_2,1-(length(borde)-1)];
end
end
agujeros_2
agujeros_2 = 1x6
1 2 2 1 2 2
caracteristica_euler_2
caracteristica_euler_2 = 1x6
0 -1 -1 0 -1 -1
Cálculo del esqueleto. Cálculo basado en la transformada de la distancia
El esqueleto es una representación simplificada de la forma de un objeto. La idea es reducir el objeto a una estructura central, fina y conectada, que mantenga su organización general. Esta representación puede ser útil para estudiar objetos alargados o ramificados. Sin embargo, pequeñas irregularidades del contorno pueden generar ramas adicionales que no siempre tienen significado real.
monedas_skel = bwskel(monedas_bin);
Cálculo basado en adelgazamiento con operadores morfologicos
monedas_skel_2 = bwmorph(monedas_bin,"skeleton",Inf);
montage({monedas_bin,monedas_skel,monedas_skel_2})

Cálculo de la transformada de la distancia
D = bwdist(~monedas_bin);
imshow(D,[])

Descriptores geométricos
Los descriptores geométricos miden propiedades relacionadas con el tamaño, la posición y la forma del objeto. Algunos, como el área y el perímetro, dependen directamente de la escala de la imagen. Otros, como la compacidad o la excentricidad, describen proporciones y pueden ser más útiles para comparar objetos de tamaños distintos.
montage(monedas_bin)

Cálculo del área.
El área se calcula contando los píxeles que pertenecen al objeto. Es una medida sencilla de tamaño, pero depende de la escala de la imagen y de la resolución. Si dos imágenes tienen escalas distintas, hay que normalizar o interpretar el área con cuidado.
Definimos un vector vacio que calculará el área de cada componente conexa.
area = [];
Iteramos a través de cada objeto etiquetado o componente conexa para ver cuál es su área
for i = 1:numero_componentes_conexas
comp_i = (comp_conexas == i);
area = [area,sum(comp_i(:))];
end
area
area = 1x8
5453 5074 3563 2436 7174 3047 8253 8731
Cálculo del perímetro.
El perímetro mide la longitud del contorno del objeto. A diferencia del área, es muy sensible a irregularidades del borde: un contorno con ruido puede tener un perímetro artificialmente grande. Por eso a menudo conviene limpiar la máscara antes de calcularlo.
perimetro = [];
for i = 1:numero_componentes_conexas
comp_i = (comp_conexas == i);
perimetro = [perimetro,regionprops(comp_i, 'Perimeter').Perimeter];
end
perimetro
perimetro = 1x8
258.9320 250.6950 209.2860 175.7500 301.8050 196.0870 320.1980 332.9510
Al igual que el perímetro, el área también se puede calcular con el comando regionprops(comp_i, 'Area').Area]
Cálculo del centroide o centro de masas
El centroide es el punto medio del objeto si todos sus píxeles tuvieran el mismo peso. Sirve para resumir la posición del objeto con un solo punto. Es útil para seguimiento, comparación de formas y cálculo de relaciones espaciales entre objetos.
centroide = [];
for i = 1:numero_componentes_conexas
comp_i = (comp_conexas == i);
centroide = [centroide;regionprops(comp_i, 'Centroid').Centroid];
end
centroide
centroide = 8x2
86.5034 185.0011
85.0530 305.2221
94.6444 91.2088
171.4179 240.7373
192.8400 143.6901
211.6853 320.4207
302.5620 252.9773
305.9201 101.5368
Cálculo de la compacidad
La compacidad relaciona el área de un objeto con su perímetro. Un objeto redondeado y con contorno suave suele ser más compacto que un objeto muy alargado o con muchas irregularidades. Por eso, esta medida puede dar información sobre la complejidad de la forma, aunque no describa todos los detalles del contorno.
compacidad = perimetro.*perimetro./area
compacidad = 1x8
12.2952 12.3863 12.2932 12.6798 12.6967 12.6190 12.4230 12.6969
Cálculo del diámetro
diametro = [];
for i = 1:numero_componentes_conexas
comp_i = (comp_conexas == i);
diametro = [diametro,regionprops(comp_i, 'MajorAxisLength').MajorAxisLength];
end
diametro
diametro = 1x8
83.5045 80.6870 67.6135 56.6321 96.2877 63.1585 104.0661 106.1058
Cálculo de la excentricidad (es decir, de la relación eje mayor-eje menor)
La excentricidad describe hasta qué punto una forma es alargada. Un objeto cercano a un círculo tiene excentricidad baja, mientras que un objeto alargado tiene excentricidad alta. Esta medida es útil porque describe proporción de forma y no solo tamaño absoluto.
diametro_menor = [];
for i = 1:numero_componentes_conexas
comp_i = (comp_conexas == i);
diametro_menor = [diametro_menor,regionprops(comp_i, 'MinorAxisLength').MinorAxisLength];
end
excentricidad = diametro./diametro_menor
excentricidad = 1x8
1.0041 1.0074 1.0074 1.0328 1.0146 1.0274 1.0304 1.0124
Cálculo de la bounding box
La bounding box es el rectángulo más simple que permite localizar aproximadamente un objeto dentro de la imagen. No describe el contorno exacto, pero resume posición, anchura y altura de una forma muy práctica. Esta idea también es fundamental en detección de objetos con aprendizaje profundo, donde las cajas delimitadoras se comparan con anotaciones mediante medidas como la IoU.
imshow(monedas_bin);
hold on;
for i = 1:numero_componentes_conexas
comp_i = (comp_conexas == i);
bounding_box = regionprops(comp_i, 'boundingbox').BoundingBox;
rectangulo = rectangle('Position', bounding_box, 'EdgeColor', 'r', 'LineWidth', 2);
end
hold off
