El Flujo Óptico

En esta semana me puse a revisar algunos algoritmos de ejemplo que traen las librerías de OpenCvSharp, y me dí con el de flujo óptico. Cuando trabaje con processing deseaba poder implementar algún algoritmo de este tipo, pero las librerías de opencv para processing no me soportaban la cámara y por la inexperiencia de esos días no entendía como hacerlo y me conforme con la detección de movimiento para poder construir juegos para niños. Ahora con un poco más de experiencia y armado de un marco mucho más rico de funciones dentro de OpenCV para C#,  he adaptado el algoritmo de ejemplo que trae y  poder mover un pequeño cuadrado (pensar que para animar un simple cuadrado tuve que romperme la cabeza con processing… hace algunos años).

Algo de teoría

Flujo óptico

El flujo óptico sucede cuando nos movemos en una dirección determinada. Si miramos hacia el punto al que nos dirigimos (el centro de expansión) éste no muestra movimiento, mientras que si miramos al espacio circundante, el campo visual parece expandirse. Este efecto, nuestro cerebro lo percibe con  precisión, y  contribuye al control de la locomoción.

El objetivo inmediato del análisis de imágenes basado en el flujo óptico es determinar el campo de movimiento (representación bidimensional de un movimiento tridimensional)

Algoritmo de Lucas-Kanade

Asumen constantes los vectores de velocidad u y v en una pequeña porción de la imagen.

lucas_a

lucas1

lucas_b

donde la matriz A contiene las derivadas espaciales de la imagen, el vector v corresponde al vector de flujo óptico (u,v) y el vector b contiene las derivadas temporales de la imagen.

lucas_c

lucas2

lucas_d

Para poder realizar el cálculo de flujo óptico tiene que ser posible la inversión de la matriz

lucas_e

Shi y Tomassi definen en las propiedades que debe cumplir una región para que el flujo óptico se estimado apropiadamente utilizando la técnica de LK. Sean λ1 y λ2 los valores propios de la matriz $$A^TA$ para cierta región R de la imagen, entonces se debe cumplir que:

  • min (λ1 y λ2) > λmin) existe en R
  • (λ1 / λ2) < t

Veamos la implementación del algoritmo original ofrecido dentro de los ejemplo de OpenCvSahrp

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace OpenCvSharp.Test
{
/// <summary>
/// Lucas & Kanade アルゴリズムによるオプティカルフローの計算
/// </summary>
/// <remarks>http://opencv.jp/sample/optical_flow.html#optflowHSLK</remarks&gt;
class OpticalFlowLK
{
public OpticalFlowLK()
{
using (IplImage srcImg1 = Cv.LoadImage(Const.ImagePenguin1, LoadMode.GrayScale))
using (IplImage srcImg2 = Cv.LoadImage(Const.ImagePenguin1b, LoadMode.GrayScale))
using (IplImage dstImg = Cv.LoadImage(Const.ImagePenguin1b, LoadMode.Color))
{
// (1)速度ベクトルを格納する構造体の確保,等
int cols = srcImg1.Width;
int rows = srcImg1.Height;
using (CvMat velx = Cv.CreateMat(rows, cols, MatrixType.F32C1))
using (CvMat vely = Cv.CreateMat(rows, cols, MatrixType.F32C1))
{
Cv.SetZero(velx);
Cv.SetZero(vely);

// (2)オプティカルフローを計算
Cv.CalcOpticalFlowLK(srcImg1, srcImg2, Cv.Size(15, 15), velx, vely);

// (3)オプティカルフローを描画
for (int i = 0; i < cols; i += 5)
{
for (int j = 0; j < rows; j += 5)
{
int dx = (int)Cv.GetReal2D(velx, j, i);
int dy = (int)Cv.GetReal2D(vely, j, i);
Cv.Line(dstImg, Cv.Point(i, j), Cv.Point(i + dx, j + dy), Cv.RGB(255, 0, 0), 1, Cv.AA, 0);
}
}

// (4)オプティカルフローの表示
Cv.NamedWindow(“ImageLK”, WindowMode.AutoSize);
Cv.ShowImage(“ImageLK”, dstImg);
Cv.NamedWindow(“velx”, WindowMode.AutoSize);
Cv.ShowImage(“velx”, velx);
Cv.NamedWindow(“vely”, WindowMode.AutoSize);
Cv.ShowImage(“vely”, vely);
Cv.WaitKey(0);
Cv.DestroyAllWindows();
}
}

}
}
}

Aquí el video de la implementación del algoritmo en tiempo real:

Y este video con el movimiento detectado sobre un cuadrado:

Juan Cadillo

Desarrollo de un Multimarcador para Realidad Aumentada

Desde Diciembre del 2012 me empeñe en desarrollar mi propia detección de marcadores en CSharp y XNA, para ello estoy empleando OpenCvSharp, un marco de trabajo muy bueno que esta siendo portado desde OpenCv.

Para el desarrollo de diferentes pasos me he inspirado en js-aruco que me permitió entender los pasos para desarrollar la homografía. La codificación del marcador es propia y esta basada en una secuencia de 2 a la 9 combinaciones, es decir el marcador esta dividido en un tablero de 7 x 7 partes (49), de las cuales solo tomo 9 que son las centrales. La parte exterior permite crear una bloque totalmente negro que separa el marcador de la parte blanca, la segunda cuadricula interior determina la orientación y la siguiente cuadricula es la que permite determinar la codificación, cuyo resultado son 512 marcadores posibles.

Una vez ubicado e identificado el marcador, el siguiente reto fue determinar la rotación, traslación y escalado. Fue realmente una tarea ardua, debido a que se tenía que transmitir estos valores a 3D en XNA, que de por si es ya es difícil sino tienes mucha experiencia en trabajar con 3D. Luego de mucho investigar en la matemática proyectiva y funciones recomendadas  (ProjectPoints2, SolvePnP, Rodrigues, etc.) que fueron casi un dolor agudo de cabeza por los datos de entrada como de salida (interpretación); me decidí hacerlo a mi modo…. básicamente basarme en el ángulo de rotación y su cambio según los sentidos de orientación que me permite trasladar y rotar el objeto en 3D según el movimiento del marcado. Esto me  permite economizar en tiempo de ejecución y memoria.

El escalado fue mucho más sencillo pues tomé como referencia el perímetro y en función de este determinar el tamaño del modelo a presentar.

Este es el video de muestra del avance logrado; ahora queda optimizar el código y documentarlo para su posterior liberación.

Juan Cadillo

Aprendiendo con Scratch y WeDo en un entorno aumentado

WeDo es una Kit limitado de introducción a la robótica; dentro de sus guías trae como construir y programar de forma guiada 12 modelos robóticos LEGO® sencillos que se pueden conectar al puerto USB. Su entorno visual de programación detecta automáticamente los sensores y motores (Plug and Play), o las propias creaciones robóticas. Pero para ser sinceros creo que pudieron hacerlo más completo; ya que dos motores no pueden ser activados al mismo tiempo algo que sinceramente es frustrante a la hora de querer hacer otras cosas o aplicaciones prácticas con los niños.
Como alternativa los bloques WeDo son compatibles y pueden ser manejas con Scratch, que es un lenguaje gráfico relativamente simple de aprender por su entorno basado en bloques que se sueltan para crear juegos, historias, arte, música, etc.

Al igual que WeDo solo reconoce un solo motor en su versión 1.4, pero existe una versión que puede trabajar hasta con dos motores.

Scratch, al ofrecer muchas otras posibilidades se convierte en un entorno en el que podemos reutilizar los bloques WeDo para enseñar muchos otros conceptos e introducir al alumno en la programación con sensores, el desarrollo de opciones de control de entrada y salida de datos programable que es lo que a manera de piloto estoy trabajando. Paralelamente estoy empeñado en la construcción de una interfaz que me permita usar los sensores en XNA y lograr aunado a OpenCV proyectos con mayores posibilidades de Aumentar la Realidad (sinceramente es un dolor de cabeza manejar la interfaz USB, por mi falta de experiencia con este tipo de programación, pero de trasteo en trasteo voy avanzando día a día).

Con los niños se ha trabajo un juego de conducción de un automóvil. este proyecto usa el sensor de inclinación para conducir un automóvil en el juego y el sensor de distancia para acelerar. Esta aplicación la hemos copiado de (http://scratch.mit.edu/) he iniciado su uso; en los siguientes días espero que los niños comprendan los diferentes bloques de código y empiece a volar su imaginación, logrando en base a disciplina nuestros propios juegos. Ya que el objetivo del trabajo es despertar la motivación por investigar e innovar.

Aquí el video:

Juan Cadillo

Juego de Realidad Aumentada en 3D usando GoblinXNA

Dentro de los avances en el uso de la Realidad Aumentada que vengo realizando; sigo explorando diferentes plataformas y entornos de trabajo; uno de ellas es XNA. XNA es un entorno creado por Microsoft para facilitar la construcción y gestión del desarrollo de video juegos; dentro de este entorno se ha optimizado ciertas funcionalidades para poder integrar 3D dentro de la plataforma, siendo GoblinXNA una distribución libre que posibilita este objetivo.

GoblinXNA me ha permitido integrar un objeto en 3D a la pantalla y poder manejarlo usando realidad aumentada a través de opencv para el entorno de programación de C#.

El juego es un primer prototipo del manejo y la funcionalidad que se requiere para permite potenciar las habilidades motrices de los niños con video juegos en 3D; que espero pueda seguir evolucionando.

Y un resumen del proyecto presentado a Microsoft Partners in Learning – Latin America y Caribbean Forum que se desarrollara el 19 y 20 de Setiembre en Lima – Perú.

Juan Cadillo

Nuestro Libro de Realidad Aumentada

El Libro de Realidad Aumentada que estamos construyendo es el resultado del trabajo conjunto entre los niños y niñas con su docente; es una experiencia que busca lograr aprendizajes significativos para los niños así como para el docente.

Los contenidos del Libro han sido elaborados en base a lo observado en las visitas al Museo Arqueológico de Ancash, para ello el paso previo fue una visita libre la museo, con el objetivo de que los niños puedan identificar las salas temáticas del Museo y en base a ellos recoger información.

La información que se muestra en el Libro parte de definir que es un museo, descripción breve del Museo Arqueológico de Ancash, el Poblamiento de América, los Primeros Pobladores del Perú, la Cultura Chavín, la Cultura Recuay, el Parque Lítico de la Cultura Recuay.

La próxima semana avanzaremos con Huilcahuain, Ichich Huilcahuain, el concepto de la muerte y las chullpas (videos que nos falta editar).

La subsiguientes semanas trataremos  la Cultura Wari y Mochica para terminar con el Imperio Inca.

Aquí nuestros videos:

Instrucciones para instalar y usar el Libro de Realidad Aumentada:

El modelo del libro de REALIDAD AUMENTADA  puede ser descargado de:

http://www.ziddu.com/download/15261273/LIBRORA.pdf.html

La demostración de la aplicación de realidad aumentada construida para el proyecto  esta en este enlace (por motivos de tamaño del archivo  solo suben las 3 primeros hojas)

Descargar el archivo del enlacehttp://www.ziddu.com/download/15269698/libroRAMuseo.rar.html

Descomprimirlo y ejecutar el archivo:  multiMarker1p.exe que esta dentro de la carpeta libroRAMuseo

Imprima el libro de Realidad aumentada en formato A3 que es el que usamos, en el formato A4  a veces no funcionan bien el reconocimiento de marcadores.

REQUISITOS DE INSTALACION

1.      Tener instalada una cámara Web (WebCam) de por lo menos 3.0 Mpx. Usando los controladores respectivos ofrecidos por el fabricante.

2.      Tener instalado la Máquina Virtual de Java (jre). Esta se encuentra en la dirección web:

http://www.box.net/shared/a1fa9mk2qb

o en su defecto en la página del fabricante

http://www.java.com/es/download/manual.jsp

3.      Instalar en el siguiente orden: OpenCV_1.0.exe, WinVDIG_101.exe y QuickTimeInstaller.exe. Software que se encuentran en un repositorio acondicionado en la Red. (Reiniciar si está utilizando Windows XP)

OpenCV_1.0.exe. Puede ser descargado de:

la URL del proyecto

http://www.box.net/shared/cndyb04x7n

O del Fabricante

http://sourceforge.net/projects/opencvlibrary/files/opencv-win/1.0/OpenCV_1.0.exe/download

WinVDIG_101.exe. Puede ser descargado de:

http://www.box.net/shared/11dp8vghsr

O del fabricante

http://www.eden.net.nz/7/20071008/  (debe descargar la versión 1.0.1) otras versiones dan problemas con el uso de processing

QuickTimeInstaller.exe. Puede ser descargado de:

http://www.ziddu.com/download/15241209/QuickTimeInstaller.exe.html

O del fabricante

http://www.apple.com/es/quicktime/download/

Juan Cadillo

Subsistema de manejo de los Bloques Lógicos

El trabajo en este último subsistema se baso en la detección de movimiento en 4 regiones:

La Primera de color

La segunda de tamaño

La tercera en la de forma

Y por último la de selección de orden (tenemos 3 figuras) de izquierda a derecha 1, 2 y 3.

El trabajo busca que el niño arme una serie de bloques lógicos que se le presenta mediante un juego y debe armar la serie según los criterios de color, forma y tamaño.

Video ilustrativo de aplicación

Juan Cadillo

Reconocimiento de Colores mediante el uso de la Realidad Aumentada

Dentro del desarrollo del proyecto PVRA (PINTURA VIRTUAL PARA REALIDAD AUMENTADA) se ha desarrollado una aplicación en processing que permite el reconocimiento de colores se tomó  como base  el libro “Learning Processing A Beginner’s Guide to Programming Images, Animation, and Interaction” de Daniel Shiffman quién propone un algoritmo de selección de color y su seguimiento del mismo en el video; el algoritmo fue adaptado para usarse con openCV. Si bien podemos seguir el color mediante la comparación de pixeles nos quedó el problema de poder identificar el color exacto; esto debido a las diferentes tonalidades que distingue el ojo humano; el modelo RGB (rojo, verde y azul) usado por los programas de diseño no  fue  eficiente para poder reconocer adecuadamente los colores debido a las variaciones de luz e intensidad a la hora de reconocer  los colores básicos, por lo que se tuvo que recurrir a una transformación del RGB al modelo CIElab, en el que se mejoró los resultados; pero sigue existiendo problemas con la cantidad de luz a la que es expone el objeto (ambiente oscuro o ambiente con demasiada luz).

El fragmento más resaltante e interesante que se desarrolló en este subsistema es el de transformación de un pixel del modelo RGB al CIElab:

//definimos la clase
//declaramos las variables de la clase
//inicializamos la clase
class transformaColor
{
  float x;
  float y;
  float z;
  //Colores RGB
  float rojo;
  float azul;
  float verde;
  //colores lab
  float l;
  float a;
  float b;
  float cab;
  float hab;
  transformaColor(color pixelColor){//inicializacion
    rojo = red(pixelColor);
    azul =  blue(pixelColor);
    verde = green(pixelColor);
    }
   void rgbAxyz(){
    float rojo1;
    float azul1;
    float verde1;
    //transformando
     rojo1 = rojo/255;
     azul1 = azul/255;
     verde1 = verde/255;
    if(rojo1>0.04045){
          rojo1 = (rojo1+0.055)/1.055;
          rojo1 = pow(rojo1,2.4);
     }
       else{
          rojo1 = rojo1/12.92;
     }
     if(verde1>0.04045){
          verde1 = (verde1+0.055)/1.055;
          verde1 = pow(verde1,2.4);
     }
       else{
          verde1 = verde1/12.92;
     }
          if(azul1>0.04045){
          azul1 = (azul1 + 0.055)/1.055;
          azul1 = pow(azul1,2.4);
     }
     else{
          azul1 = azul1/12.92;
     }
     azul1 *= 100;
     verde1 *= 100;
     rojo1 *= 100;
     //xyz definidos según estándar
     x = rojo1 * 0.4124 + verde1 * 0.3576 + azul1 * 0.1805;
     y = rojo1 * 0.2126 + verde1 * 0.7152 + azul1 * 0.0722;
     z = rojo1 * 0.0193 + verde1 * 0.1192 + azul1 * 0.9505;
  }
  //transformando a CIElab
  void xyzAlab(){
      float x1 = 0.0;
      float y1= 0.0;
      float z1= 0.0;
     x1 = x/95.047;
     y1 = y/100.0;
     z1 = z/108.883;
     if(x1 > 0.008856){
          x1 = pow(x1,1.0/3);
     }
       else{
          x1 = 7.787 * x1 + 16.0/116;
     }
     if(y1>0.008856){
          y1 = pow(y1,1.0/3);
    }
       else{
          y1 = (7.787*y1) + (16.0/116);
     }
     if(z1>0.008856){
          z1 = pow(z1,1.0/3);
    }
       else{
          z1 = 7.787 * z1 + 16.0/116;
   }
     //los valores lab
     l= 116 * y1 -16;
     a= 500*(x1 -y1);
     b= 200*(y1 - z1);
      println ("lab" + l + " " +a+ " " + b);
     //calculo la distancia
     cab = sqrt(sq(a)+sq(b));
   println ("cab" + cab);
    //calculado el {ángulo
    float angle = atan2(bra);
    hub = degrees(angle);
    println ("hab" + hab);
  }
}
    //función para diferenciar colores
    float diferenciaSegun1994 (float[] lab1, float [] lab2){
      float c1,c2,dc,dl,da,dh,db,first,segundos,third;
     c1 = sqrt(lab1[1]*lab1[1]+lab1[2]*lab1[2]);
     c2 = sqrt(lab2[1]*lab2[1]+lab2[2]*lab2[2]);
     dc = c1-c2;
     dl = lab1[0]-lab2[0];
     da = lab1[1]-lab2[1];
     db = lab1[2]-lab2[2];
     dh = sqrt((da*da)+(db*db)-(dc*dc));
     println ("dh" + dh);
     first = dl;
     segundos = dc/(1+0.045*c1);
     println ("segundos" + segundos);
     third = dh/(1+0.015*c1);
     println ("third" + third);
     println("AQUI" + sqrt(first*first+segundos*segundos+third*third));
     return(sqrt(first*first+segundos*segundos+third*third));
}
Para reforzar el reconocimiento de colores, el niño o niña que selecciona un color en el espacio (aula) podrá distinguir un elipse que se desplaza a través de la pantalla ubicándose en objetos cuyo color es similar al seleccionado con el mouse. En caso de ser un color básico: ROJO, AMARILLO O AZUL (Concepto tradicional sobre los colores básicos), o VERDE, NEGRO O BLANCO se le mostrará un video que muestre objetos y su respectivo nombre del mismo color seleccionado.
Aquí el video de Demostración:

Ing. Juan Cadillo