En esta entrada voy a describir dos métodos de seguir
visualmente objetos (tracking), y su adaptación al sistema operativo Android para
usar que en robot dirigido por un móvil. El ejemplo con el que contamos en OpenCV está basado en seguir colores y no da buenos resultados en cuanto la luz empieza a cambir o se mezcla con colores del fondo
Georg Nebehay es la
persona que ha adaptado uno de los métodos y desarrollado el otro, por lo que suyos
son los créditos de este trabajo.El primero de los métodos se denomina openTLD
que proviene e de Tracking-Learning-Detection (TLD) desarrollado por Zdenek Kalal.
TLD rastrea simultáneamente el objeto, aprende de su aspecto y lo detecta cada
vez que aparece en el video. El resultado es un seguimiento en tiempo real que
a menudo mejora con el tiempo.
El segundo es CMT, desarrollado por Nebehay. La idea principal detrás de CMT es
descomponer el objeto de interés en partes más pequeñas, conocidas como puntos
clave. Vemos cómo funciona:
En cada cuadro, trata
de encontrar de nuevo los puntos clave que ya estaban allí en la selección
inicial del objeto de interés. Esto se hace mediante el empleo de dos tipos
diferentes de métodos. En primer lugar, se hace un seguimiento de puntos
significativos de la trama anterior a la trama actual mediante la estimación de
lo que se conoce como su flujo óptico. En segundo lugar, emparejamos puntos
clave a nivel mundial mediante la comparación de sus descriptores. Como estos
dos métodos son propensos a errores, se emplea una nueva forma de buscar el
consenso en los puntos clave que se encuentran al permitir que cada voto punto
clave para el centro del objeto.
El resultado es un algoritmo que puede funcionar en
teléfonos Android de potencia de cálculo media alta.
Disponiendo de dos métodos para seguir objetos, el problema
está en cómo decir al robot cómo seguir un determinado objeto. La primera forma
puede ser el reconocimiento de una forma y después seguir a la forma
encontrada. El coste de seguir una forma
con OpenTLD o CMT es mucho menor que el reconocimiento continuo de la
forma en un vídeo. En este vídeo se ve cómo se reconoce una persona y se sigue
después sin tener que volver a buscar la forma de persona que es bastante
costoso
La segunda forma de explotar esta capacidad es identificar
un objeto en movimiento, restando el fondo a las imágenes y seguir al objeto
que se mueve con estos métodos.
Aquí se ve al pequeño robot siguiendo cualquier cosa que se
mueva.
Entre las funcionalidades que proporciona OpenCV está el reconocimiento de objetos en imágenes. En esta ocasión voy a publicar mi adaptación de esta funcionalidad que incluye un test para saber si hemos encontrado la imagen buscada, que creo que es interesante porque sin el no hay mucha seguridad de haber encontrado el objeto buscado.
Aquí el tutorial completo en
OpenCV http://docs.opencv.org/trunk/doc/py_tutorials/py_feature2d/py_matcher/py_matcher.html
Esta adaptación la he hecho sobre Android, y la he utilizado también en el robot controlado por teléfono Android.
La distancia a la que podemos reconocer una imagen se puede aumentar si mejoramos la calidad y definición de la imagen. En este vídeo apreciamos que se puede reconocer una imagen a más
Y aquí el robot buscando el dibujo del avión después de incorporarle este capacidad y la orden asociada para que sepa lo que tiene que buscar.
Como podéis ver he elegido para las pruebas dibujos esquemáticos como este que se reconocen especialmente bien con estos algoritmos.
Y aquí la clase que implementa el reconocimiento de dibujos. La imágenes se tienen que usar en el formato Mat, de OpenCV, y leer por ejemplo así: avionImg =Highgui.imread(path+"avion.jpg");
ImageMatcher() { /* * Constructor. Scans the images to match and pre calculates their * key points and features * * * */ // The path where the images to match are stored
mPath=Environment.getExternalStorageDirectory()+"/robot/objects/";
FilenameFilter pngFilter = new FilenameFilter() {
public boolean accept(File dir, String name) {
return (name.toLowerCase().endsWith(".png")||name.toLowerCase().endsWith(".jpg"));
};
};
File[] imageFiles = root.listFiles(pngFilter);
Mat img;
Mat descriptors1;
MatOfKeyPoint keypoints1;
// we calculate the images keypoints, and we store them
for (File image : imageFiles) { String p = image.getAbsolutePath();
img = Highgui.imread(p);
Mat imggray = new Mat();
Imgproc.cvtColor(img,imggray,Imgproc.COLOR_BGR2GRAY);
lObjects.add(imggray);
descriptors1= new Mat();
keypoints1 = new MatOfKeyPoint();
obj_corners.put(0, 0, new double[] {0,0}); obj_corners.put(1, 0, new double[] {img.cols(),0}); obj_corners.put(2, 0, new double[] {img.cols(),img.rows()}); obj_corners.put(3, 0, new double[] {0,img.rows()}); lObj_corners.add(obj_corners);
int i1=p.lastIndexOf("/")+1;
int i2=p.lastIndexOf(".");
Mat Match(Mat img2, boolean draw) {
/*
* Mathes img2 against the stores images.
* Returns null if there is no match, or an Mat contyaining an image with the
* key points matched
*
*
*/ long t1=System.currentTimeMillis(); FeatureDetector detector = FeatureDetector.create(FEATUREDETECTOR); DescriptorExtractor descriptor = DescriptorExtractor .create(DESCRIPTOREXTRACTOR); ; DescriptorMatcher matcher = DescriptorMatcher .create(DescriptorMatcher.BRUTEFORCE); Double max_dist = 0.0; Double min_dist = 100.0;
MatOfDMatch matches = new MatOfDMatch(); Mat descriptors2 = new Mat(); MatOfKeyPoint keypoints2 = new MatOfKeyPoint();
int n = -1; Iterator<Mat> iterator = lMatDescriptor.iterator(); Iterator<MatOfKeyPoint> iteratork = lKeypoints.iterator(); Iterator<Mat> iteratorCorners = lObj_corners.iterator(); Iterator<Mat> iteratorObjects = lObjects.iterator(); MatOfKeyPoint keypoints1; Mat obj_corners; while (iterator.hasNext()) { Mat img1 = iteratorObjects.next(); Mat descriptors1 = iterator.next(); keypoints1= iteratork.next(); obj_corners= iteratorCorners.next(); n++; if ((descriptors2!=null)&&(descriptors1.type() == descriptors2.type()) && ( descriptors1.cols() == descriptors2.cols())) { matcher.match(descriptors1, descriptors2, matches); } else continue; List<DMatch> matchesList = matches.toList(); for (int i = 0; i < descriptors1.rows(); i++) { Double dist = (double) matchesList.get(i).distance; if (dist < min_dist) min_dist = dist; if (dist > max_dist) max_dist = dist; }
System.out.println("-- Max dist : " + max_dist); System.out.println("-- Min dist : " + min_dist);
LinkedList<DMatch> good_matches = new LinkedList<DMatch>();
for (int i = 0; i < descriptors1.rows(); i++) { if (matchesList.get(i).distance < 3 * min_dist) { good_matches.addLast(matchesList.get(i)); } } ///// Calcular la homografía y su área LinkedList<Point> objList = new LinkedList<Point>(); LinkedList<Point> sceneList = new LinkedList<Point>();
MatOfPoint2f obj=new MatOfPoint2f();; MatOfPoint2f scene =new MatOfPoint2f(); obj.fromList(objList); scene.fromList(sceneList); if (good_matches.size()<10) continue; else { Mat hg = Calib3d.findHomography(obj,scene,8,10);
Mat scene_corners = new Mat(4,1,CvType.CV_32FC2);
Core.perspectiveTransform(obj_corners,scene_corners, hg); double d =Imgproc.contourArea(scene_corners); Point points[]=new Point[4]; for (int j=0;j<4;j++) { points[j]=new Point(scene_corners.get(j,0)); points[j].x+=img1.cols(); } // Test the matchig points. If they are convex, they make sense. boolean convex = isConvex(points); //////////// draw result. if ((d>10)&&(convex)) if (convex) {
mLastImage=lObject.get(n); if (!draw) return img2; Mat outputImg= new Mat(); Scalar RED = new Scalar(255,0,0); Scalar GREEN = new Scalar(0,255,0); MatOfByte drawnMatches = new MatOfByte(); MatOfDMatch matches_final_mat = new MatOfDMatch(); // matches_final_mat.fromList(good_matches); Features2d.drawMatches(img1, keypoints1, img2, keypoints2, matches_final_mat, outputImg, GREEN, RED, drawnMatches, 0);
//Core.putText(outputImg, ""+convex, new Point(40,400), 3,6, new Scalar(0, 0,255),2); //////////////////////////////////////////////// Core.line(outputImg, points[0],points[1], new Scalar(0, 0,255),14); Core.line(outputImg, points[1],points[2], new Scalar(0, 0,255),14); Core.line(outputImg, points[2],points[3], new Scalar(0, 0,255),14); Core.line(outputImg, points[3],points[0], new Scalar(0, 0,255),14); return outputImg; }
A continuación describo los elementos para construir una app en Android para reconocimiento de caras.
Algoritmos de reconocimiento de caras en Android con OpenCV
Para que nuestros robots puedan reconocernos deberíamos
tener a nuestra disposiciónalguna librería
que nos permitiera incorporar esta función.
En OpenCV existen tres algoritmos de reconocimiento de
caras:
-Eigenfaces
- Fisherfaces
- LBPH (Local Binary
Patterns Histograms)
Los dos primero Eigenfaces y Fisherfaces se basan en la
reducción de la dimensionalidad. En una imagen de 100x100 píxeles existen
10.000 píxeles o dimensiones si estamos hablando de un clasificador. Pero de
esas 10.000, ¿cúantas son de verdad necesarias para reconocer una imagen?. El análisis de componentes
principales nos ordena la importancia de estas dimensiones para que podamos
trabajar con muchas menos.
Caras reducidas a sus componentes principales.
LPBH en cambio no se basa en la comparación directa de
imágenes con dimensionalidad reducida. Se basa en la extracción de
características relevantes de cada imagen.
Para cada pixel se obtiene un histograma local. Los píxeles
de alrededor se traducen a 1 y 0 dependiendo de si tienen más intensidad o
menos que el pixel centra, y se asigna ese valor al píxel central.
La imagen se divide en una cantidad m de regiones y se
extrae un hostigrama de cada una. Estos histogramas luego se concatenan.
El problema es que estas funciones no están disponibles
directamente en la librería de OpenCV Android. Afortunadamente existe un
proyecto denominado JavaCV que pone a disposición de los desarrolladores en
Java para Android y otros sistemas todas las funciones de OpenCV, FFmpeg y
otras librerías-
La página del proyecto nos da indicaciones detalladas de
cómo incorporar la librería a un proyecto Android: https://code.google.com/p/javacv/
Implementación
Con la ayuda de OpenCV implementé una pequeña aplicación de
prueba. Esta aplicación está subida al play store y tiene disponible el
códifuente en gitHub.
La aplicación en el modo entrenamiento localiza caras en la
imagen mediante un clasificador en cascada haar. El rectángulo obtenido sirve
para entrenar al algoritmo o para que el algoritmo lo clasifique. Cuando se
clasifica una imagen se obtiene también un indicador de la “confianza” en la
clasificación.
Aunque la implementación realizada permite utilizar
cualquiera de los tres algoritmos incluidos, el que se ha elegido finalmente
para la app es LPBH, pues es el único que me ha dado un resultado aceptable en
condiciones reales.
Se necesitan al menos tener dos caras guardadas para que pueda empezar a reconocer
Modo Entrenar: Escribir el nombre de la persona, enfocar y cuando empiece a aparecer un recuadro localizando una cara pulsar el botón "Rec". Pulsar Rec varias veces para almacenar diferentes gestos. Para tener mejores resultados, hacer la grabación con iluminación adecuada.
Modo Buscar. Enfocar a una cara y si la reconoce aparecerá su nombre. Un icono aparecerá verde, amarillo o rojo dependiendo del grado de confianza en el reconocimiento.
Icorporación al robot:
jueves, 11 de julio de 2013
Robot controlado por teléfono Android.
Después de más de un año, el proyecto de Robot Personal va tomando forma y estos son los avances.
El objetivo del proyecto era desarrollar un pequeño robot controlado
por un teléfono Android como base para probar tecnología de visión e IA. No
debería ser solo un juguete controlado como un mando a distancia, sino que
debería ser autónomo, pudiendo circular, recibir órdenes habladas y responder
hablando, como debería hacer un robot. Para este objetivo, pensé en reutilizar
el software que existe disponible en la red de manera gratuita, y sobre este
desarrollar las funcionalidades deseadas.
Los móviles actuales aportan cualidades únicas para hacer de
pequeños controladores de robot. Son ligeros, integran multitud de sensores
incluyendo la cámara, tienen una enorme potencia de cálculo y conectividad. En
concreto en este proyecto he utilizado un teléfono Android Nexus 4 como cerebro
del robot.
El hardware por supuesto podría ser desarrollado más
profesionalmente y ser comercializado después para tener robots autónomos por
la casa. Estos robots podrían usarse para jugar, para vigilar o transportar cosas, con solo acoplar un
teléfono de los que tengamos al hardware,
Voy a resumiros el trabajo realizado hasta ahora. Si alguien
está interesado, quiere colaborar o incluso piensa que podemos comercializar un
hardware basado en estos desarrollos, puede contactar conmigo.
El Hardware
Estos
son los componentes actuales:
Tarjeta
IOIO. Es el elemento a través del cual el teléfono se comunicaría con los
motores y los sensores. La tarjeta IOIO se puede conectar con el teléfono por
medio de un conector USB o por medio de una bluetooth añadiendo a la tarjeta un
“dongle”. Este es el método de conexión usado actualmente.
Control
de motores a cargo del controlador de doble puente H - L298. Por medio de la señal PWM se regula la corriente que llega a los motores del chasis y también se utiliza su
salida +5V para alimentar a los servos analógicos de la “cabeza”.
Dos sensores ultrasónicos para evitar obstáculos no detectados
por el sistema de visión y para dar marcha atrás. Últimamente he integrado un
sensor de infrarrojos de distancia que da mejor resultados que los ultrasónicos.
Un kit pan/tilt para dar movilidad a la “cabeza” del robot.
Dos servos analógicos controlan el kit.
Un chip brújula GY-26-USART para conocer la orientación. Se
podría haber utilizado la brújula del teléfono pero está sujeta a magnetizarse
por la presencia de elementos metálicos cercanos. Esta brújula está montada
actualmente en una torre para aislarse lo más posible de elementos con hierro
del propio robot y del suelo de la casa, como vigas.
La alimentación es con dos baterías lipo de 7.4V, una para
la tarjeta IOIO y otra para el L298. La alimentación independiente me evita los reset de la tarjeta IOIO cuando los motores empiezan aconsumir
Esquema:
El software.
Para un robot autónomo que pretenda poder hacer diversas
tareas, el software es la parte que se llevará más horas de trabajo. Para
incorporar capacidades al robot he partido de un enfoque “de abajo a arriba”,
donde las funcionalidades de bajo nivel dan pie a la incorporación de
funcionalidades de mayor nivel. Todas las pruebas son en un entorno normal de
una casa, no en zonas o secciones preparadas o pintadas especialmente para el
robot.
Software base “Open Source” incorporado:
OpenCV. Para
proceso de imágenes. Existen versiones para Andorid e iOs. Es una de las
liberías de proceso de imágenes más importantes que existen
Chatter. Es un
“bot de conversación” similar al que se puede encontrar en algunos asistentes
que parecen conversar con nosotros, pero modificado para que pueda reconocer
órdenes y enviarlas al planificador de acciones.
Y los módulos
principales que incorpora actualmente, desarrollados por mí .
Cámara. Capacidad
básica de ver obstáculos. Para distinguir los obstáculos del suelo se calculan
los bordes alrededor de los objetos y se determina la zona más libre de
obstáculos.
Proceso de imagen. Algunas
de las capacidades actuales son:
-Localizar un color
predeterminado o uno elegido de lo que se está viendo.
-Reconocer la habitación donde está ahora por comparación
con fotografías almacenadas.
-Reconocer una
persona (con la ayuda de las librerías
OpenCV.)
Movimientos básicos:
Adelante, atrás, girar, girar sobre si mismo hacia un ángulo…
Patrones de
movimiento: Son capacidades básicas de movimiento de más nivel que el simple movimiento, y
construidas sobre los movimientos básicos. Algunos ejemplos de patrones ya
implementados:
-Deambular sorteando obstáculos,
- Avanzar evitando obstáculos en una dirección de la brújula
-Buscar un sitio libre al que moverse después de encontrar
un obstáculo que no se ha podido evitar.
- Seguir un color
determinado
- Seguir el color que esté viendo.
Planes Usando los patrones de movimientos básicos, se
puede planear el recorrido para ir de una habitación a otra. Para eso es necesario que se defina al
robot una forma de ir de un sitio a otro para que pueda hacer planes. Esto se
consigue con un fichero XML en el que se especifica como ir de cada habitación
a las habitaciones contiguas. Con estos datos, el planificador puede el plan
completo para ir de una habitación a cualquier otra.
Por ejemplo, el
siguiente trozo de XML le dice que para
pasar de la entrada al comedor debe encontrar una señal de color verde. Cuando
esté cerca de ella, debe orientarse a 90º y mover se en esa dirección 6 segundos.
<nododestino="comedor"
origen="entrada">
<accion>
<buscarcolor="verde"tiempo="20"/>
<moverangulo="90"tiempo="6"/>
</accion>
</nodo>
Posicionamiento
Para que el robot pueda planear como ir de un sitio a otro
lo primero que tiene que saber es donde está. Este es uno de los mayores problemas los que
se enfrenta un robot en un espacio cerrado, y se han estudiado muy diversas
soluciones hasta ahora.
Actualmente el sistema para determinar en que habitación en
la comparación de lo que la cámara ve con una serie de imágenes almacenadas,
clasificadas por habitación. La comparación se hace en base al histograma de
distintas secciones de las imágenes. Pero como plataforma de pruebas que es, se
seguirán probando nuevos sistemas de localización.
Conclusión.
Con este proyecto tenemos un sistema de bajo coste con gran
capacidad de procesamiento en el que experimentar y desarrollar los elementos
que un robot autónomo tendrá que tener para vivir en nuestros hogares, desde la
capacidad básica de ir y venir, reconocer su entorno, comunicarse y jugar o
hacer alguna que otra función útil, como transporte y limpieza. La adaptación
de algoritmos como SLAM, e inclusos el paso al sistema operativo para robot ROS
son otras de las posibilidades para mejorar la plataforma.
Hace muchos muchos años, allá por los años 80, existió una revista de informáticas llamada “el ordenador personal”, una de las primeras de las muchas que vendrían después.
Pues bien, después de tanto tiempo desde luego que tenemos ordenadores personales, y tablets y teléfonos personales, pero seguimos sin apenas robots personales. Lo que nos falta aún es un robot que nos acompañe en nuestra casa, que se mueva e interaccione con su entorno y con nosotros. Bien, si ya están vendiendo algunos que hacen algo útil como los robots aspiradora, pero no puedes hablar con ellos ni te conocen ni evolucionan, es como tener un reptil en casa.
Abro este blog para hablar y recopilar informaciones de robots personales así como para seguir el desarrollo del mío propio que voy a ir construyendo en n esfuerzo personal de reunir e integrar la tecnología disponible de una forma también barata y asequible.
Robots con teléfonos Android
Mi primera decisión es utilizar móviles y tabletas Android como cerebro del robot. El primer motivo es que en un móvil tenemos concentrado: una cpu potente, brújula, GPS, acelerómetro, cámaras, usb, wifi, reconocimiento y síntesis de voz. ¿Cuánto hubiera costado hace unos años reunir todo este hardware para empezar a construir un robot casero? . Y ahora lo tenemos igual que antes teníamos un ladrillo Nokia. El segundo es que es más ligero de transportar por un pequeño robot que un PC, y el tercero es la facilidad para hacer programas para Android.
Mi primer robot controlado por un móvil.
Para construirlo, estos son los materiales que reuní:
1 - Un teléfono Android. 2 - Un coche de juguete viejo pero que funciona. Tiene dos motores que se mueven sin servos, uno para la dirección y otro para avanzar. - Una tarjeta IOIO. La venden en entre otros sitios en http://www.bricogeek.com Esta tarjeta puede conectarse al teléfono Android por USB o por Blutooth. Lo que se consigue con ella es poder disponer de un montón de puertos digitáles y análogicos de entrada y salida y PWM controlados desde android 4 - Para dar corriente a los motores procedentes de las pilas, y después de de haber cortado los cables originales del coche, una tarjeta controladora de motores doble puente H - L298.
Bien, no tengo mucha idea de electrónica, lo mío es el software, pero no hace falta saber mucho para unir todo y tener el coche de juguete bajo el control de Android.
En este punto podría haber hecho un coche controlado por el móvil, pero de esos ya hay varios ejemplos. Lo que hice fue implementar un algoritmo basado en la visión de la cámara del teléfono para dirigir el coche autónomamente. El resultado se puede ver en este vídeo:
Sería posible pasar años perfeccionando el algoritmo de visión, y creo que cualquier aspecto de la robótica requiere inmensas cantidades de tiempo, y aunque he pasado bastante con este problema, quiero llegar a tener un robot personal a base de integrar tecnologías ya desarrolladas.
Más cosas desarrolladas:
1 - Órdenes por voz, ya las tengo implementadas.
2- Respuestas por voz. También puede ir contando lo que le va pasando, es fácil para un teléfono Android, pero encuentro un poco molesto que te vaya contando lo que hace delante de gente(familia) que no sabe lo que estás haciendo.
3- Reconocimiento de patrones. Esto es lo próximo, dejar al coche-androide deambular por casa buscando una forma concreta.
Recursos.
Aquí os comento algunos recursos interesantes que voy encontrado: