mercoledì 25 marzo 2015

Compilare libfreenect2 su Ubuntu e Mac

Mi e' stata prestato Kinect One, la versione aggiornata del Kinect originale per XBox 360


Le prime impressioni.
E' necessario utilizzare un calcolatore con USB3. Le specifiche di Microsoft non possono essere aggirate neanche su sistemi operativi non Windows. In Linux lanciando protonect in alcuni casi viene individuata la periferica ma poi non c'e' trasmissione dati

La libreria libfreenect2 NON funziona con il vecchio kinect per il quale bisogna ancora usare libfreenect 1

Il supporto software e' ancora abbastanza primitivo. La libreria di riferimento e' Libfreenect2 e ci sono dettagliate istruzioni sulla compilazione su Mac, Linux....peccato che nel mio caso su Ubuntu abbiano fallito con un segmentation fault

Il problema deriva dal fatto che libfreenect2 e' nata per essere impiegata su calcolatori con scheda video NVidia mentre il mio portatile Ubuntu ha una scheda Intel. Per poter compilare e far funzionare il software dimostrativo protonect si deve modificare il sorgente come indicato a questo link
https://github.com/OpenKinect/libfreenect2/issues/31

aggiungendo questo include in testa

#include <libfreenect2/packet_pipeline.h>

e cambiando la riga 61 in

libfreenect2::Freenect2Device *dev = freenect2.openDefaultDevice(new libfreenect2::CpuPacketPipeline());

in questo modo il programma non crasha piu'

Se si esegue il programma su un computer Ubuntu privo di USB3 questi saranno i messaggi

root@luca-ThinkPad-X201:/home/luca/libfreenect2/examples/protonect/bin# ./Protonect
[Freenect2Impl] enumerating devices...
[Freenect2Impl] 7 usb devices connected
[Freenect2Impl] found valid Kinect v2 @2:5 with serial 105188533647
[Freenect2Impl] found 1 devices
[Freenect2DeviceImpl] opening...
[UsbControl::claimInterfaces(IrInterfaceId)] failed! libusb error -6: LIBUSB_ERROR_BUSY
[Freenect2DeviceImpl] closing...
[Freenect2DeviceImpl] deallocating usb transfer pools...
[Freenect2DeviceImpl] closing usb device...
[Freenect2DeviceImpl] closed
[Freenect2DeviceImpl] failed to open Kinect v2 @2:5!
no device connected or failure opening the default one!

Su Mac la compilazione non e' banale. La catena di compilazione (installata via MacBrew) presente a questo link non e' completa...di fatto mancano i pacchetti di autoconf e cosi' non e' presente il comando configure (fra parentesi per usare MacBrew si deve lanciare prima il comando sudo chown root /usr/local/bin/brew per permettere la compilazione da root)
I comandi di compilazione devono essere eseguiti alla lettera lanciando gli script esattamente dalla posizione con cui sono descritti nelle istruzioni.

Seguite queste indicazioni si puo' compilare libfreenect2 su Mac
Avendo un MacBook Pro 2013 con I5 e scheda grafica Intel ho dovuto provvedere a modificare protonect cosi' come nel caso di Ubuntu
Ho modificato leggermente il programma protonect per ottenere i soli dati di profondita' del sensore sia in formato xml da OpenCV che come file testo
-------------------------------------------------------
* This file is part of the OpenKinect Project. http://www.openkinect.org
 *
 * Copyright (c) 2011 individual OpenKinect contributors. See the CONTRIB file
 * for details.
 *
 * This code is licensed to you under the terms of the Apache License, version
 * 2.0, or, at your option, the terms of the GNU General Public License,
 * version 2.0. See the APACHE20 and GPL2 files for the text of the licenses,
 * or the following URLs:
 * http://www.apache.org/licenses/LICENSE-2.0
 * http://www.gnu.org/licenses/gpl-2.0.txt
 *
 * If you redistribute this file in source form, modified or unmodified, you
 * may:
 *   1) Leave this header intact and distribute it under the same terms,
 *      accompanying it with the APACHE20 and GPL20 files, or
 *   2) Delete the Apache 2.0 clause and accompany it with the GPL2 file, or
 *   3) Delete the GPL v2 clause and accompany it with the APACHE20 file
 * In all cases you must keep the copyright notice intact and include a copy
 * of the CONTRIB file.
 *
 * Binary distributions must follow the binary distribution requirements of
 * either License.
 */


#include <iostream>
#include <signal.h>

#include <opencv2/opencv.hpp>

#include <libfreenect2/libfreenect2.hpp>
#include <libfreenect2/frame_listener_impl.h>
#include <libfreenect2/threading.h>
#include <libfreenect2/packet_pipeline.h>

bool protonect_shutdown = false;

void sigint_handler(int s)
{
  protonect_shutdown = true;
}

