PImage baseImage, targetImage, transformedImage; PImage background; // display dimensions of images int base_left, base_bot, w, h; int trans_left, trans_bot; // display dimensions of button int box_w, box_h, box_l, box_b; // display properties for crosshairs int circle_x, circle_y; int radius = 10; boolean plotCircles; // mapping of indices between base image and transform image // baseToTransform[i] = location in transform of base pixel i int[] baseToTransform, transformToBase; color[] values; // sorted list of pixels in original image Integer[] baseLocations; // indices of values[i] in the base Image color c1 = color(60, 40, 180); int image_id = -1; // which transform image did we use last? /* initialize the program */ void setup() { size(800, 350); background = loadImage("bg.png"); baseImage = loadImage("oph.png"); baseImage.resize(400, 400 * baseImage.height / baseImage.width); baseLocations = argSort(baseImage.pixels); values = sort(baseImage.pixels); baseToTransform = new int[values.length]; transformToBase = new int[values.length]; pickTarget(); transformedImage = createImage(baseImage.width, baseImage.height, RGB); transformImage(); box_w = width / 4; box_h = height / 8; box_l = width/2 - box_w/2; box_b = (int) (0.9 * height - box_h / 2); draw(); noLoop(); } /* load a new image at random */ void pickTarget() { String[] images = {"bieber.jpeg", "mandlebrot.jpg", "smiley.png", "whitehouse.jpeg", "m51.jpg", "et.jpg", "random.png", "blob.png"}; int id; do { id = int(random(images.length)); } while (id == image_id); image_id = id; targetImage = loadImage(images[id]); } /* Draw the images */ void displayImages() { fill(0); stroke(0); image(background, 0, 0); float ratio = 1. * baseImage.width / baseImage.height; int xcen = (int) (.25 * width); int ycen = (int) (.4 * height); w = (int) (.45 * width); h = (int) (.9 * height); if (ratio > 1) { h = (int) (w / ratio); } else { w = (int) (h * ratio); } base_left = xcen - w/2; base_bot = ycen - h/2; xcen = (int) (.75 * width); trans_left = xcen - w/2; trans_bot = ycen - h/2; image(baseImage, base_left, base_bot, w, h); image(transformedImage, trans_left, trans_bot, w, h); noFill(); stroke(105); strokeWeight(2); rect(base_left, base_bot, w, h); rect(trans_left, trans_bot, w, h); } /* Draw the action button */ void displayButton() { noStroke(); fill(150); rect(box_l+box_w/50, box_b+box_w/50, box_w, box_h); fill(c1); rect(box_l, box_b, box_w, box_h); fill(255); textAlign(CENTER); textSize(30); text("Transform!", box_l + box_w/2, box_b + box_h * .70); if (inBox()) { stroke(255); strokeWeight(2); noFill(); rect(box_l, box_b, box_w, box_h); } } /* returns true if the mouse is in the button */ boolean inBox() { return (mouseX >= box_l && mouseX < box_l + box_w && mouseY >= box_b && mouseY < box_b + box_h); } /* Draw a crosshairs around the pixel that matches the mouse */ void displayCircle() { if (!plotCircles) return; noFill(); stroke(c1); strokeWeight(2); ellipse(circle_x, circle_y, radius, radius); strokeWeight(1); line(circle_x, circle_y - radius/2, circle_x, circle_y + radius/2); line(circle_x-radius/2, circle_y, circle_x + radius/2, circle_y); } /* update the display */ void draw() { displayImages(); displayButton(); displayCircle(); } /* Handle mouse motion events */ void mouseMoved() { int baseIndex = screenToIndex(mouseX, mouseY, true); int transIndex = screenToIndex(mouseX, mouseY, false); if (baseIndex >= 0) { int[] xy = indexToScreen(baseToTransform[baseIndex], false); circle_x = xy[0]; circle_y = xy[1]; plotCircles = true; } else if (transIndex >= 0) { int[] xy = indexToScreen(transformToBase[transIndex], true); circle_x = xy[0]; circle_y = xy[1]; plotCircles = true; } else { plotCircles = false; } redraw(); } /* Handle mouse press events */ void mousePressed() { if (!inBox()) return; pickTarget(); transformImage(); redraw(); } /* * Convert a screen (XY) position to the corresponding 1D pixel index for * one of the images * * @param xx: The x coordinate * @param yy: The y coordinate * @param base: true if we are considering the base (left) image. * False if we want the transformed (right) image */ int screenToIndex(int xx, int yy, boolean base) { int[] result = new int[2]; float x, y; if (base) { x = 1. * (xx - base_left) / w * baseImage.width; y = 1. * (yy - base_bot) / h * baseImage.height; if (x < 0 || x >= baseImage.width || y < 0 || y >= baseImage.height) return -1; return ((int) x) + ((int) y) * baseImage.width; } else { x = 1. * (xx - trans_left) / w * transformedImage.width; y = 1. * (yy - trans_bot) / h * transformedImage.height; if (x < 0 || x >= transformedImage.width || y < 0 || y >= transformedImage.height) return -1; return ((int) x) + ((int) y) * transformedImage.width; } } /* * Convert a 1D index for one of the images to screen coordinates * @param index: The image index to convert * @param base: Whether or not we are considering the base (left) image */ int[] indexToScreen(int index, boolean base) { PImage image; int l, b; if (base) { image = baseImage; l = base_left; b = base_bot; } else { image = transformedImage; l = trans_left; b = trans_bot; } float x = (1. * (index % image.width) / image.width) * w + l; float y = (1. * (index / image.width) / image.height) * h + b; int[] result = new int[2]; result[0] = (int) x; result[1] = (int) y; return result; } /* * Redistribute the pixels in baseImage to look like targetImage * store the result in transformedImage */ void transformImage() { if (transformedImage.width != baseImage.width || transformedImage.height != baseImage.height) { transformedImage.resize(baseImage.width, baseImage.height); } PImage resizedImage = matchImageSize(targetImage, baseImage); resizedImage.loadPixels(); baseImage.loadPixels(); Integer[] locations = argSort(resizedImage.pixels); for (int i = 0; i < values.length; i++) { transformedImage.pixels[locations[i]] = values[i]; baseToTransform[baseLocations[i]] = locations[i]; transformToBase[locations[i]] = baseLocations[i]; } transformedImage.updatePixels(); } /* * Create new image that is a copy of image from, resized to match * the dimensions of image to * * @param from: Image to copy and transform * @param to: The image dimensions to match */ PImage matchImageSize(PImage from, PImage to) { PImage result = createImage(from.width, from.height, RGB); arrayCopy(from.pixels, result.pixels); result.resize(to.width, to.height); return result; }