!636 !standard functions !quicknew !ctex !* constructor import java.awt.*; import java.awt.image.*; import java.util.List; import javax.imageio.*; !include #2000447 // image classes public class main { static RGBImage originalImage; static int imageWidth = 200; static ImageSurface currentSurface, bestSurface; // XXX - main reproduce function static Reproducer reproducer = /*new Layers( new RandomSolid()) .add(2, RandomBox.class) .varyBest;*/ new OptimizeIndividual(new RandomBox()); static abstract class Img { RGBImage img; *(RGBImage *img) {} int getWidth() { return img.getWidth(); } int getHeight() { return img.getHeight(); } //abstract void setPixel(int x, int y, int rgb); abstract void setPixel(int x, int y, RGB rgb);/* { setPixel(x, y, rgb.asInt()); }*/ } static class Painting extends Img { *(RGBImage img) { super(img); } void setPixel(int x, int y, RGB rgb) { img.setPixel(x, y, rgb); } } static class Diffing extends Img { int pixelsDiffed; double totalDiff; double[] diffed; *(RGBImage img) { super(img); diffed = new double[img.getWidth()*img.getHeight()]; for (int i = 0; i < diffed.length; i++) diffed[i] = -1; } void setPixel(int x, int y, RGB rgb) { int w = img.getWidth(), h = img.getHeight(); if (x < 0 || y < 0 || x >= w || y >= h) return; double old = diffed[x+y*w]; if (old >= 0) totalDiff -= old; else pixelsDiffed++; double diff = pixelDiff(img.getPixel(x, y), rgb); diffed[x+y*w] = diff; totalDiff += diff; } double getScore() { int totalPixels = img.getWidth()*img.getHeight(); int emptyPixels = totalPixels-pixelsDiffed; double innerQuality = totalDiff/Math.max(pixelsDiffed, 1); double outerQuality = 0.8; // makes objects shrink or grow double result = mix(innerQuality, outerQuality, emptyPixels/(double) totalPixels); System.out.println("inner=" + innerQuality + ", outer=" + outerQuality + ", pixels diffed=" + pixelsDiffed + ", result=" + result); return result; } } static abstract class Base { abstract Base copy(); abstract void vary(); Base copyVary() { Base copy = copy(); copy.vary(); return copy; } } static abstract class Overlay extends Base { abstract Overlay copy(); abstract void renderOn(Img image); } // Params must be immutable! 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; } double calcScore() { return diff(originalImage, getImage()); } boolean isBetterThan(Params p) { return getScore() < p.getScore(); } } static class Box extends Overlay { double cx, cy, w, h; RGB color; Box copy() { Box p = new Box(); p.cx = cx; p.cy = cy; p.w = w; p.h = h; p.color = color; return p; } public String toString() { return "Box " + cx + " " + cy + " " + w + " " + h + " " + color; } void renderOn(Img image) { box(image, this); } void vary() { int s = random(5); if (s == 0) cx = vary01(cx); else if (s == 1) cy = vary01(cy); else if (s == 2) w = vary01(w); else if (s == 3) h = vary01(h); else color = varyColor(color); } } static class Solid extends Overlay { RGB col; *() {} *(RGB *col) {} Solid copy() { new Solid p; p.col = col; return p; } public String toString() { return "Solid " + col; } void renderOn(Img 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 RandomBox implements OverlayReproducer { int n = -1; public Overlay reproduce(RGBImage original) { ++n; new Box p; p.cx = random(); p.cy = random(); p.w = random()*0.1; p.h = random()*0.1; 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; new Solid p; 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; *(Reproducer *base) {} public Params reproduce(RGBImage original) { Params p = base.reproduce(original); if (best == null || p.isBetterThan(best)) best = p; Params variation = (Params) best.copyVary(); //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] = (Overlay) 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); Painting img = new Painting(image); for (int i = 0; i < list.length; i++) list[i].renderOn(img); return image; } void vary() { int i = random(list.length); if (random(2) == 0 || myMaker == null) { //System.out.println("Varying layer " + i); list[i] = (Overlay) list[i].copyVary(); } 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())); } } } 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; } Layers add(int n, Class xClass) ctex { OverlayReproducer l[] = new OverlayReproducer[list.length+n]; System.arraycopy(list, 0, l, 0, list.length); for (int i = 0; i < n; i++) l[list.length+i] = xClass.newInstance(); list = l; return this; } 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; } } static Params reproduce(RGBImage original) { int w = original.getWidth(), h = original.getHeight(); return reproducer.reproduce(original); } static class OptimizeIndividual implements Reproducer { OverlayReproducer producer; double bestScore; Overlay best; *(OverlayReproducer *producer) {} public Params reproduce(RGBImage original) { Overlay overlay = producer.reproduce(original); System.out.println("overlay: " + overlay); double score = scoreOverlay(overlay, original); if (best == null || score <= bestScore) { best = overlay; bestScore = score; } Layered result = new Layered(null, new Solid(new RGB(Color.black)), best); result.originalImage = original; return result; } } static double scoreOverlay(Overlay overlay, RGBImage original) { Diffing d = new Diffing(original); overlay.renderOn(d); return d.getScore(); } static class HoldBest implements Reproducer { Reproducer base; Params best; *(Reproducer *base) {} public Params reproduce(RGBImage original) { Params p = base.reproduce(original); if (best == null || p.isBetterThan(best)) best = p; return best; } } psvm { String imageID = "#1000332"; // Picture of two coins for (int i = 0; i < args.length; i++) { String arg = args[i]; if (arg.equals("width")) imageWidth = Integer.parseInt(args[++i]); else if (isSnippetID(arg)) imageID = arg; } final String _imageID = imageID; JFrame frame = new JFrame("A JavaX Frame"); JPanel grid = new JPanel(new GridLayout(3, 1)); currentSurface = new ImageSurface(); bestSurface = new ImageSurface(); final ImageSurface original = new ImageSurface(); grid.add(currentSurface); grid.add(bestSurface); grid.add(original); frame.add(grid); frame.setBounds(100, 100, 100+imageWidth+20, 100+600); frame.setVisible(true); exitOnFrameClose(frame); new Thread() { public void run() { originalImage = loadImage(_imageID); originalImage = resizeToWidth(originalImage, imageWidth); original.setImage(originalImage); reproduceOpenEnd(originalImage); } }.start(); } static void reproduceOpenEnd(RGBImage original) { //Params best = null; long lastPrint = 0, lastN = 0; for (long ntry = 1; ; ntry++) { Params p = reproduce(original); currentSurface.setImage(p.getImage()); 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(p.getScore()*100, 2) + "%, structure size: " + structureSize(p, Params.class)); //} } /*if (best == null || p != null && p.getScore() < best.getScore()) { //System.out.println("New best! " + p.getScore()); best = p; bestSurface.setImage(p.getImage()); }*/ if (p != null && p.getScore() == 0.0) break; // perfect result, end } } static void box(Img img, Box p) { int w = img.getWidth(), h = img.getHeight(); double x1 = normalize(p.cx-p.w), x2 = normalize(p.cx+p.w); double y1 = normalize(p.cy-p.h), y2 = normalize(p.cy+p.h); int xx1 = round(x1*(w-1)), xx2 = round(x2*(w-1)); int yy1 = round(y1*(h-1)), yy2 = round(y2*(h-1)); for (int yy = yy1; yy <= yy2; yy++) for (int xx = xx1; xx <= xx2; xx++) img.setPixel(xx, yy, p.color); } !include #1000522 }