[Non résolu] "Arduino, Processing, tilt pan et face tracking"

Bonjour.
Bon, voici plusieurs jours que je m'obstine et que je cherche comment faire un simple face tracking avec Arduino, Processing et 2 servomoteurs pour le tilt et le pan. J'ai bien demandé de l'aide sur youtube à tous ceux qui y sont parvenu mais pas de réponses. Donc, je viens chercher de l'aide ici, en espérant qu'une âme charitable voudrait bien me (nous) faire partager ses précieux codes pour Arduino et Processing.

Merci

En attendant voici mon code pour Arduino :

#include <Servo.h>

Servo vert;
Servo horz;
byte xy[2];

void setup() {
  horz.attach(5);
  vert.attach(6);
  
  Serial.begin(9600);
  
}

void loop() {
  
  
  if(Serial.available() > 1) {
    xy[0] = Serial.read();
    xy[1] = Serial.read();
    
    // now that we have the xy we can go ahead
    // and write them to the serial
    float ratio = 180.0/255.0;
    horz.write(xy[0] * ratio);
    vert.write(xy[1] * ratio);
    delay(25);
  }
  
}

Il faudrait peut être nous dire ce qui ne vas pas. Quel est ton problème?

Et bien, j'aimerai trouver un code fonctionnel pour Processing. Pour le moment dès que ma webcam détecte mon visage, elle le fuit et ne reste pas dessus. Suis-je si moche que ça ??? :grin:

Voici un code pour Processing que j'ai trouvé je ne sais plus où et je ne sais pas trop quoi corriger pour le rendre fonctionnel.

import hypermedia.video.*;
import java.awt.Rectangle;
import processing.serial.*;   

int gx = 15;
int gy = 35;
int spos=90;


Serial port; 

OpenCV opencv;

// contrast/brightness values
int contrast_value    = 0;
int brightness_value  = 0;



void setup() {

    size( 320, 240 );

    opencv = new OpenCV( this );
    opencv.capture( width, height );                   // open video stream
    opencv.cascade( OpenCV.CASCADE_FRONTALFACE_ALT );  // load detection description, here-> front face detection : "haarcascade_frontalface_alt.xml"


    // print usage
    println( "Drag mouse on X-axis inside this sketch window to change contrast" );
    println( "Drag mouse on Y-axis inside this sketch window to change brightness" );
println(Serial.list());
 port = new Serial(this, Serial.list()[2], 9600);
}


public void stop() {
    opencv.stop();
    super.stop();
}



void draw() {

    // grab a new frame
    // and convert to gray
    opencv.read();
    opencv.convert( RGB );
    opencv.contrast( contrast_value );
    opencv.brightness( brightness_value );

    // proceed detection
    Rectangle[] faces = opencv.detect( 1.2, 2, OpenCV.HAAR_DO_CANNY_PRUNING, 40, 40 );

    // display the image
    image( opencv.image(), 0, 0 );

    // draw face area(s)
    noFill();
    stroke(255,1,1);
    for( int i=0; i<faces.length; i++ ) {
        rect( faces[i].x, faces[i].y, faces[i].width, faces[i].height ); 
    }

}


/**
 * Changes contrast/brigthness values
 */
void mouseDragged() {
    contrast_value   = (int) map( mouseX, 0, width, -128, 128 );
    brightness_value = (int) map( mouseY, 0, width, -128, 128 );
}

void update(int x) 
{
  //Calculate servo postion from mouseX
  spos= x/30;

  //Output the servo position ( from 0 to 180)
  port.write(spos + "a"); 
    gx = x/80;
  gy = 100-x/120;

}

Ce qu'il te faut c'est la lib JMyron pour Processing :

http://webcamxtra.sourceforge.net/download.shtml

Ou la lib OpenCV toujours pour Processing (celle ci fait de la face detection) :

http://ubaa.net/shared/processing/opencv/

