Category: Assignment-06-FaceOSC

Peek-a-boo

Rather than just allow my face to be a puppeteer of a “solid” virtual object, I want to allow the motions of my face to create my puppet as my program ran. I also didn’t want the puppet to seem like a mass, but more like a swarm and so I opted to create my puppet out of particles. In order to create these particles, the user must blink or close their eyes. This effectively only allows the user to see themselves as a mirrored particle-based reflection after looking away from it. The particles are based on the particle system I created for my Thousand line Project, but modified for circular motion.

Here’s the program!

//
// a template for receiving face tracking osc messages from
// Kyle McDonald's FaceOSC https://github.com/kylemcdonald/ofxFaceTracker
//
// 2012 Dan Wilcox danomatika.com
// for the IACD Spring 2012 class at the CMU School of Art
//
// adapted from from Greg Borenstein's 2011 example
// http://www.gregborenstein.com/
// https://gist.github.com/1603230
//
import oscP5.*;
OscP5 oscP5;

ArrayList<ZoomLine> zoomers;
ZoomLine guy;

// num faces found
int found;

// pose
float poseScale;
PVector posePosition = new PVector();
PVector poseOrientation = new PVector();

// gesture
float mouthHeight;
float mouthWidth;
float eyeLeft;
float eyeRight;
float eyebrowLeft;
float eyebrowRight;
float jaw;
float nostrils;

void setup() {
  size(640, 480,OPENGL);
  frameRate(30);
  zoomers = new ArrayList<ZoomLine>();
  
  guy = new ZoomLine(50,50,200,200,10);
  
  oscP5 = new OscP5(this, 8338);
  oscP5.plug(this, "found", "/found");
  oscP5.plug(this, "poseScale", "/pose/scale");
  oscP5.plug(this, "posePosition", "/pose/position");
  oscP5.plug(this, "poseOrientation", "/pose/orientation");
  oscP5.plug(this, "mouthWidthReceived", "/gesture/mouth/width");
  oscP5.plug(this, "mouthHeightReceived", "/gesture/mouth/height");
  oscP5.plug(this, "eyeLeftReceived", "/gesture/eye/left");
  oscP5.plug(this, "eyeRightReceived", "/gesture/eye/right");
  oscP5.plug(this, "eyebrowLeftReceived", "/gesture/eyebrow/left");
  oscP5.plug(this, "eyebrowRightReceived", "/gesture/eyebrow/right");
  oscP5.plug(this, "jawReceived", "/gesture/jaw");
  oscP5.plug(this, "nostrilsReceived", "/gesture/nostrils");
}

void draw() {  
  background(255);
  stroke(0);
  makeZoomer();
  
  
  
  if(found > 0) {
    translate(posePosition.x, posePosition.y);
    rotateY(poseOrientation.y);
    rotateX(poseOrientation.x);
    rotateZ(poseOrientation.z);
    scale(poseScale);

    drawLines();
  }
}

// OSC CALLBACK FUNCTIONS

public void found(int i) {
  println("found: " + i);
  found = i;
}

public void poseScale(float s) {
  println("scale: " + s);
  poseScale = s;
}

public void posePosition(float x, float y) {
  println("pose position\tX: " + x + " Y: " + y );
  posePosition.set(x, y, 0);
}

public void poseOrientation(float x, float y, float z) {
  println("pose orientation\tX: " + x + " Y: " + y + " Z: " + z);
  poseOrientation.set(x, y, z);
}

public void mouthWidthReceived(float w) {
  println("mouth Width: " + w);
  mouthWidth = w;
}

public void mouthHeightReceived(float h) {
  println("mouth height: " + h);
  mouthHeight = h;
}

public void eyeLeftReceived(float f) {
  println("eye left: " + f);
  eyeLeft = f;
}

public void eyeRightReceived(float f) {
  println("eye right: " + f);
  eyeRight = f;
}

public void eyebrowLeftReceived(float f) {
  println("eyebrow left: " + f);
  eyebrowLeft = f;
}

public void eyebrowRightReceived(float f) {
  println("eyebrow right: " + f);
  eyebrowRight = f;
}

public void jawReceived(float f) {
  println("jaw: " + f);
  jaw = f;
}

public void nostrilsReceived(float f) {
  println("nostrils: " + f);
  nostrils = f;
}

// all other OSC messages end up here
void oscEvent(OscMessage m) {
  
//  /* print the address pattern and the typetag of the received OscMessage */
//  println("#received an osc message");
//  println("Complete message: "+m);
//  println(" addrpattern: "+m.addrPattern());
//  println(" typetag: "+m.typetag());
//  println(" arguments: "+m.arguments()[0].toString());
  
  if(m.isPlugged() == false) {
    println("UNPLUGGED: " + m);
  }
}


void makeZoomer() {
  if (found > 0) {
      if ((eyebrowLeft > 8.8) && (eyebrowLeft > 8.8)) {
          zoomers.add(new ZoomLine(random(width) / poseScale, random(height) / poseScale ,
                        random(width) / poseScale, random(height) / poseScale, int(random(10,20))));
      }
  }
}
  
void drawLines() {
  for (int i = 0; i < zoomers.size(); i++) {
    zoomers.get(i).drawMe();
    zoomers.get(i).update();
  }
  for (int j = 0; j < zoomers.size(); j++) {
    if (zoomers.get(j).aliveFor >= zoomers.get(j).lifeSpan) { 
       zoomers.remove(j);
    }
  }
}

class ZoomLine {
  Position start;
  Position lineLoc;
  Position lineEnd;
  Position goal;
  
  Position body;
  Position velocity;
  
  float lineLength;
  float angle;
  
  boolean moving;
  
  float noiseStart = random(100);
  float noiseChange = .007;
  
  float speed;
  
  float offSetX;
  float offSetY;

  float startOffX;
  float startOffY;
  
  int faceIndex;
  int posIndex;
  
  int index;
  
  float radius;
  float points;
  
  float partWidth;
  float partHeight;
  
