Tutorial y ejercicios
En esta práctica se trabajan características que permiten describir y comparar imágenes. Algunas son globales, como los histogramas, porque resumen información de toda la imagen o de una región grande. Otras son más locales, como los vértices o las rectas detectadas, porque indican puntos o estructuras concretas. Todas ellas ayudan a convertir información visual en datos más fáciles de analizar.
Cargamos las imágenes que utilizaremos en la práctica.
lena = imread("Imagenes\lena_color_512.tif");
mandril = imread("Imagenes\mandril_color.tif");
malla = imread("Imagenes\patron_malla.jpg");
textura = imread("Imagenes\astablet.tif");
chessboard = imread("Imagenes\chessboardpattern.jpg");
chessboard_inclinado = imread("Imagenes\esquinas_perspectiva.jpg");
malla = rgb2gray(malla);
chessboard_inclinado = rgb2gray(chessboard_inclinado);
montage({lena,mandril,malla,textura,chessboard,chessboard_inclinado})

Caracteristicas basadas en el histograma
Un histograma de color indica cómo se distribuyen los valores de los canales de color. Es útil para describir la apariencia general de una imagen, por ejemplo si predominan tonos oscuros, claros o determinados colores. Aun así, el histograma no conserva información espacial: dos imágenes pueden tener el mismo histograma y, al mismo tiempo, mostrar objetos dispuestos de forma completamente diferente.
Histograma del color
Primer ejemplo: Imagen Lena
Separamos la imagen en los tres canales de color
rojo = lena(:,:,1);
verde = lena(:,:,2);
azul = lena(:,:,3);
Y obtenemos los histogramas de cada canal
h_rojo = imhist(rojo);
h_verde = imhist(verde);
h_azul = imhist(azul);
bar(h_rojo)

bar(h_verde)

bar(h_azul)

Otro ejemplo: Imagen mandril
rojo = mandril(:,:,1);
verde = mandril(:,:,2);
azul = mandril(:,:,3);
h_rojo = imhist(rojo);
h_verde = imhist(verde);
h_azul = imhist(azul);
bar(h_rojo)

bar(h_verde)

bar(h_azul)

Calculamos las medidas de similitud para comparar histogramas. Para simplificar, vamos a comparar solo un histograma por imagen.
Comparar histogramas significa medir hasta qué punto dos distribuciones de intensidad o color se parecen. La distancia euclídea trata las diferencias de forma directa, la chi cuadrado da más importancia a diferencias relativas y la divergencia de Jeffrey compara distribuciones de forma más simétrica. Ninguna métrica es universalmente mejor; depende de qué se quiera considerar semejante.
lena_gris = rgb2gray(lena);
mandril_gris = rgb2gray(mandril);
h_lena = imhist(lena_gris);
h_mandril = imhist(mandril_gris);
Normalizamos el histograma
La normalización de los histogramas es necesaria cuando se quieren comparar imágenes de tamaño diferente o con distinto número de píxeles. Sin normalizar, una imagen más grande podría dar valores más altos simplemente porque contiene más píxeles. Al normalizar, la comparación se centra mejor en la forma de la distribución y no tanto en la cantidad absoluta de muestras.
h_lena_n = h_lena/numel(lena_gris);
h_mandril_n = h_mandril/numel(mandril_gris);
bar(h_lena_n)

bar(h_mandril_n)

Distancia Euclidea
dist_euclidea = sqrt(sum((h_lena_n - h_mandril_n).^2))
dist_euclidea = 0.0533
Distancia Xi-cuadrado
dist_xicuadrado = sum(((h_lena_n - h_mandril_n).^2) ./ (h_lena_n + h_mandril_n + eps))
dist_xicuadrado = 0.2997
Distancia de la divergencia de Jeffrey
divergence_jeffrey = sum(h_lena_n.*log((h_lena_n./(h_mandril_n + eps))+eps))
divergence_jeffrey = 2.1436
Histograma de los gradientes orientados
El histograma de gradientes orientados, o HOG, describe la distribución de las direcciones del gradiente en pequeñas zonas de la imagen. Como el gradiente es fuerte en los bordes, este descriptor resume información sobre contornos y formas locales. Durante muchos años fue un descriptor muy utilizado en detección de objetos, especialmente antes de que las redes convolucionales fueran tan habituales.
Primer ejemplo: Imagen Lena
Comando: extractHOGFeatures
[feature_vector,hog] = extractHOGFeatures(lena);
imshow(lena);
hold on;
plot(hog);
hold off

