K▹IN

Libfreenect2

Description

Libfreenect2 est un driver libre pour Kinect One proposé par le projet OpenKinect. Le projet est disponible sur Github sous la double licence GPL v2 et Apache 2.0. Il permet, entre autres, de faire fonctionner le Kinect One sous Linux.

Installation

Les instructions d'installation sont directement disponibles sur le site web du projet. Je reprends ici les instructions minimales pour une installation sur Ubuntu depuis un terminal.

Avant de commencer

Mise à niveau des packages existants

	sudo apt update
	sudo apt upgrade					
			

Récupération du package

Le plus simple est d'utiliser git. Si le package n'est pas encore installé :

	sudo apt install git
			
Il suffit de cloner le repository. Tous les fichiers seront alors copiés dans un sous-répertoire libfreenect2 du répertoire courant : pensez à créer l'arborescence qui vous convient pour accueillir cette bibliothèque (Dans mon cas, le répertoire Kinect à la racine du profil).

	mkdir ~/Kinect && cd ~/Kinect
	git clone https://github.com/OpenKinect/libfreenect2.git
			

Compilation

Plusieurs dépendances sont nécessaires pour fabriquer ce package

	sudo apt install build-essential cmake pkg-config libusb-1.0-0-dev libturbojpeg libjpeg-turbo8-dev libglfw3-dev
			
Le package peut dès lors être fabriqué

	cd libfreenect2
	mkdir build && cd build
	cmake .. -DCMAKE_INSTALL_PREFIX=~/Kinect/libfreenect2
	make
	make install
			

Vérification de l'installation

Le package libfreenect2 intègre un programme qui sert d'exemple et de contrôle : Protonect

	cd bin
	sudo ./Protonect
			
Si tout s'est correctement passé (Kinect correctement branché et succès de la compilation), une fenêtre de ce type devrait apparaître : Le rendu peut varier... selon votre décoration d'intérieur ;)

Intégration avec Java

Le chemin le plus classique pour une intégration avec Java est l'utilisation de JNI. Avec le code source, il est possible d'utiliser des outils comme SWIG pour une génération automatique. Pour mes besoins, je vais simplement établir une connexion socket entre le processus C et le processus Java. Le processus C est client du processus Java

Modification du code C

Je fais une transformation à minima de Protonect, l'exemple fourni avec libfreenect, pour qu'il réponde à mes besoins. La communication socket utilise la bibliothèque SDL_net 2.0

Ajout du package SDL_net


	sudo apt install libsdl2-net-dev
			

Prise en compte dans la chaîne de fabrication

Ajouter les scripts permettant la prise en compte de SDL_net. Des scripts complets sont proposés par Benjamin Eikel sous licence BSD. Dans le répertoire libfreenect2/cmake_modules, ajouter les fichiers FindSDL2.cmake et FindSDL2_net.cmake

Modifier le fichier libfreenect2/CMakeLists.txt

Ce qui donne ce fichier CMakeLists.txt (#Network pour retrouver les modifications apportées dans le fichier par rapport à l'original)

Modification de Protonect.cpp

L'envoi des trames via socket nécessite les ajouts suivants dans le fichier libfreenect2/examples/Protonect.cpp

	#include "SDL_net.h"

	// Initialisation d'une connexion vers localhost:3000

	IPaddress ip;
	TCPsocket sd;

	if (SDLNet_Init() < 0)
	{
		fprintf(stderr, "SDLNet_Init: %s\n", SDLNet_GetError());
		exit(EXIT_FAILURE);
	}

	if (SDLNet_ResolveHost(&ip, "127.0.0.1", 3000) < 0)
	{
		fprintf(stderr, "SDLNet_ResolveHost: %s\n", SDLNet_GetError());
		exit(EXIT_FAILURE);
	}

	if (!(sd = SDLNet_TCP_Open(&ip)))
	{
		fprintf(stderr, "SDLNet_TCP_Open: %s\n", SDLNet_GetError());
		exit(EXIT_FAILURE);
	}

	// Intégré dans la boucle principale

	int len = depth->width * depth->height * depth->bytes_per_pixel;
	if (SDLNet_TCP_Send(sd, (void *) depth->data, len) < len)
	{
		fprintf(stderr, "SDLNet_TCP_Send: %s\n", SDLNet_GetError());
		exit(EXIT_FAILURE);
	}
			
D'autres transformations sont possibles, comme la suppression du viewer et du paramétrage pour gérer l'activation des capteurs (RGB, profondeur)

Ce qui donne ce fichier Protonect.cpp

Recompilation

Il suffit de supprimer le contenu du répertoire libfreenect2/build avant de le reconstruire.

	cd ~/Kinect/libfreenect2/
	rm -rf build/*
	cd build
	cmake .. -DCMAKE_INSTALL_PREFIX=~/Kinect/libfreenect2
	make
	make install
			

Côté serveur

Cette première étape est un simple test de validation du composant C. Il s'agit d'un simple serveur qui écoute sur le port 3000 et consomme les données.

Java

Si ce n'est pas déjà fait, il est nécessaire d'installer Java. Le plus simple est d'installer le package

	sudo apt install default-jdk
			
Dans mon contexte, ce package correspond à java8-openjdk.

N'importe quel environnement de développement peut faire l'affaire. Ubuntu propose un package Eclipse qui tire les dépendances Java


	sudo apt install eclipse-platform
			
Le package est par contre assez ancien (3.8). L'installation avec l'installer Eclipse permet de profiter des dernières versions de l'IDE.

Un serveur de test simpliste

Au plus simple, le serveur écoute sur le port 3000, accepte la connexion d'un unique client et consomme les données entrantes, sans traitement.

	import java.io.InputStream;
	import java.net.ServerSocket;
	import java.net.Socket;

	public class TestServer extends Thread {

		public final static int SERVER_PORT = 3000;
		
		public void run() {
			
			ServerSocket ss = null;
			Socket s;
			
			try {
				ss = new ServerSocket(SERVER_PORT);
				s = ss.accept();
				
				InputStream is = s.getInputStream();
				
				while (true) {
					is.read();
				}
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				try { ss.close(); } catch (Exception e2) {}
			}
		}
		
		public static void main(String[] args) {
			new TestServer().start();
		}
	}
			
Téléchargement de TestServer.java

Exécution du test

Lancer le serveur Java puis le client Protonect. La console de Protonect devrait afficher une log de ce type

	$ sudo ./Protonect 
	Version: 0.2.0
	Environment variables: LOGFILE=
	Usage: ./Protonect [-gpu=] [gl | cl | clkde | cuda | cudakde | cpu] []
			[-noviewer] [-norgb | -nodepth] [-help] [-version]
			[-frames ]
	To pause and unpause: pkill -USR1 Protonect
	[Info] [Freenect2Impl] enumerating devices...
	[Info] [Freenect2Impl] 16 usb devices connected
	[Info] [Freenect2Impl] found valid Kinect v2 @9:3 with serial 019312533747
	[Info] [Freenect2Impl] found 1 devices
	[Info] [Freenect2DeviceImpl] opening...
	[Info] [Freenect2DeviceImpl] transfer pool sizes rgb: 20*16384 ir: 60*8*33792
	[Info] [Freenect2DeviceImpl] opened
	[Info] [Freenect2DeviceImpl] starting...
	[Debug] [Freenect2DeviceImpl] status 0x090000: 9763
	[Debug] [Freenect2DeviceImpl] status 0x090000: 9763
	[Info] [Freenect2DeviceImpl] submitting depth transfers...
	[Info] [Freenect2DeviceImpl] started
	device serial: 019312533747
	device firmware: 4.0.3916.0
	[Debug] [DepthPacketStreamParser] not all subsequences received 0
	[Debug] [DepthPacketStreamParser] skipping depth packet
	[Debug] [DepthPacketStreamParser] skipping depth packet
	[Info] [DepthPacketStreamParser] 9 packets were lost
	[Info] [OpenGLDepthPacketProcessor] avg. time: 18.7989ms -> ~53.1946Hz
	[Info] [OpenGLDepthPacketProcessor] avg. time: 18.1562ms -> ~55.0775Hz
	[Info] [OpenGLDepthPacketProcessor] avg. time: 18.306ms -> ~54.627Hz
	[Info] [OpenGLDepthPacketProcessor] avg. time: 18.0493ms -> ~55.4039Hz
	[Info] [OpenGLDepthPacketProcessor] avg. time: 18.091ms -> ~55.276Hz
	[Info] [OpenGLDepthPacketProcessor] avg. time: 18.0869ms -> ~55.2887Hz
	[Info] [OpenGLDepthPacketProcessor] avg. time: 18.2348ms -> ~54.8403Hz
	[Info] [OpenGLDepthPacketProcessor] avg. time: 18.4291ms -> ~54.2621Hz
	[Info] [OpenGLDepthPacketProcessor] avg. time: 18.2912ms -> ~54.6712Hz
	[Info] [OpenGLDepthPacketProcessor] avg. time: 18.0582ms -> ~55.3764Hz
	[Info] [OpenGLDepthPacketProcessor] avg. time: 18.1175ms -> ~55.1951Hz
	[Info] [OpenGLDepthPacketProcessor] avg. time: 18.1787ms -> ~55.0095Hz
	[Info] [OpenGLDepthPacketProcessor] avg. time: 18.1537ms -> ~55.0851Hz
	[Info] [OpenGLDepthPacketProcessor] avg. time: 18.1901ms -> ~54.975Hz
	[Info] [OpenGLDepthPacketProcessor] avg. time: 18.08ms -> ~55.3098Hz
	[...]
			
La fréquence de rafraîchissement dépend des capacités de la machine (Ici, 55Hz pour une machine ancienne [Thuban/Oland Pro]).