  float bodyPart;
  
  int aliveFor;
  int nextTime = millis() + 1000;
  
  int rot;
  
  float lifeSpan;
  
  ArrayList<Position> posList;
  
  ZoomLine(float startX, float startY, float goalX, float goalY, int points) {
    
    this.start = new Position(startX, startY);
    this.goal = new Position(goalX, goalY);
    
    this.speed = 5;
   
    this.lineLoc = new Position(startX, startY);
    
    this.index = 0;
    
    this.lifeSpan = random(4,7);
    
    if (random(1) < .5) {
      this.rot = -1;
    } else {
      this.rot = 1;
    }
   
    this.points = points;
    
    this.bodyPart = (int)random(4);
    
    findBody();
    
    makePosList();
    updateAngle();
    updateLength();
    updateLineEnd();
    
    float largest = max(width,height) / poseScale;
    this.offSetX = random(-largest * .01,largest * .01);
    this.offSetY = random(-largest * .01,largest * .01);
  }
  
  //Finds a body to float around
  void findBody() {
    //Left Eye
    if (this.bodyPart == 0) { 
      this.radius = 20;
      this.partWidth = 20;
      this.partHeight = 10;
      this.body = new Position(-25, eyeLeft * -9);
    }
    // Right Eye 
    else if (this.bodyPart == 1) {
      this.radius = 20;
      this.partWidth = 20;
      this.partHeight = 10;
      this.body = new Position(25, eyeRight * -9);
    } 
    //Nose
    else if (this.bodyPart == 2) {
      this.radius = 10;
      this.partWidth = 10;
      this.partHeight = 7;
      this.body = new Position(0,nostrils * -1);
    } 
    // Mouth
    else {
      this.radius = mouthWidth * 2;
      this.partWidth = mouthWidth * 2;
      this.partHeight = mouthHeight * 2;
      this.body = new Position(0, 20);
    }
  }
  
  //Creates a list of positions around a given point (goalX and Y in this case)
  void makePosList(){
    this.posList = new ArrayList<Position>();
    float w = this.partWidth;
    float h = this.partHeight;
    for (int i = 0; i < this.points; i++) {
        float theta = i * (6.28 / this.points );
        this.posList.add(new Position(this.body.x + (w * cos(theta)), this.body.y + (h * sin(theta)) ) );
    }
  }
  
  //Updates speed of the line based on distance to the target position.   
  void updateSpeed() {
    float distance = dist(this.goal.x, this.goal.y,
                          this.start.x, this.start.y);
    this.speed = distance / this.lineLength;
  }
      
  //Updates the angle between the goal and lineLoc.
  void updateAngle() {
    float dx = this.goal.x - this.lineLoc.x;
    float dy = this.goal.y - this.lineLoc.y;
    
    this.angle = atan2(dy,dx);
  }
  
  //Semi randomly determines length based on the start, goal and a noise value. 
  void updateLength() {
    float distance = dist(this.goal.x, this.goal.y,
                          this.start.x, this.start.y);
    
    this.noiseStart += this.noiseChange;
    
    this.lineLength = (distance * .4) * .5;
  }
  
  //Updates the end position of the line bases on the angle between the location and goal
  //as well as the length of the line. 
  void updateLineEnd() {
    float yChange = sin(this.angle) * this.lineLength;
    float xChange = cos(this.angle) * this.lineLength;
    this.lineEnd = new Position(this.lineLoc.x + xChange, this.lineLoc.y + yChange);
  }
  
  //Draws the line on the screen. 
  void drawMe() {
    strokeCap(ROUND);
    strokeWeight(4);
    line(this.lineLoc.x + offSetX, this.lineLoc.y + offSetY, 
         this.lineEnd.x + offSetX, this.lineEnd.y + offSetY);
  } 
  
  //Updates goal position.
  void updateGoal() {
    this.goal = this.posList.get(this.index);    
  }
  
  //Updates all variables for movement and drawing. 
  void update() {
    this.findBody();
    this.makePosList();
    this.updateGoal();
    this.updateAngle();
    this.updateLineEnd();
    this.move();
    if (millis() >= this.nextTime) {
      this.aliveFor += 1;
      this.nextTime = this.nextTime + 1000;
    }
  }
  
  //Moves line between lineLoc and goal. Resets Line if it hits goal.
  void move() {
    float distance = dist(this.lineLoc.x,this.lineLoc.y,this.goal.x, this.goal.y);
    this.lineLoc.x += this.speed*((this.goal.x - this.lineLoc.x)/distance);
    this.lineLoc.y += this.speed*((this.goal.y - this.lineLoc.y) / distance);
    
    if (dist(this.lineLoc.x, this.lineLoc.y,this.goal.x, this.goal.y) <= (width * .02)) {
          this.index = abs(this.index + 1) % (int)this.points;
          this.start.x = this.goal.x;
          this.start.y = this.goal.y;
          this.updateGoal();
          this.updateLength();
          this.updateSpeed();

    }
  }
}

//Returns a position vector with the input x, y.
class Position {
    float x;
    float y;
    Position(float x, float y) {
      this.x = x;
      this.y = y;
    }
}

//Returns a random Position in a rectangle drawn from startX, startY, to endX, endY.
class RandomPosition extends Position {
      RandomPosition(float startX, float endX, float startY, float endY) {
      super(random(startX, endX),random(startY, endY));
    }
}

Face OSC

Since I was little one of my favorite character was Totoro from the movie My neighbour Totoro by Hayao Miyazaki. The way that Totoro moved around in the movie was so funny to me that I tried to change his size and tried to make him move with my face and change in size and shape when I moved my mouth and eyes. I think it was quite successful since I could move him around and change in sizes but I had trouble trying to resizing him since it was too big when I first had drawn it. Also another problem that was big was that the program wouldn’t recognise my face properly and every time I moved my mouth or eyes the program would loose me and not follow me effectively enough. (That’s why I couldn’t embed a post since the program would loose my face every two seconds and it would be cut off)

