Libraryless. Click here for Pure Java version (1387L/10K/31K).
1 | !636 |
2 | !629 // standard functions |
3 | !658 // image classes |
4 | |
5 | import java.awt.*; |
6 | import java.awt.image.*; |
7 | import java.util.List; |
8 | import javax.imageio.*; |
9 | import java.lang.reflect.*; |
10 | |
11 | public class main { |
12 | static RGBImage originalImage; |
13 | static int imageWidth = 200; |
14 | |
15 | static abstract class Base { |
16 | abstract Base copy(); |
17 | abstract void vary(); |
18 | } |
19 | |
20 | static abstract class Overlay extends Base { |
21 | abstract Overlay copy(); |
22 | abstract void renderOn(RGBImage image); |
23 | } |
24 | |
25 | static abstract class Params extends Base { |
26 | RGBImage originalImage; |
27 | abstract Params copy(); |
28 | abstract RGBImage render(); |
29 | |
30 | void baseClone(Params p) { |
31 | p.originalImage = originalImage; |
32 | } |
33 | |
34 | RGBImage rendered; |
35 | RGBImage getImage() { |
36 | if (rendered == null) |
37 | rendered = render(); |
38 | return rendered; |
39 | } |
40 | |
41 | double score = 2.0; |
42 | double getScore() { |
43 | if (score == 2.0) |
44 | score = calcScore(); |
45 | return score; |
46 | } |
47 | |
48 | // when object has changed |
49 | double getFreshScore() { |
50 | resetScore(); |
51 | return getScore(); |
52 | } |
53 | |
54 | void resetScore() { |
55 | score = 2.0; |
56 | rendered = null; |
57 | } |
58 | |
59 | double calcScore() { |
60 | return diff(originalImage, getImage()); |
61 | } |
62 | |
63 | boolean isBetterThan(Params p) { |
64 | return getScore() < p.getScore(); |
65 | } |
66 | } |
67 | |
68 | static class Coin extends Overlay { |
69 | double cx, cy, radius; |
70 | RGB color; |
71 | |
72 | Coin copy() { |
73 | Coin p = new Coin(); |
74 | p.cx = cx; |
75 | p.cy = cy; |
76 | p.radius = radius; |
77 | p.color = color; |
78 | return p; |
79 | } |
80 | |
81 | public String toString() { |
82 | return "Coin " + cx + " " + cy + " " + radius + " " + color; |
83 | } |
84 | |
85 | void renderOn(RGBImage image) { |
86 | coin(image, this); |
87 | } |
88 | |
89 | void vary() { |
90 | int s = random(4); |
91 | if (s == 0) |
92 | cx = vary01(cx); |
93 | else if (s == 1) |
94 | cy = vary01(cy); |
95 | else if (s == 2) |
96 | radius = vary01(radius); |
97 | else |
98 | color = varyColor(color); |
99 | } |
100 | } |
101 | |
102 | static class Solid extends Overlay { |
103 | RGB col; |
104 | |
105 | Solid copy() { |
106 | Solid p = new Solid(); |
107 | p.col = col; |
108 | return p; |
109 | } |
110 | |
111 | public String toString() { |
112 | return "Solid " + col; |
113 | } |
114 | |
115 | void renderOn(RGBImage image) { |
116 | int w = image.getWidth(), h = image.getHeight(); |
117 | for (int y = 0; y < h; y++) |
118 | for (int x = 0; x < w; x++) |
119 | image.setPixel(x, y, col); |
120 | } |
121 | |
122 | void vary() { |
123 | col = varyColor(col); |
124 | } |
125 | } |
126 | |
127 | interface Reproducer { |
128 | public Params reproduce(RGBImage original); |
129 | } |
130 | |
131 | interface OverlayReproducer { |
132 | public Overlay reproduce(RGBImage original); |
133 | } |
134 | |
135 | static class RandomCoin implements OverlayReproducer { |
136 | int n = -1; |
137 | public Overlay reproduce(RGBImage original) { |
138 | ++n; |
139 | Coin p = new Coin(); |
140 | p.cx = random(); |
141 | p.cy = random(); |
142 | p.radius = random()*0.2; |
143 | if (n % 2 == 0) |
144 | p.color = randomColor(); |
145 | else |
146 | p.color = probeRandomPixel(original); |
147 | return p; |
148 | } |
149 | } |
150 | |
151 | static class RandomSolid implements OverlayReproducer { |
152 | int n = -1; |
153 | public Overlay reproduce(RGBImage original) { |
154 | ++n; |
155 | Solid p = new Solid(); |
156 | if (n % 2 == 0) { |
157 | p.col = randomColor(); |
158 | } else { |
159 | p.col = probeRandomPixel(original); |
160 | } |
161 | return p; |
162 | } |
163 | } |
164 | |
165 | static class WhiteSolid implements OverlayReproducer { |
166 | public Overlay reproduce(RGBImage original) { |
167 | Solid p = new Solid(); |
168 | p.col = new RGB(Color.white); |
169 | return p; |
170 | } |
171 | } |
172 | |
173 | static class VaryBest implements Reproducer { |
174 | Reproducer base; |
175 | Params best; |
176 | |
177 | VaryBest(Reproducer base) { |
178 | this.base = base; |
179 | } |
180 | |
181 | public Params reproduce(RGBImage original) { |
182 | Params p = base.reproduce(original); |
183 | if (best == null || p.isBetterThan(best)) |
184 | best = p; |
185 | Params variation = best.copy(); |
186 | variation.vary(); |
187 | //System.out.println("Best: " + best.getScore() + ", variation: " + variation.getScore()); |
188 | if (variation.isBetterThan(best)) { |
189 | //System.out.println("Using variation, diff=" + (best.getScore()-variation.getScore())); |
190 | best = variation; |
191 | } |
192 | return best; |
193 | } |
194 | |
195 | void reset() { |
196 | best = null; |
197 | } |
198 | } |
199 | |
200 | static class Layered extends Params { |
201 | Layers myMaker; |
202 | Overlay[] list; |
203 | |
204 | Layered(Layers myMaker, Overlay[] list) { |
205 | this.myMaker = myMaker; |
206 | this.list = list; |
207 | } |
208 | |
209 | Layered copy() { |
210 | Layered p = new Layered(myMaker, new Overlay[list.length]); |
211 | baseClone(p); |
212 | for (int i = 0; i < list.length; i++) |
213 | p.list[i] = list[i].copy(); |
214 | return p; |
215 | } |
216 | |
217 | public String toString() { |
218 | StringBuilder buf = new StringBuilder("layered{"); |
219 | for (int i = 0; i < list.length; i++) { |
220 | if (i != 0) |
221 | buf.append(", "); |
222 | buf.append(list[i]); |
223 | } |
224 | return buf + "}"; |
225 | } |
226 | |
227 | RGBImage render() { |
228 | RGBImage image = new RGBImage(originalImage.getWidth(), originalImage.getHeight(), Color.white); |
229 | for (int i = 0; i < list.length; i++) |
230 | list[i].renderOn(image); |
231 | return image; |
232 | } |
233 | |
234 | void vary() { |
235 | int i = random(list.length); |
236 | if (random(2) == 0) { |
237 | //System.out.println("Varying layer " + i); |
238 | list[i].vary(); |
239 | } else { |
240 | //double score = getScore(); |
241 | OverlayReproducer maker = myMaker.list[i]; |
242 | list[i] = maker.reproduce(originalImage); |
243 | //System.out.println("Exchanging layer " + i + " from " + maker + ", improvement: " + (score-getFreshScore())); |
244 | } |
245 | resetScore(); |
246 | } |
247 | } |
248 | |
249 | static class Layers implements Reproducer { |
250 | OverlayReproducer[] list; |
251 | long ntry = -1; |
252 | int stepTime = 0/*500*/; |
253 | VaryBest varyBest = new VaryBest(this); |
254 | |
255 | Layers(OverlayReproducer... list) { |
256 | this.list = list; |
257 | } |
258 | |
259 | public Params reproduce(RGBImage original) { |
260 | ++ntry; |
261 | int n = this.list.length; |
262 | if (stepTime != 0) { |
263 | // build up image gradually to optimize lower layers first |
264 | n = (int) Math.min(n, ntry/stepTime+1); |
265 | varyBest.reset(); |
266 | } |
267 | Overlay[] list = new Overlay[n]; |
268 | for (int i = 0; i < n; i++) |
269 | list[i] = this.list[i].reproduce(original); |
270 | Layered result = new Layered(this, list); |
271 | result.originalImage = original; |
272 | return result; |
273 | } |
274 | } |
275 | |
276 | // main reproduce function |
277 | static Reproducer reproducer = |
278 | /*new VaryBest*/(new Layers( |
279 | /*new RandomSolid()*/new WhiteSolid(), |
280 | new RandomCoin(), |
281 | new RandomCoin(), |
282 | new RandomCoin(), |
283 | new RandomCoin(), |
284 | new RandomCoin(), |
285 | new RandomCoin() |
286 | )).varyBest; |
287 | |
288 | static Params reproduce(RGBImage original) { |
289 | int w = original.getWidth(), h = original.getHeight(); |
290 | return reproducer.reproduce(original); |
291 | } |
292 | |
293 | public static void main(String[] args) { |
294 | String imageID = "#1000329"; // Picture of some coins |
295 | if (args.length != 0) imageID = args[0]; |
296 | final String _imageID = imageID; |
297 | |
298 | JFrame frame = new JFrame("A JavaX Frame"); |
299 | |
300 | final ImageSurface imageSurface = new ImageSurface(); |
301 | Component panel = imageSurface; |
302 | |
303 | frame.add(panel); |
304 | frame.setBounds(100, 100, 300, 300); |
305 | frame.setVisible(true); |
306 | exitOnFrameClose(frame); |
307 | |
308 | new Thread() { |
309 | public void run() { |
310 | originalImage = loadImage(_imageID); |
311 | originalImage = resizeToWidth(originalImage, imageWidth); |
312 | |
313 | reproduceOpenEnd(originalImage, imageSurface); |
314 | } |
315 | }.start(); |
316 | } |
317 | |
318 | static void reproduceOpenEnd(RGBImage original, ImageSurface imageSurface) { |
319 | Params best = null; |
320 | long lastPrint = 0, lastN = 0; |
321 | for (long ntry = 1; ; ntry++) { |
322 | long now = System.currentTimeMillis(); |
323 | if (now >= lastPrint+1000) { |
324 | long tps = (ntry-lastN)*1000/(now-lastPrint); |
325 | lastPrint = now; |
326 | lastN = ntry; |
327 | String s = "Try " + ntry + " (" + tps + "/s)"; |
328 | if (best == null) |
329 | System.out.println(s); |
330 | else { |
331 | System.out.println("Best: " + best); |
332 | System.out.println(s + ", score: " + formatDouble(best.getScore()*100, 2) + "%, structure size: " + structureSize(best, Params.class)); |
333 | } |
334 | } |
335 | Params p = reproduce(original); |
336 | if (best == null || p != null && p.getScore() < best.getScore()) { |
337 | //System.out.println("New best! " + p.getScore()); |
338 | best = p; |
339 | imageSurface.setImage(p.getImage()); |
340 | } |
341 | |
342 | if (p != null && p.getScore() == 0.0) |
343 | break; |
344 | } |
345 | } |
346 | |
347 | static RGB probeRandomPixel(RGBImage image) { |
348 | int x = (int) (random()*(image.getWidth()-1)); |
349 | int y = (int) (random()*(image.getHeight()-1)); |
350 | return image.getPixel(x, y); |
351 | } |
352 | |
353 | static RGB randomColor() { |
354 | return new RGB(random(), random(), random()); |
355 | } |
356 | |
357 | static RGBImage resizeToWidth(RGBImage image, int w) { |
358 | return resize(image, w, (int) ((image.getHeight()*(double) w)/image.getWidth())); |
359 | } |
360 | |
361 | public static RGBImage resize(RGBImage image, int w, int h) { |
362 | if (w == image.getWidth() && h == image.getHeight()) return image; |
363 | |
364 | int[] pixels = new int[w*h]; |
365 | for (int y = 0; y < h; y++) |
366 | for (int x = 0; x < w; x++) |
367 | pixels[y*w+x] = image.getInt(x*image.getWidth()/w, y*image.getHeight()/h); |
368 | return new RGBImage(w, h, pixels); |
369 | } |
370 | |
371 | static boolean useImageCache = true; |
372 | static RGBImage loadImage(String snippetID) { |
373 | try { |
374 | File dir = new File(System.getProperty("user.home"), ".tinybrain/image-cache"); |
375 | if (useImageCache) { |
376 | dir.mkdirs(); |
377 | File file = new File(dir, snippetID + ".png"); |
378 | if (file.exists() && file.length() != 0) |
379 | try { |
380 | return new RGBImage(ImageIO.read(file)); |
381 | } catch (Throwable e) { |
382 | e.printStackTrace(); |
383 | // fall back to loading from sourceforge |
384 | } |
385 | } |
386 | |
387 | String imageURL = getImageURL(parseSnippetID(snippetID)); |
388 | System.err.println("Loading image: " + imageURL); |
389 | BufferedImage image = ImageIO.read(new URL(imageURL)); |
390 | |
391 | if (useImageCache) { |
392 | File tempFile = new File(dir, snippetID + ".tmp." + System.currentTimeMillis()); |
393 | ImageIO.write(image, "png", tempFile); |
394 | tempFile.renameTo(new File(dir, snippetID + ".png")); |
395 | //Log.info("Cached image."); |
396 | } |
397 | |
398 | //Log.info("Loaded image."); |
399 | return new RGBImage(image); |
400 | } catch (IOException e) { |
401 | throw new RuntimeException(e); |
402 | } |
403 | } |
404 | |
405 | static String getImageURL(long snippetID) throws IOException { |
406 | String url; |
407 | if (snippetID == 1000010 || snippetID == 1000012) |
408 | url = "http://tinybrain.de:8080/tb/show-blobimage.php?id=" + snippetID; |
409 | else |
410 | url = "http://eyeocr.sourceforge.net/filestore/filestore.php?cmd=serve&file=blob_" + snippetID |
411 | + "&contentType=image/png"; |
412 | return url; |
413 | } |
414 | |
415 | public static long parseSnippetID(String snippetID) { |
416 | return Long.parseLong(shortenSnippetID(snippetID)); |
417 | } |
418 | |
419 | private static String shortenSnippetID(String snippetID) { |
420 | if (snippetID.startsWith("#")) |
421 | snippetID = snippetID.substring(1); |
422 | String httpBlaBla = "http://tinybrain.de/"; |
423 | if (snippetID.startsWith(httpBlaBla)) |
424 | snippetID = snippetID.substring(httpBlaBla.length()); |
425 | return snippetID; |
426 | } |
427 | |
428 | static Random _random = new Random(); |
429 | static double random() { |
430 | return _random.nextInt(100001)/100000.0; |
431 | } |
432 | |
433 | static int random(int max) { |
434 | return _random.nextInt(max); |
435 | } |
436 | |
437 | static double random(double min, double max) { |
438 | return min+random()*(max-min); |
439 | } |
440 | |
441 | static void coin(RGBImage img, Coin p) { |
442 | int w = img.getWidth(), h = img.getHeight(); |
443 | double ratio = ((double) w)/h; |
444 | for (int yy = 0; yy < h; yy++) { |
445 | double y = ((double) yy)/(h-1); |
446 | double ybla = Math.abs(y-p.cy)/p.radius; |
447 | if (ybla <= 1) { |
448 | double xbla = Math.sqrt(1-ybla*ybla)/ratio; |
449 | double l = p.cx-xbla*p.radius, r = p.cx+xbla*p.radius; |
450 | int ll = (int) (l*w), rr = (int) (r*w); |
451 | ll = Math.max(0, ll); |
452 | rr = Math.min(w-1, rr); |
453 | for (int xx = ll; xx <= rr; xx++) |
454 | img.setPixel(xx, yy, p.color); |
455 | } |
456 | } |
457 | } |
458 | |
459 | static double mix(double a, double b, double bishness) { |
460 | return a+(b-a)*bishness; |
461 | } |
462 | |
463 | public static double pixelDiff(RGB a, RGB b) { |
464 | return (Math.abs(a.r-b.r) + Math.abs(a.g-b.g) + Math.abs(a.b-b.b))/3; |
465 | } |
466 | |
467 | public static double diff(RGBImage image, RGBImage image2) { |
468 | int w = image.getWidth(), h = image.getHeight(); |
469 | double sum = 0; |
470 | for (int y = 0; y < h; y++) |
471 | for (int x = 0; x < w; x++) |
472 | sum += pixelDiff(image.getRGB(x, y), image2.getRGB(x, y)); |
473 | return sum/(w*h); |
474 | } |
475 | |
476 | public static void copy(RGBImage src, int srcX, int srcY, RGBImage dst, int dstX, int dstY, int w, int h) { |
477 | for (int y = 0; y < h; y++) |
478 | for (int x = 0; x < w; x++) |
479 | dst.setPixel(dstX+x, dstY+y, src.getPixel(srcX+x, srcY+y)); |
480 | } |
481 | |
482 | public static int structureSize(Object o, Class baseClass) { |
483 | return structureSize(o, baseClass, new IdentityHashMap()); |
484 | } |
485 | |
486 | static int structureSize(Object o, Class baseClass, |
487 | IdentityHashMap seen) { |
488 | if (o == null || seen.containsKey(o)) return 0; |
489 | seen.put(o, Boolean.TRUE); |
490 | int size = 1; |
491 | Class c = o.getClass(); |
492 | if (c.isArray()) { |
493 | int n = Array.getLength(o); |
494 | for (int i = 0; i < n; i++) |
495 | size += structureSize(Array.get(o, i), baseClass, seen); |
496 | } else |
497 | while (c != Object.class && c != baseClass) { |
498 | Field[] fields = c.getDeclaredFields(); |
499 | for (Field field : fields) { |
500 | if ((field.getModifiers() & Modifier.STATIC) != 0) |
501 | continue; |
502 | if (field.getType().isPrimitive()) |
503 | ++size; |
504 | else { |
505 | Object value = null; |
506 | try { |
507 | value = field.get(o); |
508 | } catch (IllegalAccessException e) { |
509 | throw new RuntimeException(e); |
510 | } |
511 | size += structureSize(value, baseClass, seen); |
512 | } |
513 | } |
514 | c = c.getSuperclass(); |
515 | } |
516 | return size; |
517 | } |
518 | |
519 | public static String formatDouble(double d, int digits) { |
520 | String format = "0."; |
521 | for (int i = 0; i < digits; i++) format += "#"; |
522 | return new java.text.DecimalFormat(format, new java.text.DecimalFormatSymbols(Locale.ENGLISH)).format(d); |
523 | } |
524 | |
525 | static float varyChannel(float x) { |
526 | return Math.max(0f, Math.min(1f, (float) (x+random(-0.1, 0.1)))); |
527 | } |
528 | |
529 | static double vary01(double x) { |
530 | return Math.max(0, Math.min(1, x+random(-0.1, 0.1))); |
531 | } |
532 | |
533 | static RGB varyColor(RGB rgb) { |
534 | int s = random(3); |
535 | if (s == 0) |
536 | return new RGB(varyChannel(rgb.r), rgb.g, rgb.b); |
537 | else if (s == 1) |
538 | return new RGB(rgb.r, varyChannel(rgb.g), rgb.b); |
539 | else |
540 | return new RGB(rgb.r, rgb.g, varyChannel(rgb.b)); |
541 | } |
542 | } |
Began life as a copy of #668
download show line numbers debug dex old transpilations
Travelled to 16 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, gwrvuhgaqvyk, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, onxytkatvevr, pyentgdyhuwx, pzhvpgtvlbxg, qbtsjoyahagl, teubizvjbppd, tslmcundralx, tvejysmllsmz, vouqrxazstgt
No comments. add comment
Snippet ID: | #669 |
Snippet name: | Reproducing Coins |
Eternal ID of this version: | #669/1 |
Text MD5: | 6318af112f75828bdcd43e2deb334681 |
Transpilation MD5: | d925d6b17425472b289b2033e3cdb517 |
Author: | stefan |
Category: | |
Type: | JavaX source code |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2015-07-15 14:32:59 |
Source code size: | 15380 bytes / 542 lines |
Pitched / IR pitched: | No / Yes |
Views / Downloads: | 814 / 759 |
Referenced in: | [show references] |