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.
5/3/08
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).
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.
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.
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
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.
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,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:
double timestamp, double seg_thresh );
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 :
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.
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,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:
double timestamp, double duration );
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:
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.
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);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.
int step = img->widthStep/sizeof(uchar);
uchar* data = (uchar *)img->imageData;
data[i*step+j] = 111;
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.
19/11/07
Primeros pasos con OpenCV (6)
16 y 17 de Noviembre.
Pese a modificar la forma de ir leyendo las imágenes el proceso sigue siendo mucho más lento que con un vídeo, por lo que me he decidido convertir las secuencias de imágenes en vídeos para trabajar con ellos de una forma más cómoda.
En un primer momento cree un programa utilizando la propia libreria OpenCV con este fin, pero los resultados no fueron los esperados, ya que el programa creaba un archivo con extensión avi pero con 0 bytes. El código básicamente hace lo siguiente:
writer=cvCreateVideoWriter( opciones);
for i=1 to numImagenes {
img=cvLoadImage(ruta);
cvWriteFrame(writer,img);
cvReleaseImage(&img);
}
cvReleaseVideoWriter(&writer);
No se porque no funciona, pero tras horas de intentarlo al final he utilizado un programa llamado VirtualDub que tiene licencia GNU y puede descargarse de www.virtualdub.org y finalmente he convertido las secuencias de imágenes en vídeos con facilidad.
Tras esto he probado los vídeos con mi código y he comprobado que funcionan bien y a una velocidad correcta.
También he aprovechado estos vídeos para probar el programa de ejemplo de actualización de fondos (background) y he obtenido unos resultados bastante interesantes que me pueden servir en el proyecto, pero la velocidad del algoritmo es bastante lenta y los vídeos no se muestran a una velocidad normal. Tengo que probar si esto se debe al resto de cosas que hace el programa o a la actualización de fondo en si.
Pese a modificar la forma de ir leyendo las imágenes el proceso sigue siendo mucho más lento que con un vídeo, por lo que me he decidido convertir las secuencias de imágenes en vídeos para trabajar con ellos de una forma más cómoda.
En un primer momento cree un programa utilizando la propia libreria OpenCV con este fin, pero los resultados no fueron los esperados, ya que el programa creaba un archivo con extensión avi pero con 0 bytes. El código básicamente hace lo siguiente:
writer=cvCreateVideoWriter( opciones);
for i=1 to numImagenes {
img=cvLoadImage(ruta);
cvWriteFrame(writer,img);
cvReleaseImage(&img);
}
cvReleaseVideoWriter(&writer);
No se porque no funciona, pero tras horas de intentarlo al final he utilizado un programa llamado VirtualDub que tiene licencia GNU y puede descargarse de www.virtualdub.org y finalmente he convertido las secuencias de imágenes en vídeos con facilidad.
Tras esto he probado los vídeos con mi código y he comprobado que funcionan bien y a una velocidad correcta.
También he aprovechado estos vídeos para probar el programa de ejemplo de actualización de fondos (background) y he obtenido unos resultados bastante interesantes que me pueden servir en el proyecto, pero la velocidad del algoritmo es bastante lenta y los vídeos no se muestran a una velocidad normal. Tengo que probar si esto se debe al resto de cosas que hace el programa o a la actualización de fondo en si.
14/11/07
Primeros pasos con OpenCV (5)
12 y 13 de Noviembre.
Siguiendo con el ejemplo anterior he hecho que mi programa de prueba valla obteniendo la resta de cada imagen en escala de grises con la imagen anterior del vídeo con la función cvSub y valla mostrando la imagen resultante, elresultado es el siguiente:

La en la parte de superior se pueden ver las imágenes a color y en escala de grises y en la inferior la imagen resultado de la resta y en binario(de izquierda a derecha). El resultado de la imagen resta no es muy bueno ya que no se ven muy bien los cambios.
Tras esto he modificado mi código para que en vez de leer los frames de un vídeo valla leyendo las imágenes obtenidas de http://www.cvg.rdg.ac.uk/PETS2006/data.html que contienen ejemplos de abandono de equipaje en una estación de tren.
No se si por que las imágenes son más grandes que las de los vídeos que he usado hasta ahora o porque de la forma que lo he implementado el programa va leyendo los nombres de las imágenes de un archivo de texto, pero si mantengo las cuatro ventanas anteriores las dos que quedan más atras van a "tirones", por tanto próximamente cambiare la forma de obtener los nombres de los archivos para ver si es lo que ralentiza el proceso.
Por otro lado me he dado cuenta de que estaba haciendo las restas del revés y al cambiarlo he obtenido una mejora en las imágenes resultado de esta. El resultado es el siguiente:

A la izquierda se puede ver la imagen resultado de la resta y a la derecha en escala de grises.
Siguiendo con el ejemplo anterior he hecho que mi programa de prueba valla obteniendo la resta de cada imagen en escala de grises con la imagen anterior del vídeo con la función cvSub y valla mostrando la imagen resultante, elresultado es el siguiente:
La en la parte de superior se pueden ver las imágenes a color y en escala de grises y en la inferior la imagen resultado de la resta y en binario(de izquierda a derecha). El resultado de la imagen resta no es muy bueno ya que no se ven muy bien los cambios.
Tras esto he modificado mi código para que en vez de leer los frames de un vídeo valla leyendo las imágenes obtenidas de http://www.cvg.rdg.ac.uk/PETS2006/data.html que contienen ejemplos de abandono de equipaje en una estación de tren.
No se si por que las imágenes son más grandes que las de los vídeos que he usado hasta ahora o porque de la forma que lo he implementado el programa va leyendo los nombres de las imágenes de un archivo de texto, pero si mantengo las cuatro ventanas anteriores las dos que quedan más atras van a "tirones", por tanto próximamente cambiare la forma de obtener los nombres de los archivos para ver si es lo que ralentiza el proceso.
Por otro lado me he dado cuenta de que estaba haciendo las restas del revés y al cambiarlo he obtenido una mejora en las imágenes resultado de esta. El resultado es el siguiente:
A la izquierda se puede ver la imagen resultado de la resta y a la derecha en escala de grises.
Suscribirse a:
Entradas (Atom)