====== 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. {{:wiki:projets:capture_d_e_cran_2018-11-26_a_15.47.49.png?nolink|}} **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** {{:wiki:projets:path:kinect2-01-18_a_19.55.57.png?nolink|}} 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 {{:wiki:projets:path:capture_d_e_cran_2019-01-18_a_20.02.13.png?nolink|}} //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 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(); //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); } } {{:wiki:projets:path:sans-titre---1.gif?nolink|}} **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. {{:wiki:projets:path:grill.png?nolink&300|}} {{:wiki:projets:path:grille1.png?nolink&300|}} 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. {{:wiki:projets:path:capture_d_e_cran_2019-01-18_a_19.56.34.png?nolink|}} 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); {{:wiki:projets:path:capture_d_e_cran_2019-01-21_a_11.06.41.png?nolink&300|}} {{:wiki:projets:path:capture_d_e_cran_2019-01-21_a_11.06.55.png?nolink&300|}} // 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. {{:wiki:projets:path:capture_d_e_cran_2019-01-18_a_20.12.09.png?nolink|}} if (mousePressed == true) { line(mouseX, mouseY, pmouseX, pmouseY); } ====== Ressources :====== {{:wiki:projets:path: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