import oscP5.*;
OscP5 oscP5;

// num faces found
int found;
// pose
float poseScale;
PVector posePosition = new PVector();
PVector poseOrientation = new PVector();

// gesture
float mouthHeight;
float mouthWidth;
float leftEyebrowHeight;
float rightEyebrowHeight;
float eyeLeftHeight;
float eyeRightHeight;
float nostrilHeight;
float jaw;

void setup() {
  size(800, 650);
  frameRate(30);

  oscP5 = new OscP5(this, 8338);
  oscP5.plug(this, "found", "/found");
  oscP5.plug(this, "poseScale", "/pose/scale");
  oscP5.plug(this, "posePosition", "/pose/position");
  oscP5.plug(this, "poseOrientation", "/pose/orientation");
  oscP5.plug(this, "mouthWidthReceived", "/gesture/mouth/width");
  oscP5.plug(this, "mouthHeightReceived", "/gesture/mouth/height");
  oscP5.plug(this, "eyebrowLeftReceived", "/gesture/eyebrow/left");
  oscP5.plug(this, "eyebrowRightReceived", "/gesture/eyebrow/right");
  oscP5.plug(this, "eyeLeftReceived", "/gesture/eye/left");
  oscP5.plug(this, "eyeRightReceived", "/gesture/eye/right");
  oscP5.plug(this, "nostrilsReceived", "/gesture/nostrils");
  oscP5.plug(this, "jawReceived", "/gesture/jaw");
}

void draw() {  
  background(255);
  stroke(0);
  
  if(found > 0) {
    translate(posePosition.x, posePosition.y);
    scale(poseScale*0.1);
    noFill();
    noStroke();
    fill(220);
    triangle(-50,30-leftEyebrowHeight*5, -75-leftEyebrowHeight*5,70, -30,140);
    triangle(50,30-rightEyebrowHeight*5, 75+rightEyebrowHeight*5,70, 30,140);
    
    beginShape();
    curveVertex(-130,160);
    curveVertex(0,80+mouthHeight*5);
    curveVertex(120,160);
    curveVertex(190+mouthWidth*5,500);
    curveVertex(0,620);
    curveVertex(-190-mouthWidth*5,500);
    curveVertex(-130,160);
    curveVertex(0,80+mouthHeight*5);
    curveVertex(120,160);
    endShape();
    
    fill(255);
    ellipse(-65,145,45+eyeLeftHeight*5,45+eyeLeftHeight*5);
    ellipse(65,145,45+eyeRightHeight*5,45+eyeRightHeight*5);
    ellipse(0,410, 300+mouthWidth*5,350+mouthHeight*5);
    fill(0);
    ellipse(-65,145,18+eyeLeftHeight*5,18+eyeLeftHeight*5);
    ellipse(65,145,18+eyeRightHeight*5,18+eyeRightHeight*5);
    
    triangle(0,175+nostrilHeight*5, 25,155+nostrilHeight*5, -25,155+nostrilHeight*5);
    stroke(0);
    strokeWeight(5);
    line(0,250, -15,260);
    line(0,250, 15,260);
    line(50,260, 35,270);
    line(50,260, 65,270);
    line(-50,260, -65,270);
    line(-50,260, -35,270);
    line(25,280, 10,290);
    line(25,280, 40,290);
    line(-25,280, -10,290);
    line(-25,280, -40,290);
    line(70,285, 85,295);
    line(70,285, 55,295);
    line(-70,285, -55,295);
    line(-70,285, -85,295);

  }
}

// OSC CALLBACK FUNCTIONS

public void found(int i) {
  println("found: " + i);
  found = i;
}

public void poseScale(float s) {
  println("scale: " + s);
  poseScale = s;
}

public void posePosition(float x, float y) {
  println("pose position\tX: " + x + " Y: " + y );
  posePosition.set(x, y, 0);
}

public void poseOrientation(float x, float y, float z) {
  println("pose orientation\tX: " + x + " Y: " + y + " Z: " + z);
  poseOrientation.set(x, y, z);
}

public void mouthWidthReceived(float w) {
  println("mouth Width: " + w);
  mouthWidth = w;
}

public void mouthHeightReceived(float h) {
  println("mouth height: " + h);
  mouthHeight = h;
}

public void jawReceived(float f) {
  println("jaw: " + f);
  jaw = f;
}

public void eyebrowLeftReceived(float h) {
  println("eyebrow left: " + h);
  leftEyebrowHeight = h;
}
 
public void eyebrowRightReceived(float h) {
  println("eyebrow right: " + h);
  rightEyebrowHeight = h;
}
 
public void eyeLeftReceived(float h) {
  println("eye left: " + h);
  eyeLeftHeight = h;
}
 
public void eyeRightReceived(float h) {
  println("eye right: " + h);
  eyeRightHeight = h;
}
 
public void nostrilsReceived(float h) {
  println("nostrils: " + h);
  nostrilHeight = h;
}

// all other OSC messages end up here
void oscEvent(OscMessage m) {
  
  /* print the address pattern and the typetag of the received OscMessage */
  println("#received an osc message");
  println("Complete message: "+m);
  println(" addrpattern: "+m.addrPattern());
  println(" typetag: "+m.typetag());
  println(" arguments: "+m.arguments()[0].toString());
  
  if(m.isPlugged() == false) {
    println("UNPLUGGED: " + m);
  }
}

IMG_20141006_162452

il-mio-vicino-totoro-img-pesca

FaceOSC: The Orange Gobbler

Screen Shot 2014-10-06 at 2.34.37 AM

My goal was to create a creature with expressive eyes and an expressive mouth. I tracked the positions of the left eye, right eye, left eyebrow, right eyebrow, the nostrils, and the jaw. I decided to work with primitives so I could easily be able to see what motions were changing which shapes.

The body was a bit of an afterthought, and it acts a bit like a slinky due to how the position of the jaw was used in placing shapes. If I were to revisit this project, I’d try to utilize some of the mouth parameters to make the creature more expressive. However, I’m undecided on whether or not to give “The Orange Gobbler” a set of teeth.

