« October 2006 | Main | December 2006 »

November 15, 2006

pComp Final-Observation

Our final project will be, in brief, a photo booth that presents the user with a rear-projection screen showing their silhouette and falling particles they can interact with by moving their body. As they interact, their true image will be captured four times, as in a traditional photo booth, hopefully revealing interesting poses and expressions due to their interaction. The traditional photos and a screenshot of the screen with silhouette and particles as they see it will be shown, in an alternating scroll, on a monitor outside the booth. The booth itself will be constructed of semi-transparent cloth so that the lights and motion insight will be somewhat revealed to outside observers. Details to be worked out will be how to inspire interesting movements and expressions during this experience. We currently plan to generate moments of surprise to achieve this effect, by altering the appearance and/or behavior of the particles.

As a first step, our team (Daniel Soltis, Scott Varland & Jeff Sable) observed people using several photo booths around the city. Most interestingly to us, we noticed that people prefer to use the booths in pairs or groups, that they frequently adjust their proximity to the camera between shots, and that they invariably closed the small curtain (though left open in the photos below for our purposes), despite the fact that it affords only a small amount of privacy. We will explore this concept of perceived privacy in our piece. Also of note is the fact that the vast majority of photo booths in the city are located in bars, where people's self-consciousness about pictures of themselves being taken is reduced through alcohol. We're hoping that the stylization of the "mirror" the users will see of themselves will have an equally palliative effect.

See subsequent posts for details as we continue to work on the project.

ICM Final-Concept

Further developing the video processing work I did for my midterm, my plan for the final project is to build an applet that does live image processing of the user, but allows them to adjust the color and shapes of the stylization based on "buttons" on the screen that they can select by moving their hand to the appropriate spot. I may also integrate the project I'm working on for pComp, which is an adaptation of the "falling particles" applet that tracks the user's silhouette, allowing them to interact with the particles.

November 02, 2006

ICM Midterm

For my midterm project, I played with various video effects using a webcam as input. The movie below gives a general sense of the four effects (three mirror effects, and one motion tracking), though the video quality is truly terrible (sorry). To view the applet in action, paste the code below into Processing, and attach a webcam. Once set up, click 1, 2, 3, and 4 on the keyboard to scroll through the various effects.

Processing Code

To scroll through the different effects, type 1, 2, 3 or 4. Web cam required.

import processing.video.*;
Capture video;

int cellsize = 10; // Size of each cell in the grid
int cols, rows; // Number of columns and rows in our system
int mode; //variable to hold video selection
//code for motion detection
float sensitivity; // sensitivity is a percentage of pixels changed
boolean newFrame; // switch var for determining when a new frame is received
color[] prevFrame; // vars for holding the current and previous frames

void setup()
{
  size(400, 320);
  frameRate(30);
  smooth();
  background(0);
  //set up columns and rows for mirrors
  cols = width/cellsize;
  rows = height/cellsize;
  colorMode(RGB,255,255,255,100);
  //set up for motion detection
  sensitivity=0.4;
  newFrame=false;
  prevFrame=new color[width*height]; 
  
  // Using the default capture device
  video = new Capture(this, width, height, 20);
}

void captureEvent(Capture camera)
{
  camera.read();
  newFrame=true; //for motion detection
}

void draw()
{ 
  if (mode==1){
    circles();
  }
  else if (mode==2){
    star();
  }
  else if (mode==3){
    triangles();
  }
  else if (mode==4){
    motion();
  }
}

void keyPressed() //allows user to select one of four video modes
{
  if (key == '1' || key == '!') {
  mode=1;
  } 
  if (key == '2' || key == '@') {
  mode=2;
  }
  if (key == '3' || key == '#') {
  mode=3; 
  } 
  if (key == '4' || key == '$') {
  mode=4; 
  } 
}

void triangles(){
  background(255,140,0);
  cellsize=10;
  cols = width/cellsize;
  rows = height/cellsize;
  int t; //transparency
  // Begin loop for columns
  for ( int i = 0; i < cols;i++) {
    // Begin loop for rows
    for ( int j = 0; j < rows;j++) {
      
      // Where are we, pixel-wise?
      int x = i*cellsize;
      int y = j*cellsize;
      int loc = (video.width - x - 1) + y*video.width; // Reversing x to mirror the image
      //draw only yellow triangles
      float r=250;
      float g=250;
      float b = blue(video.pixels[loc]);
      if (b < 5){
        b=0;
        }
      else if (b<25 & b>=5){
        b=15;
        }
      else if (b<50 & b>=25){
        b=35;
        }
      else if (b<75 & b>=50){
        b=60;
        }
      else if (b<=115 & b>=90){
        b=100;
        }
      else if (b<=140 & b>=75){
        b=125;
        }
      else if (b<=165 & b>=140){
        b=150;
        }
      else{
        b=200;
      }
      //set transparency
      if (r+g+b<530){
        t=1;
      }
      else {
        t=100;
      }
      // Make a new color with an alpha component
      color c = color(r,g,b,t);
      
      // Code for drawing a single triangle
      // Using translate in order for rotation to work properly
      pushMatrix();
      translate(x+cellsize/2,y+cellsize/2);
      ellipseMode(CENTER);
      fill(c);
      noStroke();
      // Rects are larger than the cell for some overlap
      triangle(cellsize/2,0,cellsize,cellsize,0,cellsize);
      popMatrix();
    }
  }
}

