// This program generates videos which demonstrate how various image
// processing algorithms work.
// Set the 'transformation' variable in order to determine which
// video you want to see.
char transformation = 's';
// 'm' - mirror image
// 'r' - rotate image
// 's' - shrink image
// 'e' - expand image
// 'c' - rotate color components
// 'd' - edge detection
// Coordinates of the virtual screen
int[] screenCorner = new int[2];
int[] screenSize = new int[2];
int charSize = 20;
// Each image is represented by an array of rectangles. Each
// rectangle represents one image.
int[][] img1 = new int[10][10];
int[][] img2 = new int[10][10];
// The top left corner of the first img are the same as the screen.
// The top left corner of the second img is below
int[] img2Corner = new int[2];
// Set up the graphics
void setup() {
size(1000, 500);
background(200);
// Figure out where the screen is drawn
screenCorner[0] = width/10;
screenCorner[1] = height/10;
screenSize[0] = width - 2 * screenCorner[0];
screenSize[1] = height - 2 * screenCorner[1];
// Draw it
drawScreen();
// The frame rate is the same as the animation rate.
frameRate(1);
}
// These pixels will also be highlighted in the video
int[] extraspecial1 = {
};
int[] extraspecial2 = {
};
// Each frame, figure out what the next image in the video
// should be. This sets particular points on each image as
// 'special' which will be highlighted in the animation.
// Then draw img1 and img2.
void draw() {
int[] special = animate();
drawPixels(img1, screenCorner[0], screenCorner[1],
screenSize[0]/2, screenSize[1], special[0], special[1], extraspecial1);
drawPixels(img2, screenCorner[0]+screenSize[0]/2,
screenCorner[1], screenSize[0]/2, screenSize[1],
special[2], special[3], extraspecial2);
}
// Draw the pixels corresponding to a particular image
void drawPixels(int[][] img, int x, int y, int w, int h, int sx, int sy, int[] extra) {
// Fill in each of the pixel colors
int pixelw = w/img.length;
int pixelh = h/img[0].length;
stroke(0);
for (int i = 0; i < img.length; i++) {
for (int j = 0; j < img.length; j++) {
// Fill with the appropriate color, then draw a rectangle corresponding
// to that pixel
fill(img[i][j]);
rect(x + pixelw * i, y + pixelh * j, pixelw, pixelh);
}
}
// erase any text I might have previously written
noStroke();
fill(200);
rect(x, screenCorner[1] + screenSize[1] + 1, 100, 100);
// If necessary, write special x and y
if ((sx >= 0) && (sy >= 0)) {
// Highlight the special pixel
stroke(255, 0, 0);
strokeWeight(3);
fill(0, 0);
rect(x + pixelw * sx+3, y + pixelh * sy+3, pixelw-6, pixelh-6);
// Write the special pixel's location below
strokeWeight(1);
fill(0);
text("x: " + sx + "; y: " + sy, x, screenCorner[1] + screenSize[1] + 2 * charSize);
stroke(0);
}
// If necessary, write extra special x and y
for (int i = 0; i < extra.length; i += 2) {
// Highlight the extra special pixels
stroke(250, 128, 5);
strokeWeight(3);
fill(0, 0);
rect(x + pixelw * extra[i]+3, y + pixelh * extra[i+1] +3, pixelw-6, pixelh-6);
strokeWeight(1);
}
}
// This draws the basic screen with widths and heights
void drawScreen() {
fill(0);
int textY = screenCorner[1] - 2 * charSize;
text("<----", screenCorner[0], textY);
text("width", width/2-charSize, textY);
text("---->", width-(2*screenCorner[1])-40, textY);
textY += charSize;
text("<----", screenCorner[0], textY);
text("img1.width", width/4-charSize, textY);
text("---->", (width-(2*screenCorner[1]))/2, textY);
text("<----", (width-(2*screenCorner[1]))/2 + 3*charSize, textY);
text("img2.width", width/4+screenSize[0]/2, textY);
text("---->", width-(2*screenCorner[1])-40, textY);
int textX = screenCorner[0] - charSize;
text("^", textX, screenCorner[1]+charSize);
text("|", textX, screenCorner[1]+2*charSize);
text("|", textX, screenCorner[1]+3*charSize);
text("height", textX - charSize, height/2);
text("|", textX, height-screenCorner[1]-2*charSize);
text("|", textX, height-screenCorner[1]-charSize);
text("v", textX, height-screenCorner[1]);
// make 'screen'
fill(255);
rect(100, 50, width-200, height-100);
}
// This runs the actual animation. Depending on the frame,
// it does different things. Essentially, it sets the colors
// of each image and notes if anything needs to be highlighted.
int[] animate() {
int[] special = new int[4];
if (frameCount == 1) {
// make both images be pure white
for (int i = 0; i < img1.length; i++) {
for (int j = 0; j < img1[0].length; j++) {
img1[i][j] = 255;
}
}
for (int i = 0; i < img2.length; i++) {
for (int j = 0; j < img2[0].length; j++) {
img2[i][j] = 255;
}
}
}
else if (frameCount == 2) {
// "load" img1
colorMode(HSB);
for (int i = 0; i < img1.length; i++) {
int j;
for (j = 0; j < i; j++) {
img1[i][j] = color(100 + round(155*(i*1.0)/(img1.length-1)),
255-round(180*(j*1.0)/(img2.length-1)),
255);
}
for(; j < img1[0].length; j++) {
img1[i][j] = color(100 + round(155*(i*1.0)/(img1.length-1)),
255-round(180*(j*1.0)/(img2.length-1)),
0+round(255*(j*1.0)/(img2.length-1)));
}
}
colorMode(RGB);
}
else if (frameCount < 3 + img1.length*img1[0].length) {
return transform(frameCount-3);
}
// if nothing has returned yet, then there is no special point
special[0] = -1;
special[1] = -1;
special[2] = -1;
special[3] = -1;
extraspecial1 = new int[0];
extraspecial2 = new int[0];
return special;
}
int[] transform(int pix) {
int[] special = new int[4];
if (transformation == 'm') {
special[0] = pix%img1[0].length;
special[1] = pix/img1[0].length;
special[2] = img1.length-special[0]-1;
special[3] = special[1];
img2[special[2]][special[3]] = img1[special[0]][special[1]];
}
else if (transformation == 'r') {
special[0] = pix%img1[0].length;
special[1] = pix/img1[0].length;
special[2] = img1[0].length-special[1]-1;
special[3] = special[0];
img2[special[2]][special[3]] = img1[special[0]][special[1]];
}
else if (transformation == 's') {
special[2] = (pix/2)%(img1[0].length/2);
special[3] = (pix/2)/(img1[0].length/2);
special[0] = special[2]*2;
special[1] = special[3]*2;
img2[special[2]][special[3]] = img1[special[0]][special[1]];
}
else if (transformation == 'e') {
int basex = (pix/4)%(img1.length/2);
int basey = (pix/4)/(img1[0].length/2);
special[0] = basex;
special[1] = basey;
special[2] = basex*2;
special[3] = basey*2;
if ((pix%4 == 1) || (pix%4 == 3)) {
special[2]++;
}
if ((pix%4 == 2) || (pix%4 == 3)) {
special[3]++;
}
img2[special[2]][special[3]] = img1[special[0]][special[1]];
}
else if (transformation == 'c') {
special[0] = pix%img1[0].length;
special[1] = pix/img1[0].length;
special[2] = special[0];
special[3] = special[1];
color old = img1[special[0]][special[1]];
img2[special[2]][special[3]] = color(blue(old), red(old), green(old));
}
else if (transformation == 'd') {
special[2] = special[0] = pix%img1[0].length;
special[3] = special[1] = pix/img1[0].length;
if (special[0] == (img1.length-1)) {
special[0]--;
special[2]--;
}
else if (special[1] == (img1[0].length - 1)) {
special[2] = special[0] = img1.length-1;
special[3] = special[1] = special[1] - 1;
}
else {
// highlight the points that are used to make the edge detection decision
extraspecial1 = new int[4];
extraspecial1[0] = special[0] + 1;
extraspecial1[1] = special[1];
extraspecial1[2] = special[0];
extraspecial1[3] = special[1] + 1;
img2[special[2]][special[3]] = edge_detection(img1[special[0]][special[1]],
img1[extraspecial1[2]][extraspecial1[3]],
img1[extraspecial1[0]][extraspecial1[1]]);
}
}
return special;
}
int lineThreshold = 2;
// The algorithm used in this function is from
// "Introduction to Computing and Programming in
// Python: A Multimedia Approach", by Mark Guzdial
// and Barbara Ericson.
color edge_detection(color c, color below, color next) {
int lineThreshold = round(map(mouseX, 0, width, 1, 50));
// convert all colors to grayscale
c = round(brightness(c));
below = round(brightness(below));
next = round(brightness(next));
// see if C differs greatly from its two neighbors
if ((abs(c-below) > lineThreshold) &&
(abs(c-next) > lineThreshold)) {
// if yes, make it black
return color(0);
}
else {
// otherwise, make it white
return color(255);
}
}