EMScreatureAt one point, The Orange Gobbler did have highlights in its eyes, but for some reason the highlights made the Gobbler look a bit too unsettling.

//Miranda Jacoby
//EMS Inetractivity Section A
//majacoby@andrew.cmu.edu
//Creature design is copyright Miranda Jacoby 2014
//Adapted from Golan Levin's "FaceOSCReceiver" program

// Don't forget to install OscP5 library for Processing, 
// or nothing will work! 
//
// A template for receiving face tracking osc messages from
// Kyle McDonald's FaceOSC https://github.com/kylemcdonald/ofxFaceTracker

import oscP5.*;
OscP5 oscP5;

// num faces found
int found;

// pose
float poseScale;
PVector posePosition = new PVector();
PVector poseOrientation = new PVector();

// gesture
float mouthHeight;
float mouthWidth;
float eyeLeft;
float eyeRight;
float eyebrowLeft;
float eyebrowRight;
float jaw;
float nostrils;

//dimensions
float nostrilHeight;

void setup() {
  size(640, 480);
  frameRate(30);

  oscP5 = new OscP5(this, 8338);
  oscP5.plug(this, "found", "/found");
  oscP5.plug(this, "poseScale", "/pose/scale");
  oscP5.plug(this, "posePosition", "/pose/position");
  oscP5.plug(this, "poseOrientation", "/pose/orientation");
  oscP5.plug(this, "mouthWidthReceived", "/gesture/mouth/width");
  oscP5.plug(this, "mouthHeightReceived", "/gesture/mouth/height");
  oscP5.plug(this, "eyeLeftReceived", "/gesture/eye/left");
  oscP5.plug(this, "eyeRightReceived", "/gesture/eye/right");
  oscP5.plug(this, "eyebrowLeftReceived", "/gesture/eyebrow/left");
  oscP5.plug(this, "eyebrowRightReceived", "/gesture/eyebrow/right");
  oscP5.plug(this, "jawReceived", "/gesture/jaw");
  oscP5.plug(this, "nostrilsReceived", "/gesture/nostrils");
  
  nostrilHeight = 7;
}

void draw() {  
  background(249, 245, 255);
  stroke(0);
  
  if(found > 0) {
    translate(posePosition.x, posePosition.y);
    scale(poseScale);
    noStroke();
 //Legs
    fill(155, 84, 14);
    pushMatrix();
    rotate(6.25);
    ellipse(20, jaw * 20, 125, 200);
    popMatrix();
    pushMatrix();
    rotate(-6.25);
    ellipse(-20, jaw * 20, 125, 200);
    popMatrix();
//Neck and Body
    fill(155, 84, 14);
    ellipse(0, jaw * 3, 110, 180);
    ellipse(0, jaw * 5, 120, 190);
    ellipse(0, jaw * 7, 130, 200);
    ellipse(0, jaw * 9, 140, 210);
    ellipse(0, jaw * 11, 150, 220);
    ellipse(0, jaw * 15, 275, 375);
    fill(206, 119, 48);
    ellipse(0, jaw * 3, 100, 150);
    ellipse(0, jaw * 7, 100, 150);
    ellipse(0, jaw * 11, 100, 150);
    ellipse(0, jaw * 15, 200, 275);
//Arms
    fill(155, 84, 14);
    pushMatrix();
    rotate(6);
    ellipse(20, jaw * 15, 125, 200);
    popMatrix();
    pushMatrix();
    rotate(-6);
    ellipse(-20, jaw * 15, 125, 200);
    popMatrix();

//Mouth Lower Jaw
    fill(155, 84, 14);
    ellipse(0, jaw * .5, 100, 100);
    fill(58, 42, 72);
    ellipse(0, jaw * .5, 73, 80);
//Mouth Lower Jaw Tip
    fill(155, 84, 14);
    ellipse(0, jaw * 2.25, 50, 30);
//Space Between the Eyes (CHECK FOR OVERLAP PROBLEMS)
    fill(155, 84, 14);
    ellipse(0, 10 * -2.7, 35, 20);
    ellipse(-20, 10 * -2.7, 35, 20);
    ellipse(20, 10 * -2.7, 35, 20);
    ellipse(-20, 5 * -2.7, 35, 20);
    ellipse(20, 5 * -2.7, 35, 20);
    quad(-20, eyeLeft * -10, 20, eyeRight * -10, 5, (nostrils + nostrilHeight * .5) * 3, -5, (nostrils + nostrilHeight * .5) * 3);
//Horns
    fill(206, 119, 48);
    pushMatrix();
    rotate(6);
    ellipse(-30, -40, 35, 55);
    popMatrix();
    pushMatrix();
    rotate(-6);
    ellipse(30, -40, 35, 55);
    popMatrix();
    fill(155, 84, 14);
    ellipse(-20, 0, 50, 30);
    ellipse(20, 0, 50, 30);
    fill(206, 119, 48);
    ellipse(-40, 0, 50, 30);
    ellipse(40, 0, 50, 30);

//Mouth Upper Jaw
    fill(206, 119, 48);
    pushMatrix();
    rotate(-5);
    ellipse(-0, 30, 40, 25);
    popMatrix();
    pushMatrix();
    rotate(5);
    ellipse(0, 30, 40, 25);
    popMatrix();
    pushMatrix();
    //rotate(-5);
    ellipse(15, 20, 30, 40);
    popMatrix();
    pushMatrix();
    //rotate(5);
    ellipse(-15, 20, 30, 40);
    popMatrix();
//    ellipse(0, jaw * .5, 100, 100);
//    ellipse(0, jaw * 2.5, 50, 30);
//Bridge of Nose
    fill(155, 84, 14);
    quad(-20, eyeLeft * -2, 20, eyeRight * -2, 5, (nostrils + nostrilHeight + (jaw * -2.5)/15) * 3, -5, (nostrils + nostrilHeight + (jaw * -2.5)/15) * 3);
    fill(206, 119, 48);
    quad(-20 + 27, eyeLeft * -5, 20 - 27, eyeRight * -5, -5, (nostrils + nostrilHeight + (jaw * -2.5)/15) * 3, 5, (nostrils + nostrilHeight + (jaw * -2.5)/15) * 3);
    fill(240, 172, 104);
    quad(-30 + 27, eyeLeft * -5, 30 - 27, eyeRight * -5, 5, (nostrils + nostrilHeight + (jaw * -2.5)/15) * 3, -5, (nostrils + nostrilHeight + (jaw * -2.5)/15) * 3);
//Eye Bags
    //fill(206, 119, 48);
    fill(58, 42, 72);
    ellipse(-20, 0, 25, 15);
    ellipse(20, 0, 25, 15);
//Eye Sclera
    fill(245, 229, 175);
    ellipse(-20, eyeLeft * -2, 25, 20);
    ellipse(20, eyeRight * -2, 25, 20);
//Eye Iris
    fill(84, 115, 134);
    ellipse(-20, eyeLeft * -2, 15, 15);
    ellipse(20, eyeRight * -2, 15, 15);
//Eye Pupil
    fill(58, 42, 72);
    ellipse(-20, eyeLeft * -2, 5, 5);
    ellipse(20, eyeRight * -2, 5, 5);
//Eye Highlight
    //fill(255, 253, 234);
    //ellipse(-27, eyeLeft * -2.5, 4, 2);
    //ellipse(13, eyeRight * -2.5, 4, 2);
    //Mouth?
    //ellipse(0, 20, mouthWidth* 3, mouthHeight * 3);
//Nostrils
    fill(58, 42, 72);
    ellipse(-5, nostrils * 3, 3, nostrilHeight);
    ellipse(5, nostrils * 3, 3, nostrilHeight);
//Mouth Upper Jaw Tip
    fill(206, 119, 48);
    ellipse(0, 80 + jaw * -2.25, 20, 30);
    fill(240, 172, 104);
    ellipse(0, 70 + jaw * -2.25, 10, 15);
    //ellipseMode(CENTER);
//Eyebrows
    fill(155, 84, 14);
    pushMatrix();
    ellipse(-20, eyebrowLeft * -2.7, 35, 20);
    popMatrix();
    pushMatrix();
    ellipse(20, eyebrowRight * -2.7, 35, 20);
    popMatrix();
    fill(206, 119, 48);
    pushMatrix();
    ellipse(-20, eyebrowLeft * -3, 35, 20);
    popMatrix();
    pushMatrix();
    ellipse(20, eyebrowRight * -3, 35, 20);
    popMatrix();

  }
}