J'ai déjà les 2 lib d'installées . Ce que j'aimerai, c'est que mes 2 servo Tilt et Pan qui sont monté sur une tourelle avec une Webcam suive un visage. Un simple face tracking avec une tourelle. Mais merci quand même.

De mémoire, il me semble que Opencv décrit un rectangle autour du visage détecté, il faut déterminer les coordonnées du centre de ce rectangle pour ensuite les comparer avec le centre de ton écran.
Si les coordonnées ne sont pas similaires (avec une petite marge pour ne pas toujours affoler tes servos :grin:), tu fais bouger tes servos afin de rapprocher ces coordonnées.

Donc:

  1. déterminer les coordonnées du centre de ton visage.
  2. faire une fonction qui calcule la xy de tes coordonnées visage/écran.
  3. faire une fonction qui fera bouger tes servos pour diminuer cette différence.

Jean-François:

  1. déterminer les coordonnées du centre de ton visage.
  2. faire une fonction qui calcule la xy de tes coordonnées visage/écran.
  3. faire une fonction qui fera bouger tes servos pour diminuer cette différence.

Pas facile tout ça pour un débutant. Mais je vais quand même essayer.

Je n'ai pas tout compris dans ton code mais il y a quelque chose qui me chiffonne.
Le servo attend un angle, il devrait y avoir quelque part une fonction qui calcule un angle en fonction de la coordonnée du centre de la fenêtre.

Bonjour,

avec nos petits amis du tetalab, nous avons réalisé quelques chose dans l'esprit,
nous avons équipé un masque réalisé par un artiste (Elie Cinqpeyres) avec des yeux motorisés par un servo qui suivent la position d'un visage repéré par une webcam et processing,
il y a des led dans les yeux dont la luminosité augmente avec la distance du visage
c'est surement très perfectible, mais peut être que cela peux te servir de base

http://git.tetalab.org/index.php/p/eyesbot/source/tree/master/

Code Arduino (dérivé du code sweep en exemple)

// Sweep
// by BARRAGAN <http://barraganstudio.com> 

#include <Servo.h> 
 
Servo myservo;  // create servo object to control a servo 
                // a maximum of eight servo objects can be created 
 
int val1 = 0;    // variable to store the servo position or led brightness

int val2 = 0;
int L=0;
void setup() 
{ 
  
  Serial.begin(115200);
  myservo.attach(11);  // attaches the servo on pin 9 to the servo object 
} 
 
void loop() 
{ 
 /**/
  if (Serial.available() > 0) {
		// read the incoming byte:
		val1 = int(Serial.read());
                val2 = int(Serial.read());
                
                Serial.print("I received: ");
                 Serial.print(val1);
		//Serial.println(pos, DEC);

  }
 /**/
  
  

              
       
    //val1=240;          
              
    if (val1<181){         
      myservo.write(val1);
      L = map(val2, 181, 255, 0, 255); 
      analogWrite(3, L);
      //analogWrite(10, L);
    }else{
      myservo.write(val2);
      L = map(val1, 181, 255, 0, 255); 
      analogWrite(3, L);
      //analogWrite(10, L); 
      //Serial.println(L);
    }
    // tell servo to go to position in variable 'pos' 
    delay(15);                       // waits 15ms for the servo to reach the position 




}

Code Processing

import processing.serial.*;
import hypermedia.video.*;
import java.awt.Rectangle;



OpenCV opencv;

int contrast_value    = 0;
int brightness_value  = 0;

int eyesX;
int eyesL;
float pupilX = map(mouseX, 0, width, 215, 265);
  float pupilY = map(mouseY, 0, height, 200, 240);
  float pupilA = map(mouseX, 0, width, 375, 425);


//serial
Serial myPort;

void setup() {

    size( 320, 240 );

    myPort=new Serial(this,"/dev/ttyUSB0",115200);

    opencv = new OpenCV(this);
    opencv.capture( width, height );
    opencv.cascade( OpenCV.CASCADE_FRONTALFACE_ALT );    // load the FRONTALFACE description file
}