int main(int argc, char *argv[])
{
  std::string program_path(argv[0]);
  size_t executable_name_idx = program_path.rfind("Protonect");


  std::string binpath = "/";

  if(executable_name_idx != std::string::npos)
  {
    binpath = program_path.substr(0, executable_name_idx);
  }


  libfreenect2::Freenect2 freenect2;
  //libfreenect2::Freenect2Device *dev = freenect2.openDefaultDevice();
  libfreenect2::Freenect2Device *dev = freenect2.openDefaultDevice(new libfreenect2::CpuPacketPipeline());


  if(dev == 0)
  {
    std::cout << "no device connected or failure opening the default one!" << std::endl;
    return -1;
  }

  signal(SIGINT,sigint_handler);
  protonect_shutdown = false;

  libfreenect2::SyncMultiFrameListener listener(libfreenect2::Frame::Color | libfreenect2::Frame::Ir | libfreenect2::Frame::Depth);
  libfreenect2::FrameMap frames;

  dev->setColorFrameListener(&listener);
  dev->setIrAndDepthFrameListener(&listener);
  dev->start();

  std::cout << "device serial: " << dev->getSerialNumber() << std::endl;
  std::cout << "device firmware: " << dev->getFirmwareVersion() << std::endl;

  while(!protonect_shutdown)
  {
    listener.waitForNewFrame(frames);
    //libfreenect2::Frame *rgb = frames[libfreenect2::Frame::Color];
    //libfreenect2::Frame *ir = frames[libfreenect2::Frame::Ir];
    libfreenect2::Frame *depth = frames[libfreenect2::Frame::Depth];

    //cv::imshow("rgb", cv::Mat(rgb->height, rgb->width, CV_8UC3, rgb->data));
    //cv::imshow("ir", cv::Mat(ir->height, ir->width, CV_32FC1, ir->data) / 20000.0f);
    cv::imshow("depth", cv::Mat(depth->height, depth->width, CV_32FC1, depth->data) / 4500.0f);
    //cv::imwrite("luca.png",cv::Mat(depth->height, depth->width, CV_32FC1, depth->data)/4500.0f);
    std::cout << cv::Mat(depth->height, depth->width, CV_32FC1, depth->data);
    cv::FileStorage fs("trilobite.xml", cv::FileStorage::WRITE);
    fs << "immagine" << cv::Mat(depth->height, depth->width, CV_32FC1, depth->data);
    fs.release();
    return 0;



    int key = cv::waitKey(1);
    protonect_shutdown = protonect_shutdown || (key > 0 && ((key & 0xFF) == 27)); // shutdown on escape

    listener.release(frames);
    //libfreenect2::this_thread::sleep_for(libfreenect2::chrono::milliseconds(100));
  }

  // TODO: restarting ir stream doesn't work!
  // TODO: bad things will happen, if frame listeners are freed before dev->stop() :(
  dev->stop();
  dev->close();

  return 0;

}-------------------------------------------------------

il file xml e' in questo formato
-------------------------------------------------------
<?xml version="1.0"?>
<opencv_storage>
<immagine type_id="opencv-matrix">
  <rows>424</rows>
  <cols>512</cols>
  <dt>f</dt>
  <data>
    0. 0. 0. 0. 0. 2.49869482e+03 0. 0. 0. 1.85178479e+03 0. 0.
    1.78918164e+03 1.88499329e+03 0. 0. 0. 1.77245947e+03 1.99891040e+03
    1.83380408e+03 0. 1.91916858e+03 1.89536816e+03 0. 0. 0.
    2.07646899e+03 0. 2.00091846e+03 2.04850122e+03 0. 0. 2.00802515e+03
    0. 2.12383228e+03 0. 2.04420789e+03 0. 0. 2.04419519e+03
    2.03690503e+03 2.00967407e+03 0. 0. 2.00150708e+03 2.10069604e+03 0.
    0. 0. 2.11520288e+03 0. 2.08436304e+03 0. 2.07646094e+03 0.

    2.13713623e+03 0. 2.12
-------------------------------------------------------
mentre i dati raw sono
-------------------------------------------------------
0, 0, 0, 0, 0, 2498.6948, 0, 0, 0, 1851.7848, 0, 0, 1789.1816, 1884.9933, 0, 0, 0, 1772.4595, 1998.9104, 1833.8041, 0, 1919.1686, 1895.3682, 0, 0, 0, 2076.469, 0, 2000.9185, 2048.5012, 0, 0, 2008.0251, 0, 2123.8323, 0, 2044.2079, 0, 0, 2044.1952, 2036.905, 2009.6741, 0, 0, 2001.5071, 2100.696, 0, 0, 0, 2115.2029, 0, 2084.363, 0, 2076.4609, 0, 2137.1362, 0, 2126.874, 2140.6592, 2108.6714, 0, 0, 2102.1692, 2100.3867, 0, 0, 2109.4075, 2118.7947, 2124.1169, 0, 2096.3901, 2067.7419, 2058.8411, 2099.4333, 2113.417, 2081.3757, 0, 2112.1855, 2143.5496, 2110.3533, 2108.1643, 2127.9106, 0, 0, 2065.8389, 0, 2135.0957, 2136.1062, 2200.4043, 0, 2113.2451, 2115.2937, 2113.6121, 2103.0654, 2104.3423, 2123.4355, 2176.7842, 0, 2059.0129, 2106.6523, 2110.292, 2082.958, 2129.3064, 2094.9377, 2106.3201, 2133.8752, 0, 0, 2118.3491, 0, 2091.9097, 2147.2163, 2119.6216, 2101.3369, 2106.2881, 2150.4028, 2120.2986, 2116.1897, 2144.3218, 0, 2099.1787, 2145.4131, 2101.3423, 2113.9082, 2132.8118, 2120.2107, 0, 2081.64
-------------------------------------------------------
come si vede Kinect 2 riporta i dati di profondita' in millimetri e a differenza di Kinect1 riesce a vedere anche i decimali dei millimetri (secondo me 4 cifre decimali sono troppe, basterebbe avere la precisione significativa al decimo di  millimetro per avere un milglioramento