• No se han encontrado resultados

Clase KinectDevice luego de la etapa de comunicación con el dispositivo

recurso. Tal como se mencionó anteriormente, el controlador se encarga de inicializar el dispositivo Kinect, y luego tomará los datos crudos de KinectDevice, los transformará y actualizará al modelo. Para mejorar la abstracción, y permitir así en un futuro la incorporación de cualquier tipo de controlador de una manera sencilla, se formalizó una estructura.

Es así que se llegó al diseño de los controladores. Cada controlador creado debe implementar una interfaz IController. Sus métodos se pueden dividir fácilmente en tres:

load, update y unload, estandarizando la división de funcionalidad (Code snippet 3.4).

interface IController

{

//Inicialización de cualquier recurso necesario

bool load();

//Transformación de datos //Actualización del modelo

void update();

//Liberación de los recursos

void unload(); }

Code snippet 3.4 - Interfaz IController

Dentro del load, el controlador debe inicializar cualquier recurso que vaya a ser necesario, además del modelo. El recurso que será común a todas las aplicaciones

30

específicas de este proyecto, es el KinectDevice. Otros recursos utilizados también en esta ocasión pueden ser un gestor de colores y/o un gestor de audio. Estos se verán explicados a lo largo de las secciones 3.2.2 y 3.2.3, junto con las últimas dos aplicaciones que los utilizan.

Si se produjo algún problema dentro de la inicialización, el método se encarga de indicar la situación a través de un booleano de retorno (Figura 3.2). De esta manera, la vista es capaz de reaccionar. Sus acciones se verán en la sección correspondiente al explicar el componente de la vista.

Figura 3.2 - Secuencia de llamados para la inicialización

El método unload debe contener la funcionalidad para liberar los recursos. Cada controlador tendrá que liberar dentro de este método, todos sus recursos utilizados (Figura 3.3). En este proyecto en particular, todos los controladores utilizan Kinect, por lo tanto deberán liberarlo a través de kinectDevice.stopKinect().

Figura 3.3 - Secuencia de llamados para terminar la aplicación.

El update se ejecuta constantemente. En su cuerpo, el controlador debe realizar un análisis de los datos y actualizar el modelo con los datos necesarios (Figura 3.4). Esta parte es muy específica de cada controlador.

31

Figura 3.4 - Secuencia de llamados para la actualización del modelo.

En este proyecto en particular, dentro del método update se debe encontrar la comunicación con el gestor de Kinect. Previo a la actualización del modelo, se deben transformar los datos. La transformación de los datos es la misma para cualquier controlador que utilice la clase KinectDevice.

Por este motivo, se decidió crear una clase abstracta, KinectController, que sea un tipo de IController. En ella se encontrarán todos los métodos necesarios para evitar la repetición de código, y así facilitar la creación de un nuevo controlador dependiente de Kinect. En la Figura 3.5 se puede ver su comunicación con KinectDevice.

Figura 3.5 - Comunicación necesaria entre el controlador y gestor de Kinect.

En las siguientes secciones se verá la forma en que cada aplicación desarrollada permitió incorporar nuevos métodos y modificar la estructura básica de la plataforma planteada, explicando también el uso de cada uno. A continuación se pasará a mencionar aquellos que fueron agregados a KinectController a lo largo de todo el desarrollo.

Como se nombró anteriormente, a través del SkeletonFrame, se mantiene un arreglo de tipo Skeleton. Esta información brinda al controlador un conjunto amplio de datos y en diversas formas. En este caso se puede determinar si hay algún esqueleto traqueado, acceder a la cantidad de esqueletos encontrados, o también a sus posiciones.

32

Figura 3.6 - Transformación de los datos por el método getTotalSkeletons.

La cantidad de esqueletos encontrados se obtiene a través de getTotalSkeletons() (Figura 3.6). Como se mencionó en las especificaciones del Kinect dentro del capítulo 2, cada esqueleto tiene la propiedad TrackingState. Sus posibles valores son PositionOnly, Tracked, o NotTracked. El estado Tracked indica que el esqueleto tiene sus articulaciones rastreadas. Un esqueleto con PositionOnly sólo contiene su posición general. NotTracked significa que no se rastreó el esqueleto. Este método en particular cuenta aquellos esqueletos que como TrackingState tengan PositionOnly o Tracked (Figura 3.7).

