Moving Poster: Big Brother
During our Graphic Design Basics course in my first semester at ZHdK I designed this moving poster. It was illustrated in Adobe Illustrator, layouted and typeset in Adobe InDesign and animated using Processing.
Presentation Slides
Illustrations in Adobe Illustrator
The illustrations of the eyes were done in Adobe Illustration. I'm used to this software from my apprenticeship as a Polygraf EFZ.
Layout and type setting in Adobe InDesign
The layout and typography were done in Adobe InDesign. I'm used to this software from my apprenticeship as a Polygraf EFZ as well and am quite proficient in using it.
Animations in Processing
The code for animating the moving poster was written in Processing. I used OpenCV for the computer vision feature. The poster was displayed on a large vertical monitor featuring a Cinnect camera. When people moved in front of the poster, the eyes followed the person standing closest and the text changed. I used facial image recognition for finding peoples faces. This was the first time I used OpenCV. It was a lot of fun.
moving-poster.pde
int posterBackground = 255;
int textId = 1;
float actorX;
float actorY;
float eyeW;
float eyeH;
float margin;
float gutter;
Eye eye1;
Eye eye2;
Eye eye3;
Eye eye4;
Eye eye5;
Eye eye6;
Eye eye7;
Eye eye8;
Eye eye9;
Text text;
void setup() {
fullScreen(2);
setupScreen();
shapeMode(CENTER);
setupOpenCV();
eyeW = width * 0.25;
eyeH = width * 0.25;
margin = width * 0.05;
gutter = width * 0.075;
eye1 = new Eye(eyeW * 0.5 + margin, eyeH * 0.5 + margin, eyeW, eyeH);
eye2 = new Eye(eyeW * 1.5 + margin + gutter, eyeH * 0.5 + margin, eyeW, eyeH);
eye3 = new Eye(eyeW * 2.5 + margin + gutter * 2, eyeH * 0.5 + margin, eyeW, eyeH);
eye4 = new Eye(eyeW * 0.5 + margin, eyeH * 1.5 + margin + gutter, eyeW, eyeH);
eye5 = new Eye(eyeW * 1.5 + margin + gutter, eyeH * 1.5 + margin + gutter, eyeW, eyeH);
eye6 = new Eye(eyeW * 2.5 + margin + gutter * 2, eyeH * 1.5 + margin + gutter, eyeW, eyeH);
eye7 = new Eye(eyeW * 0.5 + margin, eyeH * 2.5 + margin + gutter * 2, eyeW, eyeH);
eye8 = new Eye(eyeW * 1.5 + margin + gutter, eyeH * 2.5 + margin + gutter * 2, eyeW, eyeH);
eye9 = new Eye(eyeW * 2.5 + margin + gutter * 2, eyeH * 2.5 + margin + gutter * 2, eyeW, eyeH);
text = new Text(width / 2, height * 0.9);
};
void draw() {
background(posterBackground);
PVector heapPoint = faceLocation();
eye1.setActorPos(heapPoint.x, heapPoint.y);
eye2.setActorPos(heapPoint.x, heapPoint.y);
eye3.setActorPos(heapPoint.x, heapPoint.y);
eye4.setActorPos(heapPoint.x, heapPoint.y);
eye5.setActorPos(heapPoint.x, heapPoint.y);
eye6.setActorPos(heapPoint.x, heapPoint.y);
eye7.setActorPos(heapPoint.x, heapPoint.y);
eye8.setActorPos(heapPoint.x, heapPoint.y);
eye9.setActorPos(heapPoint.x, heapPoint.y);
eye1.draw();
eye2.draw();
eye3.draw();
eye4.draw();
eye5.draw();
eye6.draw();
eye7.draw();
eye8.draw();
eye9.draw();
if (faces.length > 0) {
eye1.move();
eye2.move();
eye3.move();
eye4.move();
eye5.move();
eye6.move();
eye7.move();
eye8.move();
eye9.move();
} else if (faces.length == 0) {
eye1.idle();
eye2.idle();
eye3.idle();
eye4.idle();
eye5.idle();
eye6.idle();
eye7.idle();
eye8.idle();
eye9.idle();
}
text.draw(textId);
if (faces.length > 0) {
textId = 2;
} else if (faces.length == 0) {
textId = 1;
}
};
opencv.pde
import gab.opencv.*;
import processing.video.*;
import java.awt.Rectangle;
// resolution for webcamVideo
int camWidth = 320;
int camHeight = 180;
//
boolean displayWindow = false;
Rectangle[] faces = new Rectangle[0];
Rectangle ActiveFace;
PVector[] headPosition = new PVector[15]; // a longer array means more references for calculating average
int readIndex = 0;
PVector total = new PVector();
PVector average = new PVector();
PVector heapPoint = new PVector();
Capture video;
OpenCV opencv;
void setupOpenCV() {
String[] cameras = Capture.list();
ActiveFace = new Rectangle();
if (cameras.length == 0) {
println("There are no cameras available for capture.");
exit();
} else {
println("Available cameras:");
for (int i = 0; i < cameras.length; i++) {
println(cameras[i]);
}
}
//openCV
for (int i = 0; i<headPosition.length; i++) {
headPosition[i] = new PVector();
}
// video information
video = new Capture(this, camWidth, camHeight, cameras[1]);
opencv = new OpenCV(this, camWidth, camHeight);
opencv.loadCascade(OpenCV.CASCADE_FRONTALFACE);
video.start();
}
PVector faceLocation() {
opencv.loadImage(video);
faces = opencv.detect();
if (faces.length == 0) {
ActiveFace.width = 1;
ActiveFace.height = 1;
ActiveFace.x = camWidth/2;
ActiveFace.y = camHeight/2;
} else if (faces.length>1) {
int index = 0;
float record = camWidth*2;
for (int i = 0; i < faces.length; i++) {
float d = dist(faces[i].x, faces[i].y, ActiveFace.x, ActiveFace.y);
if (d < record) {
record = d;
index = i;
}
}
ActiveFace = faces[index];
} else if (faces.length == 1) {
ActiveFace = faces[0];
}
heapPoint.x = ActiveFace.x + ActiveFace.width/2;
heapPoint.y = ActiveFace.y + ActiveFace.height/2;
heapPoint.x = map(heapPoint.x, ActiveFace.width/2, camWidth-(ActiveFace.width/2), 0, width);
heapPoint.y = map(heapPoint.y, ActiveFace.height/2, camHeight-(ActiveFace.height/2), 0, height);
heapPoint.z = map(ActiveFace.width, 30, camWidth, 100, 0);
// smooth readings out to avoid any shake
total = total.sub(headPosition[readIndex]);
headPosition[readIndex] = heapPoint.copy();
total = total.add(headPosition[readIndex]);
readIndex = readIndex + 1;
if (readIndex >= headPosition.length) {
readIndex = 0;
}
// calculate average
average.x = total.x / headPosition.length;
average.x = -average.x + width;
average.y = total.y / headPosition.length;
average.z = total.z / headPosition.length;
return average;
}
//2nd window for debuging
void captureEvent(Capture c) {
c.read();
}
public class SecondApplet extends PApplet {
public void settings() {
size(320, 180);
}
public void draw() {
image(video, 0, 0);
pushStyle();
noFill();
stroke(255, 0, 0);
rect(ActiveFace.x, ActiveFace.y, ActiveFace.width, ActiveFace.height);
popStyle();
}
}
// activate 2nd window
void keyPressed() {
if (key == ' ') {
if (!displayWindow) {
String[] args = {"video"};
SecondApplet sa = new SecondApplet();
PApplet.runSketch(args, sa);
displayWindow = true;
}
}
}