22/4/08

Reduce y Venceras !

Parece que he conseguido solucionar el problema de ralentización que tenia al usar el algoritmo de actualización de background.

La solución a resultado ser muy simple, por cada imagen que capturo del vídeo o de la webcam me creo una copia bastante más pequeña para trabajar con ella, una vez detectados los objetos como siempre pero usando la imagen pequeña, interpolo con el mismo factor que use para reducir las imágenes para obtener la posición de dichos objetos en la imagen grande y los muestro.

4/4/08

Esto empieza a coger color.

Una vez hecha la estructura para guardar los objetos, que me ha llevado más tiempo de lo esperado, como lo prometido es deuda, aquí está el vídeo que muestra los resultados:



Como se puede ver detecta las partes de la imagen que se quedan quietas un tiempo (y alguna cosilla que no :-( ). En cuanto cambie unas cuantas cosas del código que no me acaban de gustar mis siguiente tarea será diferenciar cuales de estas zonas que detecto siguen en la imagen con el paso de los frames.

Además como me sugirieron los profesores he aplicado un filtro de gauss para solucionar el problema de los objetos que se mostraban no-conexos, para esto además he bajado un poco el umbral para la umbralización. Aquí muestro los resultados del cambio.

antes:



después:



por último en el siguiente enlace que me ha pasado Gillermo(un alumno de la superior que hizo el pfc de sistemas con GAVAB) podéis ver unos vídeos de un italiano que usa el filtro de partículas para seguimiento con diferentes técnicas.

http://es.youtube.com/user/brizio73

5/3/08

Detección de objetos que se quedan quietos(II).

Ya he conseguido hacer funcionar más o menos bien el algoritmo que pensé para obtener la posición de los objetos que se quedan quietos, aunque no estaba del todo seguro porque me parecía un poco "pesado" dichos cálculos no han supuesto una ralentización apreciable ya que solo ejecuto esta parte cada vez que actualizo el fondo (cosa que hago cada cierto número de frames por ser esto si terriblemente pesado y por no ser necesario continuamente).

El algoritmo consiste en lo siguiente:
Voy recorriendo la imagen pixel a pixel, si es blanco lanzo un algoritmo recursivo que comprueba los pixels adyacentes(arriba, abajo, izquierda y derecha) y si son blancos se llama a si mismo para cada uno de los puntos blancos guardando en una lista dichos pixels y obteniendo de esta posteriormente el máximo y mínimo de la x y la y que delimitaran al objeto.

Una vez probado que funciona el algoritmo me dispongo a hacer ciertas mejoras ya que si un objeto no es conexo es detectado como varios, esto tengo pensado solucionarlo de dos formas. Una rebajando el valor del umbralizado, esto producirá más ruido en la imagen (pixels blancos sueltos) pero esto se soluciona desechando los objetos menores de cierto tamaño.
La otra forma es fusionar los objetos detectados con pocos pixels de separación.

Por otra parte he pensado en, en vez de ir pixel a pixel, ir de dos en dos o tres en tres, ya que, esto hará que sea más rápido pero no afectara al resultado ya que de esta forma como mucho se pierden pixels sueltos que no me interesa recoger.

En cuanto haga esto y meta los objetos detectados en una estructura para poder manejarlos, ya que de momento solo los dibujo según los voy detectando, colgare un vídeo en el que se pueda ver el resultado, porque como lo tengo ahora no se aprecia muy bien.

Mientras tanto os dejo una noticia relacionada con la visión artificial en los videojuegos.
link.

29/2/08

Detección de objetos que se quedan quietos.

Bueno, tras los exámenes he retomado el proyecto con como primer objetivo una vez conseguido la percepción de los objetos que se quedan quietos (vamos, conseguir una imagen binaria aislándolos) de detectarlos, esto es a partir de la imagen binaria obtener su posición.

Con esto me he atascado un poco porque se me ocurrían varias formas de hacerlo, pero no acababa de ver cual era la más apropiada para el problema, pero finalmente me decidí por una, aunque no estoy muy seguro de que sea la mejor opción no obstante sigo trabajando en esto pero aún no he obtenido resultados reseñables.

Mientras me pego un poco con las clases de c++ dejo el vídeo de GAVAB para que lo vea quien no lo halla visto (atención a la música, solo faltan Juanjo y Antonio corriendo a cámara lenta a lo David Jaseljof...jejeje).dddd






14/1/08

Percepción de objetos que se quedan quietos.

4 y 8 de Enero de 2008.

He comenzado a crear una clase historial para el fondo en la que en principio guardo un buffer de imágenes del fondo donde la ultima es la última tomada al actualizar el fondo y las anteriores corresponden a actualizaciones previas.
Además con cada actualización almaceno una imagen obtenida restando la primera y la última del buffer y umbralizada con un valor umbral relativamente alto para evitar que aparezcan los pequeños cambios. Con esto consigo que aparezcan en blanco solamente los objetos que se quedan parados y sigo detectándolos durante n-1 actualizaciones (siendo n el tamaño del buffer). Aquí dejo un vídeo que muestra el resultado:



La imagen de la derecha va mostrando la imagen obtenida como he explicado anteriormente.


Este método me permite cuanto mayor sea el buffer poder distinguir mejor los objetos que se quedan quietos, pero al aumentar el buffer también aumenta la memoria utilizada y se tarda más en detectar los cambios.
Tras esto me he propuesto dar más funcionalidad a esta clase haciendo que en cada actualización detecte los objetos que aparezcan y guarde en algún tipo de registro las posiciones de los objetos detectados, teniendo en cuenta que si un objeto no permanece en blanco en la imagen n-1 actualizaciones es que se ha movido (el número de actualizaciones que considerare para este caso probablemente sera menor de n-1 para evitar que algún tipo de interferencia provoque que se deje de detectar un objeto).

Curiosidades.

.

Os dejo el enlace de una noticia que he visto en un blog y me ha parecido interesante sobre un nuevo uso que le van a dar en PCs al procesador Cell que usa la PlayStation 3. Además trae un vídeo en el que usan un interfaz tipo "minority report".

Pulsad la imagen para verlo.


2/1/08

Nuevo enfoque para detectar los objetos en movimiento.

Tras estar haber utilizado las funciones para actualización de fondos se me ha ocurrido cambiar la forma en la que obtenía las imágenes en binario con los objetos que se movían en la imagen en blanco y el resto en negro.

En vez de restas sucesivas con la imagen anterior, he aprovechado el echo de estar usando actualización de fondo para ir restando en cada iteración la imagen actual con el fondo. Esto me genera una serie de ventajas respecto a la implementación anterior, principalmente que de esta forma puedo obtener figuras sólidas y no siluetas como anteriormente y además las figuras siguen siendo detectadas cuando se quedan quietas hasta que no se integran con el fondo.

Como única desventaja he podido apreciar que esta técnica introduce algo de ruido, pero las ventajas me hacen pensar que esta forma me será mucho más útil a la hora de hacer el seguimiento y el ruido que surge es casi despreciable.

En el siguiente video se puede apreciar el resultado:


Tras esto me voy a dedicar a crear algún tipo de historial del fondo para detectar cuando se producen cambios, pero antes voy a rehacer mi código ya que debido a que he ido haciendo muchas pruebas con varias cosas al final ha quedado muy desordenado y complicado de depurar.

10/12/07

Detección de movimiento y actualización de fondos

Bueno, tras unos cuantos días que he estado malillo, por fin actualizo el blog con las cosas que hice antes y ahora:

2, 3 y 4 de diciembre.

En esta ocasión a las funciones de detección de movimiento descritas en la entrada anterior, he añadido la función
CvSeq* cvSegmentMotion( const CvArr* mhi, CvArr* seg_mask, CvMemStorage* storage,
double timestamp, double seg_thresh );

que a partir del historial creado con las funciones anteriores crea una estructura que almacena una secuencia de zonas de la imagen que se están moviendo en ese momento. Con esto he vuelto a aplicar la función que me da la dirección del movimiento pero en esta ocasión una vez por cada elemento de la secuencia y los he marcado en la imagen con un circulo y una línea indicando la dirección del movimiento como se puede ver en este vídeo:



9 y 10 de diciembre.

Por otra parte he cogido el ejemplo de actualización de fondos que probé anteriormente y lo he aplicado a mi código utilizando las funciones cvCreateFGDStatModel y cvUpdateBGStatModel de las cuales no he encontrado documentación alguna en los manuales ni en la wiki, parece como si no existieran, pero que compilan y funcionan.

Al actualizar el fondo con este método he podido comprobar como la velocidad del programa se hace insostenible, ya que tarda muchísimo en actualizarse el fondo, pero por otra parte los resultados obtenidos me han parecido muy interesantes para mis propósitos por lo que he decidido intentar hacer un "apaño".

Se me ha ocurrido que al actualizar el fondo no es tan vital que se haga continuamente, por lo que he decidido actualizarlo cada cierto número de frames para evitar que se ralentice el proceso en exceso. De esta forma he conseguido unos resultados mejores de los previstos, ya que se ha reducido el ruido que capta con el paso de la gente en el vídeo de la estación. Haciéndolo cada 50 frames si que me aparecían figuras oscuras con el paso de algunas personas, pero haciéndolo cada 100 frames prácticamente solo se aprecian cambios en la imagen cuando alguien se queda un rato en la misma posición y cuando se abandona un objeto.

Este es el resultado:


En el vídeo, aunque se ve un poco pequeño y mal se puede ver como solo cambia la imagen de la derecha(que es el background) cuando se queda quieto el chico de la mochila y cuando deja la mochila. Pero en el caso del chico como no se queda completamente quieto mucho tiempo no deja un rastro completamente sólido, al contrario que ocurre con la mochila.

Tras esto creo que ya tengo todas las herramientas para empezar a implementar un filtro de partículas.

26/11/07

Primeros pasos con OpenCV (8)

22, 23, 25 y 26 de Noviembre.

Utilizando las funciones mencionadas anteriormente para convertir imágenes a escala de grises, hacer la resta de dos y pasar a binario, las he combinado en ese orden para obtener imágenes en las que solo aparece lo que va cambiando de las imágenes en blanco con fondo negro para usarlas en el filtro de partículas que debo implementar.

Antes de nada con esto me he dedicado a investigar las funciones de detección de movimiento de que dispone opencv. Con estas funciones he modificado mi código para que detecte la direccionalidad del movimiento en una zona concreta.

Para esto he utilizado las funciones :

void cvUpdateMotionHistory( const CvArr* silhouette, CvArr* mhi,
double timestamp, double duration );
void cvCalcMotionGradient( const CvArr* mhi, CvArr* mask, CvArr* orientation,
double delta1, double delta2, int aperture_size=3 );
double cvCalcGlobalOrientation( const CvArr* orientation, const CvArr* mask, const CvArr* mhi,
double timestamp, double duration );
Con estas funciones y utilizando región de interés para limitar los resultados a una zona en concreto he calculado la dirección en la que se producen los movimientos en una zona y lo he mostrado mediante una linea como se puede ver en el siguiente video:



En la parte derecha se puede observar el historial de movimiento que se va actualizando con cvUpdateMotionHistory, a la izquierda se puede observar la imagen que va marcando la orientación creada a partir del historial con cvCalcMotinGradient y abajo está la imagen en binario en la que he dibujado una línea indicando la dirección del movimiento dentro del recuadro con el angulo obtenido de cvCalcGlobalOrientation.

Una de las mayores dificultades que he tenido ha sido escoger los parámetros para pasarle a las funciones, ya que en los manuales no queda muy claro para que se usan algunos de ellos. Para resolver esto me ha sido de gran ayuda de un ejemplo que viene con opencv llamado motempl.cpp.

El otro gran problema que me ha surgido utilizando estas funciones es que creaba y liberaba en cada iteración las imágenes de historial y de orientación como venia haciendo con las otras imágenes usadas sin darme cuenta de que de esa forma no se actualizaba el historial, con lo que no obtenía resultados.

Una vez resueltos estos temas he utilizando las funciones para dibujar de opencv y repasando un poco de geometría básica he conseguido obtener correctamente la dirección del movimiento en una zona.

20/11/07

Primeros pasos con OpenCV (7)

19 de Noviembre.

Ahora me he dedicado a acceder a los píxeles individuales de una región de las imágenes para por ejemplo calcular el número de pixels blancos dentro de dicha región, cosa que será de utilidad para desarrollar el filtro de partículas entre otras cosas.

Entre las distintas formas disponibles de acceder individualmente a los pixels he escogido la siguiente:
IplImage* img  = cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,1);
int step = img->widthStep/sizeof(uchar);
uchar* data = (uchar *)img->imageData;
data[i*step+j] = 111;
Por lo que he leido esta es la forma más eficiente y sencilla de hacerlo, aunque también existe otra forma con C++ usando una clase que resulta aún más sencilla.

Además para probar esto sobre el ejemplo anterior le he añadido una "trackbar" a la ventana donde muestro las imágenes umbralizadas para poder variar el factor de umbralizado durante la ejecución y asi poder probar mejor el programa.

En el siguiente vídeo se puede ver el resultado:


El programa cuenta el número de pixels blancos que hay en la zona delimitada por el recuadro dibujado en la imagen y lo muestra por la consola con el número de pixels blancos del total del recuadro. Se puede apreciar como aumenta el número cuando hay más pixels blancos en el recuadro.