void draw() {
    
    opencv.read();
    
    
    opencv.contrast( contrast_value );
    opencv.brightness( brightness_value );
    image( opencv.image(), 0, 0 );
    // detect anything ressembling a FRONTALFACE
    Rectangle[] faces = opencv.detect(1.2,2,OpenCV.HAAR_DO_CANNY_PRUNING,40,40);
    
    // draw detected face area(s)
    noFill();
    stroke(255,0,0);
    //for( int i=0; i<faces.length; i++ ) {
    if (faces.length>0) {
        int i=0;
        rect( faces[i].x, faces[i].y, faces[i].width, faces[i].height ); 
        pupilX = map((faces[i].x), 0, width, 215, 265);
        pupilY = map((faces[i].y), 0, height, 205, 240);
        eyesX = round(map((faces[i].x), 0, width, 180, 20));
        eyesL = round(map((faces[i].width), 40, 200,181, 254));
        //println((faces[i].width));
  }
  fill(255);
  ellipse(240, 225, 80, 80); //left eye
  ellipse(400, 225, 80, 80); //right eye
  fill(0);
  println(eyesX);
  //println(eyesL);
  ellipse(pupilX, pupilY, 20, 20); //left pupil
  ellipse(pupilA, pupilY, 20, 20); //right pupil
   
myPort.write(eyesX);
myPort.write(eyesL); 
    
}


void mouseDragged() {
    contrast_value   = (int) map( mouseX, 0, width, -128, 128 );
    brightness_value = (int) map( mouseY, 0, width, -128, 128 );
}

Bon courage,

Lionel

Merci beaucoup Lionel de me faire profiter de tes codes. Je pratique la peinture et la sculpture et j'adore l'art dans toutes ses formes, mais aussi la robotique XD. Existe-il un lien pour voir un vidéo ou des photos de cette création ?

Merci encore, ça devrait m'aider à avancer. :wink:

Salut,

nop, j'ai pas de photo/vidéo directement sous la main,
je vais essayer d'en prendre demain soir.

Bonsoir,

bon bon ...., je tâtonne, c'est pas évident quand on débute. Pour le moment je détecte bien mon visage ainsi que le centre de celui-ci avec cette ligne rect( faces[i].x + 0.5*faces[i].width, faces[i].y + 0.34*faces[i].height, 5, 5 ); Mais pour le reste Need Help !!! J'ai rajouté les int pan et tilt, mais je ne sais pas quoi faire pour que mes servos suivent le centre du visage.

import hypermedia.video.*;
import java.awt.Rectangle;
import processing.serial.*;

OpenCV opencv;

int panDroite;
int panGauche;
int tiltHaut;
int tiltBas;


Serial myPort;

void setup() {

    size( 320, 240 );

    myPort=new Serial(this, Serial.list()[2], 9600);
 
    opencv = new OpenCV( this );
    opencv.capture( width, height );                   // open video stream
    opencv.cascade( OpenCV.CASCADE_FRONTALFACE_ALT );  // load detection description, here-> front face detection : "haarcascade_frontalface_alt.xml"
}


public void stop() {
    opencv.stop();
    super.stop();
}


void draw() {

    opencv.read();
    opencv.convert( RGB );
 

    // proceed detection
    Rectangle[] faces = opencv.detect( 1.2, 2, OpenCV.HAAR_DO_CANNY_PRUNING, 40, 40 );

    // display the image
    image( opencv.image(), 0, 0 );

    // draw face area(s)
    noFill();
    stroke(255,0,0);
    for( int i=0; i<faces.length; i++ ) {
        rect( faces[i].x, faces[i].y, faces[i].width, faces[i].height ); 
        rect( faces[i].x + 0.5*faces[i].width, faces[i].y + 0.34*faces[i].height, 5, 5 ); //détermine les coordonnées du centre du visage
    }
}