// OSC CALLBACK FUNCTIONS

public void found(int i) {
  println("found: " + i);
  found = i;
}

public void poseScale(float s) {
  println("scale: " + s);
  poseScale = s;
}

public void posePosition(float x, float y) {
  //println("pose position\tX: " + x + " Y: " + y );
  posePosition.set(x, y, 0);
}

public void poseOrientation(float x, float y, float z) {
  //println("pose orientation\tX: " + x + " Y: " + y + " Z: " + z);
  poseOrientation.set(x, y, z);
}

public void mouthWidthReceived(float w) {
  //println("mouth Width: " + w);
  mouthWidth = w;
}

public void mouthHeightReceived(float h) {
  //println("mouth height: " + h);
  mouthHeight = h;
}

public void eyeLeftReceived(float f) {
  //println("eye left: " + f);
  eyeLeft = f;
}

public void eyeRightReceived(float f) {
  //println("eye right: " + f);
  eyeRight = f;
}

public void eyebrowLeftReceived(float f) {
  //println("eyebrow left: " + f);
  eyebrowLeft = f;
}

public void eyebrowRightReceived(float f) {
  //println("eyebrow right: " + f);
  eyebrowRight = f;
}

public void jawReceived(float f) {
  println("jaw: " + f);
  jaw = f;
}

public void nostrilsReceived(float f) {
  //println("nostrils: " + f);
  nostrils = f;
}

// all other OSC messages end up here
void oscEvent(OscMessage m) {
  
  /* print the address pattern and the typetag of the received OscMessage */
  println("#received an osc message");
  println("Complete message: "+m);
  println(" addrpattern: "+m.addrPattern());
  println(" typetag: "+m.typetag());
  println(" arguments: "+m.arguments()[0].toString());
  
  if(m.isPlugged() == false) {
    println("UNPLUGGED: " + m);
  }
}


Distorting a Classic


You can use your face to turn a classical sculpture into a glitching digital artifact, eyebrow height is correlated with noise on the surface and mouth openness is correlated with twisting the sculpture.
This has been a long process. The end product resulted in a modification of my original idea, which was to be able to manipulate specific parts of the face, but I couldn’t grab only a specific section of pixels to distort, since it was an imported object. If I had made the head in processing using 3d objects I would have been able to. I used Hemesh to distort the head and toxiclibs to import the stl file into processing. The head is a model of Memory by Daniel Chester French, it is found in the Metropolitan

code:

//Charlotte Stiles
//Thank you Matthew Plummer-Fernandez for the code to import stl to hemesh using toxiclibs http://www.plummerfernandez.com/
//bend and noise code from Hemesh library http://hemesh.wblut.com/
//head is from scotta3d on thingsverse http://www.thingiverse.com/thing:24335
//the head is a model of Memory by Daniel Chester French, it is found in the Metropolitan


import wblut.math.*;
import wblut.processing.*;
import wblut.core.*;
import wblut.*;
import wblut.hemesh.*;
import wblut.geom.*;

// Toxiclibs for the import stl and save color stl

import toxi.geom.*;
import toxi.geom.mesh.*;
import toxi.math.*;
import toxi.processing.*;


import oscP5.*;
OscP5 oscP5;
//pshape is for importing my stl
PShape s;

