Mercurial > cortex
comparison org/vision.org @ 168:1c8e9d389ea4
renamed eyes.org to vision.org
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sat, 04 Feb 2012 04:08:08 -0700 |
parents | org/eyes.org@9e6a30b8c99a |
children | 94b79c191fc7 |
comparison
equal
deleted
inserted
replaced
167:9e6a30b8c99a | 168:1c8e9d389ea4 |
---|---|
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 | |
9 | |
10 * Vision | |
11 | |
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 | |
15 Here's how vision will work. | |
16 | |
17 Make the continuation in scene-processor take FrameBuffer, | |
18 byte-buffer, BufferedImage already sized to the correct | |
19 dimensions. the continuation will decide wether to "mix" them | |
20 into the BufferedImage, lazily ignore them, or mix them halfway | |
21 and call c/graphics card routines. | |
22 | |
23 (vision creature) will take an optional :skip argument which will | |
24 inform the continuations in scene processor to skip the given | |
25 number of cycles 0 means that no cycles will be skipped. | |
26 | |
27 (vision creature) will return [init-functions sensor-functions]. | |
28 The init-functions are each single-arg functions that take the | |
29 world and register the cameras and must each be called before the | |
30 corresponding sensor-functions. Each init-function returns the | |
31 viewport for that eye which can be manipulated, saved, etc. Each | |
32 sensor-function is a thunk and will return data in the same | |
33 format as the tactile-sensor functions the structure is | |
34 [topology, sensor-data]. Internally, these sensor-functions | |
35 maintain a reference to sensor-data which is periodically updated | |
36 by the continuation function established by its init-function. | |
37 They can be queried every cycle, but their information may not | |
38 necessairly be different every cycle. | |
39 | |
40 Each eye in the creature in blender will work the same way as | |
41 joints -- a zero dimensional object with no geometry whose local | |
42 coordinate system determines the orientation of the resulting | |
43 eye. All eyes will have a parent named "eyes" just as all joints | |
44 have a parent named "joints". The resulting camera will be a | |
45 ChaseCamera or a CameraNode bound to the geo that is closest to | |
46 the eye marker. The eye marker will contain the metadata for the | |
47 eye, and will be moved by it's bound geometry. The dimensions of | |
48 the eye's camera are equal to the dimensions of the eye's "UV" | |
49 map. | |
50 | |
51 #+name: eyes | |
52 #+begin_src clojure | |
53 (ns cortex.vision | |
54 "Simulate the sense of vision in jMonkeyEngine3. Enables multiple | |
55 eyes from different positions to observe the same world, and pass | |
56 the observed data to any arbitray function." | |
57 {:author "Robert McIntyre"} | |
58 (:use (cortex world sense util)) | |
59 (:use clojure.contrib.def) | |
60 (:import com.jme3.post.SceneProcessor) | |
61 (:import (com.jme3.util BufferUtils Screenshots)) | |
62 (:import java.nio.ByteBuffer) | |
63 (:import java.awt.image.BufferedImage) | |
64 (:import com.jme3.renderer.ViewPort) | |
65 (:import com.jme3.math.ColorRGBA) | |
66 (:import com.jme3.renderer.Renderer) | |
67 (:import com.jme3.scene.Node)) | |
68 | |
69 (cortex.import/mega-import-jme3) | |
70 | |
71 | |
72 (defn vision-pipeline | |
73 "Create a SceneProcessor object which wraps a vision processing | |
74 continuation function. The continuation is a function that takes | |
75 [#^Renderer r #^FrameBuffer fb #^ByteBuffer b #^BufferedImage bi], | |
76 each of which has already been appropiately sized." | |
77 [continuation] | |
78 (let [byte-buffer (atom nil) | |
79 renderer (atom nil) | |
80 image (atom nil)] | |
81 (proxy [SceneProcessor] [] | |
82 (initialize | |
83 [renderManager viewPort] | |
84 (let [cam (.getCamera viewPort) | |
85 width (.getWidth cam) | |
86 height (.getHeight cam)] | |
87 (reset! renderer (.getRenderer renderManager)) | |
88 (reset! byte-buffer | |
89 (BufferUtils/createByteBuffer | |
90 (* width height 4))) | |
91 (reset! image (BufferedImage. | |
92 width height | |
93 BufferedImage/TYPE_4BYTE_ABGR)))) | |
94 (isInitialized [] (not (nil? @byte-buffer))) | |
95 (reshape [_ _ _]) | |
96 (preFrame [_]) | |
97 (postQueue [_]) | |
98 (postFrame | |
99 [#^FrameBuffer fb] | |
100 (.clear @byte-buffer) | |
101 (continuation @renderer fb @byte-buffer @image)) | |
102 (cleanup [])))) | |
103 | |
104 (defn frameBuffer->byteBuffer! | |
105 "Transfer the data in the graphics card (Renderer, FrameBuffer) to | |
106 the CPU (ByteBuffer)." | |
107 [#^Renderer r #^FrameBuffer fb #^ByteBuffer bb] | |
108 (.readFrameBuffer r fb bb) bb) | |
109 | |
110 (defn byteBuffer->bufferedImage! | |
111 "Convert the C-style BGRA image data in the ByteBuffer bb to the AWT | |
112 style ABGR image data and place it in BufferedImage bi." | |
113 [#^ByteBuffer bb #^BufferedImage bi] | |
114 (Screenshots/convertScreenShot bb bi) bi) | |
115 | |
116 (defn BufferedImage! | |
117 "Continuation which will grab the buffered image from the materials | |
118 provided by (vision-pipeline)." | |
119 [#^Renderer r #^FrameBuffer fb #^ByteBuffer bb #^BufferedImage bi] | |
120 (byteBuffer->bufferedImage! | |
121 (frameBuffer->byteBuffer! r fb bb) bi)) | |
122 | |
123 (defn add-eye! | |
124 "Add an eye to the world, calling continuation on every frame | |
125 produced." | |
126 [#^Application world camera continuation] | |
127 (let [width (.getWidth camera) | |
128 height (.getHeight camera) | |
129 render-manager (.getRenderManager world) | |
130 viewport (.createMainView render-manager "eye-view" camera)] | |
131 (doto viewport | |
132 (.setClearFlags true true true) | |
133 (.setBackgroundColor ColorRGBA/Black) | |
134 (.addProcessor (vision-pipeline continuation)) | |
135 (.attachScene (.getRootNode world))))) | |
136 | |
137 (defn retina-sensor-image | |
138 "Return a map of pixel selection functions to BufferedImages | |
139 describing the distribution of light-sensitive components on this | |
140 geometry's surface. Each function creates an integer from the rgb | |
141 values found in the pixel. :red, :green, :blue, :gray are already | |
142 defined as extracting the red green blue and average components | |
143 respectively." | |
144 [#^Spatial eye] | |
145 (if-let [eye-map (meta-data eye "eye")] | |
146 (map-vals | |
147 load-image | |
148 (eval (read-string eye-map))))) | |
149 | |
150 (defn eye-dimensions | |
151 "returns the width and height specified in the metadata of the eye" | |
152 [#^Spatial eye] | |
153 (let [dimensions | |
154 (map #(vector (.getWidth %) (.getHeight %)) | |
155 (vals (retina-sensor-image eye)))] | |
156 [(apply max (map first dimensions)) | |
157 (apply max (map second dimensions))])) | |
158 | |
159 (defvar | |
160 ^{:arglists '([creature])} | |
161 eyes | |
162 (sense-nodes "eyes") | |
163 "Return the children of the creature's \"eyes\" node.") | |
164 | |
165 (defn attach-eye | |
166 "Attach a Camera to the appropiate area and return the Camera." | |
167 [#^Node creature #^Spatial eye] | |
168 (let [target (closest-node creature eye) | |
169 [cam-width cam-height] (eye-dimensions eye) | |
170 cam (Camera. cam-width cam-height)] | |
171 (.setLocation cam (.getWorldTranslation eye)) | |
172 (.setRotation cam (.getWorldRotation eye)) | |
173 (.setFrustumPerspective | |
174 cam 45 (/ (.getWidth cam) (.getHeight cam)) | |
175 1 1000) | |
176 (bind-sense target cam) | |
177 cam)) | |
178 | |
179 (def presets | |
180 {:all 0xFFFFFF | |
181 :red 0xFF0000 | |
182 :blue 0x0000FF | |
183 :green 0x00FF00}) | |
184 | |
185 (defn enable-vision | |
186 "return [init-function sensor-functions] for a particular eye" | |
187 [#^Node creature #^Spatial eye & {skip :skip :or {skip 0}}] | |
188 (let [retinal-map (retina-sensor-image eye) | |
189 camera (attach-eye creature eye) | |
190 vision-image | |
191 (atom | |
192 (BufferedImage. (.getWidth camera) | |
193 (.getHeight camera) | |
194 BufferedImage/TYPE_BYTE_BINARY))] | |
195 [(fn [world] | |
196 (add-eye! | |
197 world camera | |
198 (let [counter (atom 0)] | |
199 (fn [r fb bb bi] | |
200 (if (zero? (rem (swap! counter inc) (inc skip))) | |
201 (reset! vision-image (BufferedImage! r fb bb bi))))))) | |
202 (vec | |
203 (map | |
204 (fn [[key image]] | |
205 (let [whites (white-coordinates image) | |
206 topology (vec (collapse whites)) | |
207 mask (presets key)] | |
208 (fn [] | |
209 (vector | |
210 topology | |
211 (vec | |
212 (for [[x y] whites] | |
213 (bit-and | |
214 mask (.getRGB @vision-image x y)))))))) | |
215 retinal-map))])) | |
216 | |
217 (defn vision | |
218 [#^Node creature & {skip :skip :or {skip 0}}] | |
219 (reduce | |
220 (fn [[init-a senses-a] | |
221 [init-b senses-b]] | |
222 [(conj init-a init-b) | |
223 (into senses-a senses-b)]) | |
224 [[][]] | |
225 (for [eye (eyes creature)] | |
226 (enable-vision creature eye)))) | |
227 | |
228 | |
229 #+end_src | |
230 | |
231 | |
232 Note the use of continuation passing style for connecting the eye to a | |
233 function to process the output. You can create any number of eyes, and | |
234 each of them will see the world from their own =Camera=. Once every | |
235 frame, the rendered image is copied to a =BufferedImage=, and that | |
236 data is sent off to the continuation function. Moving the =Camera= | |
237 which was used to create the eye will change what the eye sees. | |
238 | |
239 * Example | |
240 | |
241 #+name: test-vision | |
242 #+begin_src clojure | |
243 (ns cortex.test.vision | |
244 (:use (cortex world util vision)) | |
245 (:import java.awt.image.BufferedImage) | |
246 (:import javax.swing.JPanel) | |
247 (:import javax.swing.SwingUtilities) | |
248 (:import java.awt.Dimension) | |
249 (:import javax.swing.JFrame) | |
250 (:import com.jme3.math.ColorRGBA) | |
251 (:import com.jme3.scene.Node) | |
252 (:import com.jme3.math.Vector3f)) | |
253 | |
254 (defn test-two-eyes | |
255 "Testing vision: | |
256 Tests the vision system by creating two views of the same rotating | |
257 object from different angles and displaying both of those views in | |
258 JFrames. | |
259 | |
260 You should see a rotating cube, and two windows, | |
261 each displaying a different view of the cube." | |
262 [] | |
263 (let [candy | |
264 (box 1 1 1 :physical? false :color ColorRGBA/Blue)] | |
265 (world | |
266 (doto (Node.) | |
267 (.attachChild candy)) | |
268 {} | |
269 (fn [world] | |
270 (let [cam (.clone (.getCamera world)) | |
271 width (.getWidth cam) | |
272 height (.getHeight cam)] | |
273 (add-eye! world cam | |
274 ;;no-op | |
275 (comp (view-image) BufferedImage!) | |
276 ) | |
277 (add-eye! world | |
278 (doto (.clone cam) | |
279 (.setLocation (Vector3f. -10 0 0)) | |
280 (.lookAt Vector3f/ZERO Vector3f/UNIT_Y)) | |
281 ;;no-op | |
282 (comp (view-image) BufferedImage!)) | |
283 ;; This is here to restore the main view | |
284 ;; after the other views have completed processing | |
285 (add-eye! world (.getCamera world) no-op))) | |
286 (fn [world tpf] | |
287 (.rotate candy (* tpf 0.2) 0 0))))) | |
288 #+end_src | |
289 | |
290 #+results: test-vision | |
291 : #'cortex.test.vision/test-two-eyes | |
292 | |
293 The example code will create two videos of the same rotating object | |
294 from different angles. It can be used both for stereoscopic vision | |
295 simulation or for simulating multiple creatures, each with their own | |
296 sense of vision. | |
297 | |
298 - As a neat bonus, this idea behind simulated vision also enables one | |
299 to [[../../cortex/html/capture-video.html][capture live video feeds from jMonkeyEngine]]. | |
300 | |
301 | |
302 * COMMENT code generation | |
303 #+begin_src clojure :tangle ../src/cortex/vision.clj | |
304 <<eyes>> | |
305 #+end_src | |
306 | |
307 #+begin_src clojure :tangle ../src/cortex/test/vision.clj | |
308 <<test-vision>> | |
309 #+end_src |