En este tutorial, aprenderás a realizar la costura de imágenes utilizando Python, OpenCV, y las funciones cv2.createStitcher
y cv2.Stitcher_create
. Usando el código de hoy serás capaz de coser múltiples imágenes juntas, creando un panorama de imágenes cosidas.
Hace poco menos de dos años publiqué dos guías sobre el cosido de imágenes y la construcción de panorámicas:
- Fundamentos de la costura de imágenes
- Panorama en tiempo real y costura de imágenes
Ambos tutoriales cubrían los fundamentos del típico algoritmo de costura de imágenes, que, como mínimo, requieren cuatro pasos clave:
- Detectar los puntos clave (DoG, Harris, etc.) y extraer descriptores locales invariantes (SIFT, SURF, etc.) a partir de dos imágenes de entrada
- Emparejando los descriptores entre las imágenes
- Usando el algoritmo RANSAC para estimar una matriz de homografía usando nuestros vectores de características emparejados
- Aplicando una transformación de warping usando la matriz de homografía obtenida en el paso #3
Sin embargo, el mayor problema de mis implementaciones originales es que no eran capaces de manejar más de dos imágenes de entrada.
En el tutorial de hoy, vamos a revisar la unión de imágenes con OpenCV, incluyendo cómo unir más de dos imágenes en una imagen panorámica.
Para aprender a unir imágenes con OpenCV y Python, ¡sólo tienes que seguir leyendo!
- ¿Buscas el código fuente de este post?
- Cosido de imágenes con OpenCV y Python
- Algoritmo de cosido de imágenes de OpenCV
- Estructura del proyecto
- Las funciones cv2.createStitcher y cv2.Stitcher_create
- Implementación de la costura de imágenes con Python
- Resultados básicos de la costura de imágenes
- Un mejor cosido de imágenes con OpenCV y Python
- Resultados del stitching de imágenes mejorado
- Limitaciones e inconvenientes
- ¿Te encuentras con errores al realizar el stitching de imágenes usando OpenCV?
- Resumen
¿Buscas el código fuente de este post?
Ir directamente a la sección de descargas
Cosido de imágenes con OpenCV y Python
En la primera parte del tutorial de hoy, vamos a revisar brevemente el algoritmo de cosido de imágenes de OpenCV que se hornea en la propia biblioteca OpenCV a través de cv2.createStitcher
y cv2.Stitcher_create
funciones.
A partir de ahí revisaremos la estructura de nuestro proyecto e implementaremos un script de Python que se puede utilizar para el stitching de imágenes.
Revisaremos los resultados de este primer script, notaremos sus limitaciones y luego implementaremos un segundo script de Python que se puede utilizar para obtener resultados de stitching de imágenes más agradables estéticamente.
Por último, revisaremos los resultados de nuestro segundo script y de nuevo notaremos cualquier limitación o inconveniente.
Algoritmo de cosido de imágenes de OpenCV
El algoritmo que utilizaremos hoy aquí es similar al método propuesto por Brown y Lowe en su artículo de 2007, Automatic Panoramic Image Stitching with Invariant Features.
A diferencia de los anteriores algoritmos de cosido de imágenes que son sensibles al orden de las imágenes de entrada, el método de Brown y Lowe es más robusto, haciéndolo insensible a:
- Ordenación de las imágenes
- Orientación de las imágenes
- Cambios de iluminación
- Imágenes ruidosas que no son realmente parte del panorama
Además, su método de cosido de imágenes es capaz de producir imágenes panorámicas de salida más agradables estéticamente mediante el uso de la compensación de ganancia y la mezcla de imágenes.
Una revisión completa y detallada del algoritmo está fuera del alcance de este post, así que si usted está interesado en aprender más, por favor refiérase a la publicación original.
Estructura del proyecto
Veamos cómo se organiza este proyecto con el comando tree
:
$ tree --dirsfirst.├── images│ └── scottsdale│ ├── IMG_1786-2.jpg│ ├── IMG_1787-2.jpg│ └── IMG_1788-2.jpg├── image_stitching.py├── image_stitching_simple.py└── output.png2 directories, 6 files
Las imágenes de entrada van en la carpeta images/
. Opté por hacer una subcarpeta para mi scottsdale/
conjunto de imágenes en caso de que quisiera añadir subcarpetas adicionales aquí más tarde.
Hoy vamos a revisar dos scripts de Python:
-
image_stitching_simple.py
: ¡Nuestra versión simple de la costura de imágenes se puede completar en menos de 50 líneas de código Python! -
image_stitching.py
: Este script incluye mi hack para extraer un ROI de la imagen cosida para un resultado estéticamente agradable.
El último archivo, output.png
, es el nombre de la imagen cosida resultante. Usando los argumentos de la línea de comandos, se puede cambiar fácilmente el nombre del archivo + la ruta de la imagen de salida.
Las funciones cv2.createStitcher y cv2.Stitcher_create
OpenCV ya ha implementado un método similar al del trabajo de Brown y Lowe a través del cv2.createStitcher
(OpenCV 3.x) y cv2.Stitcher_create
(OpenCV 4).
Suponiendo que tienes OpenCV correctamente configurado e instalado podrás investigar la firma de la función de cv2.createStitcher
para OpenCV 3.x:
createStitcher(...) createStitcher() -> retval
Nota cómo esta función sólo tiene un único parámetro, try_gpu
que puede ser utilizado para mejorar tu a lo largo de tu pipeline de stitching de imágenes. El soporte de OpenCV para la GPU es limitado y nunca he podido conseguir que este parámetro funcione, por lo que recomiendo dejarlo siempre como False
.
La función cv2.Stitcher_create
de OpenCV 4 tiene una firma similar:
Stitcher_create(...) Stitcher_create() -> retval . @brief Creates a Stitcher configured in one of the stitching .modes. . . @param mode Scenario for stitcher operation. This is usually .determined by source of images to stitch and their transformation. .Default parameters will be chosen for operation in given scenario. . @return Stitcher class instance.
Para realizar el stitching de la imagen real necesitaremos llamar al método .stitch
:
OpenCV 3.x:stitch(...) method of cv2.Stitcher instance stitch(images) -> retval, panoOpenCV 4.x:stitch(...) method of cv2.Stitcher instance stitch(images, masks) -> retval, pano . @brief These functions try to stitch the given images. . . @param images Input images. . @param masks Masks for each input image specifying where to .look for keypoints (optional). . @param pano Final pano. . @return Status code.
Este método acepta una lista de images
de entrada, y luego intenta coserlas en un panorama, devolviendo la imagen del panorama de salida a la función que lo llama.
La variable status
indica si el cosido de la imagen fue un éxito o no y puede ser una de las cuatro variables:
-
OK = 0
: El cosido de la imagen fue un éxito. -
ERR_NEED_MORE_IMGS = 1
: En caso de recibir este código de estado, necesitará más imágenes de entrada para construir su panorama. Normalmente este error ocurre si no hay suficientes puntos clave detectados en sus imágenes de entrada. -
ERR_HOMOGRAPHY_EST_FAIL = 2
: Este error ocurre cuando la estimación de la homografía RANSAC falla. De nuevo, es posible que necesites más imágenes o que tus imágenes no tengan suficientes texturas/objetos distintivos y únicos para que los puntos clave coincidan con precisión. -
ERR_CAMERA_PARAMS_ADJUST_FAIL = 3
: Nunca me he encontrado con este error antes, así que no tengo mucho conocimiento sobre él, pero lo esencial es que está relacionado con el hecho de no estimar correctamente los intrínsecos/extrínsecos de la cámara a partir de las imágenes de entrada. Si te encuentras con este error es posible que tengas que consultar la documentación de OpenCV o incluso bucear en el código C++ de OpenCV.
Ahora que hemos revisado los métodos cv2.createStitcher
, cv2.Stitcher_create
, y .stitch
, vamos a pasar a implementar realmente la costura de imágenes con OpenCV y Python.
Implementación de la costura de imágenes con Python
¡Comencemos a implementar nuestro algoritmo de costura de imágenes!
Abramos el archivo image_stitching_simple.py
e insertamos el siguiente código:
# import the necessary packagesfrom imutils import pathsimport numpy as npimport argparseimport imutilsimport cv2# construct the argument parser and parse the argumentsap = argparse.ArgumentParser()ap.add_argument("-i", "--images", type=str, required=True,help="path to input directory of images to stitch")ap.add_argument("-o", "--output", type=str, required=True,help="path to the output image")args = vars(ap.parse_args())
Nuestros paquetes necesarios se importan en las líneas 2-6. En particular, usaremos OpenCV e imutils. Si aún no lo has hecho, sigue adelante e instálalos:
- Para instalar OpenCV, sólo tienes que seguir una de mis guías de instalación de OpenCV.
- El paquete imutils se puede instalar/actualizar con pip:
pip install --upgrade imutils
. Asegúrese de actualizarlo ya que a menudo se añaden nuevas características.
A partir de ahí vamos a analizar dos argumentos de línea de comandos en las líneas 9-14:
-
--images
: La ruta del directorio de imágenes de entrada para coser. -
--output
: La ruta a la imagen de salida donde se guardará el resultado.
Si no está familiarizado con los conceptos de argparse
y los argumentos de la línea de comandos entonces lea esta entrada del blog.
Carguemos nuestras imágenes de entrada:
# grab the paths to the input images and initialize our images listprint(" loading images...")imagePaths = sorted(list(paths.list_images(args)))images = # loop over the image paths, load each one, and add them to our# images to stitch listfor imagePath in imagePaths:image = cv2.imread(imagePath)images.append(image)
Aquí cogemos nuestro imagePaths
(Línea 18).
Después, para cada imagePath
, cargaremos el image
y lo añadiremos a la lista images
(Líneas 19-25).
Ahora que los images
están en la memoria, vamos a seguir adelante y unirlos en un panorama utilizando la capacidad incorporada de OpenCV:
# initialize OpenCV's image stitcher object and then perform the image# stitchingprint(" stitching images...")stitcher = cv2.createStitcher() if imutils.is_cv3() else cv2.Stitcher_create()(status, stitched) = stitcher.stitch(images)
El objeto stitcher
se crea en la línea 30. Observe que dependiendo de si está utilizando OpenCV 3 o 4, se llama a un constructor diferente.
Posteriormente, podemos pasar nuestro images
al método .stitch
(Línea 31). La llamada a .stitch
devuelve tanto un status
como nuestra imagen stitched
(asumiendo que el cosido fue exitoso).
Por último, (1) escribiremos la imagen cosida en el disco y (2) la mostraremos en la pantalla:
# if the status is '0', then OpenCV successfully performed image# stitchingif status == 0:# write the output stitched image to diskcv2.imwrite(args, stitched)# display the output stitched image to our screencv2.imshow("Stitched", stitched)cv2.waitKey(0)# otherwise the stitching failed, likely due to not enough keypoints)# being detectedelse:print(" image stitching failed ({})".format(status))
Suponiendo que nuestra bandera status
indica éxito (Línea 35), escribimos la imagen stitched
en el disco (Línea 37) y la mostramos hasta que se pulse una tecla (Líneas 40 y 41).
De lo contrario, simplemente imprimiremos un mensaje de fallo (Líneas 45 y 46).
Resultados básicos de la costura de imágenes
Para probar nuestro script de costura de imágenes, asegúrese de utilizar la sección «Descargas» del tutorial para descargar el código fuente y las imágenes de ejemplo.
Dentro del directorio images/scottsdale/
encontrarás tres fotos que tomé cuando visité la famosa casa Taliesin West de Frank Lloyd Wright en Scottsdale, AZ:
Nuestro objetivo es coser estas tres imágenes en una sola imagen panorámica. Para realizar el stitching, abre un terminal, navega hasta donde descargaste el código + las imágenes, y ejecuta el siguiente comando:
$ python image_stitching_simple.py --images images/scottsdale --output output.png loading images... stitching images...
¡Nota cómo hemos realizado con éxito el stitching de la imagen!
¿Pero qué pasa con esas regiones negras que rodean el panorama? ¿Qué son?
Estas regiones se deben a la realización de las deformaciones de perspectiva necesarias para construir el panorama.
Hay una manera de deshacerse de ellas… pero necesitaremos implementar alguna lógica adicional en la siguiente sección.
Un mejor cosido de imágenes con OpenCV y Python
Nuestro primer script de cosido de imágenes fue un buen comienzo, pero esas regiones negras que rodean el panorama en sí no son algo que podríamos llamar «estéticamente agradable».
Y más aún, no verías una imagen de salida de las aplicaciones populares de cosido de imágenes integradas en iOS, Android, etc.
Por lo tanto, vamos a hackear nuestro script un poco e incluir alguna lógica adicional para crear panoramas más estéticos.
Voy a reiterar de nuevo que este método es un hack.
Vamos a revisar las operaciones básicas de procesamiento de imágenes, incluyendo el umbral, la extracción de contornos, las operaciones morfológicas, etc. con el fin de obtener nuestro resultado deseado.
Hasta donde yo sé, los bindings de Python de OpenCV no nos proporcionan la información necesaria para extraer manualmente la región rectangular interior máxima del panorama. Si OpenCV lo hace, por favor hágamelo saber en los comentarios ya que me encantaría saberlo.
Vamos a seguir adelante y empezar – abrir el script image_stitching.py
e insertar el siguiente código:
# import the necessary packagesfrom imutils import pathsimport numpy as npimport argparseimport imutilsimport cv2# construct the argument parser and parse the argumentsap = argparse.ArgumentParser()ap.add_argument("-i", "--images", type=str, required=True,help="path to input directory of images to stitch")ap.add_argument("-o", "--output", type=str, required=True,help="path to the output image")ap.add_argument("-c", "--crop", type=int, default=0,help="whether to crop out largest rectangular region")args = vars(ap.parse_args())# grab the paths to the input images and initialize our images listprint(" loading images...")imagePaths = sorted(list(paths.list_images(args)))images = # loop over the image paths, load each one, and add them to our# images to stich listfor imagePath in imagePaths:image = cv2.imread(imagePath)images.append(image)# initialize OpenCV's image sticher object and then perform the image# stitchingprint(" stitching images...")stitcher = cv2.createStitcher() if imutils.is_cv3() else cv2.Stitcher_create()(status, stitched) = stitcher.stitch(images)
Todo este código es idéntico a nuestro script anterior con una excepción.
Se ha añadido el argumento de línea de comandos --crop
. Cuando se proporciona un 1
para este argumento en el terminal, vamos a seguir adelante y realizar nuestro hack de recorte.
El siguiente paso es donde empezamos a implementar la funcionalidad adicional:
# if the status is '0', then OpenCV successfully performed image# stitchingif status == 0:# check to see if we supposed to crop out the largest rectangular# region from the stitched imageif args > 0:# create a 10 pixel border surrounding the stitched imageprint(" cropping...")stitched = cv2.copyMakeBorder(stitched, 10, 10, 10, 10,cv2.BORDER_CONSTANT, (0, 0, 0))# convert the stitched image to grayscale and threshold it# such that all pixels greater than zero are set to 255# (foreground) while all others remain 0 (background)gray = cv2.cvtColor(stitched, cv2.COLOR_BGR2GRAY)thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY)
Nota cómo he hecho un nuevo bloque para cuando la bandera --crop
se establece en la línea 40. Comencemos a recorrer este bloque:
- Primero, añadiremos un borde de
10
píxeles a todos los lados de nuestra imagenstitched
(Líneas 43 y 44), asegurando que podremos encontrar los contornos del contorno completo del panorama más adelante en esta sección. - Entonces vamos a crear una versión
gray
de nuestra imagenstitched
(Línea 49). - Y a partir de ahí umbralizamos la imagen
gray
(Línea 50).
Aquí está el resultado (thresh
) de esos tres pasos:
Ahora tenemos una imagen binaria de nuestro panorama donde los píxeles blancos (255) son el primer plano y los píxeles negros (0) son el fondo.
Dada nuestra imagen umbralizada podemos aplicar la extracción de contornos, calcular el cuadro delimitador del contorno más grande (es decir, (es decir, el contorno del propio panorama), y dibujar el cuadro delimitador:
# find all external contours in the threshold image then find# the *largest* contour which will be the contour/outline of# the stitched imagecnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)cnts = imutils.grab_contours(cnts)c = max(cnts, key=cv2.contourArea)# allocate memory for the mask which will contain the# rectangular bounding box of the stitched image regionmask = np.zeros(thresh.shape, dtype="uint8")(x, y, w, h) = cv2.boundingRect(c)cv2.rectangle(mask, (x, y), (x + w, y + h), 255, -1)
Los contornos se extraen y analizan en las líneas 55-57. La línea 58 toma el contorno con el área más grande (es decir, el contorno de la propia imagen cosida).
Nota: La función imutils.grab_contours
es nueva en imutils==0.5.2
para acomodar OpenCV 2.4, OpenCV 3 y OpenCV 4 y sus diferentes firmas de retorno para cv2.findContours
.
La línea 62 asigna memoria para nuestra nueva máscara rectangular. La línea 63 calcula el rectángulo delimitador de nuestro contorno más grande. Utilizando la información del rectángulo delimitador, en la línea 64, dibujamos un rectángulo blanco sólido en la máscara.
El resultado del bloque de código anterior sería el siguiente:
Esta caja delimitadora es la región rectangular más pequeña en la que puede caber todo el panorama.
Ahora viene uno de los mayores hacks que he montado para una entrada del blog:
# create two copies of the mask: one to serve as our actual# minimum rectangular region and another to serve as a counter# for how many pixels need to be removed to form the minimum# rectangular regionminRect = mask.copy()sub = mask.copy()# keep looping until there are no non-zero pixels left in the# subtracted imagewhile cv2.countNonZero(sub) > 0:# erode the minimum rectangular mask and then subtract# the thresholded image from the minimum rectangular mask# so we can count if there are any non-zero pixels leftminRect = cv2.erode(minRect, None)sub = cv2.subtract(minRect, thresh)
En las líneas 70 y 71 creamos dos copias de nuestra imagen mask
:
- La primera máscara,
minMask
, se irá reduciendo poco a poco de tamaño hasta que pueda caber dentro de la parte interior de la panorámica (ver Figura 5 al principio de esta sección). - La segunda máscara,
sub
, se utilizará para determinar si necesitamos seguir reduciendo el tamaño deminMask
.
La línea 75 inicia un bucle while
que continuará en bucle hasta que no haya más píxeles de primer plano en sub
.
La línea 79 realiza una operación morfológica de erosión para reducir el tamaño de minRect
.
La línea 80 entonces resta thresh
de minRect
– una vez que no hay más píxeles de primer plano en minRect
entonces podemos romper el bucle.
A continuación he incluido una animación del hack:
En la parte superior, tenemos nuestra imagen sub
y en la parte inferior tenemos la imagen minRect
.
Nota cómo el tamaño de minRect
se reduce progresivamente hasta que no quedan más píxeles de primer plano en sub
– en este punto sabemos que hemos encontrado la máscara rectangular más pequeña que puede caber en la región rectangular más grande del panorama.
Dado el mínimo rectángulo interior podemos volver a encontrar contornos y calcular el cuadro delimitador, pero esta vez simplemente extraeremos el ROI de la imagen stitched
:
# find contours in the minimum rectangular mask and then# extract the bounding box (x, y)-coordinatescnts = cv2.findContours(minRect.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)cnts = imutils.grab_contours(cnts)c = max(cnts, key=cv2.contourArea)(x, y, w, h) = cv2.boundingRect(c)# use the bounding box coordinates to extract the our final# stitched imagestitched = stitched
Aquí tenemos:
- Contornos encontrados en
minRect
(Líneas 84 y 85). - Manejo de contornos de análisis para múltiples versiones de OpenCV (Línea 86). Necesitarás
imutils>=0.5.2
para usar esta función. - Grabó el contorno más grande (Línea 87).
- Calculó la caja delimitadora del contorno más grande (Línea 88).
- Extrajo el ROI de nuestro
stitched
usando la información de la caja delimitadora (Línea 92).
La imagen final de stitched
puede ser mostrada en nuestra pantalla y luego guardada en el disco:
# write the output stitched image to diskcv2.imwrite(args, stitched)# display the output stitched image to our screencv2.imshow("Stitched", stitched)cv2.waitKey(0)# otherwise the stitching failed, likely due to not enough keypoints)# being detectedelse:print(" image stitching failed ({})".format(status))
Las líneas 95-99 se encargan de guardar y mostrar la imagen independientemente de si se realiza o no nuestro hack de recorte.
Al igual que antes, si la bandera status
no regresó como un éxito, vamos a imprimir un mensaje de error (Líneas 103 y 104).
Vamos a comprobar los resultados de nuestro pipeline mejorado de stitching de imágenes + OpenCV.
Resultados del stitching de imágenes mejorado
De nuevo, asegúrate de haber utilizado la sección de «Descargas» del tutorial de hoy para descargar el código fuente y las imágenes de ejemplo.
Desde ahí, abre un terminal y ejecuta el siguiente comando:
$ python image_stitching.py --images images/scottsdale --output output.png \--crop 1 loading images... stitching images... cropping...
Nota cómo esta vez hemos eliminado las regiones negras de las imágenes cosidas de salida (causadas por las transformaciones de deformación) aplicando nuestro hack detallado en la sección anterior.
Limitaciones e inconvenientes
En un tutorial anterior, demostré cómo se podía construir un panorama en tiempo real y un algoritmo de cosido de imágenes – este tutorial dependía del hecho de que estábamos realizando manualmente la detección de puntos clave, la extracción de características y la coincidencia de puntos clave, dándonos acceso a la matriz de homografía utilizada para deformar nuestras dos imágenes de entrada en un panorama.
Y aunque las funciones incorporadas de OpenCV cv2.createStitcher
y cv2.Stitcher_create
son ciertamente capaces de construir panoramas precisos y estéticamente agradables, uno de los principales inconvenientes del método es que abstrae cualquier acceso a las matrices de homografía.
Una de las suposiciones de la construcción de panoramas en tiempo real es que la escena en sí no está cambiando mucho en términos de contenido.
Una vez que calculamos la estimación inicial de la homografía sólo deberíamos tener que volver a calcular la matriz ocasionalmente.
No tener que realizar una coincidencia de puntos clave completa y una estimación RANSAC nos da un tremendo impulso de velocidad al construir nuestro panorama, por lo que sin acceso a las matrices de homografía en bruto, sería un reto tomar el algoritmo de costura de imágenes incorporado de OpenCV y convertirlo en tiempo real.
¿Te encuentras con errores al realizar el stitching de imágenes usando OpenCV?
Es posible que te encuentres con errores al intentar usar la función cv2.createStitcher
o las funciones cv2.Stitcher_create
.
Los dos errores «fáciles de resolver» que veo que la gente encuentra es olvidar qué versión de OpenCV están usando.
Por ejemplo, si estás usando OpenCV 4 pero intentas llamar a cv2.createSticher
te encontrarás con el siguiente mensaje de error:
>>> cv2.createStitcherTraceback (most recent call last): File "<stdin>", line 1, in <module>AttributeError: module 'cv2' has no attribute 'createStitcher'
En su lugar deberías usar la función cv2.Stitcher_create
.
De forma similar, si estás usando OpenCV 3 e intentas llamar a cv2.Sticher_create
recibirás este error:
>>> cv2.Stitcher_createTraceback (most recent call last): File "<stdin>", line 1, in <module>AttributeError: module 'cv2' has no attribute 'Stitcher_create'
En su lugar, usa la función cv2.createSticher
.
Si no estás seguro de qué versión de OpenCV estás usando puedes comprobarlo usando cv2.__version__
:
>>> cv2.__version__'4.0.0'
Aquí puedes ver que estoy usando OpenCV 4.0.0.
Puedes realizar la misma comprobación en tu sistema.
El último error que te puedes encontrar, y posiblemente el más común, está relacionado con que OpenCV (1) no tiene soporte para contrib y (2) está compilado sin la opción OPENCV_ENABLE_NONFREE=ON
activada.
Para resolver este error debe tener los módulos opencv_contrib
instalados junto con la opción OPENCV_ENABLE_NONFREE
establecida en ON
.
Si se encuentra con un error relacionado con los módulos no libres y contrib de OpenCV, asegúrese de consultar mis guías de instalación de OpenCV para asegurarse de que tiene la instalación completa de OpenCV.
Nota: Por favor, ten en cuenta que no puedo ayudar a depurar tu propia instalación de OpenCV si no has seguido una de mis guías de instalación, así que asegúrate de que estás usando mis guías de instalación de OpenCV cuando configures tu sistema.
Resumen
En el tutorial de hoy has aprendido a realizar la costura de múltiples imágenes usando OpenCV y Python.
Usando tanto OpenCV como Python fuimos capaces de coser múltiples imágenes juntas y crear imágenes panorámicas.
Nuestras imágenes panorámicas de salida no sólo fueron precisas en su colocación de costura, sino también estéticamente agradables.
Sin embargo, uno de los mayores inconvenientes de utilizar la clase de unión de imágenes incorporada de OpenCV es que abstrae gran parte del cálculo interno, incluyendo las propias matrices de homografía resultantes.
Si está tratando de realizar la unión de imágenes en tiempo real, como hicimos en un post anterior, puede encontrar beneficioso almacenar en caché la matriz de homografía y sólo ocasionalmente realizar la detección de puntos clave, la extracción de características y la coincidencia de características.
Omitir estos pasos y utilizar la matriz en caché para realizar la deformación de la perspectiva puede reducir la carga computacional de su tubería y, en última instancia, acelerar el algoritmo de costura de imágenes en tiempo real, pero, por desgracia, los enlaces cv2.createStitcher
Python de OpenCV no nos proporcionan acceso a las matrices en bruto.
Si estás interesado en aprender más sobre la construcción de panoramas en tiempo real, por favor, consulta mi post anterior.
Espero que hayas disfrutado del tutorial de hoy sobre la costura de imágenes!
Para descargar el código fuente del post de hoy, y ser notificado de los tutoriales que se publican aquí en PyImageSearch, sólo tienes que introducir tu dirección de correo electrónico en el siguiente formulario!