PVector posePosition;
boolean found;
float eyeLeftHeight;
float eyeRightHeight;
float mouthHeight;
float mouthWidth;
float leftEyebrowHeight;
float rightEyebrowHeight;

float MN; //eyebrow to eye
 
float poseScale;


String stlFilename = "memory_head.stl";

//this is for noise and everything else vv
HE_Mesh mesh, copymesh;
WB_Render render;

//this is for bend vv
WB_Plane P;
WB_Line L;
HEM_Bend bendModifier;
WB_GeometryFactory gf=WB_GeometryFactory.instance();



void setup() {
  size(800, 800, P3D);
 
  createMesh();
  
  
  HEM_Noise modifier=new HEM_Noise();
  modifier.setDistance(20);
  copymesh.modify(modifier);
  //for noise^^
  
  bendModifier=new HEM_Bend();
  
  P=new WB_Plane(0,0,-200,0,0,1); 
  bendModifier.setGroundPlane(P);// Ground plane of bend modifier 
 
  L=new WB_Line(0,0,-200,-1,0,-200);
  bendModifier.setBendAxis(L);// Bending axis

  
  bendModifier.setAngleFactor(30.0/400);// Angle per unit distance (in degrees) to the ground plane
  // bend axis by an angle d*angleFactor;
 
  bendModifier.setPosOnly(false);// apply modifier only on positive side of the ground plane?
  
  mesh.modify(bendModifier);
  
  
  render=new WB_Render(this);
  
    oscP5 = new OscP5(this, 8338);
  oscP5.plug(this, "mouthWidthReceived", "/gesture/mouth/width");
  oscP5.plug(this, "mouthHeightReceived", "/gesture/mouth/height");
  oscP5.plug(this, "eyebrowLeftReceived", "/gesture/eyebrow/left");
  oscP5.plug(this, "eyebrowRightReceived", "/gesture/eyebrow/right");
  oscP5.plug(this, "eyeLeftReceived", "/gesture/eye/left");
  oscP5.plug(this, "eyeRightReceived", "/gesture/eye/right");
  oscP5.plug(this, "jawReceived", "/gesture/jaw");
  oscP5.plug(this, "nostrilsReceived", "/gesture/nostrils");
  oscP5.plug(this, "found", "/found");
  oscP5.plug(this, "poseOrientation", "/pose/orientation");
  oscP5.plug(this, "posePosition", "/pose/position");
  oscP5.plug(this, "poseScale", "/pose/scale");
  
}

void draw() {
  background(230);
  directionalLight(255, 255, 255, 1, 1, -1);
  directionalLight(127, 127, 127, -1, -1, 1);
  
  if (found) {
 
    translate(posePosition.x, posePosition.y+300);
    scale(poseScale*2);
  }
  rotateY(400*1.0/width*TWO_PI);
  rotateX(200*1.0/height*TWO_PI);
  
   HEM_Noise modifier=new HEM_Noise();
  copymesh=mesh.get();
  
  MN = rightEyebrowHeight - eyeRightHeight - 4;
// println(MN);

if (MN < 0) MN=0; //eyebrow eye ratio make sure it doesnt go into negative
 
  modifier.setDistance(MN/2);
  copymesh.modify(modifier);
  
 float heightWidthRatio=mouthHeight/mouthWidth;
  println(heightWidthRatio);
  if (heightWidthRatio < .2) heightWidthRatio= 0;
  L=gf.createLineThroughPoints(0,0, heightWidthRatio-100,-1,0,heightWidthRatio-100);
  //this one controls the speed vv
  bendModifier.setAngleFactor(20* 0.030 *heightWidthRatio);
  bendModifier.setBendAxis(L);
  mesh.modify(bendModifier);
  
  noStroke();
  render.drawEdges(mesh);
  noStroke();
  render.drawFaces(copymesh);
  
  

  
}


 
public void mouthWidthReceived(float w) {
//  println("mouth Width: " + w);
  mouthWidth = w;
}
 
public void mouthHeightReceived(float h) {
 // println("mouth height: " + h);
  mouthHeight = h;
}
 
 
public void eyebrowRightReceived(float h) {
 // println("eyebrow right: " + h);
  rightEyebrowHeight = h;
}

 
public void eyeRightReceived(float h) {
 // println("eye right: " + h);
  eyeRightHeight = h;
}

public void found(int i) {
  //println("found: " + i); // 1 == found, 0 == not found
  found = i == 1;
}
 
public void posePosition(float x, float y) {
 // println("pose position\tX: " + x + " Y: " + y );
  posePosition = new PVector(x, y);
}
 
public void poseScale(float s) {
 // println("scale: " + s);
  poseScale = s;
}
 
public void poseOrientation(float x, float y, float z) {
 // println("pose orientation\tX: " + x + " Y: " + y + " Z: " + z);
}
 
 
void oscEvent(OscMessage theOscMessage) {
  if (theOscMessage.isPlugged()==false) {
   // println("UNPLUGGED: " + theOscMessage);
  }
}

void createMesh(){
  mesh = new HE_Mesh(fromStl(stlFilename));
  copymesh= mesh.get();

}



