OEUVRE INTERACTIVE "PATH"

Porteur du projet : PAPAZIAN Sévan

Date : Janvier 2019

Contexte :Réalisation d'un programme Processing incluant l'utilisation d'une librairie et d'une classe

Intention :

J'ai voulus questionner au travers de mon projet la matérialisation d'un mouvement dans espace numérique. J'ai donc imaginé une installation muséale / oeuvre interactive traduisant un mouvement dans l'espace en un champ vectoriel.

Outils :

La kinect:

La kinect grace à sa caméra infrarouge ma permis de pouvoir capter la profondeur de l'image capté pour mieux appréhender le mouvement du spectateur.

Processing:

Processing est un outil de programmation qui vas me permettre d'exploiter la Kinect, je me suis notamment appuyer sur des programmes déjà developper par Daniel Shiffman qui utilise la Kinect et plus particulièrement la librairie Kinect.

Programme :

Class : KinectTracker

Cette classe crée par Daniel S permet de créer un traqueur, ce traqueur définie une zone la plus proche détectée à l'aide du “treshold” le seuil de profondeur. Cette zone sert à définir deux points, celui qui m'intéresse et le second, c'est celui qui définie le barycentre de la zone détectée. Il est le point de référence a partir du quelle la traduction du mouvement capté vas se faire.

Barycentre : c'est un point qui est définie en interpolant l'ensemble des coordonnées d'une zone ( dans notre cas celles de la zone détectée la plus proche du seuil de profondeur

Constructeur:

KinectTracker : Créer le tracker il utilise les variables display , loc , lerped loc

Attributs:

Loc (Pvector): Position brute

LerpedLoc (Pvector): Interpolation de la position

Deph (entier): Donnée de profondeur

display (Pimage): Ce que le spectateur vas voir

Threshold (entier): Seuil de profondeur

Méthodes:

Track: Défini une zone en fonction de la profondeur et lui attribut un point à l'aide du barycentre

Display: Permet d'afficher ce que capte la kinect

SetTreshold: Permet de controller le seuil de profondeur

// Daniel Shiffman
// Tracking the average location beyond a given depth threshold
// Thanks to Dan O'Sullivan
// https://github.com/shiffman/OpenKinect-for-Processing
// http://shiffman.net/p5/kinect/
class KinectTracker {
// Seuil de profondeur
int threshold = 745;
// position brute( vague)
PVector loc;
// interpolation de la position
PVector lerpedLoc;
// Donnée de profondeur
int[] depth;
// Ce que le spectateur vas voir
PImage display;
KinectTracker() {
// This is an awkard use of a global variable here
// But doing it this way for simplicity
kinect.initDepth();
kinect.enableMirror(true);
// Make a blank image
display = createImage(kinect.width, kinect.height, RGB);
// Set up the vectors
loc = new PVector(0, 0);
lerpedLoc = new PVector(0, 0);
}
void track() {
// definie la profondeur brute dans un tableau d'entier
depth = kinect.getRawDepth();
// faire attention ici
if (depth == null) return;
float sumX = 0;
float sumY = 0;
float count = 0;
for (int x = 0; x < kinect.width; x++) {
for (int y = 0; y < kinect.height; y++) {
int offset = x + y*kinect.width;
// Grabbing the raw depth
int rawDepth = depth[offset];
// Test le seuil de profondeur
if (rawDepth < threshold) {
sumX += x;
sumY += y;
count++;
}
}
}
// tant que l'on touve quelque chose
if (count != 0) {
loc = new PVector(sumX/count, sumY/count);
}
//inerpole la position, le fait arbitrirement pour l'instant
lerpedLoc.x = PApplet.lerp(lerpedLoc.x, loc.x, 0.3f);
lerpedLoc.y = PApplet.lerp(lerpedLoc.y, loc.y, 0.3f);
}
PVector getLerpedPos() {
return lerpedLoc;
}
PVector getPos() {
return loc;
}
void display() {
PImage img = kinect.getDepthImage();
// Being overly cautious here
if (depth == null || img == null) return;
// Going to rewrite the depth image to show which pixels are in threshold
// A lot of this is redundant, but this is just for demonstration purposes
display.loadPixels();
for (int x = 0; x < kinect.width; x++) {
for (int y = 0; y < kinect.height; y++) {
int offset = x + y * kinect.width;
// Raw depth
int rawDepth = depth[offset];
int pix = x + y * display.width;
if (rawDepth < threshold) {
// A red color instead
display.pixels[pix] = color(#FF2739);
} else {
display.pixels[pix] = img.pixels[offset];
}
}
}
display.updatePixels();
// mise en place de l'image
image(display,790, 94, 401,400);
}
int getThreshold() {
return threshold;
}
void setThreshold(int t) {
threshold = t;
}
}

Mon programme

Mon programme permet en utilisant la classe KinecTracker de capter une zone à partir d'un certain seuil de profondeur de cette zone le programme interpole grace au barycente une position sur laquelle elle viens placer un point de couleur bleu ce point bleu définie une position qui est reliée par un vecteur à l'origine de l'image en 0,0 ( en haut à droite ) de cette façon quand la zone détecté se déplace le point et le vecteur se déplace traduisant ainsi le mouvement de la zone détecté en un champ de vecteur et de point

//Sévan PAPAZIAN
//DSAA1 MARSEILLE
//Projet d'oeuvre interractive "PATH"
//Tonerkebab.fr
//Basé sur le programme de: Daniel Shiffman:
// https://github.com/shiffman/OpenKinect-for-Processing
// http://shiffman.net/p5/kinect/
import org.openkinect.freenect.*;
import org.openkinect.processing.*;
PImage img;
float offset = 0;
float easing = 0.05;
int time;
// La programmation de la kinect en elle meme se passe dans les classes suivantes
KinectTracker tracker;
Kinect kinect;
// tableau dynamique pour la mémorisation de la position des points
ArrayList<PVector> dessin;
boolean colorDepth = false;
void setup() {
size(1400, 700);
img = loadImage("grille1.png");
kinect = new Kinect(this);
tracker = new KinectTracker();
// initialisation de la liste vise
dessin = new ArrayList<PVector>();
//Mode de représentation de la profondeur en couleur
kinect.enableColorDepth(colorDepth);
}
void draw() {
fill(#FF2739);
noStroke();
rect(0, 650, 6400, 50);
timer();
// Demarre l'analyse du tracking
tracker.track();
// Montre l'image
tracker.display();
// Dessine un point la localisation interpolé (barycentre) de la zonne la plus proche du seuil de profondeur
PVector v1 = tracker.getPos();
fill(50, 100, 250, 200);
noStroke();
ellipse(v1.x, v1.y, 20, 20);
// Dessine un vecteur de la zone interpolée (le barycentre) de la zone la plus proche du seuil de profondeur à l'origine du schémas
PVector v2 = tracker.getLerpedPos();
dessin.add(v2);
stroke(#FF2739);
line(v2.x, v2.y, 20, 20);
// petite fonctionalité suplémentaire permettant de tracer une ligne avec la souris
if (mousePressed == true) {
stroke(#FF2739);
line(mouseX, mouseY, pmouseX, pmouseY);
}
// permet d'affficher des info comme le seuil de profondeur
int t = tracker.getThreshold();
fill(0);
text("threshold: " + t + " " + "framerate: " + int(frameRate) + " " +
"UP augmente le seuil , DOWN descend le seuil", 10, 680);
}
// Permet d'ajuster le seuil de profondeur " treshold" avec les fleches
void keyPressed() {
int t = tracker.getThreshold();
if (key == CODED) {
if (keyCode == UP) {
t+=5;
tracker.setThreshold(t);
} else if (keyCode == DOWN) {
t-=5;
tracker.setThreshold(t);
}
// Permet de changer le mode de représentation (couleur ou pas ) du display
else if (keyCode == LEFT) {
colorDepth = !colorDepth;
kinect.enableColorDepth(colorDepth);
}
}
}
// timer qui permet de reset le background tout les 500
void timer() {
time++;
if (time>500) {
time = 0;
// saveframe() permet de faire une capture d'écran et de l'enregister dans le dossier parent du programme
saveFrame();
background(250);
image(img,0,0,1280,650);
}
}

Paramètre muséaux

Le timer() permet de reset background éffaçant ainsi le dessin fait et de permettre à un autre spectateur de tester l'installation.

Saveframe() permet de faire une capture de l'image avant le reset permettant au spectateur d'avoir une trace de son mouvement.

Le background() est une image masque qui permet de créer un univers qui alimente mon propos.

void timer() {
time++;
if (time>500) {
time = 0;
// saveframe() permet de faire une capture d'écran et de l'enregister dans le dossier parent du programme
saveFrame();
background(250);
image(img,0,0,1280,650);

keyPressed(), Treshold, colorDepth Dans cette partie du programme on permet de calibrer le seuil de profondeur et le mode de représentation du display ( en couleur ou pas) .Ainsi l'installation peut s'adapter à n'importe lieu d'exposition et vas dans la continuité de ma démarche de créer une oeuvre muséale pouvant être déplacée d'un endroit à un autre.

void keyPressed() {
int t = tracker.getThreshold();
if (key == CODED) {
if (keyCode == UP) {
t+=5;
tracker.setThreshold(t);
} else if (keyCode == DOWN) {
t-=5;
tracker.setThreshold(t);

int t = tracker.getThreshold();
fill(0);
text("threshold: " + t + " " + "framerate: " + int(frameRate) + " " +
"UP augmente le seuil , DOWN descend le seuil", 10, 680);

// Permet de changer le mode de représentation (couleur ou pas ) du display
else if (keyCode == LEFT) {
colorDepth = !colorDepth;
kinect.enableColorDepth(colorDepth);
}

mousePressed()

Cette fonctionnalité n'est pas obligatoire a l'installation elle permet de pouvoir signer son mouvement si le lieu d'installation le permet à l'oeuvre.

if (mousePressed == true) {
line(mouseX, mouseY, pmouseX, pmouseY);
}

Ressources :

path_papazian_se_van_2019.zip

Sources :

https://shiffman.net/

http://marclee.io/en/realtime-stories-mapping-the-free-flow-of-information-around-the-world-in-realtime/

https://vimeo.com/163153865