!636 import java.awt.*; import java.awt.image.*; import javax.imageio.*; import static java.lang.Math.*; !standard functions !image classes !FrameWithImages !quicknew !* ctr !721 // thread { class P { double x, y; *(double *x, double *y) {} } interface Reproducer { public main.Params reproduce(RGBImage original); } interface ReproducerMaker { public Reproducer make(); } main { static Object androidContext; static int imageWidth = 200; !include #1000522 // helper functions for image reproduction static double pi = PI; static double rotate_x(P p, double angle) { angle = angle*pi/180; return p.x*cos(angle)-p.y*sin(angle); } static double sideshiftlimit = sqrt(2)/2; static class Magnet extends Params { double angle, sideshift; RGB col1 = new RGB(Color.black), col2 = new RGB(Color.white); double p1 = -1, p2 = -1; RGB def(double x, double y) { P p = new P(x-0.5, y-0.5); x = rotate_x(p, -angle); return x < sideshift ? col1 : col2; } Magnet copy() { new Magnet p; baseClone(p); p.sideshift = sideshift; p.angle = angle; p.col1 = col1; p.col2 = col2; return p; } public String toString() { return "M " + formatDouble(sideshift, 2) + " " + (int) angle + " " + col1 + " " + col2; } RGBImage render() { int w = original.getWidth(), h = original.getHeight(); RGBImage image = new RGBImage(w, h, Color.white); paint(image, this); getP1P2(); image.setPixel(Math.min(w-1, (int) ptox(p1)), Math.min(h-1, (int) ptoy(p1)), Color.red); image.setPixel(Math.min(w-1, (int) ptox(p2)), Math.min(h-1, (int) ptoy(p2)), Color.red); return image; } double ptox(double i) { int w = original.getWidth(); return i < w ? i : i < 2*w ? w : i < 3*w ? w-(i-2*w) : 0; } double ptoy(double i) { int w = original.getWidth(); return i < w ? 0 : i < 2*w ? i-w : i < 3*w ? w : w-(i-3*w); } void getP1P2() { if (p2 != -1) return; int w = original.getWidth(); if (w != original.getHeight()) fail("not a square"); RGB last = null; for (int i = 0; i < 4*w; i++) { double x = ptox(i); double y = ptoy(i); RGB col = def(x/w, y/w); if (last == null) last = col; else if (!col.equals(last)) if (p1 == -1) p1 = i; else p2 = i; } if (p1 != -1 && p2 == -1) p2 = 0; if (p1 == -1) p1 = 0; } } static abstract class Params { RGBImage original; abstract RGBImage render(); abstract Params copy(); void baseClone(Params p) { p.original = original; } RGBImage rendered; RGBImage getImage() { if (rendered == null) rendered = render(); return rendered; } double score = 2.0; double getScore() { if (score == 2.0) score = calcScore(); return score; } double calcScore() { return diff(original, getImage()); } boolean isBetterThan(Params p) { return getScore() < p.getScore(); } } static class RandomMagnet implements Reproducer { int n = -1; public Params reproduce(RGBImage original) { ++n; new Magnet p; p.original = original; p.sideshift = random(-sideshiftlimit, sideshiftlimit); p.angle = random()*360; if (n % 2 == 0) { p.col1 = randomColor(); p.col2 = randomColor(); } else { p.col1 = probeRandomPixel(original); p.col2 = probeRandomPixel(original); } return p; } } static class VaryBest implements Reproducer { Reproducer base; Params best; VaryBest(Reproducer base) { this.base = base; } public Params reproduce(RGBImage original) { Params p = base.reproduce(original); if (best == null || p.isBetterThan(best)) best = p; Params variation = vary(best); //System.out.println("Best: " + best.getScore() + ", variation: " + variation.getScore()); if (variation.isBetterThan(best)) { //System.out.println("Using variation, diff=" + (best.getScore()-variation.getScore())); best = variation; } return best; } Params vary(Params p) { if (p instanceof Magnet) { Magnet n = ((Magnet) p).copy(); int s = random(3); if (s == 0) { s = random(2); if (s == 0) n.sideshift = varySideshift(n.sideshift); else { //double old = n.angle; n.angle = (n.angle+360+random(-5, 5)) % 360; //System.out.println("angle: " + formatDouble(old, 1) + " -> " + formatDouble(n.angle, 1)); } } else if (s == 1) n.col1 = varyColor(n.col1); else n.col2 = varyColor(n.col2); return n; } return null; } double varySideshift(double sideshift) { return max(-sideshiftlimit, min(sideshiftlimit, sideshift+random(-0.1, 0.1))); } double varyN(double x) { return Math.max(0, Math.min(1, x+random(-0.1, 0.1))); } float varyChannel(float x) { return Math.max(0f, Math.min(1f, (float) (x+random(-0.1, 0.1)))); } RGB varyColor(RGB rgb) { int s = random(3); if (s == 0) return new RGB(varyChannel(rgb.r), rgb.g, rgb.b); else if (s == 1) return new RGB(rgb.r, varyChannel(rgb.g), rgb.b); else return new RGB(rgb.r, rgb.g, varyChannel(rgb.b)); } } static class Gridded extends Params { int w, h; Params[] array; Gridded(int w, int h) { this.w = w; this.h = h; array = new Params[w*h]; } Gridded copy() { Gridded p = new Gridded(w, h); baseClone(p); for (int i = 0; i < w*h; i++) p.array[i] = array[i].copy(); return p; } public String toString() { StringBuilder buf = new StringBuilder("grid{"); for (int i = 0; i < w*h; i++) { if (i != 0) buf.append(", "); buf.append(array[i]); } return buf + "}"; } public RGBImage render() { int ow = original.getWidth(), oh = original.getHeight(); RGBImage img = new RGBImage(ow, oh, Color.white); for (int y = 0; y < h; y++) for (int x = 0; x < w; x++) { int x1 = x*ow/w, y1 = y*oh/h; int x2 = (x+1)*ow/w, y2 = (y+1)*oh/h; RGBImage part = array[y*w+x].render(); main.copy(part, 0, 0, img, x1, y1, part.getWidth(), part.getHeight()); } return img; } } static class Grid implements Reproducer { int w, h; Reproducer[] bases; Grid(ReproducerMaker maker, int w, int h) { this.w = w; this.h = h; bases = new Reproducer[w*h]; for (int i = 0; i < w*h; i++) bases[i] = maker.make(); } RGBImage getGridElement(RGBImage original, int i) { int ow = original.getWidth(), oh = original.getHeight(); int y = i / w; int x = i % w; int x1 = x*ow/w, y1 = y*oh/h; int x2 = (x+1)*ow/w, y2 = (y+1)*oh/h; return original.clip(x1, y1, x2-x1, y2-y1); } public Params reproduce(RGBImage original) { Gridded gridded = new Gridded(w, h); gridded.original = original; for (int i = 0; i < w*h; i++) { RGBImage img = getGridElement(original, i); Params p = bases[i].reproduce(img); if (p == null) return null; gridded.array[i] = p; } return gridded; } } static ReproducerMaker baseMaker = new ReproducerMaker() { public Reproducer make() { return new VaryBest(new RandomMagnet()); } }; // main reproduce function static Reproducer reproducer; static String imageID = "#1000326"; // Bryan Cranston! static RGBImage original; static FrameWithImages fwi; psvm { if (args.length != 0) imageID = args[0]; fwi = new FrameWithImages(1); fwi.setZoom(2); fwi.hop(); thread { original = loadImage(imageID); original = resizeToWidth(original, imageWidth); fwi.setInnerSize(original); int gx = original.getWidth()/8, gy = original.getHeight()/8; reproducer = new Grid(baseMaker, gx, gy); reproduceOpenEnd(original); } /* new Magnet m; m.sideshift = -0.2; m.angle = 30; while (true) { m.angle = (m.angle+1) % 360; //System.out.println("Angle: " + m.angle); RGBImage img = new RGBImage(100, 100, Color.gray); paint(img, m); fwi.setImage(0, img); } */ } static void reproduceOpenEnd(RGBImage original) { Params best = null; long lastPrint = 0, lastN = 0; for (long ntry = 1; ; ntry++) { long now = System.currentTimeMillis(); if (now >= lastPrint+1000) { long tps = (ntry-lastN)*1000/(now-lastPrint); lastPrint = now; lastN = ntry; String s = "Try " + ntry + "(" + tps + "/s)"; if (best == null) System.out.println(s); else { System.out.println("Best: " + best); System.out.println(s + ", score: " + formatDouble(best.getScore()*100, 3) + "%, structure size: " + structureSize(best, Params.class)); } } Params p = reproducer.reproduce(original); if (best == null || p != null && p.getScore() < best.getScore()) { //System.out.println("New best! " + p.getScore()); best = p; fwi.setImage(0, p.getImage()); } if (p != null && p.getScore() == 0.0) break; } } static void paint(RGBImage img, Magnet m) { int w = img.getWidth(), h = img.getHeight(); for (int y = 0; y < h; y++) for (int x = 0; x < w; x++) { double nx = x/(double)(w-1); double ny = y/(double)(h-1); img.setPixel(x, y, m.def(nx, ny)); } } }