jueves, 1 de diciembre de 2016

Kinect v2 en Linux


  • Descargar los fuentes

$ git clone https://github.com/OpenKinect/libfreenect2.git
$ cd libfreenect2


  • Herramientas para compilar

$ sudo apt-get install build-essential cmake pkg-config

Dependencias

$ sudo apt-get install libusb-1.0-0-dev
$ sudo apt-get install libturbojpeg libjpeg-turbo8-dev
$ sudo apt-get install libglfw3-dev

Problema: OpenCL con beignet-dev elimina opencl de nvidia-opencl-dev => no instalar, instalar el de nvidia

  • Habilitar driver de nvidia

$ sudo apt-get install nvidia-cuda-toolkit

  • Descargar el .deb para Ubuntu 15.04 de la página oficial y extraer el .deb con los ejemplos (samples), sale un data.tar.gz, extraerlos de ahí e instalar ejemplos

$ grep -rl "CUDA_PATH ?= /usr/local/cuda" ./ | xargs sed -i 's:CUDA_PATH ?= /usr/local/cuda:CUDA_PATH ?= /usr:g'


$ sudo apt-get install libva-dev libjpeg-dev
$ sudo apt-get install libopenni2-dev

Build


  • Modificar en CMakeLists:

CUDA_INCLUDE_DIRECTORIES(
      "${MY_DIR}/include/"      "/home/blackzafiro/Descargas/Programacion/CudaSamples/NVIDIA_CUDA-7.5_Samples/common/inc" #"${CUDA_TOOLKIT_ROOT_DIR}/samples/common/inc"
      "${NVCUDASAMPLES_ROOT}/common/inc"
    )

$ cmake .. -DCMAKE_INSTALL_PREFIX=$HOME/Programacion/libfreenect2/build/freenect2 -DCUDA_NVCC_FLAGS="-D_FORCE_INLINES"
$ make

TODO: Aquí se quedaron mis notas
https://github.com/OpenKinect/libfreenect2

Probar con los demos, sí funciona.

OpenCV

$ sudo apt install libav-tools

$ cmake -D PYTHON3_INCLUDE_DIR=/usr/include/python3.5m -DPYTHON3_INCLUDE_DIR2=/usr/include/x86_64-linux-gnu/python3.5m -D PYTHON3_NUMPY_INCLUDE_DIRS=/home/blackzafiro/.local/lib/python3.5/site-packages/numpy/core/include -DINSTALL_PYTHON_EXAMPLES=ON -DCMAKE_INSTALL_PREFIX=/home/blackzafiro/Descargas/Programacion/opencv/opencv-3.1.0/build ..

OpenCV 3.1.0

Compilar versión estable

Cada vez que compilo OpenCV, configurarlo es todo un triunfo.  Pero uso muchas dependencias, por lo que mejor anoto aquí lo usual:

Aunque existe una página con el procedimiento y es útil pasar por ahí primero, aquí dejo mi acordeón.

Dependencias

libgtk-3-dev        ?
libgtk2.0-dev
libqt4-dev

libvtk6-dev
libopenni-dev

libav-tools
libavcodec-dev
libavformat-dev
libswscale-dev

libjpeg-dev
libpng-dev
libtiff-dev
libjasper-dev

libdc1394-22        # Caḿaras IEEE
libdc1394-22-dev

libtbb2             # Paralelismo
libtbb-dev

libgtkglext1-dev

python3-dev
python3-numpy
python-dev
python-numpy

nvidia-cuda-dev
nvidia-cuda-gdb
nvidia-cuda-toolkit

Configurar y compilar

Editar el archivo cmake/FindCUDA.cmake:
Cambiar la línea 711
PATHS "/usr/lib/nvidia-current" "/usr/lib/nvidia-361"
de modo que la segunda dirección se la del driver de nvidia, donde se encuentra el archivo libnvcuvid.so.  Dara algunas advertencias, pero funciona como debe.

En la línea 799 insertar:

if(CUDA_VERSION VERSION_GREATER "7.0")
  find_cuda_helper_libs(nvcuvid)
endif()
Otra opción es indicar el directorio de este archivo a cmake, como se indica a continuación.

$ mkdir compile
$ mkdir build
$ cd compile
$ cmake -DCMAKE_INSTALL_PREFIX=</home/...> -DWITH_OPENGL=ON -DWITH_QT=ON -DWITH_VTK=ON -DWITH_OPENNI=ON -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCUDA_NVCC_FLAGS="-D_FORCE_INLINES" -D PYTHON3_INCLUDE_DIR=/usr/include/python3.5m -DINSTALL_PYTHON_EXAMPLES=ON -DCUDA_cublas_LIBRARY:FILEPATH=/usr/lib/x86_64-linux-gnu/libcublas.so -DWITH_CUBLAS=ON -DWITH_NVCUVID=ON -DCUDA_nvcuvid_LIBRARY:FILEPATH=/usr/lib/nvidia-367/libnvcuvid.so ..

Aparecerá una advertencia por haber agregado '/usr/lib/nvidia-367'.

Ojo: Qt y Vtk no se llevan, aunque puse ambas opciones arriba, elegir sólo una.

Con ffmpeg local

Para utilizar ffmpeg compilado localmente, antes de  realizar los pasos siguientes:
$ export LD_LIBRARY_PATH=/home/.../ffmpeg.../build/lib
$ export PKG_CONFIG_PATH=/home/.../lib/pkgconfig:$PKG_CONFIG_PATH
$ export PKG_CONFIG_LIBDIR=$PKG_CONFIG_LIBDIR:/home/.../build/lib

jueves, 7 de julio de 2016

Mi acordeón de git

Inicialización

Iniciar un repositorio localmente

$ git init
$ git remote add origin https://github.com/<username>/<repo>.git
$ git remote -v                 // verificar la liga
$ git push -u origin --all      // envía todo lo agregado localmente
Más detalles en Adding an existing project to GitHub using the command line

Clonar una bifurcación (fork)

$ git clone https://github.com/<username<repo>.git

Manipular los archicos

Agregar

$ git add .     // all
$ git add -u    // update
$ git add -A    // both

Remover

$ git rm --cached somefile.ext   // no remueve el archivo original
$ git rm --cached -r somedirectory
$ git rm --cached *~             // remueve archivos de respaldo

Borrar

$ git clean -f -d                // borra todos los archivos que no siga git

Manipular los repositorios

Encomendar (commit)

Registra en el repositorio local:
$ git commit -a -m "mensaje"     // agregar todo & commit
$ git commit -m "mensaje"        // sólo encomendar
$ git commit -m "mensaje" path/to/file.ext // encomendar un archivo

Empujar (Push)

Envía los datos al repo remoto:
$ git push

Ramas (branches)

$ git checkout -b branchname     // crear
$ git branck                     // ¿en qué rama me encuentro?
$ git checkout master

Solicitud de atracción (pull request)

Es cuando se solicita la fusión de ramas en Github.

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