Otro ejemplo: Imagen mandril
[feature_vector_mandril,hog_mandril] = extractHOGFeatures(mandril);
imshow(mandril);
hold on
plot(hog_mandril);
hold off

Caracteristicas basadas en la transformada de Hough
La transformada de Hough es una técnica para detectar formas geométricas simples, como rectas o círculos. La idea es que cada punto de borde vota por las formas que podrían pasar por él. Cuando muchos puntos votan por el mismo conjunto de parámetros, es probable que haya una forma real en la imagen. Esto permite detectar estructuras aunque los bordes no sean perfectos o estén parcialmente interrumpidos.
Obtención de rectas vía la transformada de Hough
Primero obtenemos los bordes de la imagen
La transformada de Hough no se aplica normalmente sobre toda la imagen, sino sobre una imagen de bordes. Esto reduce el número de puntos que votan y concentra el proceso en los píxeles que probablemente forman parte de rectas o contornos.
malla_edge = edge(malla,"canny");
Comando: hough. Da como input la discretización del plano de Hough H, con los valores de theta T y rho R
[H,T,R] = hough(malla_edge);
Calculamos los picos (puntos con más valor) en dicha discretización del plano de Hough. En particular, nos quedamos con los 16 picos de más valor que esten por encima del 35% del valor máximo.
Un pico en el espacio de Hough indica que muchos puntos de borde son compatibles con la misma recta. Seleccionar solo los picos más fuertes evita considerar rectas débiles o causadas por ruido. El umbral controla cuántas estructuras se conservan.
P = houghpeaks(H, 16,'threshold',ceil(0.35*max(H(:))));
Calculamos las rectas con dichos picos y sus valores theta y rho asociados (que definen las correspondientes rectas).
lines = houghlines(malla_edge,T,R,P);
imshow(malla)
hold on
for k = 1:length(lines)
xy = [lines(k).point1; lines(k).point2];
plot(xy(:,1),xy(:,2),'LineWidth',2,'Color','red');
end
hold off

Obtenemos los círculos en la imagen vía la transformada de Hough. Para ello, usamos el comando imfindcircles que busca círculos con un radio de valor comprendido en el rango que se define como segundo input.
Para detectar círculos, el método busca acumulaciones de votos compatibles con un centro y un radio. El rango de radios es importante: si es demasiado amplio, aumentan los falsos positivos; si es demasiado restrictivo, se pueden perder círculos reales. Por eso hay que relacionar este rango con el tamaño esperado de los objetos.
[centers, radii] = imfindcircles(textura,[10 30]);
imshow(textura)
viscircles(centers, radii,'EdgeColor','b');

Vértices
Un vértice o esquina es un punto donde la imagen cambia de forma significativa en más de una dirección. Este tipo de punto es más fácil de localizar con precisión que un borde recto, porque un borde puede desplazarse a lo largo de su dirección sin cambiar demasiado. Por eso las esquinas son útiles como puntos de interés en registro de imágenes, seguimiento y comparación entre imágenes.
Se utiliza el detector de Harris para identificar esquinas y vértices en las imágenes.
El detector de Harris analiza cómo cambia la intensidad dentro de una pequeña ventana cuando esta se desplaza. Si el cambio es grande en todas las direcciones, el punto central se considera una buena esquina. Si el cambio es grande solo en una dirección, probablemente se trata de un borde. Para obtener resultados más fiables, normalmente se conservan solo los puntos con una respuesta más fuerte.
corners = detectHarrisFeatures(chessboard);
imshow(chessboard);
hold on
plot(corners.Location(:,1),corners.Location(:,2),'o','Color','r','LineWidth',2);
hold off

Otro ejemplo. Notad que en este caso se detectan varios falsos positivos. Un análisis más detallado muestra que la métrica de estos es muy baja, con lo que se pueden eliminar fácilmente mediante seleccionar los más fuertes, es decir, los que tienen una detección más robusta.
corners_alt = detectHarrisFeatures(chessboard_inclinado);
imshow(chessboard_inclinado);
hold on
plot(corners_alt.Location(:,1),corners_alt.Location(:,2),'o',"color",'r','LineWidth',2);
hold off

imshow(chessboard_inclinado)
strongest = corners_alt.selectStrongest(20);
hold on
plot(strongest.Location(:,1),strongest.Location(:,2),'o','color','r','LineWidth',2)
hold off
