Tutorial y ejercicios
Esta práctica se centra en características locales que permiten encontrar correspondencias entre imágenes. Una correspondencia indica que un punto de una imagen y un punto de otra probablemente representan la misma parte de la escena o del mismo objeto. Para que esto funcione, el detector debe encontrar puntos repetibles y el descriptor debe representarlos de forma suficientemente estable ante cambios de escala, rotación, punto de vista o iluminación.
Cargamos las imágenes que utilizaremos en la sesión práctica
cocacola_lata_1 = imread("Imagenes\coca_cola_1.jpg");
cocacola_lata_2 = imread("Imagenes\coca_cola_2.jpg");
frame1 = imread("Imagenes\Frame 1.tif");
Pasamos las imágenes a escala de grises (para una mejor detección de las características)
Muchos detectores locales trabajan sobre intensidad porque las esquinas, bordes y gradientes se pueden definir claramente en una sola imagen de grises. Esto reduce la complejidad y evita que diferencias de color poco relevantes interfieran en la detección.
cocacola_lata_1 = rgb2gray(cocacola_lata_1);
cocacola_lata_2 = rgb2gray(cocacola_lata_2);
montage({cocacola_lata_1,cocacola_lata_2})

Características tipo SIFT
SIFT es un método diseñado para detectar puntos de interés estables. Una de sus ideas principales es analizar la imagen a diferentes escalas, porque un mismo objeto puede aparecer más grande o más pequeño según la distancia a la cámara. Además, asigna una orientación a los puntos detectados y calcula descriptores basados en gradientes, lo que ayuda a comparar puntos aunque la imagen esté girada o tenga cambios moderados de iluminación.
Usamos el comando detectSIFTFeatures para obtener todos los puntos de interés de tipo SIFT junto con sus descriptores. Luego los mostramos sobre la imagen original para ver qué tipo de características son las que detecta este algoritmo.
SIFT_features_lata_2 = detectSIFTFeatures(cocacola_lata_2);
puntos_SIFT_lata_2 = SIFT_features_lata_2.Location;
imshow(cocacola_lata_2)
hold on
plot(puntos_SIFT_lata_2(:,1),puntos_SIFT_lata_2(:,2),'+',Color='r');
hold off

Analisis de los parámetros de entrada: Umbral para los maximos y los minimos, es decir, para la supresión de no-máximos, umbral para eliminar los puntos de interés pertenecientes a rectas, definición del número de capas en las octavas y del valor inicial de la varianza).
Los parámetros del detector influyen directamente en el número y la calidad de los puntos detectados. Si los umbrales son demasiado permisivos, aparecen muchos puntos, pero algunos pueden ser poco estables o corresponder a ruido. Si los umbrales son demasiado estrictos, se reduce el número de puntos y se pueden perder zonas útiles. Por eso hay que entender estos parámetros como un equilibrio entre sensibilidad y fiabilidad.
Otro ejemplo
SIFT_features_frame_1 = detectSIFTFeatures(frame1);
puntos_SIFT_frame_1 = SIFT_features_frame_1.selectStrongest(550);
puntos_SIFT_frame_1 = puntos_SIFT_frame_1.Location;
imshow(frame1);
hold on
plot(puntos_SIFT_frame_1(:,1),puntos_SIFT_frame_1(:,2),'o',Color='r')
hold off

Características tipo FAST
FAST es un detector de esquinas pensado para ser muy rápido. Analiza los píxeles de una circunferencia alrededor del punto candidato y comprueba si hay un cambio de intensidad suficientemente claro. Esta simplicidad lo hace muy eficiente en aplicaciones donde el tiempo de cálculo es importante. Aun así, FAST detecta puntos, pero no proporciona por sí solo un descriptor completo para compararlos entre imágenes.
Usamos el comando detectFASTFeatures para obtener todos los puntos de interés de tipo FAST. Luego los mostramos sobre la imagen original para ver qué tipo de características son las que detecta este algoritmo.
FAST_features_lata_2 = detectFASTFeatures(cocacola_lata_2);
puntos_FAST_lata_2 = FAST_features_lata_2.Location;
imshow(cocacola_lata_2)
hold on
plot(puntos_FAST_lata_2(:,1),puntos_FAST_lata_2(:,2),'o',Color='r');
hold off

Otro ejemplo
FAST_features_frame_1 = detectFASTFeatures(frame1);
puntos_FAST_frame_1 = FAST_features_frame_1.Location;
imshow(frame1)
hold on
plot(puntos_FAST_frame_1(:,1),puntos_FAST_frame_1(:,2),'+',Color='r')
hold off

