view org/eyes.org @ 71:a1e421d9c485

improved test suite
author Robert McIntyre <rlm@mit.edu>
date Sun, 11 Dec 2011 22:32:28 -0700
parents 39e4e1542e4a
children b7a3ba5e879b
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 I want to make creatures with eyes. Each eye can be independely moved
13 and should see its own version of the world depending on where it is.
14 #+name: eyes
15 #+begin_src clojure
16 (ns cortex.vision
17 "Simulate the sense of vision in jMonkeyEngine3. Enables multiple
18 eyes from different positions to observe the same world, and pass
19 the observed data to any arbitray function."
20 {:author "Robert McIntyre"}
21 (:use cortex.world)
22 (:import com.jme3.post.SceneProcessor)
23 (:import (com.jme3.util Screenshots BufferUtils))
24 (:import java.nio.ByteBuffer)
25 (:import java.awt.image.BufferedImage)
26 (:import com.jme3.renderer.ViewPort)
27 (:import com.jme3.math.ColorRGBA))
29 (defn scene-processor
30 "Create a SceneProcessor object which wraps a vision processing
31 continuation function. The SceneProcessor will take care of
32 converting the rendered frame to a BufferedImage and passing that
33 BufferedImage to the continuation. The continuation should be a
34 function that takes a BufferedImage."
35 [continuation]
36 (let [byte-buffer (atom nil)
37 renderer (atom nil)
38 image (atom nil)]
39 (proxy [SceneProcessor] []
40 (initialize
41 [renderManager viewPort]
42 (let [cam (.getCamera viewPort)
43 width (.getWidth cam)
44 height (.getHeight cam)]
45 (reset! renderer (.getRenderer renderManager))
46 (reset! byte-buffer
47 (BufferUtils/createByteBuffer
48 (* width height 4)))
49 (reset! image (BufferedImage.
50 width height
51 BufferedImage/TYPE_4BYTE_ABGR))))
52 (isInitialized [] (not (nil? @byte-buffer)))
53 (reshape [_ _ _])
54 (preFrame [_])
55 (postQueue [_])
56 (postFrame
57 [#^FrameBuffer fb]
58 (.clear @byte-buffer)
59 (.readFrameBuffer @renderer fb @byte-buffer)
60 (Screenshots/convertScreenShot @byte-buffer @image)
61 (continuation @image))
62 (cleanup []))))
64 (defn add-eye
65 "Add an eye to the world, calling continuation on every frame
66 produced."
67 [world camera continuation]
68 (let [width (.getWidth camera)
69 height (.getHeight camera)
70 render-manager (.getRenderManager world)
71 viewport (.createMainView render-manager "eye-view" camera)]
72 (doto viewport
73 (.setClearFlags true true true)
74 (.setBackgroundColor ColorRGBA/Gray)
75 (.addProcessor (scene-processor continuation))
76 (.attachScene (.getRootNode world)))))
77 #+end_src
79 Note the use of continuation passing style for connecting the eye to a
80 function to process the output. You can create any number of eyes, and
81 each of them will see the world from their own =Camera=. Once every
82 frame, the rendered image is copied to a =BufferedImage=, and that
83 data is sent off to the continuation function. Moving the =Camera=
84 which was used to create the eye will change what the eye sees.
86 * Example
88 #+name: test-vision
89 #+begin_src clojure
90 (ns cortex.test.vision
91 (:use (cortex world util vision))
92 (:import java.awt.image.BufferedImage)
93 (:import javax.swing.JPanel)
94 (:import javax.swing.SwingUtilities)
95 (:import java.awt.Dimension)
96 (:import javax.swing.JFrame)
97 (:import com.jme3.math.ColorRGBA)
98 (:import com.jme3.scene.Node)
99 (:import com.jme3.math.Vector3f))
101 (defn view-image
102 "Initailizes a JPanel on which you may draw a BufferedImage of the
103 given width and height. Returns a function that accepts a
104 BufferedImage and draws it to the JPanel."
105 [width height]
106 (let [image
107 (atom
108 (BufferedImage. width height BufferedImage/TYPE_4BYTE_ABGR))
109 panel
110 (proxy [JPanel] []
111 (paint
112 [graphics]
113 (proxy-super paintComponent graphics)
114 (.drawImage graphics @image 0 0 nil)))]
115 (SwingUtilities/invokeLater
116 (fn []
117 (.setPreferredSize panel (Dimension. width height))
118 (doto (JFrame. "Eye Camera!")
119 (-> (.getContentPane) (.add panel))
120 (.pack)
121 (.setLocationRelativeTo nil)
122 (.setResizable false)
123 (.setVisible true))))
124 (fn [#^BufferedImage i]
125 (reset! image i)
126 (.repaint panel))))
128 (defn test-two-eyes
129 "Testing vision:
130 Tests the vision system by creating two views of the same rotating
131 object from different angles and displaying both of those views in
132 JFrames.
134 You should see a rotating cube, and two windows,
135 each displaying a different view of the cube."
136 []
137 (let [candy
138 (box 1 1 1 :physical? false :color ColorRGBA/Blue)]
139 (world (doto (Node.)
140 (.attachChild candy))
141 {}
142 (fn [world]
143 (let [cam (.clone (.getCamera world))
144 width (.getWidth cam)
145 height (.getHeight cam)]
146 (add-eye world cam (view-image width height))
147 (add-eye world
148 (doto (.clone cam)
149 (.setLocation (Vector3f. -10 0 0))
150 (.lookAt Vector3f/ZERO Vector3f/UNIT_Y))
151 (view-image width height))
152 ;; This is here to restore the main view
153 ;; after the other views have completed processing
154 (add-eye world (.getCamera world) no-op)))
155 (fn [world tpf]
156 (.rotate candy (* tpf 0.2) 0 0)))))
157 #+end_src
159 The example code will create two videos of the same rotating object
160 from different angles. It can be used both for stereoscopic vision
161 simulation or for simulating multiple creatures, each with their own
162 sense of vision.
164 - As a neat bonus, this idea behind simulated vision also enables one
165 to [[../../cortex/html/capture-video.html][capture live video feeds from jMonkeyEngine]].
168 * COMMENT code generation
169 #+begin_src clojure :tangle ../src/cortex/vision.clj
170 <<eyes>>
171 #+end_src
173 #+begin_src clojure :tangle ../src/cortex/test/vision.clj
174 <<test-vision>>
175 #+end_src