Salta el contingut

Tutorial i exercisis

Els descriptors són mesures numèriques que resumeixen propietats d’un objecte o d’una regió de la imatge. En lloc de comparar directament tots els píxels, es calculen valors com l’àrea, el perímetre, el nombre de forats o la forma general. Això facilita comparar objectes, classificar-los o utilitzar-los com a entrada en sistemes d’aprenentatge automàtic.

Un descriptor és útil si conserva la informació important per a la tasca i ignora variacions que no haurien de canviar la decisió. Per exemple, si volem reconèixer una forma, pot interessar que el descriptor no canviï gaire quan l’objecte es desplaça, gira o canvia lleugerament de mida. Aquesta propietat s’anomena robustesa o invariància, segons el cas.

Carreguem les imatges que utilitzarem en aquesta sessió pràctica

monedes = imread("Imagenes\monedas_color.jpg");

monedes = imresize(monedes,[400,400]);

monedes = rgb2gray(monedes);

montage(monedes)

figure_0.png

Segmentem la imatge. Volem fons negre, així que prenem la imatge negativa.

monedes = 255-monedes;

I binaritzem utilitzant el mètode d’OTSU

En aquest punt, Otsu serveix per obtenir automàticament una màscara inicial de l’objecte. Aquesta màscara no és només un resultat final: és la base sobre la qual es calcularan descriptors. Per això, si la binarització és dolenta, els descriptors també poden ser poc fiables.

T_otsu = graythresh(monedes);

monedes_bin = imbinarize(monedes,T_otsu);

montage(monedes_bin)

figure_1.png

Millorem la segmentació mitjançant mètodes morfològics per a imatges binàries

La qualitat dels descriptors depèn molt de la segmentació prèvia. Si la màscara binària conté soroll, forats no desitjats o parts d’objecte perdudes, les mesures obtingudes poden ser incorrectes. Per això és habitual aplicar operacions morfològiques abans de calcular descriptors: es busca una representació més neta i coherent de l’objecte.

monedes_bin = imfill(monedes_bin,"holes");

SE = strel("disk",5);

monedes_bin = imclose(monedes_bin,SE);

montage(monedes_bin)

figure_2.png

Descriptors topològics

Els descriptors topològics descriuen propietats relacionades amb la connectivitat. No mesuren exactament la mida o la forma, sinó quantes components hi ha, quants forats apareixen o com s’organitza l’estructura de l’objecte. Aquest tipus de descriptor és bastant estable davant deformacions suaus, sempre que no canviï la connexió entre parts de l’objecte.

Nombre de components connexes.

Comptar components connexes vol dir comptar quants objectes separats hi ha a la màscara binària. El resultat depèn de la connectivitat escollida i de la qualitat de la segmentació. Soroll petit pot aparèixer com a components addicionals si no s’ha eliminat abans.

nombre_components_connexes = bwconncomp(monedes_bin).NumObjects
nombre_components_connexes = 8
comp_connexes = bwlabel(monedes_bin);

Una altra manera de comptar components connexes

nombre_components_connexes_2 = max(comp_connexes(:))
nombre_components_connexes_2 = 8
montage({label2rgb(comp_connexes,"jet","k","shuffle")})

figure_3.png

Nombre de forats. Matlab extreu les vores de cada component connexa; si hi ha dues vores, és perquè una de les vores és interior i envolta un forat.

El nombre de forats és un descriptor topològic perquè descriu l’estructura interna de l’objecte. No depèn directament de la mida, sinó de si hi ha regions de fons completament envoltades per l’objecte. Pot ser molt útil per diferenciar formes amb aparença semblant però topologia diferent.

Definim un vector buit que portarà el compte dels forats de cada component connexa.

forats = [];

Iterem a través de cada objecte etiquetat o component connexa per veure si té forats

for i = 1:nombre_components_connexes

    comp_i = (comp_connexes == i);

    vora = bwboundaries(comp_i);

    if length(vora) > 1

        forats = [forats,length(vora)-1];

    end
end

forats
forats =

     []

Característica d’Euler. Recordeu que la característica d’Euler és el nombre de components connexes que no són negres, és a dir, fons, menys el nombre de forats.

La característica d’Euler combina en un sol valor el nombre de components connexes i el nombre de forats. És una mesura simple, però pot ser molt informativa quan els objectes tenen topologies diferents. Per exemple, pot ajudar a diferenciar objectes sense forats d’objectes amb una o més cavitats. Tot i això, no descriu la mida ni la forma exacta de l’objecte.

Definim un vector buit que portarà el compte de la característica d’Euler de cada component connexa.

caracteristica_euler = [];

Iterem a través de cada objecte etiquetat o component connexa per veure quina és la seva característica d’Euler

for i = 1:nombre_components_connexes

    comp_i = (comp_connexes == i);

    vora = bwboundaries(comp_i);

    caracteristica_euler = [caracteristica_euler,1-(length(vora)-1)];

end

caracteristica_euler
caracteristica_euler = 1x8
     1     1     1     1     1     1     1     1

Un altre exemple

figures_forats = imread("Imagenes\figuras_agujeros.png");

figures_forats = rgb2gray(figures_forats);

T_otsu_figures = graythresh(figures_forats);

figures_forats_bin = imbinarize(figures_forats,T_otsu_figures);

montage(figures_forats_bin)

figure_4.png

nombre_comps_connexes = bwconncomp(figures_forats_bin).NumObjects
nombre_comps_connexes = 6
comp_connexes_figures = bwlabel(figures_forats_bin);

montage({label2rgb(comp_connexes_figures,"jet","k","shuffle")})

figure_5.png

forats_2 = [];
caracteristica_euler_2 = [];

for i = 1:nombre_comps_connexes

    comp_i = (comp_connexes_figures == i);

    vora = bwboundaries(comp_i);

    if length(vora) > 1

        forats_2 = [forats_2,length(vora)-1];
        caracteristica_euler_2 = [caracteristica_euler_2,1-(length(vora)-1)];

    end
end

forats_2
forats_2 = 1x6
     1     2     2     1     2     2
caracteristica_euler_2
caracteristica_euler_2 = 1x6
     0    -1    -1     0    -1    -1

Càlcul de l’esquelet. Càlcul basat en la transformada de la distància

L’esquelet és una representació simplificada de la forma d’un objecte. La idea és reduir l’objecte a una estructura central, fina i connectada, que mantingui la seva organització general. Aquesta representació pot ser útil per estudiar objectes allargats o ramificats. No obstant això, petites irregularitats del contorn poden generar branques addicionals que no sempre tenen significat real.

monedes_skel = bwskel(monedes_bin);

Càlcul basat en aprimament amb operadors morfològics

monedes_skel_2 = bwmorph(monedes_bin,"skeleton",Inf);

montage({monedes_bin,monedes_skel,monedes_skel_2})

figure_6.png

Càlcul de la transformada de la distància

D = bwdist(~monedes_bin);

imshow(D,[])

figure_7.png

Descriptors geomètrics

Els descriptors geomètrics mesuren propietats relacionades amb la mida, la posició i la forma de l’objecte. Alguns, com l’àrea i el perímetre, depenen directament de l’escala de la imatge. Altres, com la compacitat o l’excentricitat, descriuen proporcions i poden ser més útils per comparar objectes de mides diferents.

montage(monedes_bin)

figure_8.png

Càlcul de l’àrea.

L’àrea es calcula comptant els píxels que pertanyen a l’objecte. És una mesura senzilla de mida, però depèn de l’escala de la imatge i de la resolució. Si dues imatges tenen escales diferents, cal normalitzar o interpretar l’àrea amb cura.

Definim un vector buit que calcularà l’àrea de cada component connexa.

area = [];

Iterem a través de cada objecte etiquetat o component connexa per veure quina és la seva àrea

for i = 1:nombre_components_connexes

    comp_i = (comp_connexes == i);

    area = [area,sum(comp_i(:))];

end

area
area = 1x8
        5453        5074        3563        2436        7174        3047        8253        8731

Càlcul del perímetre.

El perímetre mesura la longitud del contorn de l’objecte. A diferència de l’àrea, és molt sensible a irregularitats de la vora: un contorn amb soroll pot tenir un perímetre artificialment gran. Per això sovint convé netejar la màscara abans de calcular-lo.

perimetre = [];

for i = 1:nombre_components_connexes

    comp_i = (comp_connexes == i);

    perimetre = [perimetre,regionprops(comp_i, 'Perimeter').Perimeter];

end

perimetre
perimetre = 1x8
  258.9320  250.6950  209.2860  175.7500  301.8050  196.0870  320.1980  332.9510

Igual que el perímetre, l’àrea també es pot calcular amb la comanda regionprops(comp_i, 'Area').Area]

Càlcul del centroide o centre de masses

El centroide és el punt mitjà de l’objecte si tots els seus píxels tinguessin el mateix pes. Serveix per resumir la posició de l’objecte amb un sol punt. És útil per seguiment, comparació de formes i càlcul de relacions espacials entre objectes.

centroide = [];

for i = 1:nombre_components_connexes

    comp_i = (comp_connexes == 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àlcul de la compacitat

La compacitat relaciona l’àrea d’un objecte amb el seu perímetre. Un objecte arrodonit i amb contorn suau acostuma a ser més compacte que un objecte molt allargat o amb moltes irregularitats. Per això, aquesta mesura pot donar informació sobre la complexitat de la forma, encara que no descrigui tots els detalls del contorn.

compacitat = perimetre.*perimetre./area
compacitat = 1x8
   12.2952   12.3863   12.2932   12.6798   12.6967   12.6190   12.4230   12.6969

Càlcul del diàmetre

diametre = [];

for i = 1:nombre_components_connexes

    comp_i = (comp_connexes == i);

    diametre = [diametre,regionprops(comp_i, 'MajorAxisLength').MajorAxisLength];

end

diametre
diametre = 1x8
   83.5045   80.6870   67.6135   56.6321   96.2877   63.1585  104.0661  106.1058

Càlcul de l’excentricitat, és a dir, de la relació eix major-eix menor

L’excentricitat descriu fins a quin punt una forma és allargada. Un objecte proper a un cercle té excentricitat baixa, mentre que un objecte allargat té excentricitat alta. Aquesta mesura és útil perquè descriu proporció de forma i no només mida absoluta.

diametre_menor = [];

for i = 1:nombre_components_connexes

    comp_i = (comp_connexes == i);

    diametre_menor = [diametre_menor,regionprops(comp_i, 'MinorAxisLength').MinorAxisLength];

end

excentricitat = diametre./diametre_menor
excentricitat = 1x8
1.0041    1.0074    1.0074    1.0328    1.0146    1.0274    1.0304    1.0124

Càlcul de la bounding box

La bounding box és el rectangle més simple que permet localitzar aproximadament un objecte dins de la imatge. No descriu el contorn exacte, però resumeix posició, amplada i alçada d’una manera molt pràctica. Aquesta idea també és fonamental en detecció d’objectes amb aprenentatge profund, on les caixes delimitadores es comparen amb anotacions mitjançant mesures com la IoU.

imshow(monedes_bin);
hold on;

for i = 1:nombre_components_connexes

    comp_i = (comp_connexes == i);

    bounding_box = regionprops(comp_i, 'boundingbox').BoundingBox;

    rectangle = rectangle('Position', bounding_box, 'EdgeColor', 'r', 'LineWidth', 2);

end

hold off

figure_9.png