Combinar imágenes con OpenCV

Dedicaremos este tutorial a tratar de aplicar los conocimientos adquiridos en la publicación anterior, detección y descripción de puntos característicos, lo que intentaremos hacer es combinar dos imágenes que tengan una porción en común, para ello localizaremos los keypoints de ambas y haremos el pareo para luego transformar la segunda imagen de modo que se combine con la primera haciendo coincidir los keypoints.

Combinar Imágenes OpenCV

La mayor parte del código requerido lo desarrollamos en el tutorial anterior, cargamos las dos imágenes luego usamos el detector BRISK para localizar y describir los puntos de cada imagen, luego utilizamos BFMatch para hacer el pareo de los puntos, recordar que también filtramos los puntos para trabajar solo con los de mayor fiabilidad.

Por ahora el código que tenemos es el siguiente:

Ptr<Feature2D> detect = BRISK::create();

vector<KeyPoint> kpA, kpB;
Mat descA, descB;

detect->detectAndCompute(imageA, noArray(), kpA, descA);
detect->detectAndCompute(imageB, noArray(), kpB, descB);

vector<DMatch> matches;

Ptr<DescriptorMatcher> matcher = BFMatcher::create(NORM_HAMMING, true);
matcher->match(descA, descB, matches);

sort(matches.begin(), matches.end());
matches.erase(matches.begin() + 30, matches.end());

Al ejecutar hasta aquí tenemos el siguiente resultado:

Detección y pareo de puntos de interés

El siguiente paso es obtener el listado de coordenadas de cada uno de los puntos de ambas imágenes, para ello vamos a recorrer la lista de pareo vector<DMatch> cada DMatch guarda el índice del Keypoint de la primera imagen en queryIdx y trainIdx almacena el índice correspondiente a la segunda.

vector<Point2f> pts1, pts2;
for (auto& m : matches) {
    pts1.push_back(kpA[m.queryIdx].pt);
    pts2.push_back(kpB[m.trainIdx].pt);
}

Teniendo esta información podemos calcular la homografía, esta es una matriz que nos permitirá transformar la imagen de tal modo que ambos puntos coincidan. 

Mat h = findHomography(pts2, pts1, noArray(), CV_RANSAC);

Con esto solo nos resta aplicar la transformación que acabamos de calcular, primero usaremos la función cv::warpPerspective(...) para ubicar la segunda imagen en la posición que le corresponde, luego usamos el método copyTo(...) para ubicar la primera imagen ya que esta no requiere ningún tipo de ajuste, recordemos que es la segunda imagen quien se ajusta a la primera.

vector<Point2f> box;
box.push_back(Point2f(0, 0));
box.push_back(Point2f(imageB.cols, 0));
box.push_back(Point2f(imageB.cols, imageB.rows));
box.push_back(Point2f(0, imageB.rows));

vector<Point2f> box_dst;
perspectiveTransform(box, box_dst, h);

Rect rc = boundingRect(box_dst);

Mat dst;

warpPerspective(imageB, dst, h, Size(rc.width + rc.x, rc.height + rc.y));
imageA.copyTo(dst(Rect(Point(0, 0), imageA.size())));

Algo que debes tener presente es que el tamaño del Mat dst en donde se guarda el resultado debe ser suficiente para contener a ambas imágenes cuando las mismas se combinen, en nuestro caso usamos funciones cv::perspectiveTransform(...) y cv::boundingRect(...) para calcular este tamaño.

El resultado final debe ser algo como esto:

Imagen panorámica con OpenCV

Es importante mencionar que OpenCV ya cuenta con un módulo desarrollado para este tipo de tareas, para un ejemplo de ello puedes ver: creación de imágenes panorámicas.

Descargar proyecto: combinar-imágenes.zip

Comentarios

Temas relacionados

Entradas populares de este blog

tkinter Grid

tkinter Canvas

Histogramas OpenCV Python

Conectar SQL Server con Java