Figura 3.7 - Diferentes estados de rastreo: PositionOnly (izquierda) y Tracked (derecha) Para consultar si un hay un esqueleto disponible, se puede utilizar el método isSkeletonAvailable(). Se encarga de devolver un boolean indicando si existe en aquel arreglo de esqueletos, algún esqueleto con TrackingState igual a Tracked (Figura 3.8). Este método internamente consulta por el primer esqueleto traqueado.

33

Figura 3.8 - Transformación de los datos por el método isSkeletonAvailable.

A través del método getFirstSkeletonPosition(), se obtiene la posición del primer esqueleto rastreado, es decir, con TrackingState igual a Tracked (Figura 3.9). Nuevamente, este método consulta internamente por el primer esqueleto rastreado. La posición de retorno es de tipo Point, pero la posición del esqueleto es de tipo SkeletonPoint. Por lo tanto fue necesaria la incorporación de la funcionalidad para solucionar la diferencia. Se incorporó un nuevo método a KinectDevice.

Figura 3.9 - Transformación de los datos por el método getFirstSkeletonPosition.

El método skeletonPointToScreen se encarga de mapear un SkeletonPoint a un punto de pantalla. Los puntos de esqueletos están expresados en metros mapeados a la habitación (Figura 3.10). El sensor del Kinect tiene un CoordinateMapper que tiene la capacidad de traducir estos puntos a un DepthPoint a través del método

MapSkeletonPointToDepthPoint y la resolución de pantalla. Un DepthPoint se mide en milímetros, y por lo contrario al punto de esqueleto, representa la posición dentro del frame. Entonces, puede devolver un Point inicializado con los valores X e Y del DepthPoint obtenido.

Más adelante se verá que también fue necesario consultar por todas las posiciones de todos los esqueletos. Para ello existe también la transformación de

Figura 3.10 - Espacio del esqueleto (izquierda) comparado con los valores de profundidad (derecha).

34

getSkeletonPositions(), que devuelve un arreglo de Point de la longitud del arreglo de esqueletos (Figura 3.11). En cada elemento guarda la posición mapeada a pantalla de cada Skeleton.Position que se encuentra con un TrackingState de PositionOnly o Tracked. En el caso de que el esqueleto no está traqueado, se inicializa un Point con valores negativos indicando la situación.

Figura 3.11 - Transformación de los datos por el método getSkeletonPositions.

Volviendo a los diferentes tipos de frames, a través del DepthFrame se mantiene un arreglo de tipo DepthImagePixel. El gestor de Kinect sólo brinda un método, getDepthPixels(), para que cualquier controlador lo pueda acceder. KinectController se encarga de recorrer este arreglo para quedarse sólo son los datos necesarios. Así se forma un arreglo de enteros indicando el id de cada jugador en cada píxel (Figura 3.12). El método encargado de la transformación es getPlayersId().

Figura 3.12 - Transformación de los datos por el método getPlayersId.

También es posible convertirlo en un arreglo de booleanos. De esta manera, en cada posición del arreglo se indica si hay o no algún jugador (Figura 3.13).

Figura 3.13 - Transformación de los datos por el método isPlayerOnPixel.

Entonces, a lo largo de esta etapa, se obtuvo una interfaz que debe implementar todo controlador. Esta interfaz incluye métodos para la inicialización, actualización y finalización. Como todo controlador dependiente de Kinect debe transformar los datos de KinectDevice, se pensó en una clase abstracta que contenga a los transformadores. Luego, cada controlador específico para cada aplicación deberá extenderlo. A continuación se muestra la relación entre la interfaz IController, la clase abstracta KinectController y el gestor KinectDevice (Diagrama 3.3). El método incorporado sobre el gestor en esta última etapa se encuentra resaltado.

35