Características tipo ORB
ORB combina ideas de detección rápida con descriptores binarios. Parte de un detector eficiente, relacionado con FAST, y añade orientación y una descripción compacta del entorno del punto. Como el descriptor es binario, la comparación entre puntos puede hacerse con distancia de Hamming, que es computacionalmente sencilla. Por eso ORB se utiliza mucho cuando se desea un buen equilibrio entre velocidad y calidad.
Usamos el comando detectORBFeatures para obtener todos los puntos de interés de tipo ORB junto con sus descriptores. Luego los mostramos sobre la imagen original para ver qué tipo de características son las que detecta este algoritmo.
ORB_features_lata_2 = detectORBFeatures(cocacola_lata_2);
puntos_ORB_lata_2 = ORB_features_lata_2.selectStrongest(1000);
puntos_coordenadas_ORB_lata_2 = puntos_ORB_lata_2.Location;
imshow(cocacola_lata_2)
hold on
plot(puntos_coordenadas_ORB_lata_2(:,1),puntos_coordenadas_ORB_lata_2(:,2),'o',Color='r');
hold off

Analisis de los parámetros de entrada: Factor de escala para la mejora de la parte FAST del algoritmo, número de octavas y una región de interes por si se quieren detectar las características en una subregión de la imagen original.
Otro ejemplo
ORB_features_frame_1 = detectORBFeatures(frame1);
puntos_ORB_frame_1 = ORB_features_frame_1.selectStrongest(1000);
puntos_coordenadas_ORB_frame_1 = puntos_ORB_frame_1.Location;
imshow(frame1)
hold on
plot(puntos_coordenadas_ORB_frame_1(:,1),puntos_coordenadas_ORB_frame_1(:,2),'+',Color='r')
hold off

Extraer las caracteristicas de un cierto tipo y compararlas
Cuando se extraen descriptores de dos imágenes, el matching busca qué puntos tienen descriptores más parecidos. Este paso proporciona correspondencias candidatas, pero no todas tienen que ser correctas. Dos puntos diferentes pueden tener apariencia local semejante, especialmente si hay texturas repetidas o zonas poco distintivas. Por eso es habitual añadir una verificación geométrica posterior.
El comando extractFeatures permite extraer el vector descriptor de las características introducidas como argumento.
Un descriptor es un vector que resume la apariencia local alrededor de un punto de interés. El detector dice dónde hay puntos relevantes; el descriptor dice cómo es el entorno de esos puntos. Esta separación es importante porque detectar y describir no son exactamente la misma tarea.
[descriptor_lata_2, SIFT_features_lata_2] = extractFeatures(cocacola_lata_2,SIFT_features_lata_2);
Para comparar objetos entre si, repetimos el proceso con la otra imagen conteniendo una lata.
SIFT_features_lata_1 = detectSIFTFeatures(cocacola_lata_1);
[descriptor_lata_1, SIFT_features_lata_1] = extractFeatures(cocacola_lata_1,SIFT_features_lata_1);
Comparamos los dos vectores descriptores con el comando matchFeatures. Si es binario se usa la distancia de Hamming y si no, se usa la distancia Euclidea normalizada. La función devuelve los indices de las caracterísiticas emparejadas para las dos imágenes y el comando length de dicho vector nos informa de cuantas características se han emparejado.
El matching busca, para cada descriptor, el descriptor más parecido en la otra imagen. En descriptores binarios, la distancia de Hamming cuenta cuántos bits son distintos; en descriptores reales, se utilizan distancias numéricas como la euclídea. Un buen matching debería dar muchas correspondencias correctas, pero siempre puede incluir errores.
[indices_pares_features,metrica] = matchFeatures(descriptor_lata_2,descriptor_lata_1,"MatchThreshold",10);
length(indices_pares_features)
ans = 71
Nos quedamos solo con las características emparejadas
matched_SIFT_features_lata_2 = SIFT_features_lata_2(indices_pares_features(:,1),:);
matched_SIFT_features_lata_1 = SIFT_features_lata_1(indices_pares_features(:,2),:);
Mostramos el emparejamiento entre dichas características
Visualizar los emparejamientos es una comprobación cualitativa importante. Si muchas líneas unen partes coherentes del objeto, los descriptores están funcionando bien. Si muchas líneas se cruzan sin sentido o unen zonas distintas, cabe esperar que la verificación geométrica elimine estos errores.
figure
showMatchedFeatures(cocacola_lata_2,cocacola_lata_1, matched_SIFT_features_lata_2,matched_SIFT_features_lata_1,"montage");

Calculamos la transformación geométrica (via RANSAC) que permite hacer el ajuste geométrico entre las dos imágenes en función de las características emparejadas. Usamos dicha transf. para transformar una de las imágenes y comparamos el resultado.
RANSAC es un método robusto para estimar una transformación geométrica cuando hay correspondencias erróneas. Prueba diferentes subconjuntos de puntos y comprueba qué correspondencias son coherentes con el mismo modelo de transformación. Las correspondencias coherentes se llaman inliers, y las incoherentes, outliers. De esta forma, el alineamiento final depende sobre todo de los emparejamientos fiables.
T = estimateGeometricTransform(matched_SIFT_features_lata_2,matched_SIFT_features_lata_1,"affine");
T.T
ans = 3x3 single matrix
-0.6308 -0.2532 0
0.2542 -0.6325 0
329.8466 431.7157 1.0000
cocacola_lata_2_T = imwarp(cocacola_lata_2,T);
montage({cocacola_lata_2_T,cocacola_lata_1})
