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.
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:
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:
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
Publicar un comentario