Projet tango

De Wikilipo.

Voici une page où mettre les sketches processing sur lesquels nous travaillons:

Sommaire

Cercles par Andreas

ArrayList lastValues = new ArrayList();
int nbCircles = 40;
int diameterIncrement = 20;

void setup() {
  size(400, 400);
  smooth(); 
  noStroke();
  //framerate(60);
  while(lastValues.size()<nbCircles*2){
    lastValues.add(new int[]{0,0});
  }
}

void draw() {
  background(0);
  lastValues.remove(0);
  lastValues.add(new int[]{mouseX,mouseY});
  
  drawCirles(nbCircles, lastValues, diameterIncrement);
}

void drawCirles(int nb,java.util.List lastValues,int diameterIncrement){
  int firstValue = lastValues.size() -1 - (nb-1) * 2;
  int largestDiameter = 40 + diameterIncrement *nb;
  for(int i = 0; i<nb; i++){
    int fillColor = (i%2==0) ? 255 : 0;
    fill(fillColor, fillColor, fillColor);

    int [] xy = (int[])lastValues.get(firstValue + i*2);
    int diameter = largestDiameter - diameterIncrement * i;
    ellipse(xy[0], xy[1], diameter, diameter);
  }
}

Mots_objet, par Ambroise

Ce script n'est pas encore propre. Il s'agit de créer une class d'objets txt, avec un constructeur txt() et une méthode, ecrire(). Ça ne fonctionne pas encore correctement, mais c'est une esquisse pour traiter les mots qui sont tracés sur le fond comme des objets à part entière, qui peuvent donc avoir des comportements variés, indépendamment les uns des autres. Merci de faire vos corrections.

// initialisation du tableau mots contenant des objets de type txt
txt[] mots = new txt[0];
String[] dico = {"tango","salsa","electro"};
PFont myFont = createFont("Monospaced.italic", 30);

