domingo, 2 de enero de 2011

Mezclando dos imágenes en una con OpenCV

No existe forma directa de mostrar dos imágenes dentro de una misma ventana usando las funciones ofrecidas por OpenCV, pero podemos hacer uso de la región de interés (ROI - Region Of Interest) para conseguirlo.

Con las regiones de interés podemos definir a qué parte de una imagen destino queremos "pegar" el contenido de una imagen fuente. De esta manera conseguimos "componer" la imagen destino deseada a partir de todas las imágenes fuentes necesarias.

En el siguiente ejemplo obtendremos un fotograma del dispositivo de captura y lo mostraremos a tamaño completo con una versión en miniatura del mismo fotograma en su interior.

Una cosa muy importante a tener cuando trabajamos con fotogramas capturados usando OpenCV es que no debemos modificar la imagen entregada por cvQueryFrame ni podremos reutilizar la imagen obtenida por sucesivos fotogramas capturados (explicaré mejor esto último en sucesivos ejemplos).

Por este motivo la composición se hará en una imagen clonada a partir del fotograma capturado usando la función cvClone. Tras ello definiremos la región de interés en dicha imagen clonada (el área de destino donde "pegaremos" de nuevo la imagen) y usaremos de nuevo la función cvResize para copiar una versión escalada del fotograma dentro de la región deseada.

Es importante recordar que la función cvClone aloja la memoria necesaria para almacenar tanto la cabecera de la imagen como su contenido, de modo que llamadas sucesivas a dicha función sin liberar los recursos previamente provocará que nuestra aplicación esté continuamente aumentando su consumo de memoria, de modo que no debemos olvidarnos de liberar los recursos reservados mediante una llamada a la función cvReleaseImage.

Código fuente del ejemplo:

Explicación en detalle paso por paso:
  1. Obtenemos un fotograma del dispositivo de captura en "fotograma".
  2. Creamos una copia del fotograma en "copia".
  3. Seleccionamos una zona de la imagen "copia" sobre la que volcaremos una versión escalada (una miniatura) del mismo fotograma.
  4. Realizamos el escalado de imagen sobre esa zona de interés.
  5. Deshacemos la selección de zona para que la función cvShowImage no nos muestre únicamente la región de interés (nos aparecería únicamente la zona recién escalada), si no la composición completa (el fotograma en grande junto con la miniatura.
  6. Tras mostrar la composición realizada en "copia" liberamos la memoria usada.

El resultado obtenido es:
Captura de imagen con OpenCV

3 comentarios:

  1. Hola, ¿sabes como podria escribir esta instruccion en java??
    72 cvSetImageROI(copia, cvRect(fotograma->width - (ancho * 1.1) ,
    73 fotograma->height - (alto * 1.1), ancho, alto));

    ResponderEliminar
    Respuestas
    1. Se han producido muchos cambios en OpenCV desde que escribí este código. Ahora se usan clases que representan matrices "Mat" en vez de la estructura de datos IplImage, también he de decirte que he pasado de C a C++ puro prácticamente todo el código que hago en la actualidad, por lo que datos de áreas rectangulares que en C eran estructuras cvRect ahora son cv::Rect, etc... eso acerca más el código de C++ al código necesario en Java de lo que podría acercarlo C plano.

      La traducción directa del viejo cvSetImageROI debería ser adjustROI de Mat:
      http://docs.opencv.org/java/2.4.2/org/opencv/core/Mat.html#adjustROI(int, int, int, int)

      cvRect puedes cambiarlo por:
      Rect roi = new Rect(fotograma.cols() - (ancho * 1.1), fotograma.rows() - (alto * 1.1), ancho, alto);

      Aunque adjustROI no admite como parámetro un Rect, por lo que eso último no te serviría de nada.

      Tendría que darle una pasada nueva al código porque la forma en la que se hacen las cosas en versiones más modernas de OpenCV son diferentes. Siento no poder dedicarte más tiempo. Cuando pueda trataré de traducir todo el código que tengo de mi blog a C++, pero no puedo garantizarte plazos :(

      ¡Saludos y gracias por comentar!

      Eliminar
    2. hola oscar quiesiera hacerte una consulta como en para poder de una imagen sacar cierta parte de esa imagen y sacarla como una nueva

      Eliminar