kinect.initDepth();//​Macro Traffic

Porteur du projet : Rafaëlle Bosch, Caroline Brun, Victor Besse, Claire Eyraud, Amandine Rossignol, Mélodie Souty

Date : du mardi 10 novembre au vendredi 11 décembre 2015

Contexte : Médiation graphique d'œuvres artistiques présentées dans le cadre de l'exposition Histoires Parallèles.

Seamus Farrell / Spiral of Fez

Spiral of Fez de la série « 3 R’S », juillet 2008 Installation composée de 26 portières de voitures disposées en spirales avec inscription gravées sur les vitres. Métal, verre

Description

Cette œuvre imposante tire tout son intérêt par l'interaction déambulatoire du spectateur. En effet, le public ne pourra décrypter les gravures et comprendre le sens unique de l’œuvre qu'en s'engouffrant à l'intérieur de la spirale. Il semblait donc primordial de tenir compte des flux des spectateurs. La retranscription de ces mouvements est réalisée à l'aide de la kinect. Les différents capteurs nous permettent une reconnaissance physique qui sera utilisée ici pour du tracking d'individu. Les trajets reproduis et projetés en direct révèlent nos passages invisibles. En cartographiant les déplacements du public, cette médiation rend compte du lien entre le spectateur et l’œuvre.
seamus_farrell.docx

Problématique

En arpentant l'espace de Spiral of Fez, on constate aisément l'importance des flux et des déplacements des visiteurs : augmenter l'œuvre a été donc l'opportunité d'inscrire, d'illustrer et de garder en mémoire ces parcours grâce aux outils numériques.
Une Kinect (caméra de détection des mouvements développée par Microsoft en 2008) est suspendu au dessus de l'œuvre et capte l'ensemble des mouvements produits par les spectateurs ; ces signaux sont renvoyés vers un ordinateur, qui appose un traitement graphique afin de les traduire en visuels, qui sont ensuite projetés sur une surface plane face à la spirale de Seamus Farrell.
Les déambulations sont donc transcrites visuellement, ce qui permet au visiteur de saisir tout l'intérêt de cette œuvre “en mouvement” et de cristalliser en temps réel le lien entre l'humain et l'œuvre.

Matériaux

Fourni par Résonance Numérique :

Kinect

Mac Mini

Adaptateur HDMI/mac

Rallonge USB 12m

Fourni par le Frac :

Crochet de fixation en C.

Bois

Écran HDMI (taille peu importante)

Rallonge électrique 10m

Multiprise

Vidéoprojecteur

Rallonge VGA / HDMI

Monte-Charge

Sangles

Électricité en haut de la colonne

Fourni par le Lycée :

Fixation Kinect (Thomas Ricordeau)

L'accueil du public

Macro Trafic a su venir compléter l’œuvre de Seamus Farell sans envahir son espace. La forme projetée sur le mur et générée par le mouvement au sein de la spiral, a trouvait sa place et est apparu comme un compte rendu, comme le trafic final qui concluait la thématique des flux soulevé par l’artiste. Le dispositif s’avérait discret, et malgré une retranscription à échelle une du mouvement dans la spirale, les traces blanches se dessinaient timidement et avec légèreté sur le mur ne troublant pas la rencontre du spectateur avec l’œuvre de Farell. Seul les visiteurs de la spirale qui décrochaient leur regard des portières, pris d’un instant de fatigue ou de lassitude, voyaient soudainement leur attention captée par le dispositif interactif, apportant un second souffle à leur visite et les accompagnant jusqu’à la sortie de la spirale. Dès lors une partie du public prenait conscience de leur propre trafic. L’installation interactive s’est avéré alors être l’élément déclencheur d’une prise de conscience soudaine de la part du public concernant le déterminisme de leur trajectoire contraint par la forme de la spirale. Dès lors, ces mêmes personnes cherchaient à rompre cette répétition, parfois, en zigzagant dans l’œuvre afin de redessiner leur trajet. Finalement Macro trafic c’est montrait être un soutient à Spiral of Fez attirant et satisfaisant même les moins sensible au travail de l’artiste.

Projet

Restitution des Démarches

Organisation du groupe:
Semaine 1: Présentation de la kinect
Semaines 2 & 3: Séparation du groupe en deux parties. Recherches graphiques par Amandine, Claire et Victor. Recherches techniques par Melodie, Caroline et Rafaëlle.
Semaines 4 & 5: Application des Graphismes au programme.
Semaine 6: Test, gestions des bugs et finalisation.

Cahier des charges
Techniques et problèmes rencontrés:
Nous avons tout d'abord pensé à une détection chromatique: Seul les visiteurs portant une “casquette” colorée aurait été détectés par la kinect. Points positifs: Capter un grand nombre d'individu, tracking stable. Points négatifs: Obliger le visiteur à porter un couvre-chef, variations lumineuses des néons pouvant perturber la détection.
Par la suite nous avons trouvé une application s'appelant TUIO, ce qui impliquée de travailler en C++, mais cela nécessitait l'apprentissage de ce langage ce qui n'était pas réalisable dans le délais imparti.

Solutions choisies:
Depth: Création d'une zone de détection. Cette technique permet à la kinect de déterminer une distance de captation entre deux plans : le premier assez proche de la Kinect, le second situé juste au dessus de l'oeuvre. Cela permet de prendre en compte uniquement les visiteurs en interaction avec l’œuvre, le reste (portières, sol...) n'est pas détecté. Nous pouvons ainsi ne capter qu'uniquement les personnes situés dans l'installation (car elles sont plus grandes que l'oeuvre).
L'utilisation de la librairie “Blob Detection” sur processing, nous a permis de capter et de faire apparaitre une masse. Grâce à la captation de la masse nous avons réussi à faire afficher le centre de celle-ci quelque soit sa forme.
Après avoir déterminé le centre nous avons pu lui appliquer le graphisme souhaité. Le graphisme représente une forme ronde bougeant et se déformant de manière aléatoire pour générer l'impression du vivant. Sa taille change légèrement (entre deux valeurs données) pour créer des variations. Le dessin du cercle laisse une trainé transparente, et dessine ainsi le cheminement de la spirale. Toutes les dix minutes, un rafraîchissement s'effectue pour que le tracé des personnes circulant dans la spirale disparaisse pour laisser place à un autre tracé.

