!7 static int imageWidth = 200; static RGBImage originalImage; static ImageSurface imageSurface; static JLabel isTitle; !include #1000522 // helper functions for image reproduction static abstract class Base { abstract Base copy(); abstract void vary(); } static abstract class Overlay extends Base { abstract Overlay copy(); abstract void renderOn(RGBImage image); } static abstract class Params extends Base { RGBImage originalImage; abstract Params copy(); abstract RGBImage render(); void baseClone(Params p) { p.originalImage = originalImage; } 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; } // when object has changed double getFreshScore() { resetScore(); return getScore(); } void resetScore() { score = 2.0; rendered = null; } double calcScore() { return diff(originalImage, getImage()); } boolean isBetterThan(Params p) { return getScore() < p.getScore(); } } static class Coin extends Overlay { double cx, cy, radius; RGB color; Coin copy() { Coin p = new Coin(); p.cx = cx; p.cy = cy; p.radius = radius; p.color = color; return p; } public String toString() { return "Coin " + cx + " " + cy + " " + radius + " " + color; } void renderOn(RGBImage image) { coin(image, this); } void vary() { int s = random(4); if (s == 0) cx = vary01(cx); else if (s == 1) cy = vary01(cy); else if (s == 2) radius = vary01(radius); else color = varyColor(color); } } static class Solid extends Overlay { RGB col; Solid copy() { Solid p = new Solid(); p.col = col; return p; } public String toString() { return "Solid " + col; } void renderOn(RGBImage image) { int w = image.getWidth(), h = image.getHeight(); for (int y = 0; y < h; y++) for (int x = 0; x < w; x++) image.setPixel(x, y, col); } void vary() { col = varyColor(col); } } interface Reproducer { public Params reproduce(RGBImage original); } interface OverlayReproducer { public Overlay reproduce(RGBImage original); } static class RandomCoin implements OverlayReproducer { int n = -1; public Overlay reproduce(RGBImage original) { ++n; Coin p = new Coin(); p.cx = random(); p.cy = random(); p.radius = random()*0.2; if (n % 2 == 0) p.color = randomColor(); else p.color = probeRandomPixel(original); return p; } } static class RandomSolid implements OverlayReproducer { int n = -1; public Overlay reproduce(RGBImage original) { ++n; Solid p = new Solid(); if (n % 2 == 0) { p.col = randomColor(); } else { p.col = probeRandomPixel(original); } return p; } } static class WhiteSolid implements OverlayReproducer { public Overlay reproduce(RGBImage original) { Solid p = new Solid(); p.col = new RGB(Color.white); 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 = best.copy(); variation.vary(); //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; } void reset() { best = null; } } static class Layered extends Params { Layers myMaker; Overlay[] list; Layered(Layers myMaker, Overlay[] list) { this.myMaker = myMaker; this.list = list; } Layered copy() { Layered p = new Layered(myMaker, new Overlay[list.length]); baseClone(p); for (int i = 0; i < list.length; i++) p.list[i] = list[i].copy(); return p; } public String toString() { StringBuilder buf = new StringBuilder("layered{"); for (int i = 0; i < list.length; i++) { if (i != 0) buf.append(", "); buf.append(list[i]); } return buf + "}"; } RGBImage render() { RGBImage image = new RGBImage(originalImage.getWidth(), originalImage.getHeight(), Color.white); for (int i = 0; i < list.length; i++) list[i].renderOn(image); return image; } void vary() { int i = random(list.length); if (random(2) == 0) { //System.out.println("Varying layer " + i); list[i].vary(); } else { //double score = getScore(); OverlayReproducer maker = myMaker.list[i]; list[i] = maker.reproduce(originalImage); //System.out.println("Exchanging layer " + i + " from " + maker + ", improvement: " + (score-getFreshScore())); } resetScore(); } } static class Layers implements Reproducer { OverlayReproducer[] list; long ntry = -1; int stepTime = 0/*500*/; VaryBest varyBest = new VaryBest(this); Layers(OverlayReproducer... list) { this.list = list; } public Params reproduce(RGBImage original) { ++ntry; int n = this.list.length; if (stepTime != 0) { // build up image gradually to optimize lower layers first n = (int) Math.min(n, ntry/stepTime+1); varyBest.reset(); } Overlay[] list = new Overlay[n]; for (int i = 0; i < n; i++) list[i] = this.list[i].reproduce(original); Layered result = new Layered(this, list); result.originalImage = original; return result; } } // main reproduce function static Reproducer reproducer = /*new VaryBest*/(new Layers( /*new RandomSolid()*/new WhiteSolid(), new RandomCoin(), new RandomCoin() )).varyBest; static Params reproduce(RGBImage original) { int w = original.getWidth(), h = original.getHeight(); return reproducer.reproduce(original); } p-substance-thread { String imageID = "#1000332"; // Picture of two coins if (args.length != 0) imageID = args[0]; final String _imageID = imageID; originalImage = loadImage(_imageID); originalImage = resizeToWidth(originalImage, imageWidth); swing { imageSurface = new ImageSurface; isTitle = jCenteredLabel("Reproduction"); showPackedFrame(vgrid( withCenteredTitle("Original Image", new ImageSurface(originalImage)), withTitle(isTitle, imageSurface))); } reproduceOpenEnd(originalImage, imageSurface); } static void reproduceOpenEnd(RGBImage original, ImageSurface imageSurface) { 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, 2) + "%, structure size: " + structureSize(best, Params.class)); } } Params p = reproduce(original); if (best == null || p != null && p.getScore() < best.getScore()) { //System.out.println("New best! " + p.getScore()); best = p; imageSurface.setImage(p.getImage()); setText(isTitle, print("Reproduction (" + formatDouble(100*(1-p.getScore()), 1) + "%)")); } if (p != null && p.getScore() == 0.0) break; } } static void coin(RGBImage img, Coin p) { int w = img.getWidth(), h = img.getHeight(); double ratio = ((double) w)/h; for (int yy = 0; yy < h; yy++) { double y = ((double) yy)/(h-1); double ybla = Math.abs(y-p.cy)/p.radius; if (ybla <= 1) { double xbla = Math.sqrt(1-ybla*ybla)/ratio; double l = p.cx-xbla*p.radius, r = p.cx+xbla*p.radius; int ll = (int) (l*w), rr = (int) (r*w); ll = Math.max(0, ll); rr = Math.min(w-1, rr); for (int xx = ll; xx <= rr; xx++) img.setPixel(xx, yy, p.color); } } }