viernes, 31 de octubre de 2014

Diagramas de clases y secuencia de KinectFusionBasics-D2D y KinectFusionExplorer-D2D

Tratando de comprender los demos de Microsoft, comienzo por hacer los diagramas de clases. ¿Quién es responsable de qué en KinectFusionBasics-D2D?

Sin embargo, al hacer el diagrama del KinectFusionExplorer-D2D, no estoy muy segura de haber ganado mucha información. Tal vez es un asunto de eficiencia, pero la cantidad de atributos y métodos en cada clase me parece impresionante, mientras que el número de clases es muy pequeño.  O tal vez este programa es más un asunto de programación estructurada que de orientación a objetos. Inclusive eliminando información dentro de cada clase, este diagrama no ofrece mayor información.

El siguiente paso ha sido crear los diagramas de secuencia, para ver qué lógica sigue el programa.  Trato de eliminar la información referente a la GUI y enfocarme en el manejo del Kinect y la reconstrucción de la superficie.  Los diagramas se toman algunas libertades, por ejemplo, se escribe el nombre del archivo que contiene a funciones independientes, donde se hubiera requerido el nombre de un objeto.

Al seguir la secuencia de ejecución desde la función de entrada int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow) pareciera que el verdadero trabajo inicia con LRESULT CALLBACK CKinectFusionExplorer::MessageRouter.


El diagrama siguiente muestra lo que sucede cuando se ejecuta MessageRouter() y se está inicializando la aplicación.  Aquí aparece la creación al segundo hilo de ejecución, que menciona la documentación del demo.  La clase encargada de su creación y manejo es KinectFusionProcessor

Otros dos casos relevantes dentro de DlgProc() son cuando los mensajes son WM_FRAMEREADY y WM_UPDATESENSORSTATUS.  Este último caso es el más sencillo, pero refiere una llama al objeto de tipo NuiSensorChooserUI para actualizar parámetros del sensor.
Cuando el cuadro se encuentra listo, se muestran las imágenes correspondientes en pantalla y se actualizan los elementos de la interfaz que así lo requieran.

Segundo hilo

Aquí es donde realmente se realiza el procesamiento de la información del Kinect y la reconstrucción de la superficie.  La función enviada como parámetro al crear este hilo DWORD WINAPI KinectFusionProcessor::ThreadProc(LPVOID lpParameter), devuelve una referencia al método MainLoop() de la misma clase.  Aquí también hay un ciclo atendiendo eventos: fin del procesamiento, cuadro (de profundidad) siguiente y cambio de estado del sensor.  El diagrama siguiente ilustra qué hace esta función cuando se tiene disponible el siguiente cuadro.

Cada vez que es necesario reiniciar la reconstrucción del volumen, se manda llamar a la función HRESULT KinectFusionProcessor::RecreateVolume().  El diagrama siguiente ilustra la secuencia principal de llamadas.

Por último, la parte más pesada del procesamiento se encuentra en la función void KinectFusionProcessor::ProcessDepth().  Me interesa especialmente la reconstrucción con captura de color, por lo que sigo esa línea de ejecución.

Como se puede ver, esta es la función encargada de llamar a todas las demás funciones que realizan alguna parte del procesamiento.  Una vez alcanzado este punto ya es mucho más natural saber qué función realiza qué tarea y en qué orden.

Sincronización de hilos

Existen regiones críticas en las que se modifica el valor de variables, que deben se bloqueadas antes de re-escribirlas (las solicitudes para realizar estos bloqueos fueron ilustradas en los diagramas de secuencia de arriba).  Para los bloqueos se definen tres candados: m_lockParams, m_lockFrame y m_lockVolume.

Varias funciones dentro de KinectFusionProcessor revisan si fueron llamadas desde el segundo hilo creado o desde otro hilo.  Para ello utilizan el valor del atributo m_threadId, cuyo valor es asignado al crear el hilo con la llamada a la función CreateThread.

Las funciones que solicitan ser llamadas desde su propio hilo (segundo hilo creado, (AssertOwnThread())), se encargan del manejo del Kinect y el procesamiento de la información recibida.  Estas son:
  • MainLoop()
  • ShutdownSensor()
  • CreateFirstConnected()
  • InitializeKinectFusion()
  • RecreateVolume()
  • CopyExtendedDepth(NUI_IMAGE_FRAME &imageFrame)
  • ProcessDepth()
  • UpdateCameraPoseFinder()
  • StoreImageToFrameBuffer(const NUI_FUSION_IMAGE_FRAME* imageFrame, BYTE* buffer)
  • InternalResetReconstruction()
  • SetStatusMessage(WCHAR * szMessage)
  • NotifyFrameReady()
  • NotifyEmptyFrame()
Las funciones que solicitan ser llamadas desde otro hilo (AssertOtherThread()), reciben información desde la interfaz de usuario y almacenan los valores correspondientes en los atributos que podrá revisar el hilo encargado del procesamiento.  Antes y después de asignar estos valores, deben solicitar la aplicación de los candados correspondiente.  Estas funciones son:
  • ~KinectFusionProcessor()
  • StartProcessing(): Crea el segundo hilo.
  • StopProcessing()
  • ResolveSensorConflict()
  • IsVolumeInitialized()
  • SetWindow(HWND hWnd, UINT msgFrameReady, UINT msgUpdateSensorStatus)
  • SetParams(const KinectFusionParams& params)
  • LockFrame(KinectFusionProcessorFrame const** ppFrame)
  • UnlockFrame()
  • ResetReconstruction(): Llamada cuando el usuario selecciona alguna opción en la interfaz que provoca que se reinicie la reconstrucción.
  • CalculateMesh(INuiFusionColorMesh** ppMesh)

martes, 28 de octubre de 2014

Hola mundo Kinect con wxWidgets

Como paso inicial para un proyecto de visión por computadora, fue necesario separar claramente el código que controla el Kinect en DepthBasics-D2D © Microsoft, del código de la interfaz gráfica, que utiliza las Microsoft Foundation Classes (MFC). Con este objetivo, creé un pequeño programa que utiliza wxWidgets, el Kinect Developer Toolkit y Kinect SDK con los drivers oficiales para controlar el Kinect, que pueden ser descargados gratuitamente del sitio de Microsoft. Este programa únicamente lee la información de profundidad y la muestra en pantalla, manteniendo al mínimo el código requerido. La conexión al Kinect y lectura de información se realizan dentro de la clase definida en el archivo KinectManager.h. Las otras clases se encargan de crear y manejar la interfaz gráfica. Como wxWidgets requiere, se utiliza un timer para generar eventos que obliguen al programa a leer la información del Kinect. El proyecto de Visual Studio 2012 (Express) puede ser descargado de aquí.