Petite question à snootlab, (ou à ceux qui pourront m'éclairer). J'aimerai comprendre à quoi correspond précisément cette partie de ton code.

pupilX = map((faces[i].x), 0, width, 215, 265);
        pupilY = map((faces[i].y), 0, height, 205, 240);
        eyesX = round(map((faces[i].x), 0, width, 180, 20));
        eyesL = round(map((faces[i].width), 40, 200,181, 254));

Merci

Je rame... je rame... :roll_eyes:

Bonjour,
Alors j'ai consulté le codeur.

il m'a dit :

pupilX et pupilY sont les coordonnées de la détection dans l'animation processing
eyesX et eyesY c'est un mapping [N.D.T : changement d'échelle] de ces coordonnées sur une plage différente pour les envoyer sur l'arduino

et aussi

(...) et on avait bricolé pour avoir des seuils sympas
(...)
d'ou le mapping avec des nombres zarbi

après il traîne aussi des bout de code de ça :
http://www.openprocessing.org/visuals/?visualID=7078
qu'il avait mis en place pour débugger le code (plutot que du println...)

Merci Snootlab pour tes précisions. Ce que je ne comprends pas , c'est pourquoi ma webcam s'agite dans tous les sens dés qu'elle perçoit mon visage, aussi bien avec tes codes Arduino et Processing qu'avec ce que j'ai fais ?

Salut à tous, j'ai enfin fais un pas en avant. En faites, mon problème venait de mon code Arduino et pas de Processing :~ . Pour le moment, mes codes fonctionnent avec le servo Pan et suit un visage de droite à gauche. J'aimerai faire la même chose avec le servo Tilt mais j'ai un peu de mal. Si quelqu'un pouvait jeter un oeil ( voir les deux :wink: ) ça pourrait pas mal m'avancer. Merci
Voici les codes :

ARDUINO

#include <Servo.h> // Include the servo library

Servo panServo; // Create a new servo object
Servo tiltServo;

char incomingData[4] = {
  0, 0, 0, 0}; // A buffer to store the ASCII value read in from the serial port
int distance = 0; // The distance of the object from the center of the screen
int panAngle = 90; // The current angle of the servo
int tiltAngle = 90;
int i = 0; // counter


void setup(){
  Serial.begin(9600); // Open the serial port with a 9600 baud rate
  Serial.println("Serial port ready"); // Print on screen
  panServo.attach(9); 
  tiltServo.attach(10);
  panServo.write(panAngle);
  tiltServo.write(tiltAngle); // Set the starting angle at 90 degrees
}

void loop(){
 
  if (Serial.available()){
    
    while (i < 4){
      incomingData[i] = Serial.read(); // Assign the input value to the incomingData buffer
      i++; // Increment the counter
    }

    distance = atoi(incomingData); // Convert ASCII to Int

    // pan
    if (distance < 15){
      panAngle++;
      panAngle = constrain(panAngle, 0, 180); // Constrain the value of panAngle to within 0-180 degrees
      panServo.write(panAngle);
    }
    else if (distance > 15){
      panAngle--;
      panAngle = constrain(panAngle, 0, 180); // Constrain the value of panAngle to within 0-180 degrees
      panServo.write(panAngle);
    }
       
    //tilt
       if (distance < 15){
      tiltAngle++;
      tiltAngle = constrain(tiltAngle, 0, 180); // Constrain the value of panAngle to within 0-180 degrees
      tiltServo.write(tiltAngle);
    }
    else if (distance > 15){
      tiltAngle--;
      tiltAngle = constrain(tiltAngle, 0, 180); // Constrain the value of panAngle to within 0-180 degrees
      tiltServo.write(tiltAngle);
    }
  }

  i = 0; // Reset the counter
  delay(50); // Delay 20ms to allow the servo to rotate to the position
}

PROCESSING

