Pages

10 sept. 2009

Motion Tracking avec Processing

Cette semaine je me suis essayé au motion tracking avec Processing.
En fait il est assez facile d'utiliser sur un macbook la webcam intégrée, notament les I/O gérées par Quicktime. (d'ou coup le code est aussi portable sous Windows :) ).
Pour ma part, en implémentant une classe Cible, j'ai pu "traquer" un pixel de couleur donné et raffraichir la position de ma cible s'affichant a l'écran. Le résultat est assez bluffant même s'il peut être facilement amélioré. En fait mon code est plus un proof of concept, n'étant même pas sur que mon code et ma manière de procéder fonctionnerait.
Je ne sais pas si le tracking se fait habituellement comme cela, mais j'ai intuitivement essayé de "balayer" chaque pixel de mon canvas, si je tombe sur un pixel de couleur voulu, par un habil calcul mathématique je trouve les coordonnées de ce point (loooooooool) et le donne ces coordonnées à ma cible, qui se repositionnera en temps voulu ^^. La capture vidéo est assez facile à comprendre, puisqu'en fait on instancie un objet de classe Capture qui va recevoir a chaque "tic" d'horloge une image raffraichie en provenance de la webcam, le tracking s'effectue sur le canvas, et non sur l'objet myCapture.
Dans mon onglet principal, je code le corps de l'application.

D'abord quelques déclarations et import:
import processing.video.*;
PFont maFont;
Capture myCapture;
Cible maCible;
Ceci est la fonction qui serra lue une fois par Processing, elle sert à initialiser nos variables:

void setup()
{
  size(640, 480);
  Point p = new Point(width / 2, height / 2);
  int[] t = new int[3];
  t[0] = 255;
  t[1] = 0;
  t[2] = 0;
  maCible = new Cible(p, t);
  myCapture = new Capture(this, width, height, 30);
}

Cette fonction sert à raffraichir notre objet de capture vidéo:

void captureEvent(Capture myCapture) {
  myCapture.read();
}

La fonction de tracking, qui pour chaque pixel du canvas, va comparer sa couleur, ici si le pixel sur lequel on itère est blanc, alors sa position est envoyé à la cible:

void tracking() {
  int[] t = maCible.getColor();
 
  // dans un premier temps je traque le point que je veux
  loadPixels();
  for(int i = 0; i < width * height; i++) {
    if(pixels[i] == color(255, 255, 255)) {            // <-- couleur du pt a tracker
      Point p = new Point(i % width, i / width);
      maCible.setPos(p);
    }
  }
  updatePixels();
 
  // et je dessine la cible
  maFont = loadFont("SynchroLET-12.vlw");
  textFont(maFont);
  Point pos = maCible.getPos();
  stroke(t[0], t[1], t[2]);
  strokeWeight(1);
  line(pos.x - 30, pos.y - 30, pos.x - 20, pos.y - 30);
  line(pos.x - 30, pos.y - 30, pos.x - 30, pos.y - 20);
  line(pos.x + 30, pos.y - 30, pos.x + 20, pos.y - 30);
  line(pos.x + 30, pos.y - 30, pos.x + 30, pos.y - 20);
  line(pos.x - 30, pos.y + 30, pos.x - 30, pos.y + 20);
  line(pos.x - 30, pos.y + 30, pos.x - 20, pos.y + 30);
  line(pos.x + 30, pos.y + 30, pos.x + 20, pos.y + 30);
  line(pos.x + 30, pos.y + 30, pos.x + 30, pos.y + 20);
  fill(t[0], t[1], t[2]);
  text(pos.x + " - " + pos.y, pos.x + 35, pos.y + 40);
}

La fonction principale, appelée à chaque "tic" d'horloge, et qui permet de contrôler le déroulement de notre application:

void draw() {
  image(myCapture, 0, 0);
  tracking();
  println(get(width / 2, height / 2));
}



Et ma bete classe Cible qui servira à iconer le point que je tracke:


import java.awt.Point;

public class Cible {
    private Point pos;
    private int[] couleur;

    public Cible() {
    pos = new Point();
    couleur = new int[3];
        for(int i = 0; i < 3; i++) {
          couleur[i] = 0;
        }
    }

    public Cible(Point pos, int[] couleur) {
    this.pos = pos;
        this.couleur = new int[3];
        for(int i = 0; i < 3; i++) {
          this.couleur[i] = couleur[i];
        }
    }
   
    public void setPos(Point pos) {
    this.pos = pos;
    }

    public Point getPos() {
    return pos;
    }

    public void setColor(int[] couleur) {
    this.couleur = couleur;
    }

    public int[] getColor() {
    return couleur;
    }

// marche pas, la classe est presque pur java
//    public void afficheToi() {
//    stroke(couleur[0], couleur[1], couleur[2]);
//    line(pos.x - 30, pos.y - 30, pos.x - 20, pos.y - 30);
//    line(pos.x - 30, pos.y - 30, pos.x - 30, pos.y - 20);
//    line(pos.x + 30, pos.y - 30, pos.x + 20, pos.y - 30);
//    line(pos.x + 30, pos.y - 30, pos.x + 30, pos.y - 20);
//    line(pos.x - 30, pos.y + 30, pos.x - 30, pos.y + 20);
//    line(pos.x - 30, pos.y + 30, pos.x - 20, pos.y + 30);
//    line(pos.x + 30, pos.y + 30, pos.x + 20, pos.y + 30);
//    line(pos.x + 30, pos.y + 30, pos.x + 30, pos.y + 20);
//    }

    public String toString() {
    return "Cible:[pos=" + pos.x + ", " + pos.y + ", couleur=(" + couleur[0] + ", " + couleur[1] + ", " + couleur[2] + ")";
    }

}



Aucun commentaire: