domingo, 22 de septiembre de 2019

Registro de imágenes con SIFT en OpenCV

Se le llama registro al proceso de trasformación entre varios conjuntos de datos que permiten "encajar" o superponer patrones similares. Quizá el proceso más conocido sea la creación automática de fotos panorámicas a partir de varias fotos independientes. En esta entrada haremos un ejemplo más sencillo. Observen estas dos fotografías de un mismo objeto (uno de mis libros favoritos) tomadas en ángulos diferentes:
Lo que haremos sera colocar la imagen de la izquierda con la misma orientación y escala que la imagen de la derecha. Tomaremos de referencia las características comunes entre ambas imágenes (el libro principalmente). ¿Cómo encontramos estos puntos? En 2004 David Lowe publicó un algoritmo llamado SIFT (Scale-Invariant Feature Transform)  que permite encontrar puntos clave invariantes a las rotaciones y, en buena medida, a los cambios de iluminación. Los puntos clave con mejor correspondencia entre ambas imágenes se ven así:
Este algoritmo está patentado y no aparece en la instalación estándar de OpenCV. Deben seguir las instrucciones que puse en mi entrada anterior para poderlo utilizar. El código que escribí es una modificación para SIFT del código que pueden encontrar acá. Lo he reducido y comentado para mejor claridad:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sat Sep 21 10:22:29 2019
@author: Rodolfo Escobar [version para SIFT]
"""
import cv2
import numpy as np
import matplotlib.pyplot as plt
# Importar imagenes como escala de grises
ImA = cv2.imread('Ref.JPG',0)
ImB = cv2.imread('Des.JPG',0)
#--------------------- SIFT -------------------------------
sift = cv2.xfeatures2d.SIFT_create()
keypointsA, descriptorsA = sift.detectAndCompute(ImA, None)
keypointsB, descriptorsB = sift.detectAndCompute(ImB, None)
#----------------------------------------------------------
#---------------- Correspondencias ------------------------
#NOTA: para SIFT no debe usarse la norma de Hamming
matcher = cv2.BFMatcher(cv2.NORM_L2,crossCheck=True)
matches = matcher.match(descriptorsA, descriptorsB, None)
# Ordenacion de correspondencias por distancias
matches.sort(key=lambda x: x.distance, reverse=False)
# Elegir solo un porcentaje de las correspondencias
# (las que tengan distancias más cortas)
Porcentaje_Corr = 0.05
numMatches = int(len(matches) * Porcentaje_Corr)
matches = matches[:numMatches]
#---------------------------------------------------------
# Grafica de correspondencias
imMatches = cv2.drawMatches(ImA, keypointsA, ImB, keypointsB, matches, None)
plt.imshow(imMatches)
plt.axis("off")
plt.show()
# Localizar correpondencias
points1 = np.zeros((len(matches), 2), dtype=np.float32)
points2 = np.zeros((len(matches), 2), dtype=np.float32)
# Arrays de puntos de la forma [[x0,y0],...,[xn,yn]]
for i, match in enumerate(matches):
points1[i, :] = keypointsA[match.queryIdx].pt
points2[i, :] = keypointsB[match.trainIdx].pt
# Encontrae matriz de homografía
h, mask = cv2.findHomography(points1, points2, cv2.RANSAC)
print(h)
# Aplicar homografía
height, width= ImB.shape
im1Reg = cv2.warpPerspective(ImA, h, (width, height))
# Grafica de imagen alineada
plt.figure(2)
plt.imshow(im1Reg,cmap='gray')
plt.axis("off")
plt.show()
La imagen final registrada es esta:

No hay comentarios: