jueves, 19 de diciembre de 2013

Integracion Unity3D con FaceAPI

Integración de FaceAPI con Unity 3D

Finalmente se pudo hacer funcionar el 6dofstreamer, la aplicación open source que interactúa con FaceAPI para poder detectar caras utilizando una webcam.

Las pruebas en una PC con Windows 7 64bits fueron exitosas, la aplicación detectó correctamente la cara en diferentes posiciones e imprimió las coordenadas en la consola.


Imagen 1: Toma frontal distante, a la izquierda la consola haciendo un stream con las coordenadas.



Imágenes 2 y 3: Tomas frontales.



Imagen 4: Toma de perfil.



Luego se creó un proyecto en Unity muy simple para probar la detección de caras en está plataforma. El proyecto simplemente contiene un plano con un cilindro y una cámara que representa la vista del jugador. La cámara contiene un script que recibe la información del streamer y utiliza las coordenadas que este le envía para moverse. La transferencia de datos se realiza mediante sockets.


Imagen 5: Screenshot del proyecto en Unity.

Script:

using UnityEngine;
using System.Collections;
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
public class script: MonoBehaviour {
/*Creacion del cliente UDP que escucha al puerto 29129*/
private const int listenPort=29129;
UdpClient listener;
       IPEndPoint groupEP;
float pz,ry,ry_old=0;
Vector3 newpos;
void Start() {
listener = new UdpClient(listenPort);
groupEP = new IPEndPoint(IPAddress.Any, listenPort);
}

/*Funcion de actualizacion continua de la posicion de la camara*/
void Update () {
         byte[] receive_byte_array = listener.Receive(ref groupEP);
 string op=Encoding.ASCII.GetString(receive_byte_array, 0, receive_byte_array.Length);
string[] words=op.Split(' ');
pz=(float)Convert.ToDouble(words[3]);
ry=(float)Convert.ToDouble(words[4]);
if(pz<=-0.5 && pz>-0.4)
pz=-50;
else if(pz<=-0.4 && pz>-0.3)
pz=-40;
else if(pz<=-0.3 && pz>-0.2)
pz=-30;
else if(pz<=-0.2 && pz>-0.1)
pz=-20;
else if(pz<=-0.1 && pz>0)
pz=-10;
else if(pz<=-0 && pz>=0)
pz=0;
else if(pz>0 && pz<=0.1)
pz=10;
else if(pz>0.1 && pz<=0.2)
pz=20;
else if(pz>0.2 && pz<=0.3)
pz=30;
else if(pz>0.3 && pz<=0.4)
pz=40;
else if(pz>0.4 && pz<=0.5)
pz=50;
pz=-pz;
if(ry<=-0.5 && ry>-0.4)
ry=-50;
else if(ry<=-0.4 && ry>-0.3)
ry=-40;
else if(ry<=-0.3 && ry>-0.2)
ry=-30;
else if(ry<=-0.2 && ry>-0.1)
ry=-20;
else if(ry<=-0.1 && ry>0)
ry=-10;
else if(ry<=-0 && ry>=0)
ry=0;
else if(ry>0 && ry<=0.1)
ry=10;
else if(ry>0.1 && ry<=0.2)
ry=20;
else if(ry>0.2 && ry<=0.3)
ry=30;
else if(ry>0.3 && ry<=0.4)
ry=40;
else if(ry>0.4 && ry<=0.5)
ry=50;
if(ry_old!=0)
{
newpos.y=ry_old-ry;
}
ry_old=ry;
/*Cambiar la posicion de la camara*/
transform.Rotate(newpos);
gameObject.transform.position = new Vector3(
     gameObject.transform.position.x,
     gameObject.transform.position.y,pz);
}
}



A modo de resumen para entender la prueba, en pasos:

  1. Se corre el archivo del 6dofstreamer socket.exe, que genera un stream de datos de la posición de la cara usando FaceAPI.
  2. Transmite los datos en el puerto 29128 usando UDP.
  3. Desde Unity, la cámara contiene el script que crea un cliente UDP, escucha al puerto 29129 y toma los datos de la posición de la cara en los ejes x, y y z.
  4. Se pasan los datos a la escala necesaria para que coincidan con la pantalla de la aplicación de Unity (usando un script opensource).
  5. Se utiliza la función de Update() para que la posición de la cámara se actualice continuamente.

Al correr el streamer y el proyecto de Unity al mismo tiempo, se detectó un error. Si bien el streamer detecta la cara y muestra las coordenadas en la consola, la aplicación de Unity no respondió. Tras un análisis más detallado, se notó que al abrir el streamer se puede leer el mensaje de error: “Connection closing...Send failed 10093”. Esto da la idea de que hay un problema en la transmisión de datos con sockets.
Para asegurar que no era una cuestión de la PC, se configuró el ambiente en otra PC con Windows 7 64bits. Los resultados fueron los mismos que los anteriores.
Se enviaron mails al desarrollador del streamer y al desarrollador del script que parsea los datos a Unity para consultar posibles soluciones al problema. Al mismo tiempo se buscaron casos de usuarios que hayan tenido el mismo problema.  El primero respondió que podía correrlo sin problema, y que quizás la solución es probarlo en otras PCs. El segundo sugirió correr un script de C# que imprima en consola los datos que recibe del puerto UDP 29129 de la siguiente manera:
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
UdpClient udp = new UdpClient(29129);
IPEndPoint ipe = new IPEndPoint(IPAddress.Any, 29129);
while (true)
{
byte[] rcv = udp.Receive(ref ipe);
Console.WriteLine(Encoding.ASCII.GetString(rcv));
}
}
}
}

Al correr el script, no se imprimió nada en la consola, por lo que se puede concluir que el problema está en la comunicación de UDP.

Se continuará investigando para encontrar una solución al problema de comunicación. A su vez, se intentará configurar el ambiente en una MacBook para ver los resultados. En el caso de no poder avanzar más por este camino, se investigarán otras posibilidades como el uso de OpenCvSharp.

1 comentario:

  1. Hi,

    how do you fix "Connection closing...Send failed 10093” problem?

    ResponderEliminar