void circles(){
  background(0);
  cellsize=12;
  cols = width/cellsize;
  rows = height/cellsize;
  // Begin loop for columns
  for ( int i = 0; i < cols;i++) {
    // Begin loop for rows
    for ( int j = 0; j < rows;j++) {
      
      // Where are we, pixel-wise?
      int x = i*cellsize;
      int y = j*cellsize;
      int loc = (video.width - x - 1) + y*video.width; // Reversing x to mirror the image
      
      float r = red(video.pixels[loc]);
      if (r < 50){
        r=0;
        }
      else if (r<100 & r>=50){
        r=50;
        }
      else if (r<150 & r>=100){
        r=100;
        }
      else if (r<200 & r>=150){
        r=150;
        }
      else if (r<=255 & r>=200){
        r=255;
        }
      float g = green(video.pixels[loc]);
      if (g < 50){
        g=0;
        }
      else if (g<100 & g>=50){
        g=50;
        }
      else if (g<150 & g>=100){
        g=100;
        }
      else if (g<200 & g>=150){
        g=150;
        }
      else if (g<=255 & g>=200){
        g=255;
        }
      float b = blue(video.pixels[loc]);
      if (b < 50){
        b=0;
        }
      else if (b<100 & b>=50){
        b=50;
        }
      else if (b<150 & b>=100){
        b=100;
        }
      else if (b<200 & b>=150){
        b=150;
        }
      else if (b<=255 & b>=200){
        b=255;
        }
      // Make a new color with an alpha component
      color c = color(r,g,b,75);
      
      // Code for drawing a single ellipse
      // Using translate in order for rotation to work properly
      pushMatrix();
      translate(x+cellsize/2,y+cellsize/2);
      ellipseMode(CENTER);
      fill(c);
      noStroke();
      // ellipses are larger than the cell for some overlap
      ellipse(0,0,cellsize-4,cellsize-4);
      popMatrix();
    }
  }
}

void star(){
 background(0);
  cellsize=14;
  cols = width/cellsize;
  rows = height/cellsize;
  // Begin loop for columns
  for ( int i = 0; i < cols;i++) {
    // Begin loop for rows
    for ( int j = 0; j < rows;j++) {
      
      // Where are we, pixel-wise?
      int x = i*cellsize;
      int y = j*cellsize;
      int loc = (video.width - x - 1) + y*video.width; // Reversing x to mirror the image
      
      float r = red(video.pixels[loc]);
      if (r < 25){
        r=0;
        }
      else if (r<75 & r>=25){
        r=50;
        }
      else if (r<125 & r>=75){
        r=100;
        }
      else if (r<200 & r>=125){
        r=175;
        }
      else if (r<=255 & r>=200){
        r=245;
        }
      float g = green(video.pixels[loc]);
      if (g < 25){
        g=0;
        }
      else if (g<75 & g>=25){
        g=50;
        }
      else if (g<200 & g>=75){
        g=100;
        }
      else if (g<200 & g>=125){
        g=175;
        }
      else if (g<=255 & g>=200){
        g=250;
        }
      float b = blue(video.pixels[loc]);
      if (b < 25){
        b=0;
        }
      else if (b<75 & b>=25){
        b=50;
        }
      else if (b<200 & b>=75){
        b=100;
        }
      else if (b<200 & b>=125){
        b=175;
        }
      else if (b<=255 & b>=200){
        b=255;
        }

      // Make a new color 
      color c = color(r,g,b,75);
      
      //draw a star
      // Using translate in order for rotation to work properly
      pushMatrix();
      translate(x+cellsize/2,y+cellsize/2);
      ellipseMode(CENTER);
      fill(c);
      noStroke();
      beginShape();
      vertex(7.5, 0);
      vertex(10, 5);
      vertex(15, 6);
      vertex(11, 10);
      vertex(12, 15);
      vertex(7.5, 12.5);
      vertex(2.8, 15);
      vertex(3.75, 10);
      vertex(0, 6);
      vertex(5, 5);
      vertex(7.5, 0);
      endShape();
      popMatrix();
    }
  }
}

void motion(){
  if(newFrame){
    newFrame=false;
    image(video,0,0); // update display with current frame
    for(int y=0; y<64-1; y++){ // loop through video in 60x80 grid
      for(int x=0; x<80-1; x++){
        int cxLoc=x*(width/80); // x position on grid
        int cyLoc=y*(height/64); // y position on grid
        if(motionTest(cxLoc,cyLoc,width/80,height/64)==true){
          float timer=random(5,10);
          fill(0,0,255,timer);
          noStroke();
          ellipse(cxLoc+random(0,7),cyLoc+random(0,7),5,5); //draw sparkles
          timer=random(10,15);
          fill(100,100,255,timer);
          ellipse(cxLoc+random(0,7),cyLoc+random(0,7),4,4); //draw sparkles
          timer=random(10,15);
          fill(200,200,255,timer);
          ellipse(cxLoc+random(0,7),cyLoc+random(0,7),3,3); //draw sparkles
          timer=random(15,20);
          fill(255,timer);
          ellipse(cxLoc+random(0,7),cyLoc+random(0,7),2,2); //draw sparkles
        }
      }
    }
  }
}
// this is used to determine when the person is moving for the motion effect
boolean motionTest(int srcX,int srcY,int tw, int th){
    int dc=0; // counter to track number of differences
    color newPixel; 
    for(int y=0;y23) dc++; 
        else if(abs(green(newPixel)-green(prevFrame[srcPos]))>23) dc++;
        else if(abs(blue(newPixel)-blue(prevFrame[srcPos]))>23) dc++;
        prevFrame[srcPos]=newPixel; // update prevFrame w/ current pixel clr
      }
    }
    // test to see if number of difference is 'significant'
    if(dc>(sensitivity*(tw*th))){  
      return true;
    }
    else{
      return false;
    }
  }