import hypermedia.video.*;
import java.awt.Rectangle;
import processing.serial.*;        

int gx = 15;
int gy = 35;
int spos=90;

Serial port; 
OpenCV opencv;

void setup() {

    size( 320, 240 );

    opencv = new OpenCV( this );
    opencv.capture( width, height );                   // open video stream
    opencv.cascade( OpenCV.CASCADE_FRONTALFACE_ALT );  // load detection description, here-> front face detection : "haarcascade_frontalface_alt.xml"

 println(Serial.list());
 port = new Serial(this, Serial.list()[2], 9600);
}

public void stop() {
    opencv.stop();
    super.stop();
}

void draw() {

    // grab a new frame
    // and convert to gray
    opencv.read();
    opencv.convert( RGB );
   
   // proceed detection
    Rectangle[] faces = opencv.detect( 1.2, 2, OpenCV.HAAR_DO_CANNY_PRUNING, 40, 40 );

    // display the image
    image( opencv.image(), 0, 0 );

    // draw face area(s)
    noFill();
    stroke(255,0,0);
    for( int i=0; i<faces.length; i++ ) {update( faces[i].x); 
        rect( faces[i].x, faces[i].y, faces[i].width, faces[i].height ); 
    }
}


void update(int x) 
{
 spos= x/4;

//Output the servo position ( from 0 to 180)
  port.write(spos + "a"); 
    gx = x/2;
  gy = 100-x/2;

}

Flûte, j'ai oublié de détailler le problème et ça va pas être simple à expliquer :roll_eyes: En faites pour le Pan, pas de problème, ma webcam suit mon visage de droite à gauche. Ce qui me pose problème c'est le Tilt, c'est à dire de haut en bas. Quand mon visage est détecté et que je le promène de droite à gauche et de haut en bas, ma webcam monte quand mon visage est gauche et descend quand il est à droite et peu importe qu'il soit en haut ou en bas . Alors, qu'elle devrait monter quand mon visage est en haut et descendre quand il est en bas et peu importe qu'il soit à droite ou à gauche. A votre avis cela vient de mon code Arduino ou de Processing ?

PS: Je pense que mon problème vient de mon code Processing car il a d'abord été écris pour un servomoteur en Pan. Une idée sur le code à rajouter pour le tilt dans Processing ?

Re PS: Voici la ligne de code qui pose problème :

for( int i=0; i<faces.length; i++ ) {update( faces[i].x); 
        rect( faces[i].x, faces[i].y, faces[i].width, faces[i].height ); 
    }

Quand je modifie le x
update( faces[i].x
par un y

update( faces[i].y

le servo Tilt fonctionne correctement mais cela inverse le problème avec le servo Pan.
Heu, j'ai un peu l'impression de faire un monologue là. Personne n'a de piste à proposer. :~

C'est cette fonction qui envoie les données à l'Arduino ?

void update(int x)

Bonsoir, il me semble que oui. Quand je change tous les x par y, ma fonction tilt fonctionne correctement. Je dois donc modifier ceci dans Processing, je suppose :

void update(int x, int y) 
{
  //Calculate servo postion from mouseX
  spos= x/4;
  spos= y/4;

  //Output the servo position ( from 0 to 180)
  port.write(spos + "a"); 
    gx = x/2;
    gy = 100-x/2;
  
   port.write(spos + "b"); 
    gx = y/2;
    gy = 100-y/2;

}

Mais j'obtiens ce message d'erreur :

the method update(int,int) in the pan_tilt is not applicable for the arguments (int)

et cette ligne de code est surligné en jaune.

for( int i=0; i<faces.length; i++ ) {update( faces*.x); [/quote]*

Essaye plutôt une truc du genre (il faudra adapter coté arduino aussi) :

myPort.write(spos + "a" + spos + "b" );
myPort.clear();

Ce que je ne comprend pas trop c'est que tu envoyes tes valeurs et que tu fais encore des calcul avec après l'envoi.