lunes, 4 de mayo de 2015

Demos de funcionamiento

Aquí os dejo 2 vídeos de demo del funcionamiento del detector (imagen en negro = no hay puerta, imagen en rojo = hay puerta). El decisor tiene como umbral que si al menos 1/3 de los pixels de la imagen filtrada son 1, considera que hay puerta.




Explicación de los videos: las imágenes superiores son los canales H S y V de la imagen tomada en cada instante. Abajo a la izquierda está la imagen original que se obtiene de la cámara, en el centro abajo el filtro en el canal H y a la derecha el detector antes mencionado (rojo o negro, puerta o no puerta).

Más que la precisión del detector lo importante es ver como sin modificar los parámetros los resultados varían debido a los cambios de iluminación debido:
  1. Cambio de la imagen de rango visual a infrarrojo cuando disminuye la iluminación
  2. Cambio de las intensidades de la imagen por cambio del tiempo de obturación de la imagen
El primer caso se puede arreglar fijando si se va a trabajar en el visual o en el infrarrojo. Sin embargo el segundo problema afecta a medida que se van presentando objetos y escenas con mayor o menor cantidad de luz, lo que hace que la cámara cambie el tiempo de obturación y cambiando los valores de los pixels afectando al resultado del filtro.

domingo, 3 de mayo de 2015

Tasa de refresco

En las primeras pruebas al representar las imágenes con Python nos encontramos con el problema de que a pesar de que recibíamos varias imágenes tal y como le pedíamos a la cámara a través de Arduino, éstas eran durante varios ciclos del bucle la misma imágen. Aquí se puede ver un ejemplo tomando imágenes sin ninguna espera entre imágenes con un vídeo delante de la cámara.


Tras muchas pruebas se han llegado a las siguientes conclusiones.

  • La cámara es capaz de devolverte imágenes independientemente de el tiempo de espera entre cada imagen. Ahora bien, si no esperas entre cada toma de imagen un cierto tiempo, la cámara te devolverá una imagen, si, pero puede ser la misma imagen que la vez anterior ya que necesita un tiempo de refresco.
  • La cámara, aunque no hayamos esperado el tiempo suficiente para refrescar el frame, si le pedimos que tome otra foto y nos la mande nos responderá con las cabeceras correspondientes de que así se va a hacer, pero la imagen obtenida será la misma en varios ciclos. Esto quiere decir que la cámara ya está preparada para recibir nuevas peticiones, pero sin embargo el proceso de tomar una nueva imagen (en alguno de los pasos del proceso) no.
  • Es necesario realizar una espera entre cada toma de imagen para que la cámara se refresque, por lo que se ha probado con el código antes comentado, descomentando la línea que realiza una espera fija (delay(X);). Probando diferentes valores de X se ha llegado a la conclusión de que cuanto menor es la espera (X tiende a 0), mayor es el número de frames en los que la cámara envía la misma imagen. Sin embargo, si ponemos que X>=33 (milisegundos) los frames que obtenemos son siempre diferentes.

De las hipótesis manejadas la que más plausible nos ha parecido es pensar que esto se debe al software de la cámara y el comportamiento que establece. Se cree que en la primera imágen, puesto que la cámara aún no ha tomado ninguna imágen, hace una llamada bloqueante que no acaba hasta que ha terminado de tomar y procesar la imagen a JPEG, y obtenemos el primer frame. Sin embargo, para las peticiones sucesivas, se hacen 2 cosas, mandar orden de hacer foto y mandar orden de transmitir los datos. Al no haber ninguna espera entre estos 2 comandos y la cámara tener una imagen ya en memoria, al solicitarle datos del JPEG, la cámara nos devuelve la imagen que detecta que tiene en memoria (la del frame anterior) ya que aún no le ha dado tiempo a tomar el siguiente frame (necesita un tiempo para tomar la imagen de los sensores y hacer el procesado para pasar a JPEG). Esto ocurre durante varios ciclos, ya que el único momento que tiene la cámara para procesar la nueva imagen es el intervalo en el que arduino a recibido todos los datos y aún no le ha pedido la siguiente imagen (que son unos milisegundos en lo que tarda en volver al bucle). Durante ese periodo, continua el procesado del JPEG y cuando lo termina nos comienza a dar la nueva imágen.

Por todo lo anterior, la aproximación con un mejor rendimiento ha sido establecer una comunicación entre arduino y python para que el primero no pida una nueva imagen a la cámara hasta que el segundo no se lo diga. De esta forma podemos controlar la tasa de refresco con la espera que se utiliza para mostrar un frame en pantalla durante un tiempo (waitKey de OpenCV). De esta forma, hay que añadir al código de python y arduino la lógica bloqueante para la espera de intercambio de mensajes.

Lectores serial de Python


Los códigos de Python desarrollados se encuentran en bitbucket y son los siguientes:
  • serieport.py: muestra los bytes recibidos en varias de sus posibles representaciones (char, hexadecimal y decimal). Útil para hacerse una idea de lo que se está transmitiendo y con qué tipo.

  • imagereader.py: detecta la cabecera de comienzo y fin de un JPEG para almacenar la imagen en un fichero auxiliar llamado output.jpg. El fichero se va sobrescribiendo a medida que se van tomando nuevas imágenes.
  • imagesreader.py: similar al anterior pro guardamos las imágenes en una carpeta images con un índice para distinguirlos.
  • imagevisor: va mostrando las imágenes que detecta por el puerto serie en una ventana mediante OpenCV.
El código utilizado para estas pruebas es un bucle sin espera en el que vamos pidiendo imágenes a la cámara y transmitiendolas por el puerto serie al PC sin ninguna espera.


En este punto podemos ver como recibimos imágenes de la cámara y las representamos con OpenCV.
Sin embargo, se han encontrado problemas relacionados con la tasa de refresco de las imágenes, que se contará más adelante.