Problèmes rencontrés:
Dans un premier temps, la retro-projection inversait le cheminement de la spirale en la mettant en miroir (l'entrée de l’œuvre était à droite et à gauche sur la retro-projection).
Dans un second temps le champ de vision de la kinect était trop restreint, nous n'avions donc pas l'intégralité de l’œuvre. Nous avions mis un fish-eyes pour élargir la prise de vue de la kinect, mais cela causait des problèmes de détection, (déformation convergente de la vision et ainsi des hauteurs de détection). Nous avons décidé de remonter la kinect en hauteur pour obtenir son champ de vision plus large.

Présentation du programme
Algorithmes:

Tout d'abord la kinect réduit son champ de détection à la distance donnée, avec une initialisation du depth

Elle repère les masses, elle calcule leur centre

Elle affiche le centre des blobs avec le graphisme choisi, une trace est laissée lorsque le blob détecté bouge. La trace est repérable car chaque “point” blanc n'est pas effacé lors de sa création, on aperçoit donc la continuité de déplacement.

Toute les dix minutes, les traces laissés s'effacent. Rafraichissement de la page

Tutoriel

Télécharger Processing3

Installer les librairies “OpenKinect” et “BlobDetection”

Code:

/*
* Kinect Spiral
* Contexte : OpenFRAC2 - Spiral from Fez
*/
// Bibliothèques Kinect et Blobs
import org.openkinect.freenect.*;
import org.openkinect.processing.*; // http://shiffman.net/p5/kinect/reference/
import blobDetection.*; // http://www.v3ga.net/processing/BlobDetection/index-page-documentation.html
// Variables à modifier
boolean DEBUG = false; // réglage profondeur
int minDepth = 800; //distance min de profondeur
int maxDepth = 1022; //distance max de profondeur
float angle = 0; //angle
int blurSmooth = 10; // flou adouci
float blobThreshold = 0.4; //détection de blob en fonction du seuil de luminosité
int transparence = 2; // couche de transparence alpha/frame (o-255)
// Déclaration des variables globales
Kinect kinect; // Objet issu de la classe Kinect pour contrôler la kinect
BlobDetection theBlobDetection; // Objet issu de la classe BlobDetection permettant la détection des blobs
PImage imgBlob;
PImage depthImg;
void setup() {
fullScreen();// plein écran
smooth(0); // lissage des tracés
// initialisation de la kinect
kinect = new Kinect(this);
kinect.initDepth();
// Création de l'image qui va permettre l'affichage de l'image en profondeur générée par la Kinect
depthImg = new PImage(kinect.width, kinect.height);
// Initialisation des blobs
imgBlob = new PImage(80,60);
// Instanciation et initialisation des blobs qui vont être détectés par la kinect
theBlobDetection = new BlobDetection(depthImg.width, depthImg.height);
theBlobDetection.setPosDiscrimination(true);
theBlobDetection.setThreshold(blobThreshold);
background(0); //
// masquage de la sourie
noCursor();
}
void draw() {
// ajuster le min / max Depth
if (DEBUG) {
//minDepth = (int) map (mouseX, 0, width, 0, maxDepth);
//maxDepth = (int) map (mouseY, 0, height, minDepth, 2047);
}
// Transparence
fill(0,transparence); //Remplir de noir, couche alpha = valeur de la variable transparence
noStroke(); // Pas de contour
rect(0,0,width,height); // Rectangle placé à 0,0 de la taille de la fenetre
if (DEBUG) background(0);
// Dessin de l'image brute
//image(kinect.getDepthImage(), 0, 0);
// Seuillage de la profondeur
int[] rawDepth = kinect.getRawDepth();
for (int i=0; i < rawDepth.length; i++) {
if (rawDepth[i] >= minDepth && rawDepth[i] <= maxDepth) {
depthImg.pixels[i] = color(255);
} else {
depthImg.pixels[i] = color(0);
}
}
// Dessin de l'image une fois le seuil de profondeur effectué
depthImg.updatePixels();
if (DEBUG) image(depthImg, 0, 0);
fastblur(depthImg, blurSmooth); // flou sur la detection des blobs
theBlobDetection.computeBlobs(depthImg.pixels);
// Dessin des blobs et de leurs centres
if (!DEBUG) drawBlobsAndEdges(true,false); // (Centres, contours)
// Afficher la valeurs des variables pour pouvoir les modifier lorsque le programme est lancé en plein écran
if (DEBUG) {
fill(255,0,0); // Remplir en rouge
text("TILT: " + angle, 10, 20); // afficher la valeur de l'angle
text("THRESHOLD: [" + minDepth + ", " + maxDepth + "]", 10, 36); //Afficher la valeur de minDepth et maxDepth
text("NB: " + theBlobDetection.getBlobNb(), 10, 50); //Afficher le nombre de blob détectés
}
}
// touches qui actionnent le changements des variables
void keyPressed() {
if (key == CODED) {
// Flèche du haut et du bas pour changer l'angle de la kinect
if (keyCode == UP) {
angle++;
} else if (keyCode == DOWN) {
angle--;
}
//L'angle est contraint dans une fourchette de 0 à 30
angle = constrain(angle, 0, 30);
kinect.setTilt(angle);
//Augmenter le minimum de profondeur de 5 avec la touche z
} else if (key == 'z') {
minDepth = constrain(minDepth+5, 0, maxDepth);
//Diminuer le minimum de profondeur de 5 avec la touche a
} else if (key == 'a') {
minDepth = constrain(minDepth-5, 0, maxDepth);
//Augmenter le maximum de profondeur de 5 avec la touche s
} else if (key == 's') {
maxDepth = constrain(maxDepth+5, minDepth, 2047);
//Diminuer le maximum de profondeur de 5 avec la touche q
} else if (key =='q') {
maxDepth = constrain(maxDepth-5, minDepth, 2047);
}
}
// ==================================================
// Méthode drawBlobsAndEdges()
// ==================================================
void drawBlobsAndEdges(boolean _drawBlobs, boolean _drawEdges)
{
noFill();
Blob b;
EdgeVertex eA,eB;
for (int n=0 ; n<theBlobDetection.getBlobNb() ; n++)
{
b=theBlobDetection.getBlob(n);
eA = b.getEdgeVertexA(0);
eB = b.getEdgeVertexB(0);
// centre
if (_drawEdges) drawEdges(b, eA, eB);
// Blobs
if (_drawBlobs) drawCenters(b, eA, eB);
}
}
// ==================================================
// Super Fast Blur v1.1
// by Mario Klingemann
// <http://incubator.quasimondo.com>
// Nous ne sommes pas parvenus à expliquer ce code avec le LFO que nous avons récupéré en l'état, que ce soit pour son fonctionnement précis et son utilité dans l'ensemble du code.
// ==================================================
//Méthode fastblur
void fastblur(PImage img,int radius)//flou en temps réel
{
if (radius<1){
return;
}
int w=img.width;
int h=img.height;
int wm=w-1;
int hm=h-1;
int wh=w*h;
int div=radius+radius+1;
int r[]=new int[wh];
int g[]=new int[wh];
int b[]=new int[wh];
int rsum,gsum,bsum,x,y,i,p,p1,p2,yp,yi,yw;
int vmin[] = new int[max(w,h)];
int vmax[] = new int[max(w,h)];
int[] pix=img.pixels;
int dv[]=new int[256*div];
for (i=0;i<256*div;i++){
dv[i]=(i/div);
}
yw=yi=0;
for (y=0;y<h;y++){
rsum=gsum=bsum=0;
for(i=-radius;i<=radius;i++){
p=pix[yi+min(wm,max(i,0))];
rsum+=(p & 0xff0000)>>16;
gsum+=(p & 0x00ff00)>>8;
bsum+= p & 0x0000ff;
}
for (x=0;x<w;x++){
r[yi]=dv[rsum];
g[yi]=dv[gsum];
b[yi]=dv[bsum];
if(y==0){
vmin[x]=min(x+radius+1,wm);
vmax[x]=max(x-radius,0);
}
p1=pix[yw+vmin[x]];
p2=pix[yw+vmax[x]];
rsum+=((p1 & 0xff0000)-(p2 & 0xff0000))>>16;
gsum+=((p1 & 0x00ff00)-(p2 & 0x00ff00))>>8;
bsum+= (p1 & 0x0000ff)-(p2 & 0x0000ff);
yi++;
}
yw+=w;
}
for (x=0;x<w;x++){
rsum=gsum=bsum=0;
yp=-radius*w;
for(i=-radius;i<=radius;i++){
yi=max(0,yp)+x;
rsum+=r[yi];
gsum+=g[yi];
bsum+=b[yi];
yp+=w;
}
yi=x;
for (y=0;y<h;y++){
pix[yi]=0xff000000 | (dv[rsum]<<16) | (dv[gsum]<<8) | dv[bsum];
if(x==0){
vmin[y]=min(y+radius+1,hm)*w;
vmax[y]=max(y-radius,0)*w;
}
p1=x+vmin[y];
p2=x+vmax[y];
rsum+=r[p1]-r[p2];
gsum+=g[p1]-g[p2];
bsum+=b[p1]-b[p2];
yi+=w;
}
}
}
//les valeurs de profondeur brutes de la kinect
float rawDepthToMeters(int depthValue) {
if (depthValue < 2047) {
return (float)(1.0 / ((double)(depthValue) * -0.0030711016 + 3.3309495161));
}
return 0.0f;
}
// Méthode pour dessiner les contours
void drawEdges(Blob b, EdgeVertex eA, EdgeVertex eB){
strokeWeight(1); //épaisseur du trait
stroke(255); //Contour blanc
noStroke(); // Pas de contour
fill(255); // Remplissage blanc
for (int m=0;m<b.getEdgeNb();m++) // parcours des contours
{
eA = b.getEdgeVertexA(m); // premier sommet
eB = b.getEdgeVertexB(m); // second sommet
// Si les deux sommets ont une valeur valide alors
if (eA !=null && eB !=null) {
strokeWeight(random(5)); // Épaisseur du trait jusqu'à 5
line(eA.x*width, eA.y*height, eB.x*width, eB.y*height);
float ww=random(3); // la variable ww peut aller jusqu'à 3
ellipse(eA.x*width, eA.y*height, ww,ww); // Une éllipse est crée avce comme rayon la variable ww
}
// }
}
}
// Centres
void drawCenters(Blob b, EdgeVertex eA, EdgeVertex eB ){
// déclaration des variables
float resolution = 26; // Nombre de points dans le cercles
float rad = 30; //rayon du cercle
//Variables x et y calculé plus tard en fonction du bruit et de l'amplitude, servant à créer la forme
float x = 1;
float y = 1;
// Variables correspondant au temps, nécessaire pour la crétaion d'un bruit
float t = 0; // temps passé
float tChange = .02; // Vitesse à laquelle le temps passe
float nVal; // Valeur du bruit
float nInt = 2; // intensité du bruit
float nAmp = 2; // amplitud edu bruit
boolean filled = false;
fill(0,7); // Remplir en noir avec une transparence de 7
noStroke(); // Pas de contour
rect(0,0,width, height); // Un rectangle placé à 0,0 de la taille de la fen^être
pushMatrix(); //Enregistre le système de coordonée
translate(width-(b.xMin*width+b.w*width/2),b.yMin*height+b.h*height/2); //Translation
// Conditions réalisés en fonction du booléen filled
if (filled) {
fill(255); //Remplir en blanc
frameRate(2); //deux raffraichissement par seconde
}
else {
noFill(); // Pas de remplissage
stroke(255,255,255,58); // Contour balnc avec une couche alpha de 58
strokeWeight(random(50)); // Épaisseur du trait jusqu'à 50
}
nInt =6; // intensité
nAmp = 2; // amplitude
//Fonction qui sert à dessiner une forme
beginShape();
for (float a=0; a<=TWO_PI; a+=TWO_PI/resolution) {
//La fonction map sert a recartographier des valeurs, ici c'est les valeurs du bruits, compris dans un intervale à un autre
// Elles passent de 0.0, 1.0 à la valeur de nAmp, jusqu'à 1.0
// Ce calcule sert à faire correspondre le buit à l'amplitude
nVal = map(noise( cos(a)*nInt+1, sin(a)*nInt+1, t ), 0.0, 1.0, nAmp, 1.0);
//Calcul de x et et y en fonction de nVal
x = cos(a)*rad *nVal;
y = sin(a)*rad *nVal;
//Construction d'une forme à partir des sommet x et y calculé ci dessus
vertex(x, y);
}
endShape(CLOSE);
// fin du dessin de la forme
popMatrix(); //restaure l'ancien système de coordonées précédement sauvegardé
t += tChange;
println(mouseY);
}

Réalisation techniques

Une Kinect est placée en hauteur, au niveau du passe-câbles (si possible le plus au-dessus de l’œuvre). Celle-ci permet de capter les déplacements des spectateurs.

Un ordinateur est installé sur le passe-câbles au niveau du poteau n°3 (de l'entrée vers le fond de la salle). Un logiciel adapté (Processing3) récupère les données et les retranscrit graphiquement.

Le visuel est projeté sur le mur en face (entre Anne-Valérie GASC et Christof YVORÉ) à l'aide d'un vidéoprojecteur placé au même endroit que l'ordinateur.

Recherches Graphiques

Sources

http://www.openprocessing.org/
Open Processing est un site de partage d'échange et de collaboration pour le logiciel processing. Sur ce site nous avons peu trouver des exemples et des inspirations tel que :

http://www.openprocessing.org/sketch/100840

http://www.openprocessing.org/sketch/44342

http://www.openprocessing.org/sketch/45662

http://www.openprocessing.org/sketch/193411

http://www.openprocessing.org/sketch/180734

http://www.openprocessing.org/sketch/112858

http://www.openprocessing.org/sketch/26072 (particules)

http://www.openprocessing.org/sketch/18798 (attraction/repulsion)

Recherches appropriation kinect: TUIO

http://www.tuio.org/?software

https://code.google.com/p/tuiokinect/

Photos / Vidéos

vid_20151127_180931.mp4 test_kinect.mp4video-1448880486.mp4.mp4 video-1448880469.mp4.mp4 processing_2vid.mp4 processing3.mp4

Conclusion

Contact

Mails étudiants

caroline-brun@live.fr

rafaelle.bosch@gmail.com

amandine.rossignol@hotmail.fr

melodie.souty@gmail.com

avrimake@gmail.com

claire.eyraud@free.fr

Reso-Nance numérique

contact@reso-nance.org

Annabelle Arnaud, responsable projets milieu scolaire et formation

04 91 90 28 72

annabelle.arnaud@fracpaca.org

Stéphanie Putaggio, projets milieu scolaire

04 91 90 28 72

stephanie.putaggio@fracpaca.org