Libraryless. Click here for Pure Java version (1443L/10K/31K).
1 | !747 |
2 | |
3 | import java.awt.*; |
4 | import java.awt.image.*; |
5 | import javax.imageio.*; |
6 | import static java.lang.Math.*; |
7 | |
8 | !image classes |
9 | !FrameWithImages |
10 | |
11 | class P { double x, y; *(double *x, double *y) {} } |
12 | |
13 | interface Reproducer { |
14 | public main.Params reproduce(RGBImage original); |
15 | } |
16 | |
17 | interface ReproducerMaker { |
18 | public Reproducer make(); |
19 | } |
20 | |
21 | main { |
22 | static Object androidContext; |
23 | static int imageWidth = 200; |
24 | |
25 | !include #1000522 // helper functions for image reproduction |
26 | !include #1000898 // exitOnFrameClose |
27 | |
28 | static double pi = PI; |
29 | |
30 | static double rotate_x(P p, double angle) { |
31 | angle = angle*pi/180; |
32 | return p.x*cos(angle)-p.y*sin(angle); |
33 | } |
34 | |
35 | static double sideshiftlimit = sqrt(2)/2; |
36 | |
37 | static class Magnet extends Params { |
38 | double angle, sideshift; |
39 | RGB col1 = new RGB(Color.black), col2 = new RGB(Color.white); |
40 | |
41 | RGB def(double x, double y) { |
42 | P p = new P(x-0.5, y-0.5); |
43 | x = rotate_x(p, -angle); |
44 | return x < sideshift ? col1 : col2; |
45 | } |
46 | |
47 | Magnet copy() { |
48 | new Magnet p; |
49 | baseClone(p); |
50 | p.sideshift = sideshift; |
51 | p.angle = angle; |
52 | p.col1 = col1; |
53 | p.col2 = col2; |
54 | return p; |
55 | } |
56 | |
57 | public String toString() { |
58 | return "M " + formatDouble(sideshift, 2) + " " + (int) angle + " " + col1 + " " + col2; |
59 | } |
60 | |
61 | RGBImage render() { |
62 | int w = originalImage.getWidth(), h = originalImage.getHeight(); |
63 | RGBImage image = new RGBImage(w, h, Color.white); |
64 | paint(image, this); |
65 | return image; |
66 | } |
67 | } |
68 | |
69 | static abstract class Params { |
70 | RGBImage originalImage; |
71 | abstract RGBImage render(); |
72 | abstract Params copy(); |
73 | |
74 | void baseClone(Params p) { |
75 | p.originalImage = originalImage; |
76 | } |
77 | |
78 | RGBImage rendered; |
79 | RGBImage getImage() { |
80 | if (rendered == null) |
81 | rendered = render(); |
82 | return rendered; |
83 | } |
84 | |
85 | double score = 2.0; |
86 | double getScore() { |
87 | if (score == 2.0) |
88 | score = calcScore(); |
89 | return score; |
90 | } |
91 | |
92 | double calcScore() { |
93 | return diff(originalImage, getImage()); |
94 | } |
95 | |
96 | boolean isBetterThan(Params p) { |
97 | return getScore() < p.getScore(); |
98 | } |
99 | } |
100 | |
101 | static class RandomMagnet implements Reproducer { |
102 | int n = -1; |
103 | public Params reproduce(RGBImage original) { |
104 | ++n; |
105 | new Magnet p; |
106 | p.originalImage = original; |
107 | p.sideshift = random(-sideshiftlimit, sideshiftlimit); |
108 | p.angle = random()*360; |
109 | if (n % 2 == 0) { |
110 | p.col1 = randomColor(); |
111 | p.col2 = randomColor(); |
112 | } else { |
113 | p.col1 = probeRandomPixel(original); |
114 | p.col2 = probeRandomPixel(original); |
115 | } |
116 | return p; |
117 | } |
118 | } |
119 | |
120 | static class VaryBest implements Reproducer { |
121 | Reproducer base; |
122 | Params best; |
123 | |
124 | VaryBest(Reproducer base) { |
125 | this.base = base; |
126 | } |
127 | |
128 | public Params reproduce(RGBImage original) { |
129 | Params p = base.reproduce(original); |
130 | if (best == null || p.isBetterThan(best)) |
131 | best = p; |
132 | Params variation = vary(best); |
133 | //System.out.println("Best: " + best.getScore() + ", variation: " + variation.getScore()); |
134 | if (variation.isBetterThan(best)) { |
135 | //System.out.println("Using variation, diff=" + (best.getScore()-variation.getScore())); |
136 | best = variation; |
137 | } |
138 | return best; |
139 | } |
140 | |
141 | Params vary(Params p) { |
142 | if (p instanceof Magnet) { |
143 | Magnet n = ((Magnet) p).copy(); |
144 | int s = random(3); |
145 | if (s == 0) { |
146 | s = random(2); |
147 | if (s == 0) |
148 | n.sideshift = varySideshift(n.sideshift); |
149 | else { |
150 | //double old = n.angle; |
151 | n.angle = (n.angle+360+random(-5, 5)) % 360; |
152 | //System.out.println("angle: " + formatDouble(old, 1) + " -> " + formatDouble(n.angle, 1)); |
153 | } |
154 | } else if (s == 1) |
155 | n.col1 = varyColor(n.col1); |
156 | else |
157 | n.col2 = varyColor(n.col2); |
158 | return n; |
159 | } |
160 | |
161 | return null; |
162 | } |
163 | |
164 | double varySideshift(double sideshift) { |
165 | return max(-sideshiftlimit, min(sideshiftlimit, sideshift+random(-0.1, 0.1))); |
166 | } |
167 | |
168 | double varyN(double x) { |
169 | return Math.max(0, Math.min(1, x+random(-0.1, 0.1))); |
170 | } |
171 | |
172 | float varyChannel(float x) { |
173 | return Math.max(0f, Math.min(1f, (float) (x+random(-0.1, 0.1)))); |
174 | } |
175 | |
176 | RGB varyColor(RGB rgb) { |
177 | int s = random(3); |
178 | if (s == 0) |
179 | return new RGB(varyChannel(rgb.r), rgb.g, rgb.b); |
180 | else if (s == 1) |
181 | return new RGB(rgb.r, varyChannel(rgb.g), rgb.b); |
182 | else |
183 | return new RGB(rgb.r, rgb.g, varyChannel(rgb.b)); |
184 | } |
185 | } |
186 | |
187 | static class Gridded extends Params { |
188 | int w, h; |
189 | Params[] array; |
190 | |
191 | Gridded(int w, int h) { |
192 | this.w = w; |
193 | this.h = h; |
194 | array = new Params[w*h]; |
195 | } |
196 | |
197 | Gridded copy() { |
198 | Gridded p = new Gridded(w, h); |
199 | baseClone(p); |
200 | for (int i = 0; i < w*h; i++) |
201 | p.array[i] = array[i].copy(); |
202 | return p; |
203 | } |
204 | |
205 | public String toString() { |
206 | StringBuilder buf = new StringBuilder("grid{"); |
207 | for (int i = 0; i < w*h; i++) { |
208 | if (i != 0) |
209 | buf.append(", "); |
210 | buf.append(array[i]); |
211 | } |
212 | return buf + "}"; |
213 | } |
214 | |
215 | public RGBImage render() { |
216 | int ow = originalImage.getWidth(), oh = originalImage.getHeight(); |
217 | RGBImage img = new RGBImage(ow, oh, Color.white); |
218 | for (int y = 0; y < h; y++) |
219 | for (int x = 0; x < w; x++) { |
220 | int x1 = x*ow/w, y1 = y*oh/h; |
221 | int x2 = (x+1)*ow/w, y2 = (y+1)*oh/h; |
222 | RGBImage part = array[y*w+x].render(); |
223 | main.copy(part, 0, 0, img, x1, y1, part.getWidth(), part.getHeight()); |
224 | } |
225 | return img; |
226 | } |
227 | } |
228 | |
229 | static class Grid implements Reproducer { |
230 | int w, h; |
231 | Reproducer[] bases; |
232 | |
233 | Grid(ReproducerMaker maker, int w, int h) { |
234 | this.w = w; |
235 | this.h = h; |
236 | bases = new Reproducer[w*h]; |
237 | for (int i = 0; i < w*h; i++) |
238 | bases[i] = maker.make(); |
239 | } |
240 | |
241 | RGBImage getGridElement(RGBImage originalImage, int i) { |
242 | int ow = originalImage.getWidth(), oh = originalImage.getHeight(); |
243 | int y = i / w; |
244 | int x = i % w; |
245 | int x1 = x*ow/w, y1 = y*oh/h; |
246 | int x2 = (x+1)*ow/w, y2 = (y+1)*oh/h; |
247 | return originalImage.clip(x1, y1, x2-x1, y2-y1); |
248 | } |
249 | |
250 | public Params reproduce(RGBImage original) { |
251 | Gridded gridded = new Gridded(w, h); |
252 | gridded.originalImage = original; |
253 | for (int i = 0; i < w*h; i++) { |
254 | RGBImage img = getGridElement(original, i); |
255 | Params p = bases[i].reproduce(img); |
256 | if (p == null) |
257 | return null; |
258 | gridded.array[i] = p; |
259 | } |
260 | return gridded; |
261 | } |
262 | } |
263 | |
264 | static ReproducerMaker baseMaker = new ReproducerMaker() { |
265 | public Reproducer make() { |
266 | return new VaryBest(new RandomMagnet()); |
267 | } |
268 | }; |
269 | |
270 | // main reproduce function |
271 | static Reproducer reproducer; |
272 | |
273 | static String imageID = "#1000326"; // Bryan Cranston! |
274 | static RGBImage originalImage; |
275 | static FrameWithImages fwi; |
276 | static double zoom = 1; |
277 | |
278 | psvm { |
279 | for (int i = 0; i < args.length; i++) { |
280 | String arg = args[i]; |
281 | if (isSnippetID(arg)) |
282 | imageID = arg; |
283 | else if (arg.equals("zoom")) |
284 | zoom = Double.parseDouble(args[++i]); |
285 | else if (arg.equals("w") || arg.equals("width")) { |
286 | String w = args[++i]; |
287 | imageWidth = w.equals("original") ? -1 : Integer.parseInt(w); |
288 | } else |
289 | System.out.println("Unknown argument: " + arg); |
290 | } |
291 | |
292 | fwi = new FrameWithImages(1); |
293 | fwi.hop(); |
294 | |
295 | thread { |
296 | originalImage = loadImage(imageID); |
297 | if (imageWidth < 0) |
298 | imageWidth = originalImage.getWidth(); |
299 | else |
300 | originalImage = resizeToWidth(originalImage, imageWidth); |
301 | fwi.setZoom(zoom); |
302 | fwi.setInnerSize(originalImage); |
303 | |
304 | int gx = originalImage.getWidth()/8, gy = originalImage.getHeight()/8; |
305 | reproducer = new Grid(baseMaker, gx, gy); |
306 | |
307 | reproduceOpenEnd(originalImage); |
308 | } |
309 | |
310 | /* |
311 | new Magnet m; |
312 | m.sideshift = -0.2; |
313 | m.angle = 30; |
314 | |
315 | while (true) { |
316 | m.angle = (m.angle+1) % 360; |
317 | //System.out.println("Angle: " + m.angle); |
318 | RGBImage img = new RGBImage(100, 100, Color.gray); |
319 | paint(img, m); |
320 | fwi.setImage(0, img); |
321 | } |
322 | */ |
323 | } |
324 | |
325 | |
326 | static void reproduceOpenEnd(RGBImage original) { |
327 | Params best = null; |
328 | long lastPrint = 0, lastN = 0; |
329 | for (long ntry = 1; ; ntry++) { |
330 | long now = System.currentTimeMillis(); |
331 | if (now >= lastPrint+1000) { |
332 | long tps = (ntry-lastN)*1000/(now-lastPrint); |
333 | lastPrint = now; |
334 | lastN = ntry; |
335 | String s = "Try " + ntry + "(" + tps + "/s)"; |
336 | if (best == null) |
337 | System.out.println(s); |
338 | else { |
339 | //System.out.println("Best: " + best); |
340 | System.out.println(s + ", score: " + formatDouble(best.getScore()*100, 3) + "%"); |
341 | //, structure size: " + structureSize(best, Params.class)); |
342 | } |
343 | } |
344 | Params p = reproducer.reproduce(original); |
345 | if (best == null || p != null && p.getScore() < best.getScore()) { |
346 | //System.out.println("New best! " + p.getScore()); |
347 | best = p; |
348 | fwi.setImage(0, p.getImage()); |
349 | } |
350 | |
351 | if (p != null && p.getScore() == 0.0) |
352 | break; |
353 | } |
354 | } |
355 | |
356 | static void paint(RGBImage img, Magnet m) { |
357 | int w = img.getWidth(), h = img.getHeight(); |
358 | for (int y = 0; y < h; y++) |
359 | for (int x = 0; x < w; x++) { |
360 | double nx = x/(double)(w-1); |
361 | double ny = y/(double)(h-1); |
362 | img.setPixel(x, y, m.def(nx, ny)); |
363 | } |
364 | } |
365 | |
366 | } |
Began life as a copy of #1000541
download show line numbers debug dex old transpilations
Travelled to 15 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, gwrvuhgaqvyk, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, onxytkatvevr, pyentgdyhuwx, pzhvpgtvlbxg, teubizvjbppd, tslmcundralx, tvejysmllsmz, vouqrxazstgt
No comments. add comment
Snippet ID: | #1000550 |
Snippet name: | Magnetize image (current) |
Eternal ID of this version: | #1000550/1 |
Text MD5: | b547d264a0550449b25f102aaac558ae |
Transpilation MD5: | 29ca80eb49bc360e234d561d4182b960 |
Author: | stefan |
Category: | |
Type: | JavaX source code |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2015-09-13 15:10:58 |
Source code size: | 10027 bytes / 366 lines |
Pitched / IR pitched: | No / Yes |
Views / Downloads: | 632 / 629 |
Referenced in: | [show references] |