void setup() 
{
  size(400, 400);
  background(0.0);
  colorMode(RGB, 1.0);
  noStroke();
  
  textFont(myFont);
  fill(#FF0000);

}

void draw() 
{
  // creer un nouveau mot chaque 1.5 secondes
  if (frameCount % 90 == 0) {
    mots = (txt[]) append(mots, new txt());
  }
  // tracer tous les mots du tableau mots
  // il y a un problème à l'exécution, outofmemory après une quinzaine d'objets créés
  for (int a=0;a<mots.length;a++) {
    mots[a].ecrire();
  }
}

// la classe txt, avec son constructeur txt() qui choisit un mot aléatoirement dans dico
// et une methode ecrire() qui place ce mot à la position de la souris:

class txt {
  int x=mouseX; int y=mouseY;
  int i = int(random(dico.length));
  void ecrire() {
    text(dico[i],x,y);
  }
}

J'ai pas pu m'empêcher de faire une version sans la classe txt, qui ne sert pas encore à grand chose. La classe txt commencera à être utile si on se décide à représenter les mots différemment selon le temps, par exemple si on veut les faire vibrer ou disparaitre.

//version simplifiée du sketch précédent
String[] dico = {"tango","salsa","electro"};

void setup() 
{
  size(400, 400);
  background(0.0);
  colorMode(RGB, 1.0);

  textFont(createFont("Monospaced.italic", 30));
  fill(#FF0000);

}

void draw() 
{
  // creer un nouveau mot chaque 1.5 secondes
  if (frameCount % 90 == 0) {
    int index = int(random(dico.length));
    for(int i=0; i<10;i++){
      //pour répliquer l'effet du sketch original il faut écrire plusieurs fois le même mot
      //au même endroit. Il y a surement une meilleure manière d
      text(dico[index],mouseX,mouseY);
    }
  }
}

brouillon

Ce sketch montre comment obtenir des effets avec lesquels nous avions joué lors de la première réunion processing:

String [] mots = {"bonjour","au revoir","merci"};
int index;

void setup() 
{
  size(400, 400);
  colorMode(RGB, 1.0);
  noStroke();
  PFont myFont = createFont("FFScala", 32);
  textFont(myFont);
}

void draw() 
{
  background(0);
  fill(0.0, 100, 100); 
  
  //plus on s'approche de "l'horizon", plus les mots changent vite
  float distToCenter = abs(mouseY - width/2.0)/width;
  int refreshPeriod = max(1,(int)(50 * distToCenter));
  if(frameCount % refreshPeriod== 0){
    index = (int)random(mots.length);
  }
  String mot = mots[index];
  
  //rotation du mot sur lui-même
  translate(mouseX,mouseY);
  rotate(TWO_PI*(frameCount%200/200.0));
  text(mot,-textWidth(mot)/2, 8); 
}

Glissant

/* glissant.pde
   Ce sketch utilise la librairie minim 
   http://code.compartmental.net/minim/distro/minim-2.0.2-lib.zip
   Il faut mettre cette librairie dans repertoire nommé libraries.
   Il faut aussi mettre le tango (ici "Isabela.mp3") dans un repertoire data. 
   Ca marche pas mal avec http://www.archive.org/details/Tango_de_Liliane___Marcos_Oliva
   Ca marche un peu avec http://www.archive.org/details/Isabela
   Le tango peut être au format WAV, AIFF, AU, SND, ou MP3.
   On a donc cette structure de repertoires:
   glissant -|
             |- glissant.pde
             |- data -|
                      |- Isabela.mp3
             |- librairies -|
                            |- minim -| (etc..)

PDF, le 18.3.10 - Note aux utilisateurs de Mac OSX : le chemin pour le fichier son (en tenant compte
de la structure proposée ci-dessus) doit avoir une syntaxe très explicite du type :
"/Users/votre_identifiant/Documents/Processing/data/fichier_son-tango.aif"
   
*/

import ddf.minim.*;
import ddf.minim.analysis.*;

String fichier_du_tango = "Isabela.mp3";

int tempsDernierMot = 0;

Minim minim;
AudioSource in;
BeatDetect beat;
BeatListener bl;

String [] dico = {"bonjour","au revoir","merci"};
Txt[] mots = new Txt[0];

int index;
AngleMeter angleMeter = new AngleMeter(20);

void setup() 
{
  size(600, 450);
  
  minim = new Minim(this);
  // get a stereo line-in: sample buffer length of 2048
  // default sample rate is 44100, default bit depth is 16
  //in = minim.getLineIn(Minim.STEREO, 2048);
  in = minim.loadFile(fichier_du_tango, 2048);
  ((AudioPlayer)in).play();
  // a beat detection object that is FREQ_ENERGY mode that 
  // expects buffers the length of song's buffer size
  // and samples captured at songs's sample rate
  beat = new BeatDetect(in.bufferSize(), in.sampleRate());
  // set the sensitivity to 300 milliseconds
  // After a beat has been detected, the algorithm will wait for 300 milliseconds 
  // before allowing another beat to be reported. You can use this to dampen the 
  // algorithm if it is giving too many false-positives. The default value is 10, 
  // which is essentially no damping. If you try to set the sensitivity to a negative value, 
  // an error will be reported and it will be set to 10 instead. 
  beat.setSensitivity(300);  
  // make a new beat listener, so that we won't miss any buffers for the analysis
  bl = new BeatListener(beat, in);  
  
  //colorMode(RGB, 1.0);
  PFont myFont = createFont("FFScala", 32);
  textFont(myFont);
  fill(255); 
}

void draw() 
{
  background(0);

  //rotation
  angleMeter.drag(mouseX,mouseY);
  //pour voir la "regle" qu'on traine
  //angleMeter.draw();
 
 boolean battement = beat.isRange(0,5,1); //arguments: low_band, high_band, threshold
 //boolean battement = beat.isKick();
 if(battement){
    //pour dessiner un rectangle quand on voit un battement    
    rect(0,width/2,20,20); 
 }
 //pour voir les bandes qu'on peut donner en argument à isRange (?)
 beat.drawGraph(this);

 //if(frameCount > tempsDernierMot+20 && beat.isKick()){
 if(frameCount > tempsDernierMot+20 && battement){
    
    index = (int)random(dico.length);
    mots = (Txt[]) append(mots, new Txt(mouseX,mouseY,angleMeter.angle,dico[index]));
    tempsDernierMot = frameCount;
  }

  // tracer tous les mots du tableau mots
  for (int a=0;a<mots.length;a++) {
    mots[a].ecrire();
  }
}

class Txt {
  int x,y;
  float angle;
  String palabra;  
  float originalDrift = 100;
  float drift = originalDrift;
  
  
  Txt(int x, int y, float angle, String palabra){
    this.x = x;
    this.y = y;
    this.angle = angle;
    this.palabra = palabra;
  }
  
  void ecrire() {
    drift = drift*0.99;
    pushMatrix();
    translate(x,y);
    rotate(angle);
    text(palabra,-textWidth(palabra)+originalDrift-drift,0);
    popMatrix();
  }
}

/** Une espece de regle qu'on traine traine derriere soi 
    pour savoir dans l'angle dans lequel on se deplace**/
class AngleMeter{
  float [] draggedPoint = new float[2];
  float angle, distance;
  AngleMeter(float distance){
    this.distance = distance;
  }
  void drag(float x, float y){
    angle = atan2(y - draggedPoint[1],x - draggedPoint[0]);  
    draggedPoint[0] = x - (cos(angle) * distance);
    draggedPoint[1] = y - (sin(angle) * distance);
  }
  void draw(){
    strokeWeight(2.0);
    stroke(145,0,0);
    pushMatrix();
    translate(draggedPoint[0],draggedPoint[1]);
    rotate(angle);
    line(0, 0, distance, 0);
    popMatrix();
    noStroke();
  }
}


class BeatListener implements AudioListener
{
  private BeatDetect beat;
  private AudioSource source;
  
  BeatListener(BeatDetect beat, AudioSource source)
  {
    this.source = source;
    this.source.addListener(this);
    this.beat = beat;
  }
  
  void samples(float[] samps)
  {
    beat.detect(source.mix);
  }
  
  void samples(float[] sampsL, float[] sampsR)
  {
    beat.detect(source.mix);
  }
}

Capture par Nicolas

// Programme de capture vidéo de deux positions par couleur
import JMyron.*;
import blobDetection.*;


//*********** PARAMETRES ***************

//------- Debug -----------
boolean retourEcran = false; //mettre true pour voir les niveaux de gris du blob
boolean retourImageNonFiltree = false; //mettre true pour juste voir le retour video (pas de blob)
int couleurTestee = 1; //1 ou 2. On affiche les blobs seulement pour cette couleur.
int crossSize = 50; //taille de la mire pendant le test.

//-------- capture ---------

//-- capture 1
//couleur de référence, en hue, saturation, brightness
int h1 = 251;
int s1 = 246;
int b1 = 255;

//* seuil de distance entre couleurs; plus c'est petit, plus la tolérance est faible (donc écran blanc)
int seuil1 = 102;

//* seuil pour la détection des blobs (entre 0 et 1)
float seuilBlob1 = 0.7;

//-- capture 2
//couleur de référence
int h2 = 0;
int s2 = 15;
int b2 = 130;

//* seuil de distance entre couleurs 
int seuil2 = 25;

//* seuil pour la détection des blobs (entre 0 et 1)
float seuilBlob2 = 0.65;

//* en relatif (entre 0 et 1), taille du côté d'un carré représentant la surface minimale d'un blob acceptable.
float seuilTailleBlob = 0.017;
//float seuilTailleBlob = 0.0;

//------- taille camera -----------
int camW = 320;
int camH = 240;


//------- taille écran -----------
int screenW = 1280;
int screenH = 800;

//------- transformation de coordonnées -----//

//* value of translation for X
float transX = 0;
//** value of multiplication for X
float multX = 1;
//* value of translation for Y
float transY = 0;
//** value of multiplication for Y
float multY = 1;



//*********** VARIABLES ***************

JMyron m;//a camera object

BlobDetection theBlobDetection;

int xPrecedent1 =0;
int yPrecedent1 = 0;
int xPrecedent2 =0;
int yPrecedent2 = 0;


//************** PROGRAMME ************


// ==================================================
// Initialise la capture de positions.
// A appeler dans le setup.
// ==================================================
void initCapture(){
  size( screenW ,screenH );
  
  m = new JMyron();//make a new instance of the object
  m.start(camW,camH);//start a capture
  m.findGlobs(0);
  
  noFill();
  
  theBlobDetection = new BlobDetection( camW , camH );
}


// ==================================================
// Capture deux positions
// Renvoie un tableau de tableau, contenant les deux points.
// ==================================================
int[][] getSpots()
{
  int[][] result = new int[2][2]; 
  int[] spot1 = new int[2];
  int[] spot2 = new int[2];
  
  m.update();
  int[] img = m.image();
  
  // l'ordre change selon la couleur testée. Inutile en dehors du debuggage. 
  if ( couleurTestee == 1 )
  {
     spot2 = getSpot( 2 , img , h2 ,s2 , b2 , seuil2 , seuilBlob2 , xPrecedent2 , yPrecedent2 );  
     spot1 = getSpot( 1 , img , h1 , s1 , b1 , seuil1 , seuilBlob1 , xPrecedent1 , yPrecedent1 );
  }
  else
  {
    spot1 = getSpot( 1 , img , h1 , s1 , b1 , seuil1 , seuilBlob1 , xPrecedent1 , yPrecedent1 );   
    spot2 = getSpot( 2 , img , h2 , s2 , b2 , seuil2 , seuilBlob2 , xPrecedent2 , yPrecedent2 );  
   }
  
 
  if ( ( spot1[0] != 0 ) || ( spot1[1] != 0 ) )
  {
    
    // élimine les déplacements trop petits. si 0, aucun effet.
    if ( ( abs( spot1[0] - xPrecedent1  ) <=0 ) && ( abs( spot1[1] - yPrecedent1 )  <=0 ) ) //ICI
    {
      spot1[0] = xPrecedent1;
      spot1[1] = yPrecedent1;  
    }
    else
    {
      xPrecedent1 = spot1[0];
      yPrecedent1 = spot1[1];
    }
    
  }
  else
    println(" NULL " + spot1[0] + "  " + spot1[1] ); //pas de blob trouvé
  
  //Remarque: filtre des petits déplacements non fait pour le deuxième point.
  if ( ( spot2[0] != 0 ) || ( spot2[1] != 0 ) )
  {  
    xPrecedent2 = spot2[0];
    yPrecedent2 = spot2[1];
  }
  

  // convertit dans la résolution de l'écran
  result[0] = coordConvert( spot1 );
  result[1] = coordConvert( spot2 );
  
  return result;
}

//
// Funtion that get one colored spot.
//
int[] getSpot( int index , int[] img , int hu , int sat , int bri , int seuilCouleur , float seuilBlob , int xPrec , int yPrec )
{
  int[] result = new int[2];
  int[] filteredImage = new int[camW * camH];
  int xSpot = 0;
  int ySpot = 0;


  
  //***** construction de l'image filtrée *****
//The red() function is easy to use and undestand, but is slower than another technique. To achieve the same results when working in colorMode(RGB, 255), but with greater speed, use the >> (right shift) operator with a bit mask. For example, the following two lines of code are equivalent:
//
//float r1 = red(myColor);
//float r2 = myColor >> 16 & 0xFF;  

  
   for (int i = 0; i < camW * camH; i++)
   {
   
   
     //float dist = colorDistance( (int)red(img[i]) , (int)green(img[i]) , (int)blue(img[i]) , r , g , b );
     
     float dist = colorDistanceHue ( (int)hue(img[i]) , (int)saturation(img[i]) , (int)brightness(img[i]) , hu , sat , bri );
     
     float ratio = 255/(float)seuilCouleur;

     if ( dist < seuilCouleur )
     {
          filteredImage[i] = color(ratio*dist , ratio*dist , ratio*dist); //niveau de gris
     }
      else
      {
          filteredImage[i] = color(255, 255, 255); //blanc
      }
    
    }
  
  //***** détection des blobs *********
  theBlobDetection.setThreshold( seuilBlob ); // will detect bright areas whose luminosity > 0.2f;
  theBlobDetection.computeBlobs(filteredImage);
  
  //******* détermination de la position ********
  int nbBlobs = theBlobDetection.getBlobNb ();
  
  if ( index == couleurTestee )
    print("|" + nbBlobs );
  
  int indexMin = -1;
  if ( nbBlobs > 0 )
  {
    float distMin = 1000000000;
    float currentDist;
    int xCurrentBlob , yCurrentBlob;
    Blob currentBlob;
    //on prend le blob qui est plus proche du spot précédent (évite les accoups)
    for (int indexBlob=0; indexBlob < nbBlobs; indexBlob++)
    {
      currentBlob = theBlobDetection.getBlob( indexBlob );
      xCurrentBlob = (int)( currentBlob.x * camW );
      yCurrentBlob = (int)( currentBlob.y * camH );
      
      currentDist = sq( xCurrentBlob - xPrec ) + sq( yCurrentBlob - yPrec );
      
      if ( currentDist < distMin )
      {
        if ( currentBlob.w * currentBlob.h > seuilTailleBlob*seuilTailleBlob )
        {
          distMin = currentDist;
          indexMin = indexBlob;
        }
      }     
    }
    if ( indexMin > -1 )
    {
    
      xSpot = (int)( theBlobDetection.getBlob(indexMin).x * camW );
      ySpot = (int)( theBlobDetection.getBlob(indexMin).y * camH ); 
  
      result[0] = (int)( theBlobDetection.getBlob(indexMin).x * camW );
      result[1] = (int)( theBlobDetection.getBlob(indexMin).y * camH );   
    }
  }
  
  /******** visu pour réglages ********/
  if ( retourEcran)
  {
    PImage imageRetour = createImage(camW, camH, RGB);
    for (int i = 0; i < camW * camH; i++)
    {
      imageRetour.pixels[i] = filteredImage[i];
      if ( retourImageNonFiltree )
        imageRetour.pixels[i] = img[i];
    }  
   
    imageRetour.resize (screenW , screenH );
    loadPixels();
    for (int i = 0; i < screenW * screenH; i++)
    {
      pixels[i] = imageRetour.pixels[i];
    }   
    updatePixels();
    
    int[] ScreenCoordinateSpot;
    
    ScreenCoordinateSpot = coordConvert( result );
    
    int xSpot2 = ScreenCoordinateSpot[0];
    int ySpot2 = ScreenCoordinateSpot[1];
    
    line( xSpot2 , ySpot2-crossSize , xSpot2 , ySpot2 + crossSize );
    line( xSpot2-crossSize , ySpot2 , xSpot2 + crossSize , ySpot2 );
    if ( index == 1 )
      ellipse( xSpot2 , ySpot2 , crossSize , crossSize );
//    drawBlobsAndEdges( true , false );
      
  }
  
    
  return result;    

}


// computes the distance betwwen two colors //UNUSED
float colorDistance( int r1 , int g1 , int b1 , int r2 , int g2 , int b2 )
{
  return sqrt(
                  sq( r1 - r2 ) +
                  sq( g1 - g2 ) +
                  sq( b1 - b2 ));
}

// computes the distance betwwen two colors - HSB mode
float colorDistanceHue( int h1 , int s1 , int b1 , int h2 , int s2 , int b2 )
{
    return sqrt(
                  sq( h1 - h2 ) +
                  sq( s1 - s2 ) +
                  sq( b1 - b2 ));
}


// ==================================================
// coordConvert()
//
//  Converts from cam coordinates to screen coordinates
//
// ==================================================
int[] coordConvert( int[] p )
{
    int[] p2 = new int[2];
    
//    p2[0] = p[0]*screenW/camW;
//    p2[1] = p[1]*screenH/camH;
    
    p2[0] = (int)( transX + multX * p[0]*screenW/camW );
    p2[1] = (int)( transY + multY * p[1]*screenH/camH );
    
    return p2;
}
void keyPressed() {
 
 
  if (key == 's' ) {
      m.settings();
    }
}

cercles d'Andreas appelant la capture de Nicolas

ArrayList lastValues = new ArrayList();
int nbCircles = 30;
int diameterIncrement = 40;

int xDanseur1;
int yDanseur1;

void setup() {
  //size(400, 400);
  initCapture();
  smooth(); 
  noStroke();
  //framerate(60);
  while(lastValues.size()<nbCircles*2){
    lastValues.add(new int[]{screenW/2, screenH/2});
  }
}

void draw(){
  cerclesDraw();
}

void cerclesDraw() {
// Si on veut juste la version souris:
//  xDanseur1 = mouseX;
//  yDanseur1 = mouseY;

  int [][] spots = getSpots();
  
  // on ne prend pas les blobs à l'origine (pas de blob détecté)
  if ( spots[0][0] != 0 )
  {
    xDanseur1 = spots[0][0];
    yDanseur1 = spots[0][1];
  }
  
  background(255);
  lastValues.remove(0);
  lastValues.add(new int[]{xDanseur1,yDanseur1});
  
  drawCirles(nbCircles, lastValues, diameterIncrement);
}

void drawCirles(int nb,java.util.List lastValues,int diameterIncrement){
  int firstValue = lastValues.size() -1 - (nb-1) * 2;
  int largestDiameter = 80 + diameterIncrement *nb;
  for(int i = 0; i<nb; i++){
    int fillColor = (i%2==0) ? 0:255;
    fill(fillColor, fillColor, fillColor);

    int [] xy = (int[])lastValues.get(firstValue + i*2);
    int diameter = largestDiameter - diameterIncrement * i;
    ellipse(xy[0], xy[1], diameter, diameter);
  }
}

autres

webcam avec delai