// 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); } }