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í.

domingo, 6 de julio de 2014

Cómo crear TabControl en CEGUI sólo con el archivo .layout

CEGUI incluye un buen ejemplo de cómo utilizar el TabControl, sin embargo require código.  Si se desea crear todas las pestañas con un .layout no hay instrucciones a la vista.  El orden correcto para insertar los elementos es:
<Window type="TaharezLook/TabControl" name="TabControl" >
           <Property name="TooltipText" value="This is the tab control" />
           <Property name="TabHeight" value="{0,-1}" />
           <Property name="MaxSize" value="{{1,0},{1,0}}" />
           <Property name="TabPanePosition" value="Top" />
           <Property name="Area" value="{{0.5,0},{0.15,0},{0.95,0},{0.95,0}}" />
           <Window name="PageArmor" type="TaharezLook/TabButtonPane">
                <Property name="Area" value="{{0.05,0},{0.05,0},{0.95,0},{0.95,0}}" />
                <Property name="Text" value="Armadura" />
           </Window>
           <Window name="PageItems" type="TaharezLook/TabButtonPane">
                <Property name="Area" value="{{0.05,0},{0.05,0},{0.95,0},{0.95,0}}" />
                <Property name="Text" value="Artículos" />
           </Window>
           <Window name="PageSkills" type="TaharezLook/TabButtonPane">
                <Property name="Area" value="{{0.05,0},{0.05,0},{0.95,0},{0.95,0}}" />
                <Property name="Text" value="Habilidades" />
           </Window>
</Window>
Ahora sí, dentro del TabButtonPane se puede incluir cualquier elemento que se necesite en el panel de esa pestaña.

sábado, 28 de junio de 2014

Cómo compilar CEGUI 8.3 con Ogre 1.9 localmente en linux

Compilar Ogre

Después de pelearme con la instalación de apt-get en Debian, he terminado por compilarlo desde la fuente.
  1. Instalar los Prerrequisitos. (Aguas con nvidia-cg-toolkit, si ya estaba instalado, tener cuidado de no arruinar lo que ya estaba, asegurarse de que todos los elementos de nvidia tengan la misma versión el comando nvidia-smi falla si hay inconsistencias, se puede usar para probar).
  2. En esta ocación me tocó descargar el código del repo en Mercurial, por lo que tuve que instalar la herramienta para crear el clon:sudo apt-get install hgsvn
  3. Se crea el clon con:
    hg clone https://bitbucket.org/sinbad/ogre
  4. cd ogre
    mkdir build
    mkdir compile
    cd compile
  5. cmake -D CMAKE_INSTALL_PREFIX=[/home/.../ogre/build/] ..
    make                  # Se puede usar make -j2 si el procesador tiene dos nucleos
    make install
  6. Intentar correr algunos ejemplos.Para ejecutar las aplicaciones, es necesario indicar dónde se encuentran las bibliotecas (dado que instalé en un directorio inusual).
    export LD_LIBRARY_PATH=/home/.../ogre/build/lib:$LD_LIBRARY_PATH
    Para no repetir este paso cada vez que se abre una terminal, se puede agregar esta línea al final del archivo ~/.bashrc. Ejecutar:
    ./SampleBrowser
  7. Si aparece el error
    terminate called after throwing an instance of 'std::runtime_error'
    what(): locale::facet::_S_create_c_locale name not valid
    Instalar:
    sudo apt-get install locales
    sudo dpkg-reconfigure locales   # Seleccionar alguno en inglés, aunque se puede dejar el español como locale por defecto

Compilar CEGUI

CEGUI utiliza cmake para encontrar una instalación a nivel sistema de Ogre, por lo que hay que indicar dónde encontrar los archivos que dicen dónde está y qué necesita Ogre.  Además, FindFreetype.cmake tiene un error.

  1. Primero hay que arreglar FindFreetype.cmake como se indica en https://bbs.archlinux.org/viewtopic.php?id=174300, para evitar el error:
    CEGUI was compiled without freetype support
  2. Luego, al utilizar cmake para compilar CEGUI se indica la ubicación de ogre:
    cd <CEGUI directory>
    mkdir compile
    mkdir build
    cd compile
    cmake -D CMAKE_INSTALL_PREFIX=/home/../cegui-0.8.3/build -D OGRE_HOME=/home/.../ogre/build ..
    make
    make install

CEGUI 0.8.4

En esta versión es necesario agregar específicamente en qué directorios se encuentran las bibliotecas de Ogre.  Afortunadamente lo indica con:

-- Could NOT find OGRE (missing:  OGRE_LIB)

La secuencia de comandos queda entonces:
cd <CEGUI directory>
mkdir compile
mkdir build
cd compile
cmake -D CMAKE_INSTALL_PREFIX=/home/../cegui-0.8.4/build -D OGRE_HOME=/home/.../ogre/build -D OGRE_LIB=/home/.../ogre/build/lib/libOgreMain.so ..
make
make install


lunes, 23 de junio de 2014

Cómo utilizar SmallCaps en latex, con fuente SansSerif

En varias ocasiones se me ha indicado que es mejor escribir mis reportes con un tipo de letra SansSerif, utilizando:

\renewcommand{\familydefault}{\sfdefault} % Mucha gente considera que las fuentes tipo sans son más fáciles de leer.
\renewcommand*{\sfdefault}{cmss}

Sin embargo cuando combino esto con títulos en SmallCaps inmediatamente surge el error:

Font shape `OTI/cms/m/sc'

ó, si no uso cmss:

Font shape `OTI/lmss/m/sc' in size <10.95> not available(Font) Font shape `OTI/lmr/m/sc' tried instead on input line...

Existen dos formas sencillas para resolverlo:
La primera es utilizar la fuente Helvetica.

\usepackage{Helvetic}

Sin embargo, a mí me agrada más utilizar lmss o cms para el texto.  Si sólo quiero cambiar de fuente para conseguir el efecto SmallCaps, la solución es redefinir únicamente el comando para este estilo, utilizando Helvetica (phv):

\usepackage{letltxmacro}
\LetLtxMacro{\oldtextsc}{\textsc}
\renewcommand{\textsc}[1]{{\fontfamily{phv}\selectfont\oldtextsc{#1}}} % Usa Helvetica para las small caps, ya que cmss no tiene esa opción.