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 maxStructureSize = 1000; |
14 | |
15 | static abstract class Params { |
16 | RGBImage originalImage; |
17 | abstract RGBImage render(); |
18 | abstract Params copy(); |
19 | |
20 | void baseClone(Params p) { |
21 | p.originalImage = originalImage; |
22 | } |
23 | |
24 | RGBImage rendered; |
25 | RGBImage getImage() { |
26 | if (rendered == null) |
27 | rendered = render(); |
28 | return rendered; |
29 | } |
30 | |
31 | double score = 2.0; |
32 | double getScore() { |
33 | if (score == 2.0) |
34 | score = calcScore(); |
35 | return score; |
36 | } |
37 | |
38 | double calcScore() { |
39 | if (/*originalImage == main.originalImage |
40 | &&*/ structureSize(this, Params.class) > maxStructureSize) |
41 | return 1.5; |
42 | return diff(originalImage, getImage()); |
43 | } |
44 | |
45 | boolean isBetterThan(Params p) { |
46 | return getScore() < p.getScore(); |
47 | } |
48 | } |
49 | |
50 | static class VSplit extends Params { |
51 | double splitPoint; |
52 | RGB col1, col2; |
53 | |
54 | VSplit copy() { |
55 | VSplit p = new VSplit(); |
56 | baseClone(p); |
57 | p.splitPoint = splitPoint; |
58 | p.col1 = col1; |
59 | p.col2 = col2; |
60 | return p; |
61 | } |
62 | |
63 | public String toString() { |
64 | return "VSplit " + splitPoint + " " + col1 + " " + col2; |
65 | } |
66 | |
67 | RGBImage render() { |
68 | int w = originalImage.getWidth(), h = originalImage.getHeight(); |
69 | RGBImage image = new RGBImage(w, h, Color.white); |
70 | vsplit(image, this); |
71 | return image; |
72 | } |
73 | } |
74 | |
75 | static class Solid extends Params { |
76 | RGB col; |
77 | |
78 | Solid copy() { |
79 | Solid p = new Solid(); |
80 | baseClone(p); |
81 | p.col = col; |
82 | return p; |
83 | } |
84 | |
85 | public String toString() { |
86 | return "Solid " + col; |
87 | } |
88 | |
89 | RGBImage render() { |
90 | int w = originalImage.getWidth(), h = originalImage.getHeight(); |
91 | return new RGBImage(w, h, col); |
92 | } |
93 | } |
94 | |
95 | interface Reproducer { |
96 | public Params reproduce(RGBImage original); |
97 | } |
98 | |
99 | static class RandomVSplit implements Reproducer { |
100 | int n = -1; |
101 | public Params reproduce(RGBImage original) { |
102 | ++n; |
103 | VSplit p = new VSplit(); |
104 | p.originalImage = original; |
105 | p.splitPoint = random(); |
106 | if (n % 2 == 0) { |
107 | p.col1 = randomColor(); |
108 | p.col2 = randomColor(); |
109 | } else { |
110 | p.col1 = probeRandomPixel(original); |
111 | p.col2 = probeRandomPixel(original); |
112 | } |
113 | return p; |
114 | } |
115 | } |
116 | |
117 | static class RandomSolid implements Reproducer { |
118 | int n = -1; |
119 | public Params reproduce(RGBImage original) { |
120 | ++n; |
121 | Solid p = new Solid(); |
122 | p.originalImage = original; |
123 | if (n % 2 == 0) { |
124 | p.col = randomColor(); |
125 | } else { |
126 | p.col = probeRandomPixel(original); |
127 | } |
128 | return p; |
129 | } |
130 | } |
131 | |
132 | static class VaryBest implements Reproducer { |
133 | Reproducer base; |
134 | Params best; |
135 | |
136 | VaryBest(Reproducer base) { |
137 | this.base = base; |
138 | } |
139 | |
140 | public Params reproduce(RGBImage original) { |
141 | Params p = base.reproduce(original); |
142 | if (best == null || p.isBetterThan(best)) |
143 | best = p; |
144 | Params variation = vary(best); |
145 | //System.out.println("Best: " + best.getScore() + ", variation: " + variation.getScore()); |
146 | if (variation.isBetterThan(best)) { |
147 | //System.out.println("Using variation, diff=" + (best.getScore()-variation.getScore())); |
148 | best = variation; |
149 | } |
150 | return best; |
151 | } |
152 | |
153 | Params vary(Params p) { |
154 | if (p instanceof VSplit) { |
155 | VSplit n = ((VSplit) p).copy(); |
156 | int s = random(3); |
157 | if (s == 0) |
158 | varySplitPoint(n); |
159 | else if (s == 1) |
160 | n.col1 = varyColor(n.col1); |
161 | else |
162 | n.col2 = varyColor(n.col2); |
163 | return n; |
164 | } else if (p instanceof Solid) { |
165 | Solid n = ((Solid) p).copy(); |
166 | n.col = varyColor(n.col); |
167 | return n; |
168 | } else if (p instanceof Gridded) { |
169 | //print "Varying Gridded" |
170 | Gridded n = ((Gridded) p).copy(); |
171 | int i = random(n.array.length); |
172 | n.array[i] = vary(n.array[i]); |
173 | return n; |
174 | } |
175 | return null; |
176 | } |
177 | |
178 | void varySplitPoint(VSplit p) { |
179 | p.splitPoint = Math.max(0, Math.min(1, p.splitPoint+random(-0.1, 0.1))); |
180 | } |
181 | |
182 | float varyChannel(float x) { |
183 | return Math.max(0f, Math.min(1f, (float) (x+random(-0.1, 0.1)))); |
184 | } |
185 | |
186 | RGB varyColor(RGB rgb) { |
187 | int s = random(3); |
188 | if (s == 0) |
189 | return new RGB(varyChannel(rgb.r), rgb.g, rgb.b); |
190 | else if (s == 1) |
191 | return new RGB(rgb.r, varyChannel(rgb.g), rgb.b); |
192 | else |
193 | return new RGB(rgb.r, rgb.g, varyChannel(rgb.b)); |
194 | } |
195 | } |
196 | |
197 | static class Alternate implements Reproducer { |
198 | int n = -1; |
199 | Reproducer[] list; |
200 | |
201 | Alternate(Reproducer... list) { |
202 | this.list = list; |
203 | } |
204 | |
205 | public Params reproduce(RGBImage original) { |
206 | ++n; |
207 | return list[n % list.length].reproduce(original); |
208 | } |
209 | } |
210 | |
211 | interface ReproducerMaker { |
212 | public Reproducer make(); |
213 | } |
214 | |
215 | static class Gridded extends Params { |
216 | int w, h; |
217 | Params[] array; |
218 | |
219 | Gridded(int w, int h) { |
220 | this.w = w; |
221 | this.h = h; |
222 | array = new Params[w*h]; |
223 | } |
224 | |
225 | Gridded copy() { |
226 | Gridded p = new Gridded(w, h); |
227 | baseClone(p); |
228 | for (int i = 0; i < w*h; i++) |
229 | p.array[i] = array[i].copy(); |
230 | return p; |
231 | } |
232 | |
233 | public String toString() { |
234 | StringBuilder buf = new StringBuilder("grid{"); |
235 | for (int i = 0; i < w*h; i++) { |
236 | if (i != 0) |
237 | buf.append(", "); |
238 | buf.append(array[i]); |
239 | } |
240 | return buf + "}"; |
241 | } |
242 | |
243 | public RGBImage render() { |
244 | int ow = originalImage.getWidth(), oh = originalImage.getHeight(); |
245 | RGBImage img = new RGBImage(ow, oh, Color.white); |
246 | for (int y = 0; y < h; y++) |
247 | for (int x = 0; x < w; x++) { |
248 | int x1 = x*ow/w, y1 = y*oh/h; |
249 | int x2 = (x+1)*ow/w, y2 = (y+1)*oh/h; |
250 | RGBImage part = array[y*w+x].render(); |
251 | main.copy(part, 0, 0, img, x1, y1, part.getWidth(), part.getHeight()); |
252 | } |
253 | return img; |
254 | } |
255 | } |
256 | |
257 | static class Grid implements Reproducer { |
258 | int w, h; |
259 | Reproducer[] bases; |
260 | |
261 | Grid(ReproducerMaker maker, int w, int h) { |
262 | this.w = w; |
263 | this.h = h; |
264 | bases = new Reproducer[w*h]; |
265 | for (int i = 0; i < w*h; i++) |
266 | bases[i] = maker.make(); |
267 | } |
268 | |
269 | RGBImage getGridElement(RGBImage originalImage, int i) { |
270 | int ow = originalImage.getWidth(), oh = originalImage.getHeight(); |
271 | int y = i / w; |
272 | int x = i % w; |
273 | int x1 = x*ow/w, y1 = y*oh/h; |
274 | int x2 = (x+1)*ow/w, y2 = (y+1)*oh/h; |
275 | return originalImage.clip(x1, y1, x2-x1, y2-y1); |
276 | } |
277 | |
278 | public Params reproduce(RGBImage original) { |
279 | Gridded gridded = new Gridded(w, h); |
280 | gridded.originalImage = original; |
281 | for (int i = 0; i < w*h; i++) { |
282 | RGBImage img = getGridElement(original, i); |
283 | Params p = bases[i].reproduce(img); |
284 | if (p == null) |
285 | return null; |
286 | gridded.array[i] = p; |
287 | } |
288 | return gridded; |
289 | } |
290 | } |
291 | |
292 | static ReproducerMaker baseMaker = new ReproducerMaker() { |
293 | public Reproducer make() { |
294 | return new VaryBest( |
295 | new Alternate( |
296 | new RandomSolid(), new RandomVSplit() |
297 | )); |
298 | } |
299 | }; |
300 | |
301 | // main reproduce function |
302 | static Reproducer reproducer = |
303 | new VaryBest(new Alternate( |
304 | baseMaker.make(), |
305 | new Grid(baseMaker, 4, 4), |
306 | new Grid(baseMaker, 16, 8) |
307 | )); |
308 | |
309 | static Params reproduce(RGBImage original, int ntry) { |
310 | int w = original.getWidth(), h = original.getHeight(); |
311 | return reproducer.reproduce(original); |
312 | } |
313 | |
314 | public static void main(String[] args) { |
315 | String imageID = "#1000326"; // Bryan Cranston! |
316 | if (args.length != 0) imageID = args[0]; |
317 | final String _imageID = imageID; |
318 | |
319 | JFrame frame = new JFrame("A JavaX Frame"); |
320 | |
321 | final ImageSurface imageSurface = new ImageSurface(); |
322 | Component panel = imageSurface; |
323 | |
324 | frame.add(panel); |
325 | frame.setBounds(100, 100, 300, 200); |
326 | frame.setVisible(true); |
327 | exitOnFrameClose(frame); |
328 | |
329 | new Thread() { |
330 | public void run() { |
331 | originalImage = loadImage(_imageID); |
332 | originalImage = resizeToWidth(originalImage, 100); |
333 | |
334 | reproduceOpenEnd(originalImage, imageSurface); |
335 | } |
336 | }.start(); |
337 | } |
338 | |
339 | static void reproduceOpenEnd(RGBImage original, ImageSurface imageSurface) { |
340 | Params best = null; |
341 | long lastPrint = 0; |
342 | for (int ntry = 1; ; ntry++) { |
343 | if (System.currentTimeMillis() >= lastPrint+1000) { |
344 | lastPrint = System.currentTimeMillis(); |
345 | if (best == null) |
346 | System.out.println("Try " + ntry); |
347 | else { |
348 | System.out.println("Best: " + best); |
349 | System.out.println("Try " + ntry + ", score: " + formatDouble(best.getScore()*100, 2) + "%, structure size: " + structureSize(best, Params.class)); |
350 | } |
351 | } |
352 | Params p = reproduce(original, ntry); |
353 | if (best == null || p != null && p.getScore() < best.getScore()) { |
354 | //System.out.println("New best! " + p.getScore()); |
355 | best = p; |
356 | imageSurface.setImage(p.getImage()); |
357 | } |
358 | |
359 | if (p != null && p.getScore() == 0.0) |
360 | break; |
361 | } |
362 | } |
363 | |
364 | static RGB probeRandomPixel(RGBImage image) { |
365 | int x = (int) (random()*(image.getWidth()-1)); |
366 | int y = (int) (random()*(image.getHeight()-1)); |
367 | return image.getPixel(x, y); |
368 | } |
369 | |
370 | static RGB randomColor() { |
371 | return new RGB(random(), random(), random()); |
372 | } |
373 | |
374 | static RGBImage resizeToWidth(RGBImage image, int w) { |
375 | return resize(image, w, (int) ((image.getHeight()*(double) w)/image.getWidth())); |
376 | } |
377 | |
378 | public static RGBImage resize(RGBImage image, int w, int h) { |
379 | if (w == image.getWidth() && h == image.getHeight()) return image; |
380 | |
381 | int[] pixels = new int[w*h]; |
382 | for (int y = 0; y < h; y++) |
383 | for (int x = 0; x < w; x++) |
384 | pixels[y*w+x] = image.getInt(x*image.getWidth()/w, y*image.getHeight()/h); |
385 | return new RGBImage(w, h, pixels); |
386 | } |
387 | |
388 | static boolean useImageCache = true; |
389 | static RGBImage loadImage(String snippetID) { |
390 | try { |
391 | File dir = new File(System.getProperty("user.home"), ".tinybrain/image-cache"); |
392 | if (useImageCache) { |
393 | dir.mkdirs(); |
394 | File file = new File(dir, snippetID + ".png"); |
395 | if (file.exists() && file.length() != 0) |
396 | try { |
397 | return new RGBImage(ImageIO.read(file)); |
398 | } catch (Throwable e) { |
399 | e.printStackTrace(); |
400 | // fall back to loading from sourceforge |
401 | } |
402 | } |
403 | |
404 | String imageURL = getImageURL(parseSnippetID(snippetID)); |
405 | System.err.println("Loading image: " + imageURL); |
406 | BufferedImage image = ImageIO.read(new URL(imageURL)); |
407 | |
408 | if (useImageCache) { |
409 | File tempFile = new File(dir, snippetID + ".tmp." + System.currentTimeMillis()); |
410 | ImageIO.write(image, "png", tempFile); |
411 | tempFile.renameTo(new File(dir, snippetID + ".png")); |
412 | //Log.info("Cached image."); |
413 | } |
414 | |
415 | //Log.info("Loaded image."); |
416 | return new RGBImage(image); |
417 | } catch (IOException e) { |
418 | throw new RuntimeException(e); |
419 | } |
420 | } |
421 | |
422 | static String getImageURL(long snippetID) throws IOException { |
423 | String url; |
424 | if (snippetID == 1000010 || snippetID == 1000012) |
425 | url = "http://tinybrain.de:8080/tb/show-blobimage.php?id=" + snippetID; |
426 | else |
427 | url = "http://eyeocr.sourceforge.net/filestore/filestore.php?cmd=serve&file=blob_" + snippetID |
428 | + "&contentType=image/png"; |
429 | return url; |
430 | } |
431 | |
432 | public static long parseSnippetID(String snippetID) { |
433 | return Long.parseLong(shortenSnippetID(snippetID)); |
434 | } |
435 | |
436 | private static String shortenSnippetID(String snippetID) { |
437 | if (snippetID.startsWith("#")) |
438 | snippetID = snippetID.substring(1); |
439 | String httpBlaBla = "http://tinybrain.de/"; |
440 | if (snippetID.startsWith(httpBlaBla)) |
441 | snippetID = snippetID.substring(httpBlaBla.length()); |
442 | return snippetID; |
443 | } |
444 | |
445 | static Random _random = new Random(); |
446 | static double random() { |
447 | return _random.nextInt(100001)/100000.0; |
448 | } |
449 | |
450 | static int random(int max) { |
451 | return _random.nextInt(max); |
452 | } |
453 | |
454 | static double random(double min, double max) { |
455 | return min+random()*(max-min); |
456 | } |
457 | |
458 | static void vsplit(RGBImage img, VSplit p) { |
459 | int w = img.getWidth(), h = img.getHeight(); |
460 | for (int yy = 0; yy < h; yy++) |
461 | for (int xx = 0; xx < w; xx++) { |
462 | //double x = ((double) xx)/(w-1); |
463 | double y = ((double) yy)/(h-1); |
464 | RGB col = y <= p.splitPoint ? p.col1 : p.col2; |
465 | img.setPixel(xx, yy, col); |
466 | } |
467 | } |
468 | |
469 | public static double pixelDiff(RGB a, RGB b) { |
470 | return (Math.abs(a.r-b.r) + Math.abs(a.g-b.g) + Math.abs(a.b-b.b))/3; |
471 | } |
472 | |
473 | public static double diff(RGBImage image, RGBImage image2) { |
474 | int w = image.getWidth(), h = image.getHeight(); |
475 | double sum = 0; |
476 | for (int y = 0; y < h; y++) |
477 | for (int x = 0; x < w; x++) |
478 | sum += pixelDiff(image.getRGB(x, y), image2.getRGB(x, y)); |
479 | return sum/(w*h); |
480 | } |
481 | |
482 | public static void copy(RGBImage src, int srcX, int srcY, RGBImage dst, int dstX, int dstY, int w, int h) { |
483 | for (int y = 0; y < h; y++) |
484 | for (int x = 0; x < w; x++) |
485 | dst.setPixel(dstX+x, dstY+y, src.getPixel(srcX+x, srcY+y)); |
486 | } |
487 | |
488 | public static int structureSize(Object o, Class baseClass) { |
489 | if (o == null) return 0; |
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); |
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); |
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 | } |
Began life as a copy of #666
download show line numbers debug dex old transpilations
Travelled to 14 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, gwrvuhgaqvyk, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, qbtsjoyahagl, tslmcundralx, tvejysmllsmz, vouqrxazstgt
No comments. add comment
Snippet ID: | #667 |
Snippet name: | vsplit v7 (with structure size limit) |
Eternal ID of this version: | #667/1 |
Text MD5: | 731caa9ffc185015375cc7972840fb89 |
Author: | stefan |
Category: | |
Type: | JavaX source code |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2015-07-13 08:35:43 |
Source code size: | 15126 bytes / 525 lines |
Pitched / IR pitched: | No / Yes |
Views / Downloads: | 743 / 595 |
Referenced in: | [show references] |