comparison org/vision.org @ 216:f5ea63245b3b

completed vision demonstration video and first draft of vision.org
author Robert McIntyre <rlm@mit.edu>
date Fri, 10 Feb 2012 11:34:07 -0700
parents f283c62bd212
children ac46ee4e574a
comparison
equal deleted inserted replaced
215:f283c62bd212 216:f5ea63245b3b
6 #+SETUPFILE: ../../aurellem/org/setup.org 6 #+SETUPFILE: ../../aurellem/org/setup.org
7 #+INCLUDE: ../../aurellem/org/level-0.org 7 #+INCLUDE: ../../aurellem/org/level-0.org
8 #+babel: :mkdirp yes :noweb yes :exports both 8 #+babel: :mkdirp yes :noweb yes :exports both
9 9
10 * Vision 10 * Vision
11 11
12
13 Vision is one of the most important senses for humans, so I need to 12 Vision is one of the most important senses for humans, so I need to
14 build a simulated sense of vision for my AI. I will do this with 13 build a simulated sense of vision for my AI. I will do this with
15 simulated eyes. Each eye can be independely moved and should see its 14 simulated eyes. Each eye can be independely moved and should see its
16 own version of the world depending on where it is. 15 own version of the world depending on where it is.
17 16
141 maintain a reference to sensor-data which is periodically updated 140 maintain a reference to sensor-data which is periodically updated
142 by the continuation function established by its init-function. 141 by the continuation function established by its init-function.
143 They can be queried every cycle, but their information may not 142 They can be queried every cycle, but their information may not
144 necessairly be different every cycle. 143 necessairly be different every cycle.
145 144
146
147
148 * Physical Eyes 145 * Physical Eyes
149 146
150 The vision pipeline described above handles the flow of rendered 147 The vision pipeline described above handles the flow of rendered
151 images. Now, we need simulated eyes to serve as the source of these 148 images. Now, we need simulated eyes to serve as the source of these
152 images. 149 images.
159 with =(bind-sense=). 156 with =(bind-sense=).
160 157
161 #+name: add-eye 158 #+name: add-eye
162 #+begin_src clojure 159 #+begin_src clojure
163 (in-ns 'cortex.vision) 160 (in-ns 'cortex.vision)
164
165 (import com.jme3.math.Vector3f)
166
167 (def blender-rotation-correction
168 (doto (Quaternion.)
169 (.fromRotationMatrix
170 (doto (Matrix3f.)
171 (.setColumn 0
172 (Vector3f. 1 0 0))
173 (.setColumn 1
174 (Vector3f. 0 -1 0))
175 (.setColumn 2
176 (Vector3f. 0 0 -1)))
177
178 (doto (Matrix3f.)
179 (.setColumn 0
180 (Vector3f.
181
182 161
183 (defn add-eye! 162 (defn add-eye!
184 "Create a Camera centered on the current position of 'eye which 163 "Create a Camera centered on the current position of 'eye which
185 follows the closest physical node in 'creature and sends visual 164 follows the closest physical node in 'creature and sends visual
186 data to 'continuation. The camera will point in the X direction and 165 data to 'continuation. The camera will point in the X direction and
195 (.setLocation cam (.getWorldTranslation eye)) 174 (.setLocation cam (.getWorldTranslation eye))
196 (.lookAtDirection cam (.mult rot Vector3f/UNIT_X) 175 (.lookAtDirection cam (.mult rot Vector3f/UNIT_X)
197 ;; this part is consistent with using Z in 176 ;; this part is consistent with using Z in
198 ;; blender as the UP vector. 177 ;; blender as the UP vector.
199 (.mult rot Vector3f/UNIT_Y)) 178 (.mult rot Vector3f/UNIT_Y))
200
201 (println-repl "eye unit-z ->" (.mult rot Vector3f/UNIT_Z))
202 (println-repl "eye unit-y ->" (.mult rot Vector3f/UNIT_Y))
203 (println-repl "eye unit-x ->" (.mult rot Vector3f/UNIT_X))
204 (.setFrustumPerspective 179 (.setFrustumPerspective
205 cam 45 (/ (.getWidth cam) (.getHeight cam)) 1 1000) 180 cam 45 (/ (.getWidth cam) (.getHeight cam)) 1 1000)
206 (bind-sense target cam) cam)) 181 (bind-sense target cam) cam))
207 #+end_src 182 #+end_src
208 183
277 =(retina-sensor-profile)= extracts a map from the eye-node in the same 252 =(retina-sensor-profile)= extracts a map from the eye-node in the same
278 format as the example maps above. =(eye-dimensions)= finds the 253 format as the example maps above. =(eye-dimensions)= finds the
279 dimansions of the smallest image required to contain all the retinal 254 dimansions of the smallest image required to contain all the retinal
280 sensor maps. 255 sensor maps.
281 256
257 #+name: retina
282 #+begin_src clojure 258 #+begin_src clojure
283 (defn retina-sensor-profile 259 (defn retina-sensor-profile
284 "Return a map of pixel sensitivity numbers to BufferedImages 260 "Return a map of pixel sensitivity numbers to BufferedImages
285 describing the distribution of light-sensitive components of this 261 describing the distribution of light-sensitive components of this
286 eye. :red, :green, :blue, :gray are already defined as extracting 262 eye. :red, :green, :blue, :gray are already defined as extracting
304 280
305 * Eye Creation 281 * Eye Creation
306 282
307 First off, get the children of the "eyes" empty node to find all the 283 First off, get the children of the "eyes" empty node to find all the
308 eyes the creature has. 284 eyes the creature has.
309 285 #+name: eye-node
310 #+begin_src clojure 286 #+begin_src clojure
311 (defvar 287 (defvar
312 ^{:arglists '([creature])} 288 ^{:arglists '([creature])}
313 eyes 289 eyes
314 (sense-nodes "eyes") 290 (sense-nodes "eyes")
316 #+end_src 292 #+end_src
317 293
318 Then, add the camera created by =(add-eye!)= to the simulation by 294 Then, add the camera created by =(add-eye!)= to the simulation by
319 creating a new viewport. 295 creating a new viewport.
320 296
297 #+name: add-camera
321 #+begin_src clojure 298 #+begin_src clojure
322 (defn add-camera! 299 (defn add-camera!
323 "Add a camera to the world, calling continuation on every frame 300 "Add a camera to the world, calling continuation on every frame
324 produced." 301 produced."
325 [#^Application world camera continuation] 302 [#^Application world camera continuation]
341 sensors sensitivity. I have the option to do this filtering in native 318 sensors sensitivity. I have the option to do this filtering in native
342 code for a slight gain in speed. I could also do it in the GPU for a 319 code for a slight gain in speed. I could also do it in the GPU for a
343 massive gain in speed. =(vision-kernel)= generates a list of such 320 massive gain in speed. =(vision-kernel)= generates a list of such
344 continuation functions, one for each channel of the eye. 321 continuation functions, one for each channel of the eye.
345 322
323 #+name: kernel
346 #+begin_src clojure 324 #+begin_src clojure
347 (in-ns 'cortex.vision) 325 (in-ns 'cortex.vision)
348 326
349 (defrecord attached-viewport [vision-fn viewport-fn] 327 (defrecord attached-viewport [vision-fn viewport-fn]
350 clojure.lang.IFn 328 clojure.lang.IFn
351 (invoke [this world] (vision-fn world)) 329 (invoke [this world] (vision-fn world))
352 (applyTo [this args] (apply vision-fn args))) 330 (applyTo [this args] (apply vision-fn args)))
331
332 (defn pixel-sense [sensitivity pixel]
333 (let [s-r (bit-shift-right (bit-and 0xFF0000 sensitivity) 16)
334 s-g (bit-shift-right (bit-and 0x00FF00 sensitivity) 8)
335 s-b (bit-and 0x0000FF sensitivity)
336
337 p-r (bit-shift-right (bit-and 0xFF0000 pixel) 16)
338 p-g (bit-shift-right (bit-and 0x00FF00 pixel) 8)
339 p-b (bit-and 0x0000FF pixel)
340
341 total-sensitivity (* 255 (+ s-r s-g s-b))]
342 (float (/ (+ (* s-r p-r)
343 (* s-g p-g)
344 (* s-b p-b))
345 total-sensitivity))))
353 346
354 (defn vision-kernel 347 (defn vision-kernel
355 "Returns a list of functions, each of which will return a color 348 "Returns a list of functions, each of which will return a color
356 channel's worth of visual information when called inside a running 349 channel's worth of visual information when called inside a running
357 simulation." 350 simulation."
376 (vec 369 (vec
377 (map 370 (map
378 (fn [[key image]] 371 (fn [[key image]]
379 (let [whites (white-coordinates image) 372 (let [whites (white-coordinates image)
380 topology (vec (collapse whites)) 373 topology (vec (collapse whites))
381 mask (color-channel-presets key key)] 374 sensitivity (sensitivity-presets key key)]
382 (attached-viewport. 375 (attached-viewport.
383 (fn [world] 376 (fn [world]
384 (register-eye! world) 377 (register-eye! world)
385 (vector 378 (vector
386 topology 379 topology
387 (vec 380 (vec
388 (for [[x y] whites] 381 (for [[x y] whites]
389 (bit-and 382 (pixel-sense
390 mask (.getRGB @vision-image x y)))))) 383 sensitivity
384 (.getRGB @vision-image x y))))))
391 register-eye!))) 385 register-eye!)))
392 retinal-map)))) 386 retinal-map))))
393 387
394 (defn gen-fix-display 388 (defn gen-fix-display
395 "Create a function to call to restore a simulation's display when it 389 "Create a function to call to restore a simulation's display when it
396 is disrupted by a Viewport." 390 is disrupted by a Viewport."
397 [] 391 []
398 (runonce 392 (runonce
399 (fn [world] 393 (fn [world]
400 (add-camera! world (.getCamera world) no-op)))) 394 (add-camera! world (.getCamera world) no-op))))
401
402 #+end_src 395 #+end_src
403 396
404 Note that since each of the functions generated by =(vision-kernel)= 397 Note that since each of the functions generated by =(vision-kernel)=
405 shares the same =(register-eye!)= function, the eye will be registered 398 shares the same =(register-eye!)= function, the eye will be registered
406 only once the first time any of the functions from the list returned 399 only once the first time any of the functions from the list returned
417 410
418 All the hard work has been done, all that remains is to apply 411 All the hard work has been done, all that remains is to apply
419 =(vision-kernel)= to each eye in the creature and gather the results 412 =(vision-kernel)= to each eye in the creature and gather the results
420 into one list of functions. 413 into one list of functions.
421 414
415 #+name: main
422 #+begin_src clojure 416 #+begin_src clojure
423 (defn vision! 417 (defn vision!
424 "Returns a function which returns visual sensory data when called 418 "Returns a function which returns visual sensory data when called
425 inside a running simulation" 419 inside a running simulation"
426 [#^Node creature & {skip :skip :or {skip 0}}] 420 [#^Node creature & {skip :skip :or {skip 0}}]
434 428
435 It's vital to have a visual representation for each sense. Here I use 429 It's vital to have a visual representation for each sense. Here I use
436 =(view-sense)= to construct a function that will create a display for 430 =(view-sense)= to construct a function that will create a display for
437 visual data. 431 visual data.
438 432
433 #+name: display
439 #+begin_src clojure 434 #+begin_src clojure
435 (in-ns 'cortex.vision)
436
440 (defn view-vision 437 (defn view-vision
441 "Creates a function which accepts a list of visual sensor-data and 438 "Creates a function which accepts a list of visual sensor-data and
442 displays each element of the list to the screen." 439 displays each element of the list to the screen."
443 [] 440 []
444 (view-sense 441 (view-sense
446 [[coords sensor-data]] 443 [[coords sensor-data]]
447 (let [image (points->image coords)] 444 (let [image (points->image coords)]
448 (dorun 445 (dorun
449 (for [i (range (count coords))] 446 (for [i (range (count coords))]
450 (.setRGB image ((coords i) 0) ((coords i) 1) 447 (.setRGB image ((coords i) 0) ((coords i) 1)
451 (sensor-data i)))) 448 (gray (int (* 255 (sensor-data i)))))))
452 image)))) 449 image))))
453 #+end_src 450 #+end_src
454 451
455 * Tests 452 * Tests
456 453
545 : "(let [retina \"Models/test-creature/retina-small.png\"] 542 : "(let [retina \"Models/test-creature/retina-small.png\"]
546 : {:all retina :red retina :green retina :blue retina})" 543 : {:all retina :red retina :green retina :blue retina})"
547 544
548 This is the approximation to the human eye described earlier. 545 This is the approximation to the human eye described earlier.
549 546
547 #+name: test-2
550 #+begin_src clojure 548 #+begin_src clojure
551 (in-ns 'cortex.test.vision) 549 (in-ns 'cortex.test.vision)
552 550
553 (import com.aurellem.capture.Capture) 551 (defn change-color [obj color]
552 (println-repl obj)
553 (if obj
554 (.setColor (.getMaterial obj) "Color" color)))
555
556 (defn colored-cannon-ball [color]
557 (comp #(change-color % color)
558 (fire-cannon-ball)))
554 559
555 (defn test-worm-vision [] 560 (defn test-worm-vision []
556 (let [the-worm (doto (worm)(body!)) 561 (let [the-worm (doto (worm)(body!))
557 vision (vision! the-worm) 562 vision (vision! the-worm)
558 vision-display (view-vision) 563 vision-display (view-vision)
564 y-axis 569 y-axis
565 (box 0.01 1 0.01 :physical? false :color ColorRGBA/Green 570 (box 0.01 1 0.01 :physical? false :color ColorRGBA/Green
566 :position (Vector3f. 0 -5 0)) 571 :position (Vector3f. 0 -5 0))
567 z-axis 572 z-axis
568 (box 0.01 0.01 1 :physical? false :color ColorRGBA/Blue 573 (box 0.01 0.01 1 :physical? false :color ColorRGBA/Blue
569 :position (Vector3f. 0 -5 0))] 574 :position (Vector3f. 0 -5 0))
575 timer (RatchetTimer. 60)]
570 576
571 (world (nodify [(floor) the-worm x-axis y-axis z-axis me]) 577 (world (nodify [(floor) the-worm x-axis y-axis z-axis me])
572 standard-debug-controls 578 (assoc standard-debug-controls
579 "key-r" (colored-cannon-ball ColorRGBA/Red)
580 "key-b" (colored-cannon-ball ColorRGBA/Blue)
581 "key-g" (colored-cannon-ball ColorRGBA/Green))
573 (fn [world] 582 (fn [world]
574 (light-up-everything world) 583 (light-up-everything world)
584 (speed-up world)
585 (.setTimer world timer)
586 (display-dialated-time world timer)
575 ;; add a view from the worm's perspective 587 ;; add a view from the worm's perspective
576 (add-camera! 588 (add-camera!
577 world 589 world
578 (add-eye! the-worm 590 (add-eye! the-worm
579 (.getChild 591 (.getChild
581 (comp 593 (comp
582 (view-image 594 (view-image
583 (File. "/home/r/proj/cortex/render/worm-vision/worm-view")) 595 (File. "/home/r/proj/cortex/render/worm-vision/worm-view"))
584 BufferedImage!)) 596 BufferedImage!))
585 (set-gravity world Vector3f/ZERO) 597 (set-gravity world Vector3f/ZERO)
586 (Capture/captureVideo 598 (try
587 world 599 (Capture/captureVideo
588 (File. "/home/r/proj/cortex/render/worm-vision/main-view"))) 600 world
601 (File. "/home/r/proj/cortex/render/worm-vision/main-view"))))
602
589 (fn [world _ ] 603 (fn [world _ ]
590 (.setLocalTranslation me (.getLocation (.getCamera world))) 604 (.setLocalTranslation me (.getLocation (.getCamera world)))
591 (vision-display 605 (vision-display
592 (map #(% world) vision) 606 (map #(% world) vision)
593 (File. "/home/r/proj/cortex/render/worm-vision")) 607 (File. "/home/r/proj/cortex/render/worm-vision"))
594 (fix-display world))))) 608 (fix-display world)))))
595 #+end_src 609 #+end_src
596 610
611 ** Methods to Generate the Worm Video
612 #+name: magick2
613 #+begin_src clojure
614 (ns cortex.video.magick2
615 (:import java.io.File)
616 (:use clojure.contrib.shell-out))
617
618 (defn images [path]
619 (sort (rest (file-seq (File. path)))))
620
621 (def base "/home/r/proj/cortex/render/worm-vision/")
622
623 (defn pics [file]
624 (images (str base file)))
625
626 (defn combine-images []
627 (let [main-view (pics "main-view")
628 worm-view (pics "worm-view")
629 blue (pics "0")
630 green (pics "1")
631 red (pics "2")
632 gray (pics "3")
633 blender (let [b-pics (pics "blender")]
634 (concat b-pics (repeat 9001 (last b-pics))))
635 background (repeat 9001 (File. (str base "background.png")))
636 targets (map
637 #(File. (str base "out/" (format "%07d.png" %)))
638 (range 0 (count main-view)))]
639 (dorun
640 (pmap
641 (comp
642 (fn [[background main-view worm-view red green blue gray blender target]]
643 (println target)
644 (sh "convert"
645 background
646 main-view "-geometry" "+18+17" "-composite"
647 worm-view "-geometry" "+677+17" "-composite"
648 green "-geometry" "+685+430" "-composite"
649 red "-geometry" "+788+430" "-composite"
650 blue "-geometry" "+894+430" "-composite"
651 gray "-geometry" "+1000+430" "-composite"
652 blender "-geometry" "+0+0" "-composite"
653 target))
654 (fn [& args] (map #(.getCanonicalPath %) args)))
655 background main-view worm-view red green blue gray blender targets))))
656 #+end_src
657
658 #+begin_src sh :results silent
659 cd /home/r/proj/cortex/render/worm-vision
660 ffmpeg -r 25 -b 9001k -i out/%07d.png -vcodec libtheora worm-vision.ogg
661 #+end_src
662
663 * Demonstration of Vision
664 #+begin_html
665 <div class="figure">
666 <video controls="controls" width="755">
667 <source src="../video/worm-vision.ogg" type="video/ogg"
668 preload="none" poster="../images/aurellem-1280x480.png" />
669 </video>
670 <p>Simulated Vision in a Virtual Environment</p>
671 </div>
672 #+end_html
673
597 * Headers 674 * Headers
598 675
599 #+name: vision-header 676 #+name: vision-header
600 #+begin_src clojure 677 #+begin_src clojure
601 (ns cortex.vision 678 (ns cortex.vision
602 "Simulate the sense of vision in jMonkeyEngine3. Enables multiple 679 "Simulate the sense of vision in jMonkeyEngine3. Enables multiple
603 eyes from different positions to observe the same world, and pass 680 eyes from different positions to observe the same world, and pass
604 the observed data to any arbitray function. Automatically reads 681 the observed data to any arbitray function. Automatically reads
605 eye-nodes from specially prepared blender files and instanttiates 682 eye-nodes from specially prepared blender files and instantiates
606 them in the world as actual eyes." 683 them in the world as actual eyes."
607 {:author "Robert McIntyre"} 684 {:author "Robert McIntyre"}
608 (:use (cortex world sense util)) 685 (:use (cortex world sense util))
609 (:use clojure.contrib.def) 686 (:use clojure.contrib.def)
610 (:import com.jme3.post.SceneProcessor) 687 (:import com.jme3.post.SceneProcessor)
611 (:import (com.jme3.util BufferUtils Screenshots)) 688 (:import (com.jme3.util BufferUtils Screenshots))
612 (:import java.nio.ByteBuffer) 689 (:import java.nio.ByteBuffer)
613 (:import java.awt.image.BufferedImage) 690 (:import java.awt.image.BufferedImage)
614 (:import (com.jme3.renderer ViewPort Camera)) 691 (:import (com.jme3.renderer ViewPort Camera))
615 (:import com.jme3.math.ColorRGBA) 692 (:import (com.jme3.math ColorRGBA Vector3f Matrix3f))
616 (:import com.jme3.renderer.Renderer) 693 (:import com.jme3.renderer.Renderer)
617 (:import com.jme3.app.Application) 694 (:import com.jme3.app.Application)
618 (:import com.jme3.texture.FrameBuffer) 695 (:import com.jme3.texture.FrameBuffer)
619 (:import (com.jme3.scene Node Spatial))) 696 (:import (com.jme3.scene Node Spatial)))
620 #+end_src 697 #+end_src
630 (:import java.awt.Dimension) 707 (:import java.awt.Dimension)
631 (:import javax.swing.JFrame) 708 (:import javax.swing.JFrame)
632 (:import com.jme3.math.ColorRGBA) 709 (:import com.jme3.math.ColorRGBA)
633 (:import com.jme3.scene.Node) 710 (:import com.jme3.scene.Node)
634 (:import com.jme3.math.Vector3f) 711 (:import com.jme3.math.Vector3f)
635 (:import java.io.File)) 712 (:import java.io.File)
636 #+end_src 713 (:import (com.aurellem.capture Capture RatchetTimer)))
637 714 #+end_src
638 715
639 716 * Onward!
640 - As a neat bonus, this idea behind simulated vision also enables one 717 - As a neat bonus, this idea behind simulated vision also enables one
641 to [[../../cortex/html/capture-video.html][capture live video feeds from jMonkeyEngine]]. 718 to [[../../cortex/html/capture-video.html][capture live video feeds from jMonkeyEngine]].
719 - Now that we have vision, it's time to tackle [[./hearing.org][hearing]].
720
721 * Source Listing
722 - [[../src/cortex/vision.clj][cortex.vision]]
723 - [[../src/cortex/test/vision.clj][cortex.test.vision]]
724 - [[../src/cortex/video/magick2.clj][cortex.video.magick2]]
725 - [[../assets/Models/subtitles/worm-vision-subtitles.blend][worm-vision-subtitles.blend]]
726 #+html: <ul> <li> <a href="../org/sense.org">This org file</a> </li> </ul>
727 - [[http://hg.bortreb.com ][source-repository]]
728
642 729
643 730
644 * COMMENT Generate Source 731 * COMMENT Generate Source
645 #+begin_src clojure :tangle ../src/cortex/vision.clj 732 #+begin_src clojure :tangle ../src/cortex/vision.clj
646 <<eyes>> 733 <<vision-header>>
734 <<pipeline-1>>
735 <<pipeline-2>>
736 <<retina>>
737 <<add-eye>>
738 <<sensitivity>>
739 <<eye-node>>
740 <<add-camera>>
741 <<kernel>>
742 <<main>>
743 <<display>>
647 #+end_src 744 #+end_src
648 745
649 #+begin_src clojure :tangle ../src/cortex/test/vision.clj 746 #+begin_src clojure :tangle ../src/cortex/test/vision.clj
650 <<test-header>> 747 <<test-header>>
651 <<test-1>> 748 <<test-1>>
652 #+end_src 749 <<test-2>>
750 #+end_src
751
752 #+begin_src clojure :tangle ../src/cortex/video/magick2.clj
753 <<magick2>>
754 #+end_src