stop motion shaving

I’m heading to the beach on Monday, which means that the beard had to go.  I don’t know when it might return — certainly not for a long time.  It was fun, but I think I’m probably just not cut out to operate a beard at a professional level.  Ah well.

Of course, there was still the question of how to shave it off.  Usually this is taken as an opportunity to briefly sport some funny or Wolverine-related facial hair configurations, but I decided I’d waste even more time and nerd it up a bit.

I adapted one of the demo sketches that ships with Processing to facilitate lining the shots up; if you want to do something similarly dumb, you can find the code after the jump (you’ll need to update the system path near the bottom).

I encourage those of you with access to small, gullible children to tell them that the above gif represents how beards actually grow in.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
/*

a tool for creating an animated gif of yourself doing something dumb

based heavily on the background diff example that ships with processing

displays diffs between frames over a threshold so that you can see where you've moved
hit spacebar to take a snapshot
all frames from a given run will have the same timestamp prefix, e.g. frame-1300552348-#.png

can then convert to an animated gif using eg http://www.lcdf.org/gifsicle/ or (w/ imagemagick) "convert -delay 13 -loop 0 *.png output.gif"

*/



import processing.video.*;

int numPixels;
int[] backgroundPixels;
Capture video;
long startTime = 0;
int counter = 0;

void setup() {
  // Change size to 320 x 240 if too slow at 640 x 480
  size(640, 480, P2D);
 
  video = new Capture(this, width, height, 24);
  numPixels = video.width * video.height;
  // Create array to store the background image
  backgroundPixels = new int[numPixels];
  // Make the pixels[] array available for direct manipulation
  loadPixels();
 
  Date d = new Date();
  startTime=d.getTime()/1000;
}

void draw() {
  if (video.available()) {
    video.read(); // Read a new video frame
    video.loadPixels(); // Make the pixels of video available
    // Difference between the current frame and the stored background
    int presenceSum = 0;
    for (int i = 0; i < numPixels; i++) { // For each pixel in the video frame...
     
      // flip the display horizontally -- makes it mirrorlike; easier to line yourself up with it
      int j = (i - (i%width)) + ((width-1) - (i%width));
     
     
      // Fetch the current color in that location, and also the color
      // of the background in that spot
      color currColor = video.pixels[j];
      color bkgdColor = backgroundPixels[j];
     
      // Extract the red, green, and blue components of the current pixel’s color
      int currR = (currColor >> 16) & 0xFF;
      int currG = (currColor >> 8) & 0xFF;
      int currB = currColor & 0xFF;
      // Extract the red, green, and blue components of the background pixel’s color
      int bkgdR = (bkgdColor >> 16) & 0xFF;
      int bkgdG = (bkgdColor >> 8) & 0xFF;
      int bkgdB = bkgdColor & 0xFF;

      // Compute the difference of the red, green, and blue values
      int diffR = abs(currR - bkgdR);
      int diffG = abs(currG - bkgdG);
      int diffB = abs(currB - bkgdB);

      // provide a "ghost" image to help line things up
      float c = 0.5;
      int newR = round((c*currR) + ((1-c)*bkgdR));
      int newG = round((c*currG) + ((1-c)*bkgdG));
      int newB = round((c*currB) + ((1-c)*bkgdB));
      float totalDiff = (diffR+diffG+diffB) / (255.0*3);

      if (totalDiff>0.3)
        pixels[i] = 0xFFFF0000;
      else
        pixels[i] = 0xFF000000 | (newR<<16) | (newG<<8) | newB;      
 
    }
    updatePixels(); // Notify that the pixels[] array has changed
  }
}

// When a key is pressed, capture the background image into the backgroundPixels
// buffer, by copying each of the current frame’s pixels into it.
void keyPressed() {
  video.loadPixels();
  arraycopy(video.pixels, backgroundPixels);
 
  PImage temp = createImage(width, height, RGB);
  temp.loadPixels();
  arraycopy(video.pixels, temp.pixels);
  temp.updatePixels();
  temp.save("/path-to-output-directory/frame-" + startTime + "-" + counter + ".png");
  counter = counter + 1;
}

4 Responses to “stop motion shaving”

  1. Gunnar says:

    My favorite part is the boogie you’re doing, even if it’s an artifact.

  2. jeff says:

    Marie says, “he sort of looks like an alien”.

  3. Tom says:

    I agree with both of you. But yes, the boogying is a problem. It was even worse in the original version, before I cropped it more tightly.

  4. Eric Mill says:

    There’s a certain yearning that comes through the canvas.

Leave a Reply