Uses 3874K of libraries. Click here for Pure Java version (6166L/42K/151K).
1 | !7 |
2 | |
3 | static int imageWidth = 200; |
4 | |
5 | static RGBImage originalImage; |
6 | static ImageSurface imageSurface; |
7 | static JLabel isTitle; |
8 | |
9 | !include #1000522 // helper functions for image reproduction |
10 | |
11 | static abstract class Base { |
12 | abstract Base copy(); |
13 | abstract void vary(); |
14 | } |
15 | |
16 | static abstract class Overlay extends Base { |
17 | abstract Overlay copy(); |
18 | abstract void renderOn(RGBImage image); |
19 | } |
20 | |
21 | static abstract class Params extends Base { |
22 | RGBImage originalImage; |
23 | abstract Params copy(); |
24 | abstract RGBImage render(); |
25 | |
26 | void baseClone(Params p) { |
27 | p.originalImage = originalImage; |
28 | } |
29 | |
30 | RGBImage rendered; |
31 | RGBImage getImage() { |
32 | if (rendered == null) |
33 | rendered = render(); |
34 | return rendered; |
35 | } |
36 | |
37 | double score = 2.0; |
38 | double getScore() { |
39 | if (score == 2.0) |
40 | score = calcScore(); |
41 | return score; |
42 | } |
43 | |
44 | // when object has changed |
45 | double getFreshScore() { |
46 | resetScore(); |
47 | return getScore(); |
48 | } |
49 | |
50 | void resetScore() { |
51 | score = 2.0; |
52 | rendered = null; |
53 | } |
54 | |
55 | double calcScore() { |
56 | return diff(originalImage, getImage()); |
57 | } |
58 | |
59 | boolean isBetterThan(Params p) { |
60 | return getScore() < p.getScore(); |
61 | } |
62 | } |
63 | |
64 | static class Coin extends Overlay { |
65 | double cx, cy, radius; |
66 | RGB color; |
67 | |
68 | Coin copy() { |
69 | Coin p = new Coin(); |
70 | p.cx = cx; |
71 | p.cy = cy; |
72 | p.radius = radius; |
73 | p.color = color; |
74 | return p; |
75 | } |
76 | |
77 | public String toString() { |
78 | return "Coin " + cx + " " + cy + " " + radius + " " + color; |
79 | } |
80 | |
81 | void renderOn(RGBImage image) { |
82 | coin(image, this); |
83 | } |
84 | |
85 | void vary() { |
86 | int s = random(4); |
87 | if (s == 0) |
88 | cx = vary01(cx); |
89 | else if (s == 1) |
90 | cy = vary01(cy); |
91 | else if (s == 2) |
92 | radius = vary01(radius); |
93 | else |
94 | color = varyColor(color); |
95 | } |
96 | } |
97 | |
98 | static class Solid extends Overlay { |
99 | RGB col; |
100 | |
101 | Solid copy() { |
102 | Solid p = new Solid(); |
103 | p.col = col; |
104 | return p; |
105 | } |
106 | |
107 | public String toString() { |
108 | return "Solid " + col; |
109 | } |
110 | |
111 | void renderOn(RGBImage image) { |
112 | int w = image.getWidth(), h = image.getHeight(); |
113 | for (int y = 0; y < h; y++) |
114 | for (int x = 0; x < w; x++) |
115 | image.setPixel(x, y, col); |
116 | } |
117 | |
118 | void vary() { |
119 | col = varyColor(col); |
120 | } |
121 | } |
122 | |
123 | interface Reproducer { |
124 | public Params reproduce(RGBImage original); |
125 | } |
126 | |
127 | interface OverlayReproducer { |
128 | public Overlay reproduce(RGBImage original); |
129 | } |
130 | |
131 | static class RandomCoin implements OverlayReproducer { |
132 | int n = -1; |
133 | public Overlay reproduce(RGBImage original) { |
134 | ++n; |
135 | Coin p = new Coin(); |
136 | p.cx = random(); |
137 | p.cy = random(); |
138 | p.radius = random()*0.2; |
139 | if (n % 2 == 0) |
140 | p.color = randomColor(); |
141 | else |
142 | p.color = probeRandomPixel(original); |
143 | return p; |
144 | } |
145 | } |
146 | |
147 | static class RandomSolid implements OverlayReproducer { |
148 | int n = -1; |
149 | public Overlay reproduce(RGBImage original) { |
150 | ++n; |
151 | Solid p = new Solid(); |
152 | if (n % 2 == 0) { |
153 | p.col = randomColor(); |
154 | } else { |
155 | p.col = probeRandomPixel(original); |
156 | } |
157 | return p; |
158 | } |
159 | } |
160 | |
161 | static class WhiteSolid implements OverlayReproducer { |
162 | public Overlay reproduce(RGBImage original) { |
163 | Solid p = new Solid(); |
164 | p.col = new RGB(Color.white); |
165 | return p; |
166 | } |
167 | } |
168 | |
169 | static class VaryBest implements Reproducer { |
170 | Reproducer base; |
171 | Params best; |
172 | |
173 | VaryBest(Reproducer base) { |
174 | this.base = base; |
175 | } |
176 | |
177 | public Params reproduce(RGBImage original) { |
178 | Params p = base.reproduce(original); |
179 | if (best == null || p.isBetterThan(best)) |
180 | best = p; |
181 | Params variation = best.copy(); |
182 | variation.vary(); |
183 | //System.out.println("Best: " + best.getScore() + ", variation: " + variation.getScore()); |
184 | if (variation.isBetterThan(best)) { |
185 | //System.out.println("Using variation, diff=" + (best.getScore()-variation.getScore())); |
186 | best = variation; |
187 | } |
188 | return best; |
189 | } |
190 | |
191 | void reset() { |
192 | best = null; |
193 | } |
194 | } |
195 | |
196 | static class Layered extends Params { |
197 | Layers myMaker; |
198 | Overlay[] list; |
199 | |
200 | Layered(Layers myMaker, Overlay[] list) { |
201 | this.myMaker = myMaker; |
202 | this.list = list; |
203 | } |
204 | |
205 | Layered copy() { |
206 | Layered p = new Layered(myMaker, new Overlay[list.length]); |
207 | baseClone(p); |
208 | for (int i = 0; i < list.length; i++) |
209 | p.list[i] = list[i].copy(); |
210 | return p; |
211 | } |
212 | |
213 | public String toString() { |
214 | StringBuilder buf = new StringBuilder("layered{"); |
215 | for (int i = 0; i < list.length; i++) { |
216 | if (i != 0) |
217 | buf.append(", "); |
218 | buf.append(list[i]); |
219 | } |
220 | return buf + "}"; |
221 | } |
222 | |
223 | RGBImage render() { |
224 | RGBImage image = new RGBImage(originalImage.getWidth(), originalImage.getHeight(), Color.white); |
225 | for (int i = 0; i < list.length; i++) |
226 | list[i].renderOn(image); |
227 | return image; |
228 | } |
229 | |
230 | void vary() { |
231 | int i = random(list.length); |
232 | if (random(2) == 0) { |
233 | //System.out.println("Varying layer " + i); |
234 | list[i].vary(); |
235 | } else { |
236 | //double score = getScore(); |
237 | OverlayReproducer maker = myMaker.list[i]; |
238 | list[i] = maker.reproduce(originalImage); |
239 | //System.out.println("Exchanging layer " + i + " from " + maker + ", improvement: " + (score-getFreshScore())); |
240 | } |
241 | resetScore(); |
242 | } |
243 | } |
244 | |
245 | static class Layers implements Reproducer { |
246 | OverlayReproducer[] list; |
247 | long ntry = -1; |
248 | int stepTime = 0/*500*/; |
249 | VaryBest varyBest = new VaryBest(this); |
250 | |
251 | Layers(OverlayReproducer... list) { |
252 | this.list = list; |
253 | } |
254 | |
255 | public Params reproduce(RGBImage original) { |
256 | ++ntry; |
257 | int n = this.list.length; |
258 | if (stepTime != 0) { |
259 | // build up image gradually to optimize lower layers first |
260 | n = (int) Math.min(n, ntry/stepTime+1); |
261 | varyBest.reset(); |
262 | } |
263 | Overlay[] list = new Overlay[n]; |
264 | for (int i = 0; i < n; i++) |
265 | list[i] = this.list[i].reproduce(original); |
266 | Layered result = new Layered(this, list); |
267 | result.originalImage = original; |
268 | return result; |
269 | } |
270 | } |
271 | |
272 | // main reproduce function |
273 | static Reproducer reproducer = |
274 | /*new VaryBest*/(new Layers( |
275 | /*new RandomSolid()*/new WhiteSolid(), |
276 | new RandomCoin(), |
277 | new RandomCoin() |
278 | )).varyBest; |
279 | |
280 | static Params reproduce(RGBImage original) { |
281 | int w = original.getWidth(), h = original.getHeight(); |
282 | return reproducer.reproduce(original); |
283 | } |
284 | |
285 | p-substance-thread { |
286 | String imageID = "#1000332"; // Picture of two coins |
287 | if (args.length != 0) imageID = args[0]; |
288 | final String _imageID = imageID; |
289 | originalImage = loadImage(_imageID); |
290 | originalImage = resizeToWidth(originalImage, imageWidth); |
291 | |
292 | swing { |
293 | imageSurface = new ImageSurface; |
294 | isTitle = jCenteredLabel("Reproduction"); |
295 | |
296 | showPackedFrame(vgrid( |
297 | withCenteredTitle("Original Image", new ImageSurface(originalImage)), |
298 | withTitle(isTitle, imageSurface))); |
299 | } |
300 | |
301 | reproduceOpenEnd(originalImage, imageSurface); |
302 | } |
303 | |
304 | static void reproduceOpenEnd(RGBImage original, ImageSurface imageSurface) { |
305 | Params best = null; |
306 | long lastPrint = 0, lastN = 0; |
307 | for (long ntry = 1; ; ntry++) { |
308 | long now = System.currentTimeMillis(); |
309 | if (now >= lastPrint+1000) { |
310 | long tps = (ntry-lastN)*1000/(now-lastPrint); |
311 | lastPrint = now; |
312 | lastN = ntry; |
313 | String s = "Try " + ntry + " (" + tps + "/s)"; |
314 | if (best == null) |
315 | System.out.println(s); |
316 | else { |
317 | System.out.println("Best: " + best); |
318 | System.out.println(s + ", score: " + formatDouble(best.getScore()*100, 2) + "%, structure size: " + structureSize(best, Params.class)); |
319 | } |
320 | } |
321 | Params p = reproduce(original); |
322 | if (best == null || p != null && p.getScore() < best.getScore()) { |
323 | //System.out.println("New best! " + p.getScore()); |
324 | best = p; |
325 | imageSurface.setImage(p.getImage()); |
326 | setText(isTitle, print("Reproduction (" + formatDouble(100*(1-p.getScore()), 1) + "%)")); |
327 | } |
328 | |
329 | if (p != null && p.getScore() == 0.0) |
330 | break; |
331 | } |
332 | } |
333 | |
334 | static void coin(RGBImage img, Coin p) { |
335 | int w = img.getWidth(), h = img.getHeight(); |
336 | double ratio = ((double) w)/h; |
337 | for (int yy = 0; yy < h; yy++) { |
338 | double y = ((double) yy)/(h-1); |
339 | double ybla = Math.abs(y-p.cy)/p.radius; |
340 | if (ybla <= 1) { |
341 | double xbla = Math.sqrt(1-ybla*ybla)/ratio; |
342 | double l = p.cx-xbla*p.radius, r = p.cx+xbla*p.radius; |
343 | int ll = (int) (l*w), rr = (int) (r*w); |
344 | ll = Math.max(0, ll); |
345 | rr = Math.min(w-1, rr); |
346 | for (int xx = ll; xx <= rr; xx++) |
347 | img.setPixel(xx, yy, p.color); |
348 | } |
349 | } |
350 | } |
Began life as a copy of #669
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: | #671 |
Snippet name: | Reproducing 2 Coins |
Eternal ID of this version: | #671/17 |
Text MD5: | 7875379eb79815532f7e6b97b5b0c9c2 |
Transpilation MD5: | c4cea5ac04af475a769dca57de6422c2 |
Author: | stefan |
Category: | |
Type: | JavaX source code |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2017-02-19 13:40:24 |
Source code size: | 8677 bytes / 350 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 781 / 805 |
Version history: | 16 change(s) |
Referenced in: | [show references] |