Take any Processing sketch you previously created that involves mouse interaction and replace the mouse with color tracking. Create an environment for the camera that is simple and high contrast. For example, point the camera at a black tabletop with a small white object. Control your sketch with the object’s location.

The answer below uses the “snake” example, see Example 9-8 and Exercise 9-7 for help. (Also, note there are some typos with this exercise in the book. See: Errata, Exercise 16-5.

example
Example
// Learning Processing
// Daniel Shiffman
// http://www.learningprocessing.com

// Exercise 16-5: Take any Processing sketch you previously created that involves mouse interaction and 
// replace the mouse with color tracking. Create an environment for the camera that is simple and high contrast. 
// For example, point the camera at a black tabletop with a small white object. 
// Control your sketch with the object's location. 

import processing.video.*;

// Variable for capture device
Capture video;

// A variable for the color we are searching for.
color trackColor; 

// A Snake variable
Snake snake;

void setup() {
  size(320,240);
  video = new Capture(this,width,height,15);
  // Start off tracking for red
  trackColor = color(255,0,0);
  smooth();
  
  // Initialize the snake
  snake = new Snake(50);
  
}

void draw() {
  // Capture and display the video
  if (video.available()) {
    video.read();
  }
  
  video.loadPixels();
  image(video,0,0);

  // Before we begin searching, the "world record" for closest color is set to a high number that is easy for the first pixel to beat.
  float worldRecord = 500; 

  // XY coordinate of closest color
  int closestX = 0;
  int closestY = 0;

  // Begin loop to walk through every pixel
  for (int x = 0; x < video.width; x ++ ) {
    for (int y = 0; y < video.height; y ++ ) {
      int loc = x + y*video.width;
      // What is current color
      color currentColor = video.pixels[loc];
      float r1 = red(currentColor);
      float g1 = green(currentColor);
      float b1 = blue(currentColor);
      float r2 = red(trackColor);
      float g2 = green(trackColor);
      float b2 = blue(trackColor);

      // Using euclidean distance to compare colors
      float d = dist(r1,g1,b1,r2,g2,b2); // We are using the dist( ) function to compare the current color with the color we are tracking.

      // If current color is more similar to tracked color than
      // closest color, save current location and current difference
      if (d < worldRecord) {
        worldRecord = d;
        closestX = x;
        closestY = y;
      }
    }
  }

  // We only consider the color found if its color distance is less than 10. 
  // This threshold of 10 is arbitrary and you can adjust this number depending on how accurate you require the tracking to be.
  if (worldRecord < 10) { 
    // Update the snake's location
    snake.update(closestX,closestY);
  }
  
  snake.display();
  
}

void mousePressed() {
  // Save color where the mouse is clicked in trackColor variable
    saveFrame("blah.tif");
  int loc = mouseX + mouseY*video.width;
  trackColor = video.pixels[loc];

}

// Learning Processing
// Daniel Shiffman
// http://www.learningprocessing.com

// Exercise 16-5: Snake Class

class Snake {
  // x and y positions
  int[] xpos;
  int[] ypos;

  // The constructor determines the length of the snake
  Snake(int n) {
    xpos = new int[n];
    ypos = new int[n];
  }

  void update(int newX, int newY) {
    // Shift all elements down one spot. 
    // xpos[0] = xpos[1], xpos[1] = xpos = [2], and so on. Stop at the second to last element.
    for (int i = 0; i < xpos.length-1; i ++ ) {
      xpos[i] = xpos[i+1]; 
      ypos[i] = ypos[i+1];
    }

    // Update the last spot in the array with the mouse location.
    xpos[xpos.length-1] = newX; 
    ypos[ypos.length-1] = newY;
  }

  void display() {
    // Draw everything
    for (int i = 0; i < xpos.length; i ++ ) {
      // Draw an ellipse for each element in the arrays. 
      // Color and size are tied to the loop's counter: i.
      stroke(0);
      fill(255-i*5);
      ellipse(xpos[i],ypos[i],i,i); 
    }

  }

}
  • Cole

    hi.
    I was able to connect the provided Color Tracker/Snake example, but now I am trying to connect a Boid sketch. (code below) I believe my issues are with class naming, duplicate voids in each sketch. I was going to post an attempt that was giving me problems but felt like it would be more confusing than helpful. Best to see both in their original code.
    Thanks!

    // Boid Sketch

    final int WINDOW_WIDTH = 1000;
    final int WINDOW_HEIGHT = 800;
    final int FRAME_RATE = 60;

    final int NUM_BOIDS = 40;
    final int CENTER_PULL_FACTOR = 80000;
    final float BOUNCE_ABSORPTION = 0.75;

    final color backColor = color(0, 0, 0);

    class Vector
    {
    float x, y;

    public Vector()
    {
    x = 0;
    y = 0;
    }

    public Vector(float initX, float initY)
    {
    x = initX;
    y = initY;
    }
    } // end class Vector

    class Boid
    {
    final int TRAIL_SCALE = 35;

    color myColor = color(255, 255, 255);
    int mySize = 2;

    float xpos, ypos;
    float vx, vy;

    void drawMe()
    {
    stroke(myColor);
    strokeWeight(mySize+3*sqrt(vx*vx+vy*vy));

    line(xpos, ypos, xpos-TRAIL_SCALE*vx, ypos-TRAIL_SCALE*vy);
    }

    } // end class Boid

    // ************************* GLOBAL VARIABLES **************************

    Boid[] flock = new Boid[NUM_BOIDS];
    Vector v1 = new Vector();
    Vector v2 = new Vector();
    Vector v3 = new Vector();
    Vector v4 = new Vector();

    void setup()
    {
    size(WINDOW_WIDTH, WINDOW_HEIGHT);
    background(backColor);

    randomSeed(int(random(1,1000)));

    for(int i=0; i<NUM_BOIDS; ++i)
    {
    flock[i] = new Boid();
    flock[i].xpos = random(0, WINDOW_WIDTH);
    flock[i].ypos = random(0, WINDOW_HEIGHT);

    flock[i].vx = random(-0.5, 0.5);
    flock[i].vy = random(-0.5, 0.5);

    } // end for

    frameRate(FRAME_RATE);
    smooth();
    }

    void draw()
    {
    background(backColor);

    for (int i=0; i<NUM_BOIDS; ++i)
    {
    flock[i].drawMe();
    updateFlock();
    } // end for
    }

    void updateFlock()
    {
    for (int i=0; i<NUM_BOIDS; ++i)
    {
    v1 = rule1(flock[i]);
    v2 = rule2(flock[i]);
    v3 = rule3(flock[i]);
    v4 = rule4(flock[i]);

    // add vectors to velocities
    flock[i].vx += v1.x + v2.x + v3.x + v4.x;
    flock[i].vy += v1.y + v2.y + v3.y + v4.y;

    limitVelocity(flock[i]);

    // update new position with previously calculated velocities
    flock[i].xpos += flock[i].vx;
    flock[i].ypos += flock[i].vy;

    if (flock[i].xpos WINDOW_WIDTH)
    flock[i].vx = -flock[i].vx*BOUNCE_ABSORPTION;

    if (flock[i].ypos WINDOW_HEIGHT)
    flock[i].vy = -flock[i].vy*BOUNCE_ABSORPTION;

    } // end for
    }

    void limitVelocity(Boid b)
    {
    float vlim = 1.5;
    Vector v = new Vector();

    float velocity = sqrt(sq(b.vx) + sq(b.vy));

    if (velocity > vlim)
    {
    float vx = (b.vx/velocity)/vlim;
    float vy = (b.vy/velocity)/vlim;
    b.vx = vx;
    b.vy = vy;
    }

    } // end limitVelocity()

    Vector rule1(Boid b)
    {
    Vector pc = new Vector();

    for (int i=0; i < NUM_BOIDS; ++i)
    {
    if (b != flock[i])
    {
    pc.x += flock[i].xpos;
    pc.y += flock[i].ypos;
    } // end if
    } // end for

    pc.x /= (NUM_BOIDS-1);
    pc.y /= (NUM_BOIDS-1);

    pc.x = (pc.x – b.xpos) / CENTER_PULL_FACTOR;
    pc.y = (pc.y – b.ypos) / CENTER_PULL_FACTOR;

    return pc;
    } // end rule1()

    Vector rule2(Boid b)
    {
    Vector v = new Vector();

    for (int i=0; i < NUM_BOIDS; ++i)
    {
    if (b != flock[i])
    {
    if (distance2d(b, flock[i]) < 20)
    {
    v.x -= flock[i].xpos – b.xpos;
    v.y -= flock[i].ypos – b.ypos;
    } // end if
    } // end if
    } // end for

    return v;
    } // end rule2()

    Vector rule3(Boid b)
    {
    Vector v = new Vector();

    for (int i=0; i < NUM_BOIDS; ++i)
    {
    if (b != flock[i])
    {
    v.x += b.vx;
    v.y += b.vy;
    } // end if
    } // end for

    v.x /= (NUM_BOIDS – 1);
    v.y /= (NUM_BOIDS – 1);

    v.x = (v.x – b.vx)/8;
    v.y = (v.y – b.vy)/8;

    return v;
    } // end rule3()

    Vector rule4(Boid b)
    {
    Vector t = new Vector(0,0);
    //return t;

    Vector v = new Vector();

    v.x = (mouseX – b.xpos) / CENTER_PULL_FACTOR;
    v.y = (mouseY – b.ypos) / CENTER_PULL_FACTOR;

    return v;

    } // end rule4()

    float distance2d(Boid v1, Boid v2)
    {
    float x = abs(v1.xpos – v2.xpos);
    float y = abs(v1.ypos – v2.ypos);

    return((sqrt(x*x + y*y)));
    }

  • http://profiles.google.com/ste.e.white Stephen White

    Hi.
    I downloaded both the snake code and the camera code,

    I’m wondering how i would go around changing the colour of the snake, to either randomly change in say RGB colours, or colours of what the camera is picking up.

    Cheers

  • http://twitter.com/shiffman Daniel Shiffman

    The colors of the camera is the code that is on this very page! Just click “reveal answer”. To make a random color in Processing you can say:

    color c = color(random(255),random(255),random(255));

  • http://profiles.google.com/ste.e.white Stephen White

    Thanks for the reply, where abouts when i implement that code to get the ellipses to change color?

  • http://twitter.com/shiffman Daniel Shiffman

    If you want them to always be a new random every frame you can pick the random color right when you draw the ellipse:

    color c = color(random(255),random(255),random(255));
    fill(c);
    ellipse(xpos[i],ypos[i],i,i);

    You might also consider making an array of random colors when the Snake object is made.

  • Derder131

    This is really great, but how do i mirror the camera image?

  • Anonymous

    pushMatrix();
    scale(-1,1);
    image(video,0,-video.width);
    popMatrix();

  • Derder131

    Okay thanks but now all the snake is all wrong :(

  • Anonymous

    you can reverse the snake’s location by saying

    snake.update(width-1-closestX,closestY);