view 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
line wrap: on
line source
1 #+title: Simulated Sense of Sight
2 #+author: Robert McIntyre
3 #+email: rlm@mit.edu
4 #+description: Simulated sight for AI research using JMonkeyEngine3 and clojure
5 #+keywords: computer vision, jMonkeyEngine3, clojure
6 #+SETUPFILE: ../../aurellem/org/setup.org
7 #+INCLUDE: ../../aurellem/org/level-0.org
8 #+babel: :mkdirp yes :noweb yes :exports both
10 * Vision
12 Vision is one of the most important senses for humans, so I need to
13 build a simulated sense of vision for my AI. I will do this with
14 simulated eyes. Each eye can be independely moved and should see its
15 own version of the world depending on where it is.
17 Making these simulated eyes a reality is fairly simple bacause
18 jMonkeyEngine already conatains extensive support for multiple views
19 of the same 3D simulated world. The reason jMonkeyEngine has this
20 support is because the support is necessary to create games with
21 split-screen views. Multiple views are also used to create efficient
22 pseudo-reflections by rendering the scene from a certain perspective
23 and then projecting it back onto a surface in the 3D world.
25 #+caption: jMonkeyEngine supports multiple views to enable split-screen games, like GoldenEye
26 [[../images/goldeneye-4-player.png]]
28 * Brief Description of jMonkeyEngine's Rendering Pipeline
30 jMonkeyEngine allows you to create a =ViewPort=, which represents a
31 view of the simulated world. You can create as many of these as you
32 want. Every frame, the =RenderManager= iterates through each
33 =ViewPort=, rendering the scene in the GPU. For each =ViewPort= there
34 is a =FrameBuffer= which represents the rendered image in the GPU.
36 Each =ViewPort= can have any number of attached =SceneProcessor=
37 objects, which are called every time a new frame is rendered. A
38 =SceneProcessor= recieves a =FrameBuffer= and can do whatever it wants
39 to the data. Often this consists of invoking GPU specific operations
40 on the rendered image. The =SceneProcessor= can also copy the GPU
41 image data to RAM and process it with the CPU.
43 * The Vision Pipeline
45 Each eye in the simulated creature needs it's own =ViewPort= so that
46 it can see the world from its own perspective. To this =ViewPort=, I
47 add a =SceneProcessor= that feeds the visual data to any arbitray
48 continuation function for further processing. That continuation
49 function may perform both CPU and GPU operations on the data. To make
50 this easy for the continuation function, the =SceneProcessor=
51 maintains appropriatly sized buffers in RAM to hold the data. It does
52 not do any copying from the GPU to the CPU itself.
54 #+name: pipeline-1
55 #+begin_src clojure
56 (defn vision-pipeline
57 "Create a SceneProcessor object which wraps a vision processing
58 continuation function. The continuation is a function that takes
59 [#^Renderer r #^FrameBuffer fb #^ByteBuffer b #^BufferedImage bi],
60 each of which has already been appropiately sized."
61 [continuation]
62 (let [byte-buffer (atom nil)
63 renderer (atom nil)
64 image (atom nil)]
65 (proxy [SceneProcessor] []
66 (initialize
67 [renderManager viewPort]
68 (let [cam (.getCamera viewPort)
69 width (.getWidth cam)
70 height (.getHeight cam)]
71 (reset! renderer (.getRenderer renderManager))
72 (reset! byte-buffer
73 (BufferUtils/createByteBuffer
74 (* width height 4)))
75 (reset! image (BufferedImage.
76 width height
77 BufferedImage/TYPE_4BYTE_ABGR))))
78 (isInitialized [] (not (nil? @byte-buffer)))
79 (reshape [_ _ _])
80 (preFrame [_])
81 (postQueue [_])
82 (postFrame
83 [#^FrameBuffer fb]
84 (.clear @byte-buffer)
85 (continuation @renderer fb @byte-buffer @image))
86 (cleanup []))))
87 #+end_src
89 The continuation function given to =(vision-pipeline)= above will be
90 given a =Renderer= and three containers for image data. The
91 =FrameBuffer= references the GPU image data, but it can not be used
92 directly on the CPU. The =ByteBuffer= and =BufferedImage= are
93 initially "empty" but are sized to hold to data in the
94 =FrameBuffer=. I call transfering the GPU image data to the CPU
95 structures "mixing" the image data. I have provided three functions to
96 do this mixing.
98 #+name: pipeline-2
99 #+begin_src clojure
100 (defn frameBuffer->byteBuffer!
101 "Transfer the data in the graphics card (Renderer, FrameBuffer) to
102 the CPU (ByteBuffer)."
103 [#^Renderer r #^FrameBuffer fb #^ByteBuffer bb]
104 (.readFrameBuffer r fb bb) bb)
106 (defn byteBuffer->bufferedImage!
107 "Convert the C-style BGRA image data in the ByteBuffer bb to the AWT
108 style ABGR image data and place it in BufferedImage bi."
109 [#^ByteBuffer bb #^BufferedImage bi]
110 (Screenshots/convertScreenShot bb bi) bi)
112 (defn BufferedImage!
113 "Continuation which will grab the buffered image from the materials
114 provided by (vision-pipeline)."
115 [#^Renderer r #^FrameBuffer fb #^ByteBuffer bb #^BufferedImage bi]
116 (byteBuffer->bufferedImage!
117 (frameBuffer->byteBuffer! r fb bb) bi))
118 #+end_src
120 Note that it is possible to write vision processing algorithms
121 entirely in terms of =BufferedImage= inputs. Just compose that
122 =BufferedImage= algorithm with =(BufferedImage!)=. However, a vision
123 processing algorithm that is entirely hosted on the GPU does not have
124 to pay for this convienence.
126 * COMMENT asdasd
128 (vision creature) will take an optional :skip argument which will
129 inform the continuations in scene processor to skip the given
130 number of cycles 0 means that no cycles will be skipped.
132 (vision creature) will return [init-functions sensor-functions].
133 The init-functions are each single-arg functions that take the
134 world and register the cameras and must each be called before the
135 corresponding sensor-functions. Each init-function returns the
136 viewport for that eye which can be manipulated, saved, etc. Each
137 sensor-function is a thunk and will return data in the same
138 format as the tactile-sensor functions the structure is
139 [topology, sensor-data]. Internally, these sensor-functions
140 maintain a reference to sensor-data which is periodically updated
141 by the continuation function established by its init-function.
142 They can be queried every cycle, but their information may not
143 necessairly be different every cycle.
145 * Physical Eyes
147 The vision pipeline described above handles the flow of rendered
148 images. Now, we need simulated eyes to serve as the source of these
149 images.
151 An eye is described in blender in the same way as a joint. They are
152 zero dimensional empty objects with no geometry whose local coordinate
153 system determines the orientation of the resulting eye. All eyes are
154 childern of a parent node named "eyes" just as all joints have a
155 parent named "joints". An eye binds to the nearest physical object
156 with =(bind-sense=).
158 #+name: add-eye
159 #+begin_src clojure
160 (in-ns 'cortex.vision)
162 (defn add-eye!
163 "Create a Camera centered on the current position of 'eye which
164 follows the closest physical node in 'creature and sends visual
165 data to 'continuation. The camera will point in the X direction and
166 use the Z vector as up as determined by the rotation of these
167 vectors in blender coordinate space. Use XZY rotation for the node
168 in blender."
169 [#^Node creature #^Spatial eye]
170 (let [target (closest-node creature eye)
171 [cam-width cam-height] (eye-dimensions eye)
172 cam (Camera. cam-width cam-height)
173 rot (.getWorldRotation eye)]
174 (.setLocation cam (.getWorldTranslation eye))
175 (.lookAtDirection cam (.mult rot Vector3f/UNIT_X)
176 ;; this part is consistent with using Z in
177 ;; blender as the UP vector.
178 (.mult rot Vector3f/UNIT_Y))
179 (.setFrustumPerspective
180 cam 45 (/ (.getWidth cam) (.getHeight cam)) 1 1000)
181 (bind-sense target cam) cam))
182 #+end_src
184 Here, the camera is created based on metadata on the eye-node and
185 attached to the nearest physical object with =(bind-sense)=
188 ** The Retina
190 An eye is a surface (the retina) which contains many discrete sensors
191 to detect light. These sensors have can have different-light sensing
192 properties. In humans, each discrete sensor is sensitive to red,
193 blue, green, or gray. These different types of sensors can have
194 different spatial distributions along the retina. In humans, there is
195 a fovea in the center of the retina which has a very high density of
196 color sensors, and a blind spot which has no sensors at all. Sensor
197 density decreases in proportion to distance from the retina.
199 I want to be able to model any retinal configuration, so my eye-nodes
200 in blender contain metadata pointing to images that describe the
201 percise position of the individual sensors using white pixels. The
202 meta-data also describes the percise sensitivity to light that the
203 sensors described in the image have. An eye can contain any number of
204 these images. For example, the metadata for an eye might look like
205 this:
207 #+begin_src clojure
208 {0xFF0000 "Models/test-creature/retina-small.png"}
209 #+end_src
211 #+caption: The retinal profile image "Models/test-creature/retina-small.png". White pixels are photo-sensitive elements. The distribution of white pixels is denser in the middle and falls off at the edges and is inspired by the human retina.
212 [[../assets/Models/test-creature/retina-small.png]]
214 Together, the number 0xFF0000 and the image image above describe the
215 placement of red-sensitive sensory elements.
217 Meta-data to very crudely approximate a human eye might be something
218 like this:
220 #+begin_src clojure
221 (let [retinal-profile "Models/test-creature/retina-small.png"]
222 {0xFF0000 retinal-profile
223 0x00FF00 retinal-profile
224 0x0000FF retinal-profile
225 0xFFFFFF retinal-profile})
226 #+end_src
228 The numbers that serve as keys in the map determine a sensor's
229 relative sensitivity to the channels red, green, and blue. These
230 sensitivity values are packed into an integer in the order _RGB in
231 8-bit fields. The RGB values of a pixel in the image are added
232 together with these sensitivities as linear weights. Therfore,
233 0xFF0000 means sensitive to red only while 0xFFFFFF means sensitive to
234 all colors equally (gray).
236 For convienence I've defined a few symbols for the more common
237 sensitivity values.
239 #+name: sensitivity
240 #+begin_src clojure
241 (defvar sensitivity-presets
242 {:all 0xFFFFFF
243 :red 0xFF0000
244 :blue 0x0000FF
245 :green 0x00FF00}
246 "Retinal sensitivity presets for sensors that extract one channel
247 (:red :blue :green) or average all channels (:gray)")
248 #+end_src
250 ** Metadata Processing
252 =(retina-sensor-profile)= extracts a map from the eye-node in the same
253 format as the example maps above. =(eye-dimensions)= finds the
254 dimansions of the smallest image required to contain all the retinal
255 sensor maps.
257 #+name: retina
258 #+begin_src clojure
259 (defn retina-sensor-profile
260 "Return a map of pixel sensitivity numbers to BufferedImages
261 describing the distribution of light-sensitive components of this
262 eye. :red, :green, :blue, :gray are already defined as extracting
263 the red, green, blue, and average components respectively."
264 [#^Spatial eye]
265 (if-let [eye-map (meta-data eye "eye")]
266 (map-vals
267 load-image
268 (eval (read-string eye-map)))))
270 (defn eye-dimensions
271 "Returns [width, height] specified in the metadata of the eye"
272 [#^Spatial eye]
273 (let [dimensions
274 (map #(vector (.getWidth %) (.getHeight %))
275 (vals (retina-sensor-profile eye)))]
276 [(apply max (map first dimensions))
277 (apply max (map second dimensions))]))
278 #+end_src
281 * Eye Creation
283 First off, get the children of the "eyes" empty node to find all the
284 eyes the creature has.
285 #+name: eye-node
286 #+begin_src clojure
287 (defvar
288 ^{:arglists '([creature])}
289 eyes
290 (sense-nodes "eyes")
291 "Return the children of the creature's \"eyes\" node.")
292 #+end_src
294 Then, add the camera created by =(add-eye!)= to the simulation by
295 creating a new viewport.
297 #+name: add-camera
298 #+begin_src clojure
299 (defn add-camera!
300 "Add a camera to the world, calling continuation on every frame
301 produced."
302 [#^Application world camera continuation]
303 (let [width (.getWidth camera)
304 height (.getHeight camera)
305 render-manager (.getRenderManager world)
306 viewport (.createMainView render-manager "eye-view" camera)]
307 (doto viewport
308 (.setClearFlags true true true)
309 (.setBackgroundColor ColorRGBA/Black)
310 (.addProcessor (vision-pipeline continuation))
311 (.attachScene (.getRootNode world)))))
312 #+end_src
315 The continuation function registers the viewport with the simulation
316 the first time it is called, and uses the CPU to extract the
317 appropriate pixels from the rendered image and weight them by each
318 sensors sensitivity. I have the option to do this filtering in native
319 code for a slight gain in speed. I could also do it in the GPU for a
320 massive gain in speed. =(vision-kernel)= generates a list of such
321 continuation functions, one for each channel of the eye.
323 #+name: kernel
324 #+begin_src clojure
325 (in-ns 'cortex.vision)
327 (defrecord attached-viewport [vision-fn viewport-fn]
328 clojure.lang.IFn
329 (invoke [this world] (vision-fn world))
330 (applyTo [this args] (apply vision-fn args)))
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)
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)
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))))
347 (defn vision-kernel
348 "Returns a list of functions, each of which will return a color
349 channel's worth of visual information when called inside a running
350 simulation."
351 [#^Node creature #^Spatial eye & {skip :skip :or {skip 0}}]
352 (let [retinal-map (retina-sensor-profile eye)
353 camera (add-eye! creature eye)
354 vision-image
355 (atom
356 (BufferedImage. (.getWidth camera)
357 (.getHeight camera)
358 BufferedImage/TYPE_BYTE_BINARY))
359 register-eye!
360 (runonce
361 (fn [world]
362 (add-camera!
363 world camera
364 (let [counter (atom 0)]
365 (fn [r fb bb bi]
366 (if (zero? (rem (swap! counter inc) (inc skip)))
367 (reset! vision-image
368 (BufferedImage! r fb bb bi))))))))]
369 (vec
370 (map
371 (fn [[key image]]
372 (let [whites (white-coordinates image)
373 topology (vec (collapse whites))
374 sensitivity (sensitivity-presets key key)]
375 (attached-viewport.
376 (fn [world]
377 (register-eye! world)
378 (vector
379 topology
380 (vec
381 (for [[x y] whites]
382 (pixel-sense
383 sensitivity
384 (.getRGB @vision-image x y))))))
385 register-eye!)))
386 retinal-map))))
388 (defn gen-fix-display
389 "Create a function to call to restore a simulation's display when it
390 is disrupted by a Viewport."
391 []
392 (runonce
393 (fn [world]
394 (add-camera! world (.getCamera world) no-op))))
395 #+end_src
397 Note that since each of the functions generated by =(vision-kernel)=
398 shares the same =(register-eye!)= function, the eye will be registered
399 only once the first time any of the functions from the list returned
400 by =(vision-kernel)= is called. Each of the functions returned by
401 =(vision-kernel)= also allows access to the =Viewport= through which
402 it recieves images.
404 The in-game display can be disrupted by all the viewports that the
405 functions greated by =(vision-kernel)= add. This doesn't affect the
406 simulation or the simulated senses, but can be annoying.
407 =(gen-fix-display)= restores the in-simulation display.
409 ** Vision!
411 All the hard work has been done, all that remains is to apply
412 =(vision-kernel)= to each eye in the creature and gather the results
413 into one list of functions.
415 #+name: main
416 #+begin_src clojure
417 (defn vision!
418 "Returns a function which returns visual sensory data when called
419 inside a running simulation"
420 [#^Node creature & {skip :skip :or {skip 0}}]
421 (reduce
422 concat
423 (for [eye (eyes creature)]
424 (vision-kernel creature eye))))
425 #+end_src
427 ** Visualization of Vision
429 It's vital to have a visual representation for each sense. Here I use
430 =(view-sense)= to construct a function that will create a display for
431 visual data.
433 #+name: display
434 #+begin_src clojure
435 (in-ns 'cortex.vision)
437 (defn view-vision
438 "Creates a function which accepts a list of visual sensor-data and
439 displays each element of the list to the screen."
440 []
441 (view-sense
442 (fn
443 [[coords sensor-data]]
444 (let [image (points->image coords)]
445 (dorun
446 (for [i (range (count coords))]
447 (.setRGB image ((coords i) 0) ((coords i) 1)
448 (gray (int (* 255 (sensor-data i)))))))
449 image))))
450 #+end_src
452 * Tests
454 ** Basic Test
456 This is a basic test for the vision system. It only tests the
457 vision-pipeline and does not deal with loadig eyes from a blender
458 file. The code creates two videos of the same rotating cube from
459 different angles.
461 #+name: test-1
462 #+begin_src clojure
463 (in-ns 'cortex.test.vision)
465 (defn test-two-eyes
466 "Testing vision:
467 Tests the vision system by creating two views of the same rotating
468 object from different angles and displaying both of those views in
469 JFrames.
471 You should see a rotating cube, and two windows,
472 each displaying a different view of the cube."
473 []
474 (let [candy
475 (box 1 1 1 :physical? false :color ColorRGBA/Blue)]
476 (world
477 (doto (Node.)
478 (.attachChild candy))
479 {}
480 (fn [world]
481 (let [cam (.clone (.getCamera world))
482 width (.getWidth cam)
483 height (.getHeight cam)]
484 (add-camera! world cam
485 (comp
486 (view-image
487 (File. "/home/r/proj/cortex/render/vision/1"))
488 BufferedImage!))
489 (add-camera! world
490 (doto (.clone cam)
491 (.setLocation (Vector3f. -10 0 0))
492 (.lookAt Vector3f/ZERO Vector3f/UNIT_Y))
493 (comp
494 (view-image
495 (File. "/home/r/proj/cortex/render/vision/2"))
496 BufferedImage!))
497 ;; This is here to restore the main view
498 ;; after the other views have completed processing
499 (add-camera! world (.getCamera world) no-op)))
500 (fn [world tpf]
501 (.rotate candy (* tpf 0.2) 0 0)))))
502 #+end_src
504 #+begin_html
505 <div class="figure">
506 <video controls="controls" width="755">
507 <source src="../video/spinning-cube.ogg" type="video/ogg"
508 preload="none" poster="../images/aurellem-1280x480.png" />
509 </video>
510 <p>A rotating cube viewed from two different perspectives.</p>
511 </div>
512 #+end_html
514 Creating multiple eyes like this can be used for stereoscopic vision
515 simulation in a single creature or for simulating multiple creatures,
516 each with their own sense of vision.
518 ** Adding Vision to the Worm
520 To the worm from the last post, we add a new node that describes its
521 eyes.
523 #+attr_html: width=755
524 #+caption: The worm with newly added empty nodes describing a single eye.
525 [[../images/worm-with-eye.png]]
527 The node highlighted in yellow is the root level "eyes" node. It has
528 a single node, highlighted in orange, which describes a single
529 eye. This is the "eye" node. The two nodes which are not highlighted describe the single joint
530 of the worm.
532 The metadata of the eye-node is:
534 #+begin_src clojure :results verbatim :exports both
535 (cortex.sense/meta-data
536 (.getChild
537 (.getChild (cortex.test.body/worm)
538 "eyes") "eye") "eye")
539 #+end_src
541 #+results:
542 : "(let [retina \"Models/test-creature/retina-small.png\"]
543 : {:all retina :red retina :green retina :blue retina})"
545 This is the approximation to the human eye described earlier.
547 #+name: test-2
548 #+begin_src clojure
549 (in-ns 'cortex.test.vision)
551 (defn change-color [obj color]
552 (println-repl obj)
553 (if obj
554 (.setColor (.getMaterial obj) "Color" color)))
556 (defn colored-cannon-ball [color]
557 (comp #(change-color % color)
558 (fire-cannon-ball)))
560 (defn test-worm-vision []
561 (let [the-worm (doto (worm)(body!))
562 vision (vision! the-worm)
563 vision-display (view-vision)
564 fix-display (gen-fix-display)
565 me (sphere 0.5 :color ColorRGBA/Blue :physical? false)
566 x-axis
567 (box 1 0.01 0.01 :physical? false :color ColorRGBA/Red
568 :position (Vector3f. 0 -5 0))
569 y-axis
570 (box 0.01 1 0.01 :physical? false :color ColorRGBA/Green
571 :position (Vector3f. 0 -5 0))
572 z-axis
573 (box 0.01 0.01 1 :physical? false :color ColorRGBA/Blue
574 :position (Vector3f. 0 -5 0))
575 timer (RatchetTimer. 60)]
577 (world (nodify [(floor) the-worm x-axis y-axis z-axis me])
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))
582 (fn [world]
583 (light-up-everything world)
584 (speed-up world)
585 (.setTimer world timer)
586 (display-dialated-time world timer)
587 ;; add a view from the worm's perspective
588 (add-camera!
589 world
590 (add-eye! the-worm
591 (.getChild
592 (.getChild the-worm "eyes") "eye"))
593 (comp
594 (view-image
595 (File. "/home/r/proj/cortex/render/worm-vision/worm-view"))
596 BufferedImage!))
597 (set-gravity world Vector3f/ZERO)
598 (try
599 (Capture/captureVideo
600 world
601 (File. "/home/r/proj/cortex/render/worm-vision/main-view"))))
603 (fn [world _ ]
604 (.setLocalTranslation me (.getLocation (.getCamera world)))
605 (vision-display
606 (map #(% world) vision)
607 (File. "/home/r/proj/cortex/render/worm-vision"))
608 (fix-display world)))))
609 #+end_src
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))
618 (defn images [path]
619 (sort (rest (file-seq (File. path)))))
621 (def base "/home/r/proj/cortex/render/worm-vision/")
623 (defn pics [file]
624 (images (str base file)))
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
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
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
674 * Headers
676 #+name: vision-header
677 #+begin_src clojure
678 (ns cortex.vision
679 "Simulate the sense of vision in jMonkeyEngine3. Enables multiple
680 eyes from different positions to observe the same world, and pass
681 the observed data to any arbitray function. Automatically reads
682 eye-nodes from specially prepared blender files and instantiates
683 them in the world as actual eyes."
684 {:author "Robert McIntyre"}
685 (:use (cortex world sense util))
686 (:use clojure.contrib.def)
687 (:import com.jme3.post.SceneProcessor)
688 (:import (com.jme3.util BufferUtils Screenshots))
689 (:import java.nio.ByteBuffer)
690 (:import java.awt.image.BufferedImage)
691 (:import (com.jme3.renderer ViewPort Camera))
692 (:import (com.jme3.math ColorRGBA Vector3f Matrix3f))
693 (:import com.jme3.renderer.Renderer)
694 (:import com.jme3.app.Application)
695 (:import com.jme3.texture.FrameBuffer)
696 (:import (com.jme3.scene Node Spatial)))
697 #+end_src
699 #+name: test-header
700 #+begin_src clojure
701 (ns cortex.test.vision
702 (:use (cortex world sense util body vision))
703 (:use cortex.test.body)
704 (:import java.awt.image.BufferedImage)
705 (:import javax.swing.JPanel)
706 (:import javax.swing.SwingUtilities)
707 (:import java.awt.Dimension)
708 (:import javax.swing.JFrame)
709 (:import com.jme3.math.ColorRGBA)
710 (:import com.jme3.scene.Node)
711 (:import com.jme3.math.Vector3f)
712 (:import java.io.File)
713 (:import (com.aurellem.capture Capture RatchetTimer)))
714 #+end_src
716 * Onward!
717 - As a neat bonus, this idea behind simulated vision also enables one
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]].
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]]
731 * COMMENT Generate Source
732 #+begin_src clojure :tangle ../src/cortex/vision.clj
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>>
744 #+end_src
746 #+begin_src clojure :tangle ../src/cortex/test/vision.clj
747 <<test-header>>
748 <<test-1>>
749 <<test-2>>
750 #+end_src
752 #+begin_src clojure :tangle ../src/cortex/video/magick2.clj
753 <<magick2>>
754 #+end_src