HEC_FromFacelist fromStl(String stlName) { 
  println("Start Build");
  WETriangleMesh wemesh = (WETriangleMesh) new STLReader().loadBinary(sketchPath(stlName), STLReader.WEMESH);
  //convert toxi mesh to a hemesh. Thanks to wblut
  int n=wemesh.getVertices().size();
  ArrayList points= new ArrayList(n);
  for (Vec3D v : wemesh.getVertices ()) { 
    points.add(new WB_Point(v.x, v.y, v.z));
  }
  int[] toxiFaces=wemesh.getFacesAsArray();
  int nf=toxiFaces.length/3;
  int[][] faces=new int[nf][3];
  for (int i=0; i

DragFace

Drag Face low

 

For this assignment, I remembered that the creator had created something that meshed a celebrity face with your face.  I wanted to do something similar but with drag makeup.  I attempted this idea but adding eye shadow, large red lips and contoured nose line.  However, due to the short time frame, this failed immensely but I do not want to give up on this idea I want to learn more so that I can apply this concept properly.  I hess this assignment was just to learn about the basics of this face program.

 

import oscP5.*;
OscP5 oscP5;

// num faces found
int found;

// pose
float poseScale;
PVector posePosition = new PVector();
PVector poseOrientation = new PVector();

// gesture
float mouthHeight;
float mouthWidth;
float eyeLeft;
float eyeRight;
float eyebrowLeft;
float eyebrowRight;
float jaw;
float nostrils;

void setup() {
size(640, 480);
frameRate(30);

oscP5 = new OscP5(this, 8338);
oscP5.plug(this, "found", "/found");
oscP5.plug(this, "poseScale", "/pose/scale");
oscP5.plug(this, "posePosition", "/pose/position");
oscP5.plug(this, "poseOrientation", "/pose/orientation");
oscP5.plug(this, "mouthWidthReceived", "/gesture/mouth/width");
oscP5.plug(this, "mouthHeightReceived", "/gesture/mouth/height");
oscP5.plug(this, "eyeLeftReceived", "/gesture/eye/left");
oscP5.plug(this, "eyeRightReceived", "/gesture/eye/right");
oscP5.plug(this, "eyebrowLeftReceived", "/gesture/eyebrow/left");
oscP5.plug(this, "eyebrowRightReceived", "/gesture/eyebrow/right");
oscP5.plug(this, "jawReceived", "/gesture/jaw");
oscP5.plug(this, "nostrilsReceived", "/gesture/nostrils");
}

void draw() {
background(255,255,255, 10);
stroke(0);

if(found > 0) {
translate(posePosition.x, posePosition.y);
scale(poseScale);
noFill();
ellipse(-20, eyeLeft * -9, 20, 7);
ellipse(20, eyeRight * -9, 20, 7);

fill(255,0,0, 200);
ellipse(0, 20, mouthWidth* 3, mouthHeight * 7);

fill(255);
ellipse(0, 20, mouthWidth* 3, mouthHeight * 3);
noFill();
ellipse(-5, nostrils * -1, 7, 3);
ellipse(5, nostrils * -1, 7, 3);
line(-5, nostrils * -2, -7, eyebrowLeft*-5);
line(5, nostrils * -2, 7, eyebrowLeft*-5);
noStroke();
rectMode(CENTER);
fill(140,100,40);
rect(-20, eyebrowLeft * -5, 25, 5);
rect(20, eyebrowRight * -5, 25, 5);

noStroke();
fill(90,150,200,150);
ellipse(-20, (eyebrowLeft*-5)+10, 30,eyebrowLeft*2);
ellipse(20, (eyebrowRight*-5)+10, 30,eyebrowRight*2);
fill(10,50,100,150);
ellipse(-30, (eyebrowLeft*-5)+10, 30,eyebrowLeft*2);
ellipse(30, (eyebrowRight*-5)+10, 30,eyebrowRight*2);
}
}

// OSC CALLBACK FUNCTIONS

public void found(int i) {
println("found: " + i);
found = i;
}

public void poseScale(float s) {
println("scale: " + s);
poseScale = s;
}

public void posePosition(float x, float y) {
println("pose position\tX: " + x + " Y: " + y );
posePosition.set(x, y, 0);
}

public void poseOrientation(float x, float y, float z) {
println("pose orientation\tX: " + x + " Y: " + y + " Z: " + z);
poseOrientation.set(x, y, z);
}

public void mouthWidthReceived(float w) {
println("mouth Width: " + w);
mouthWidth = w;
}

public void mouthHeightReceived(float h) {
println("mouth height: " + h);
mouthHeight = h;
}

public void eyeLeftReceived(float f) {
println("eye left: " + f);
eyeLeft = f;
}

public void eyeRightReceived(float f) {
println("eye right: " + f);
eyeRight = f;
}

public void eyebrowLeftReceived(float f) {
println("eyebrow left: " + f);
eyebrowLeft = f;
}

public void eyebrowRightReceived(float f) {
println("eyebrow right: " + f);
eyebrowRight = f;
}

public void jawReceived(float f) {
println("jaw: " + f);
jaw = f;
}

public void nostrilsReceived(float f) {
println("nostrils: " + f);
nostrils = f;
}

// all other OSC messages end up here
void oscEvent(OscMessage m) {

/* print the address pattern and the typetag of the received OscMessage */
println("#received an osc message");
println("Complete message: "+m);
println(" addrpattern: "+m.addrPattern());
println(" typetag: "+m.typetag());
println(" arguments: "+m.arguments()[0].toString());

if(m.isPlugged() == false) {
println("UNPLUGGED: " + m);
}
}

FlowerFace

I don’t know why, but it appears to be simply so that whenever one mentions face tracking, one of the first things that comes to mind is a flower. Even before Golan explicitly stated “Don’t all make daisies,” I was thinking about building a face-tracking flower, even while I was still in high school. Now, having learned to use to OSC with Kyle’s FaceOSC demo, I now have a method to make it into reality. So here I am with my flower-y face. In light of the monotony of petal-laced visages, I decided to make mine in 3 dimensions. Tis’ a simple design, but it works decently well. It tracks the orientation of the whole head, the positions of both eyes and eyebrows, and also the dimensions of the mouth. I threw in some wavy little leaf-arms for the heck of it (not tracked). In implementing this 3D flower face, I’ve also learned that Processing’s 3D transformation system is based on local spaces rather than world space – which is why the eyes look slightly “off” when I tilt my head. All in all, this was an interesting learning experience for me and now I know how to track and map faces in real time.

 
/* FlowerFace by John Choi.
 * Built off of Golan's sample.  Runs off of FaceOSC:
 * Kyle McDonald's FaceOSC https://github.com/kylemcdonald/ofxFaceTracker */

/* --- PRE SETUP --- */
//import necessray libraries
import oscP5.*;
OscP5 oscP5;

//create a sky background
PImage sky;
//create 3d object vars
PShape leftEye;
PShape rightEye;
PShape mouth;
PShape face;
PShape stem;
PShape leftLeaf;
PShape rightLeaf;
// num faces found
int found;
// pose
float poseScale;
PVector posePosition = new PVector();
PVector poseOrientation = new PVector();
// gesture
float mouthHeight;
float mouthWidth;
float eyeLeft;
float eyeRight;
float eyebrowLeft;
float eyebrowRight;
float jaw;
float nostrils;

float timer = 0;

/* ----- SETUP ----- */
void setup() {
  //set up 3D view
  size(640, 480,P3D);
  perspective();
  
  //load sky image
  sky = loadImage("sky.png");
  
  //load 3d objects
  leftEye = loadShape("eye.obj");
  rightEye = loadShape("eye.obj"); 
  mouth = loadShape("mouth.obj"); 
  face = loadShape("face.obj"); 
  stem = loadShape("stem.obj"); 
  leftLeaf = loadShape("leftLeaf.obj"); 
  rightLeaf = loadShape("rightLeaf.obj"); 

  //setup FaceOSC
  oscP5 = new OscP5(this, 8338);
  oscP5.plug(this, "found", "/found");
  oscP5.plug(this, "poseScale", "/pose/scale");
  oscP5.plug(this, "posePosition", "/pose/position");
  oscP5.plug(this, "poseOrientation", "/pose/orientation");
  oscP5.plug(this, "mouthWidthReceived", "/gesture/mouth/width");
  oscP5.plug(this, "mouthHeightReceived", "/gesture/mouth/height");
  oscP5.plug(this, "eyeLeftReceived", "/gesture/eye/left");
  oscP5.plug(this, "eyeRightReceived", "/gesture/eye/right");
  oscP5.plug(this, "eyebrowLeftReceived", "/gesture/eyebrow/left");
  oscP5.plug(this, "eyebrowRightReceived", "/gesture/eyebrow/right");
  oscP5.plug(this, "jawReceived", "/gesture/jaw");
  oscP5.plug(this, "nostrilsReceived", "/gesture/nostrils");

}

/* --- MAIN LOOP --- */
void draw() {  
  background(0);
  lights();
  timer += 1;

  //draw skybox
  hint(DISABLE_DEPTH_MASK);
  image(sky, 0, 0, width, height);
  hint(ENABLE_DEPTH_MASK);
  
  //draw 3D objects
  drawShape(leftEye,-.1*eyeLeft,.03*eyebrowLeft,0, 
            poseOrientation.x,poseOrientation.y,-poseOrientation.z, 
            1,.1*eyebrowLeft,1);
  drawShape(rightEye,.1*eyeRight,.03*eyebrowRight,0,
            poseOrientation.x,poseOrientation.y,-poseOrientation.z, 
            1,.1*eyebrowRight,1);
  drawShape(mouth,0,-.5,0, 
            poseOrientation.x,poseOrientation.y,-poseOrientation.z,  
            .1*mouthWidth,.2*mouthHeight,1);
  drawShape(face,0,0,0, 
            poseOrientation.x,poseOrientation.y,-poseOrientation.z, 
            1,1+.02*(eyebrowLeft+eyebrowRight),1);
  drawShape(stem,0,0,0, 0,0,0, 1,1,1);
  drawShape(leftLeaf,0,-2.75,0, 
            0,.2*sin(timer*0.01)-.1,.5*sin(timer*0.04)-.25, 
            1,1,1);
  drawShape(rightLeaf,0,-2.75,0,
            0,.2*sin(timer*0.02)-.0,.5*sin(timer*0.03)-.25, 
            1,1,1);

}

//helper function to shape
void drawShape(PShape obj, float x, float y, float z, 
               float rx, float ry, float rz,
               float sx, float sy, float sz) {
  pushMatrix();
  translate(width/2,height/2,300);
  scale(20,-20,20);
  translate(x,y,z);
  rotateY(ry);
  rotateX(rx);
  rotateZ(rz);
  scale(sx,sy,sz);
  shape(obj);
  popMatrix();
}

/* --- OSC CALLBACK FUNCTIONS --- */
public void found(int i) {
  println("found: " + i);
  found = i;
}
public void poseScale(float s) {
  println("scale: " + s);
  poseScale = s;
}
public void posePosition(float x, float y) {
  println("pose position\tX: " + x + " Y: " + y );
  posePosition.set(x, y, 0);
}
public void poseOrientation(float x, float y, float z) {
  println("pose orientation\tX: " + x + " Y: " + y + " Z: " + z);
  poseOrientation.set(x, y, z);
}
public void mouthWidthReceived(float w) {
  println("mouth Width: " + w);
  mouthWidth = w;
}
public void mouthHeightReceived(float h) {
  println("mouth height: " + h);
  mouthHeight = h;
}
public void eyeLeftReceived(float f) {
  println("eye left: " + f);
  eyeLeft = f;
}
public void eyeRightReceived(float f) {
  println("eye right: " + f);
  eyeRight = f;
}
public void eyebrowLeftReceived(float f) {
  println("eyebrow left: " + f);
  eyebrowLeft = f;
}
public void eyebrowRightReceived(float f) {
  println("eyebrow right: " + f);
  eyebrowRight = f;
}
public void jawReceived(float f) {
  println("jaw: " + f);
  jaw = f;
}
public void nostrilsReceived(float f) {
  println("nostrils: " + f);
  nostrils = f;
}
// all other OSC messages end up here
void oscEvent(OscMessage m) {
  /* print the address pattern and the typetag of the received OscMessage */
  println("#received an osc message");
  println("Complete message: "+m);
  println(" addrpattern: "+m.addrPattern());
  println(" typetag: "+m.typetag());
  println(" arguments: "+m.arguments()[0].toString());
  if(m.isPlugged() == false) { println("UNPLUGGED: " + m); }
}