Tuesday, July 3, 2012

in difference...

         ...we may find the answers we seek.  Or at least a cool way to do motion-ish tracking.      

        This makes the second time this week i've been researching something just for the hell of it and suddenly find a use for it a day later, altho I guess the rounding algorithm stuff did have an actual purpose.  Oddly enough, the project i want to use it for was waiting for me to figure out some version of this little snippet.  Originally I was thinking i was going to have to do it as a full on hand-tracking/skeletal tracking thing, but if I can figure out some smoothing, I think this'll work pretty nicely on its own.  We're putting together a mini-installation at work, details of which I'll not spoil for you here, but should be fun...

        I started wondering about frame differencing after repeated visits to testwebcam.com (yes, it's SFW).  Simple effect, but it looks really cool.  Also a great way to approximate motion on a webcam stream.  The initial implementation didn't take me too long, now I just have to implement some optical flow or maybe even just a cheap trailing-3 tap, who knows?  Of course, you're welcome to solve that problem yourself if you wanna copy-paste this into your own copy of processing and hit Ctrl-R...seriously, your copy of p5 looks a little lonely and unloved, you should do something with it...I did some tests with lerping and vector/distancing, but i think i'm going to need a real filter...

faketracker
This is totally not a photoshop, run the sketch if you don't believe me...

        One of these days I need to start seriously optimizing some (all) of these sketches and my ofx projects. It's ok to suck out loud for now, but truth be told, I think i'm actually a better programmer than that.  I mean, not that I'm a good programmer, i'm just decent enough not to make silly un-optimization mistakes.  Eh, this one'll optimize itself out anyway me thinks, i imagine the filtering isn't going to be cheap...I also really need to start taking video...
//PREPARE YOURSELF FOR THE COMING OF 2.0
//GET GSVIDEOOOOkay it's actually not going to be
//that big of a transition.
import codeanticode.gsvideo.*;

PImage lastFrame;
GSCapture vStream;
int diff;
int thresh = 32;
ArrayList<PVector> dVals = new ArrayList();
PVector p_m;
PVector lastP;
void setup()
{
  p_m = new PVector(0,0);  
  lastP = new PVector(0,0);
  size(640, 480, P2D);
  frameRate(30);
  lastFrame = createImage(width,height,RGB);
  vStream = new GSCapture(this, width, height);
  vStream.start();
  background(0);
}

void draw()
{
  diff = 0;
  loadPixels();
  dVals.clear();
  
  if(vStream.available())
  {
    vStream.read();
    vStream.loadPixels();
    lastFrame.loadPixels();
    for (int x=0;x<width;x++)
    {
      for (int y=0;y<height;y++)
      {
        int i = y*width+x;
        color c = vStream.pixels[i];
        color l = lastFrame.pixels[i];
        int c_r = int(red(c));
        int c_g = int(green(c));
        int c_b = int(blue(c));
        int l_r = int(red(l));
        int l_g = int(green(l));
        int l_b = int(blue(l));
        
        int d_r = max(0,(c_r-l_r)-thresh);
        int d_g = max(0,(c_g-l_g)-thresh);
        int d_b = max(0,(c_b-l_b)-thresh);
        
        int d_s = d_r+d_g+d_b;
        diff += d_s;
        if(d_s>0)
        {
          dVals.add(new PVector(x,y));
        }
        pixels[i] = vStream.pixels[i];
        lastFrame.pixels[i] = c;
      }
    }
  }
  updatePixels();
  if(diff>0)
  {
    p_m = avgArrayList(dVals);
    fill(255,255,255);
    ellipse(p_m.x,p_m.y,40,40);    
  }
  lastP = p_m;
}

PVector avgArrayList(ArrayList<PVector> arr)
{
  float sumx=0;
  float sumy=0;
  for(int i=0;i<arr.size();i++)
  {
    PVector c = (PVector)arr.get(i);
    sumx+=c.x;
    sumy+=c.y;
  }
  return new PVector(sumx/arr.size(),sumy/arr.size());
}

void keyPressed()
{
  if(key=='q')
  {
    thresh+=1;
    if(thresh>128)
      thresh=128;
  }
  if(key=='a')
  {
    thresh-=1;
    if(thresh<8)
      thresh=8;
  }
}

void stop()
{
  vStream.stop();
  vStream.dispose();
}