Mercurial > lasercutter
comparison src/clojureDemo/GenesisPlay.clj @ 1:6d9bdaf919f7
added clojureDemo source
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Fri, 20 Aug 2010 00:32:44 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
0:163bf9b2fd13 | 1:6d9bdaf919f7 |
---|---|
1 (ns clojureDemo.GenesisPlay) | |
2 | |
3 | |
4 (use 'clojure.contrib.import-static) | |
5 (import '(java.io File)) | |
6 (import '(org.apache.commons.io FileUtils)) | |
7 (import '(javax.imageio ImageIO) ) | |
8 (import '(javax.swing JFrame)) | |
9 (import '(java.awt Color BorderLayout)) | |
10 (import '(ij.plugin PlugIn)) | |
11 (import '(ij ImagePlus IJ)) | |
12 (import '(java.lang Math)) | |
13 | |
14 (use 'clojureDemo.appeture) | |
15 | |
16 (import-static java.lang.Math pow abs) | |
17 | |
18 (import '(ij Macro)) | |
19 | |
20 (import '(java.io BufferedReader InputStreamReader)) | |
21 (import '(java.awt.image BufferedImage)) | |
22 (import '(genesis Genesis)) | |
23 (import '(utils Mark)) | |
24 (import '(capenLow StoryProcessor)) | |
25 (import '(connections Connections WiredBox)) | |
26 (import '(specialBoxes BasicBox MultiFunctionBox)) | |
27 (import '(http Start)) | |
28 (import '(engineering NewHardWiredTranslator)) | |
29 | |
30 (import '(java.awt Polygon)) | |
31 (import '(java.awt.geom Line2D$Double)) | |
32 (use 'clojure.contrib.str-utils) | |
33 | |
34 | |
35 (use '[clojureDemo.Xuggle :only (video-seq trans-save flash display video-data)]) | |
36 (use 'clojureDemo.MegaDeath) | |
37 | |
38 | |
39 (use 'clojure.contrib.combinatorics) | |
40 | |
41 (use 'clojure.contrib.repl-utils) | |
42 (use ['clojureDemo.Defines | |
43 :only '( | |
44 lian look getto human0 blow base app0 app1 app2 app3 app4 app5 | |
45 bounce0 bounce1 bounce2 bounce3 bounce4 bounce5 bounce6 | |
46 collide0 collide1 collide2 collide3 collide4 | |
47 give0 give1 give2 give3 give4 target default)]) | |
48 | |
49 | |
50 ;(proxy | |
51 | |
52 | |
53 | |
54 | |
55 | |
56 (defn startInFrame-rm | |
57 [genesis] | |
58 (.start genesis) | |
59 (let [frame (JFrame.)] | |
60 (doto frame | |
61 (.setTitle "Genesis") | |
62 (.setBounds 0 0 1024 768) | |
63 (doto (.getContentPane) | |
64 (.setBackground Color/WHITE) | |
65 (.setLayout (BorderLayout.)) | |
66 (.add genesis)) | |
67 (.setJMenuBar (.getMenuBar genesis)) | |
68 (.setVisible true)) | |
69 frame)) | |
70 | |
71 | |
72 (defn run-genesis | |
73 ([] (startInFrame-rm (Genesis.))) | |
74 ([genesis] (startInFrame-rm genesis))) | |
75 | |
76 (defn lazy->hashMap | |
77 [lazy] | |
78 (zipmap (map first lazy) (map last lazy))) | |
79 | |
80 (defn make-box | |
81 "constructs a wired box sutiable for interfacing to Genesis" | |
82 [name process-fn] | |
83 (let [box (proxy [BasicBox] [] (getName [] name) | |
84 (process [obj] (.transmit (Connections/getPorts this) (process-fn obj))))] | |
85 (.addSignalProcessor (Connections/getPorts box) "process") | |
86 box)) | |
87 | |
88 | |
89 (defn make-generator-box | |
90 "makes a box which only outputs a constant" | |
91 [name constant] | |
92 (let [box (proxy [BasicBox] [] (getName [] name) (process [obj] (.transmit (Connections/getPorts this) constant)))] | |
93 (.addSignalProcessor (Connections/getPorts box) "process") | |
94 box)) | |
95 | |
96 (defn naturals [] (iterate inc 0)) | |
97 | |
98 ;; ;(defn make-multifn-box [& args] | |
99 ;; ; (apply hash-map args) | |
100 | |
101 ;; ; (map mega-macro naturals ) | |
102 | |
103 ;; ; ) | |
104 | |
105 | |
106 | |
107 | |
108 (defmacro function-name | |
109 [function] | |
110 (list str (list 'quote function))) | |
111 | |
112 (defn make-vision-box | |
113 "eventually I'll generalize this to arbitary functions and port names, but for now this is good enough" | |
114 [function1 function2] | |
115 (let [box (proxy [MultiFunctionBox] [] (getName [] "vision-box") | |
116 (process1 [obj] (.transmit (Connections/getPorts this) (function1 obj))) | |
117 (process2 [obj] (.transmit (Connections/getPorts this) (function2 obj))))] | |
118 (.addSignalProcessor (Connections/getPorts box) "PORT1" "process1") | |
119 (.addSignalProcessor (Connections/getPorts box) "PORT2" "process2") | |
120 box)) | |
121 | |
122 ;; (defn make-box | |
123 ;; [name & functions] | |
124 ;; (let [box (proxy [MultiFunctionBox] [] (getName [] name) | |
125 ;; (for [indexed-fun (clojure.contrib.seq-utils/indexed functions)] | |
126 ;; ((symbol (str "process" (first indexed-fun))) | |
127 ;; [obj] (.transmit (Connections/getPorts this) ((last indexed-fun) obj)))))] | |
128 | |
129 ;; (for [indexed-fun (clojure.contrib.seq-utils/indexed functions)] | |
130 ;; (.addSignalProcessor (Connections/getPorts box) (str "PORT" (first indexed-fun)) (str "process" (first indexed-fun)))) | |
131 ;; box)) | |
132 | |
133 ;; (defmacro proxy-functions | |
134 ;; [ name & functions] | |
135 ;; (into | |
136 ;; (for [indexed-fun (clojure.contrib.seq-utils/indexed functions)] | |
137 ;; (list (symbol (str "process" (first indexed-fun))) (vector 'obj) | |
138 ;; (list '.transmit '(Connections/getPorts this) (list (last indexed-fun) 'obj)))) | |
139 ;; (list (list 'getName (vector) name) (vector) (vector MultiFunctionBox) 'proxy))) | |
140 | |
141 | |
142 | |
143 ;; ((symbol (str "process" (first indexed-fun))) | |
144 ;; [obj] (.transmit (Connections/getPorts this) ((last indexed-fun) obj))))) | |
145 | |
146 ;; (defmacro make-fun2-box | |
147 ;; [name & functions] | |
148 | |
149 | |
150 | |
151 ;; (defmacro make-fun-box | |
152 ;; [name & functions] | |
153 ;; (let [proxy-functions | |
154 ;; (for [indexed-fun (clojure.contrib.seq-utils/indexed functions)] | |
155 ;; ((symbol (str "process" (first indexed-fun))) | |
156 ;; [`obj#] (.transmit (Connections/getPorts 'this) ((last indexed-fun) `obj#))))] | |
157 | |
158 | |
159 | |
160 ;; `(let [box# (proxy [MultiFunctionBox] [] (getName [] ~name))] | |
161 ;; ~proxy-functions | |
162 ;; box#)) | |
163 | |
164 ;; (defmacro return | |
165 ;; [name & functions] | |
166 ;; (let [out (for [x functions] | |
167 ;; x)] | |
168 ;; out)) | |
169 | |
170 | |
171 | |
172 | |
173 | |
174 (defn local-genesis | |
175 "connects the custom vision interperter to genesis" | |
176 [function1 function2] | |
177 (let [vision-box (make-vision-box function1 function2) genesis (Genesis.) ] | |
178 (Connections/wire Start/TRIPLES (.getStartParser genesis) "PORT1" vision-box) | |
179 (Connections/wire NewHardWiredTranslator/RESULT (.getNewSemanticTranslator genesis) "PORT2" vision-box) | |
180 genesis)) | |
181 | |
182 | |
183 | |
184 | |
185 | |
186 | |
187 (defn frame-hash | |
188 "yields a convienent representation for the pixles in an image. | |
189 Because of the size of the structvre generated, this must only be used | |
190 in a transient way so that java can do it's garbage collection." | |
191 [imagePlus] | |
192 (with-meta | |
193 (let [buf (.. imagePlus getBufferedImage) | |
194 color (.getColorModel buf)] | |
195 (doall (apply hash-map | |
196 (interleave | |
197 (doall (for [x (range (.getWidth imagePlus)) y (range (.getHeight imagePlus))] | |
198 (vector x y))) | |
199 (doall (for [x (range (.getWidth imagePlus)) y (range (.getHeight imagePlus))] | |
200 (let [data (.getRGB buf x y)] | |
201 (hash-map :r (bit-shift-right (bit-and 0xff0000 data) 16) | |
202 :g (bit-shift-right (bit-and 0x00ff00 data) 8) | |
203 :b (bit-and 0x0000ff data))))))))) | |
204 {:width (.getWidth imagePlus) :height (.getHeight imagePlus)})) | |
205 | |
206 | |
207 | |
208 (defn vid-seq | |
209 [video] | |
210 (with-meta (doall (map frame-hash (video-seq video))) (video-data video))) | |
211 | |
212 | |
213 | |
214 | |
215 | |
216 (defn video-hash | |
217 "turns an entire video into a nice hash-map | |
218 .... or at least it would, if java didn't suck and only give me | |
219 2 GB to work with with no way to increase it. | |
220 linear processing... grumble grumble ....." | |
221 [video-seq] | |
222 (apply hash-map | |
223 (interleave | |
224 (naturals) | |
225 (doall (map #(frame-hash %) video-seq))))) | |
226 | |
227 | |
228 | |
229 | |
230 (defn frame-hash->bufferedImage | |
231 [frame-hash] | |
232 (let [data (meta frame-hash) | |
233 image (BufferedImage. (:width data) (:height data) BufferedImage/TYPE_INT_BGR)] | |
234 | |
235 (doall (for [element frame-hash] | |
236 (let [coord (key element) | |
237 rgb (val element) | |
238 packed-RGB | |
239 (+ (bit-shift-left (:r rgb) 16) | |
240 (bit-shift-left (:g rgb) 8) | |
241 (:b rgb))] | |
242 (.setRGB image (first coord) (last coord) packed-RGB)))) | |
243 image)) | |
244 | |
245 (defmethod display | |
246 clojure.lang.PersistentHashMap [frame-hash] | |
247 (display (frame-hash->bufferedImage frame-hash))) | |
248 | |
249 (defmethod display | |
250 clojure.lang.PersistentArrayMap [frame-hash] | |
251 (display (frame-hash->bufferedImage frame-hash))) | |
252 | |
253 ;; (defmethod display | |
254 ;; clojure.lang.LazySeq [frame-hash] | |
255 ;; (display (frame-hash->bufferedImage frame-hash))) | |
256 | |
257 | |
258 | |
259 | |
260 | |
261 (defn rectangle-window | |
262 "efficiently grabs a rectangle from the frame-hash. | |
263 Values that don't exisist in the picture are colored negative green!" | |
264 [x y l w frame-hash] | |
265 (let [coords (for [m (range (- x l) (+ 1 x l)) n (range (- y w) ( + 1 y w))] (vector m n))] | |
266 | |
267 (with-meta | |
268 (zipmap | |
269 coords | |
270 (map #(frame-hash % {:r 0 :g -500 :b 0}) coords)) | |
271 (meta frame-hash)))) | |
272 | |
273 | |
274 (defn sum | |
275 "squashes all the dinensions of the picture together into a single dimension | |
276 sutiable for analysis." | |
277 [window] | |
278 (zipmap | |
279 (keys window) | |
280 (map (fn [rgb] (+ (:r rgb) (:b rgb) (:g rgb))) (vals window)))) | |
281 | |
282 (defn b&w | |
283 "turn everything grey" | |
284 [window] | |
285 (with-meta | |
286 (zipmap | |
287 (keys window) | |
288 (map (fn [rgb] | |
289 (let [sum (int (/ (+ (:r rgb) (:b rgb) (:g rgb)) 3))] | |
290 {:r sum :g sum :b sum })) (vals window))) (meta window))) | |
291 | |
292 (defn green-select-x-form | |
293 "find green things" | |
294 [window] | |
295 (with-meta | |
296 (zipmap | |
297 (keys window) | |
298 (map (fn [rgb] | |
299 (if (and (> (:g rgb) (:b rgb)) (> (:g rgb) (:r rgb))) | |
300 rgb | |
301 {:r 0 :g 0 :b 0})) (vals window))) (meta window))) | |
302 | |
303 | |
304 (defn manual-line-detect | |
305 "Ty as I might, this can never be truly effective until higher level | |
306 processes contribute to dynamicaly adjusting these paramaters. For | |
307 now I'll settle with simple manual calibration." | |
308 [var1 mean1 var2 mean2] | |
309 (> | |
310 (if (or (< var1 250) (< var2 250)) | |
311 (abs (int (- mean1 mean2))) | |
312 0) 55)) | |
313 ;30 looks good | |
314 | |
315 | |
316 | |
317 | |
318 (defn frame-windows | |
319 "analyzes a frame in terms of lots of tiny windows which | |
320 each try to find some sort of edge." | |
321 ([ x-form frame] | |
322 (with-meta | |
323 (let [width (:width (meta frame) 500) | |
324 height(:height (meta frame) 500 )] | |
325 (filter (comp not nil?) | |
326 (for [x (range 0 width 2) y (range 0 height 2)] | |
327 (:line (window-line (rectangle-window x y 1 1 frame) (comp sum x-form) manual-line-detect))))) (meta frame))) | |
328 ([frame] (frame-windows identity frame))) | |
329 | |
330 | |
331 (defn static-segmentation | |
332 "divides a single picture frame into appropiate objects using a | |
333 simple watershed method based on sharp color variation. | |
334 radius: the general size of the window in pixels | |
335 gradient: threshold for a color gradient to be recognized as a edge" | |
336 [radius gradient frame] | |
337 (let [ah (frame-hash frame)] | |
338 ah)) | |
339 | |
340 | |
341 (defn video-parse | |
342 "this is the equilivalent to the S.T.A.R.T Parser for videos | |
343 right now it's just a simple blob detector" | |
344 [video-seq] | |
345 | |
346 ) | |
347 | |
348 | |
349 | |
350 (defn overlay-draw | |
351 [frame-hash overlay] | |
352 (let [image (frame-hash->bufferedImage frame-hash) | |
353 g2 (.getGraphics image)] | |
354 (doall (for [ x overlay] | |
355 (let [x1 (ffirst x) y1 (second (first x)) x2 (first (last x)) y2 (last (last x))] | |
356 (.drawLine g2 x1 y1 x2 y2)))) | |
357 image)) | |
358 | |
359 | |
360 | |
361 (defn video-seq->b&w | |
362 [video-seq] | |
363 (with-meta | |
364 (map #(ImagePlus. "B and W" (frame-hash->bufferedImage %)) | |
365 | |
366 (map (fn [imgPlus] | |
367 (let [play (frame-hash imgPlus)] | |
368 (b&w play))) | |
369 video-seq)) | |
370 (meta video-seq))) | |
371 | |
372 | |
373 | |
374 (defn vid-save | |
375 [filename vid-seq] | |
376 (trans-save filename | |
377 (with-meta (map (comp #(ImagePlus. "reverse-x-form" %) frame-hash->bufferedImage) vid-seq) (meta vid-seq)))) | |
378 | |
379 | |
380 | |
381 ;(def g0 (video-seq give0)) | |
382 (def gen (proxy [Genesis] [] )) | |
383 (def short-give (with-meta (take 60 (drop 30 (video-seq give0))) {:fps 30 :width 320 :height 240 })) | |
384 | |
385 (def sg short-give) | |
386 (def g1 (first sg)) | |
387 (def gs sg) | |
388 (def play (frame-hash (first sg))) | |
389 (def blank (with-meta (zipmap (keys play) (repeat (count play) {:r 0 :g 0 :b 0})) (meta play))) | |
390 | |
391 (def blank (with-meta (zipmap (keys play) (repeat (count play) {:r 0 :g 0 :b 0})) (meta play))) | |
392 (def b+w-play (b&w play)) | |
393 (def rgb (rectangle-window 50 50 1 1 play)) | |
394 (def invertedPlay (with-meta (zipmap (keys play) (map #(hash-map :r (- 255 (:r %)) :g (- 255 (:g %)) :b (- 255 (:b %))) (vals b+w-play))) (meta play))) | |
395 | |
396 (def invertedColorPlay (with-meta (zipmap (keys play) (map #(hash-map :r (- 255 (:r %)) :g (- 255 (:g %)) :b (- 255 (:b %))) (vals play))) (meta play))) | |
397 | |
398 (def play1 (with-meta (frame-hash (nth sg 0)) {:width 320 :height 240})) | |
399 (def play2 (with-meta (frame-hash (nth sg 1)) {:width 320 :height 240})) | |
400 (def play3 (with-meta (frame-hash (nth sg 2)) {:width 320 :height 240})) | |
401 (def play4 (with-meta (frame-hash (nth sg 3)) {:width 320 :height 240})) | |
402 (def play5 (with-meta (frame-hash (nth sg 4)) {:width 320 :height 240})) | |
403 | |
404 | |
405 | |
406 | |
407 | |
408 | |
409 | |
410 | |
411 | |
412 | |
413 | |
414 | |
415 | |
416 | |
417 | |
418 | |
419 | |
420 | |
421 | |
422 | |
423 | |
424 | |
425 | |
426 | |
427 | |
428 | |
429 (comment | |
430 ok here's the plan-- | |
431 | |
432 "genesis/language" | |
433 raw text -> START -> representations/memory -> story tree | |
434 | |
435 "genesis/vision" | |
436 raw video -> blob detector -> representations/memory -> event/structure tree | |
437 | |
438 first, we start off with a video. | |
439 the video get's passed through the blob detector. | |
440 | |
441 (blob-detector | |
442 first-pass- divide up each frame into exasutive polygons. no temporal dependence | |
443 second-pass- do a pairwise comparison of frames to link the polygons from each frame. | |
444 polygons can either split apart or merge, but this step establishes their geneology. | |
445 third-pass- link the polygons together into higher objects using hueristic rules about motion | |
446 these rules are determined by the language system, but for now they will be hardcoded. | |
447 the only thing for now is that things that move together are the same object. | |
448 ) | |
449 | |
450 | |
451 so now, we have a temporal history of polygons. | |
452 the language part of the story may specify that certain characters | |
453 with certain qualities do certain actions. | |
454 | |
455 "Bob is wearing a red shirt. Shirts are big. Bob is a person. | |
456 Mary is wearing a green shirt. | |
457 Bob is person-sized. | |
458 Bob is moving. | |
459 The green object is a ball. | |
460 Bob gives the ball to Mary." | |
461 | |
462 Now, Genesis can select just the polygons that are important to the story, | |
463 and it also learns important facts such as the relative size of a person to a ball. | |
464 | |
465 The details which are captured in the polygon-transition space are-- | |
466 x (location of the center of each polygon), dx/dt , ((d^2)x)/(dt)^2, color (average), [left|right], polygon area | |
467 polygon shape | |
468 | |
469 This information recurses on every component polygon as well. | |
470 | |
471 When genesis want's to learn about verbs in particular, | |
472 it selects the aproapiate blobs from the linguistic desctiption (in bob's | |
473 case it's "the big red blob on the left", for example.) | |
474 | |
475 after selecting a subset of the blobs, it calculates the angles and distances between | |
476 those blobs' centers as erll as whether they are touching or overlaping. | |
477 | |
478 From this sequence it derives an example of the verb. | |
479 | |
480 From other examples it can do arch earning to refine the sequence to its salient features. | |
481 ) | |
482 | |
483 | |
484 | |
485 (comment (things you can do that will actually work!) | |
486 | |
487 (do (use :reload-all 'clojureDemo.GenesisPlay) (in-ns 'clojureDemo.GenesisPlay)) | |
488 ;genesis integration: | |
489 (def gen5 (make-generator-box "the 5th element" 5)) | |
490 (Connections/wire gen5 (make-box "printer" println)) | |
491 (Connections/viewNetwork) | |
492 (.process gen5 :ignore) ; causes 5 to be printed | |
493 (Connections/obliterateNetwork) | |
494 (.process gen5 :ignore); since the network connections were dissolved, nothing prints. | |
495 | |
496 | |
497 | |
498 ) | |
499 | |
500 | |
501 |