Mercurial > cortex
diff 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 |
line wrap: on
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/org/vision.org Sat Feb 04 04:08:08 2012 -0700 1.3 @@ -0,0 +1,309 @@ 1.4 +#+title: Simulated Sense of Sight 1.5 +#+author: Robert McIntyre 1.6 +#+email: rlm@mit.edu 1.7 +#+description: Simulated sight for AI research using JMonkeyEngine3 and clojure 1.8 +#+keywords: computer vision, jMonkeyEngine3, clojure 1.9 +#+SETUPFILE: ../../aurellem/org/setup.org 1.10 +#+INCLUDE: ../../aurellem/org/level-0.org 1.11 +#+babel: :mkdirp yes :noweb yes :exports both 1.12 + 1.13 +* Vision 1.14 + 1.15 +I want to make creatures with eyes. Each eye can be independely moved 1.16 +and should see its own version of the world depending on where it is. 1.17 + 1.18 +Here's how vision will work. 1.19 + 1.20 +Make the continuation in scene-processor take FrameBuffer, 1.21 +byte-buffer, BufferedImage already sized to the correct 1.22 +dimensions. the continuation will decide wether to "mix" them 1.23 +into the BufferedImage, lazily ignore them, or mix them halfway 1.24 +and call c/graphics card routines. 1.25 + 1.26 +(vision creature) will take an optional :skip argument which will 1.27 +inform the continuations in scene processor to skip the given 1.28 +number of cycles 0 means that no cycles will be skipped. 1.29 + 1.30 +(vision creature) will return [init-functions sensor-functions]. 1.31 +The init-functions are each single-arg functions that take the 1.32 +world and register the cameras and must each be called before the 1.33 +corresponding sensor-functions. Each init-function returns the 1.34 +viewport for that eye which can be manipulated, saved, etc. Each 1.35 +sensor-function is a thunk and will return data in the same 1.36 +format as the tactile-sensor functions the structure is 1.37 +[topology, sensor-data]. Internally, these sensor-functions 1.38 +maintain a reference to sensor-data which is periodically updated 1.39 +by the continuation function established by its init-function. 1.40 +They can be queried every cycle, but their information may not 1.41 +necessairly be different every cycle. 1.42 + 1.43 +Each eye in the creature in blender will work the same way as 1.44 +joints -- a zero dimensional object with no geometry whose local 1.45 +coordinate system determines the orientation of the resulting 1.46 +eye. All eyes will have a parent named "eyes" just as all joints 1.47 +have a parent named "joints". The resulting camera will be a 1.48 +ChaseCamera or a CameraNode bound to the geo that is closest to 1.49 +the eye marker. The eye marker will contain the metadata for the 1.50 +eye, and will be moved by it's bound geometry. The dimensions of 1.51 +the eye's camera are equal to the dimensions of the eye's "UV" 1.52 +map. 1.53 + 1.54 +#+name: eyes 1.55 +#+begin_src clojure 1.56 +(ns cortex.vision 1.57 + "Simulate the sense of vision in jMonkeyEngine3. Enables multiple 1.58 + eyes from different positions to observe the same world, and pass 1.59 + the observed data to any arbitray function." 1.60 + {:author "Robert McIntyre"} 1.61 + (:use (cortex world sense util)) 1.62 + (:use clojure.contrib.def) 1.63 + (:import com.jme3.post.SceneProcessor) 1.64 + (:import (com.jme3.util BufferUtils Screenshots)) 1.65 + (:import java.nio.ByteBuffer) 1.66 + (:import java.awt.image.BufferedImage) 1.67 + (:import com.jme3.renderer.ViewPort) 1.68 + (:import com.jme3.math.ColorRGBA) 1.69 + (:import com.jme3.renderer.Renderer) 1.70 + (:import com.jme3.scene.Node)) 1.71 + 1.72 +(cortex.import/mega-import-jme3) 1.73 + 1.74 + 1.75 +(defn vision-pipeline 1.76 + "Create a SceneProcessor object which wraps a vision processing 1.77 + continuation function. The continuation is a function that takes 1.78 + [#^Renderer r #^FrameBuffer fb #^ByteBuffer b #^BufferedImage bi], 1.79 + each of which has already been appropiately sized." 1.80 + [continuation] 1.81 + (let [byte-buffer (atom nil) 1.82 + renderer (atom nil) 1.83 + image (atom nil)] 1.84 + (proxy [SceneProcessor] [] 1.85 + (initialize 1.86 + [renderManager viewPort] 1.87 + (let [cam (.getCamera viewPort) 1.88 + width (.getWidth cam) 1.89 + height (.getHeight cam)] 1.90 + (reset! renderer (.getRenderer renderManager)) 1.91 + (reset! byte-buffer 1.92 + (BufferUtils/createByteBuffer 1.93 + (* width height 4))) 1.94 + (reset! image (BufferedImage. 1.95 + width height 1.96 + BufferedImage/TYPE_4BYTE_ABGR)))) 1.97 + (isInitialized [] (not (nil? @byte-buffer))) 1.98 + (reshape [_ _ _]) 1.99 + (preFrame [_]) 1.100 + (postQueue [_]) 1.101 + (postFrame 1.102 + [#^FrameBuffer fb] 1.103 + (.clear @byte-buffer) 1.104 + (continuation @renderer fb @byte-buffer @image)) 1.105 + (cleanup [])))) 1.106 + 1.107 +(defn frameBuffer->byteBuffer! 1.108 + "Transfer the data in the graphics card (Renderer, FrameBuffer) to 1.109 + the CPU (ByteBuffer)." 1.110 + [#^Renderer r #^FrameBuffer fb #^ByteBuffer bb] 1.111 + (.readFrameBuffer r fb bb) bb) 1.112 + 1.113 +(defn byteBuffer->bufferedImage! 1.114 + "Convert the C-style BGRA image data in the ByteBuffer bb to the AWT 1.115 + style ABGR image data and place it in BufferedImage bi." 1.116 + [#^ByteBuffer bb #^BufferedImage bi] 1.117 + (Screenshots/convertScreenShot bb bi) bi) 1.118 + 1.119 +(defn BufferedImage! 1.120 + "Continuation which will grab the buffered image from the materials 1.121 + provided by (vision-pipeline)." 1.122 + [#^Renderer r #^FrameBuffer fb #^ByteBuffer bb #^BufferedImage bi] 1.123 + (byteBuffer->bufferedImage! 1.124 + (frameBuffer->byteBuffer! r fb bb) bi)) 1.125 + 1.126 +(defn add-eye! 1.127 + "Add an eye to the world, calling continuation on every frame 1.128 + produced." 1.129 + [#^Application world camera continuation] 1.130 + (let [width (.getWidth camera) 1.131 + height (.getHeight camera) 1.132 + render-manager (.getRenderManager world) 1.133 + viewport (.createMainView render-manager "eye-view" camera)] 1.134 + (doto viewport 1.135 + (.setClearFlags true true true) 1.136 + (.setBackgroundColor ColorRGBA/Black) 1.137 + (.addProcessor (vision-pipeline continuation)) 1.138 + (.attachScene (.getRootNode world))))) 1.139 + 1.140 +(defn retina-sensor-image 1.141 + "Return a map of pixel selection functions to BufferedImages 1.142 + describing the distribution of light-sensitive components on this 1.143 + geometry's surface. Each function creates an integer from the rgb 1.144 + values found in the pixel. :red, :green, :blue, :gray are already 1.145 + defined as extracting the red green blue and average components 1.146 + respectively." 1.147 + [#^Spatial eye] 1.148 + (if-let [eye-map (meta-data eye "eye")] 1.149 + (map-vals 1.150 + load-image 1.151 + (eval (read-string eye-map))))) 1.152 + 1.153 +(defn eye-dimensions 1.154 + "returns the width and height specified in the metadata of the eye" 1.155 + [#^Spatial eye] 1.156 + (let [dimensions 1.157 + (map #(vector (.getWidth %) (.getHeight %)) 1.158 + (vals (retina-sensor-image eye)))] 1.159 + [(apply max (map first dimensions)) 1.160 + (apply max (map second dimensions))])) 1.161 + 1.162 +(defvar 1.163 + ^{:arglists '([creature])} 1.164 + eyes 1.165 + (sense-nodes "eyes") 1.166 + "Return the children of the creature's \"eyes\" node.") 1.167 + 1.168 +(defn attach-eye 1.169 + "Attach a Camera to the appropiate area and return the Camera." 1.170 + [#^Node creature #^Spatial eye] 1.171 + (let [target (closest-node creature eye) 1.172 + [cam-width cam-height] (eye-dimensions eye) 1.173 + cam (Camera. cam-width cam-height)] 1.174 + (.setLocation cam (.getWorldTranslation eye)) 1.175 + (.setRotation cam (.getWorldRotation eye)) 1.176 + (.setFrustumPerspective 1.177 + cam 45 (/ (.getWidth cam) (.getHeight cam)) 1.178 + 1 1000) 1.179 + (bind-sense target cam) 1.180 + cam)) 1.181 + 1.182 +(def presets 1.183 + {:all 0xFFFFFF 1.184 + :red 0xFF0000 1.185 + :blue 0x0000FF 1.186 + :green 0x00FF00}) 1.187 + 1.188 +(defn enable-vision 1.189 + "return [init-function sensor-functions] for a particular eye" 1.190 + [#^Node creature #^Spatial eye & {skip :skip :or {skip 0}}] 1.191 + (let [retinal-map (retina-sensor-image eye) 1.192 + camera (attach-eye creature eye) 1.193 + vision-image 1.194 + (atom 1.195 + (BufferedImage. (.getWidth camera) 1.196 + (.getHeight camera) 1.197 + BufferedImage/TYPE_BYTE_BINARY))] 1.198 + [(fn [world] 1.199 + (add-eye! 1.200 + world camera 1.201 + (let [counter (atom 0)] 1.202 + (fn [r fb bb bi] 1.203 + (if (zero? (rem (swap! counter inc) (inc skip))) 1.204 + (reset! vision-image (BufferedImage! r fb bb bi))))))) 1.205 + (vec 1.206 + (map 1.207 + (fn [[key image]] 1.208 + (let [whites (white-coordinates image) 1.209 + topology (vec (collapse whites)) 1.210 + mask (presets key)] 1.211 + (fn [] 1.212 + (vector 1.213 + topology 1.214 + (vec 1.215 + (for [[x y] whites] 1.216 + (bit-and 1.217 + mask (.getRGB @vision-image x y)))))))) 1.218 + retinal-map))])) 1.219 + 1.220 +(defn vision 1.221 + [#^Node creature & {skip :skip :or {skip 0}}] 1.222 + (reduce 1.223 + (fn [[init-a senses-a] 1.224 + [init-b senses-b]] 1.225 + [(conj init-a init-b) 1.226 + (into senses-a senses-b)]) 1.227 + [[][]] 1.228 + (for [eye (eyes creature)] 1.229 + (enable-vision creature eye)))) 1.230 + 1.231 + 1.232 +#+end_src 1.233 + 1.234 + 1.235 +Note the use of continuation passing style for connecting the eye to a 1.236 +function to process the output. You can create any number of eyes, and 1.237 +each of them will see the world from their own =Camera=. Once every 1.238 +frame, the rendered image is copied to a =BufferedImage=, and that 1.239 +data is sent off to the continuation function. Moving the =Camera= 1.240 +which was used to create the eye will change what the eye sees. 1.241 + 1.242 +* Example 1.243 + 1.244 +#+name: test-vision 1.245 +#+begin_src clojure 1.246 +(ns cortex.test.vision 1.247 + (:use (cortex world util vision)) 1.248 + (:import java.awt.image.BufferedImage) 1.249 + (:import javax.swing.JPanel) 1.250 + (:import javax.swing.SwingUtilities) 1.251 + (:import java.awt.Dimension) 1.252 + (:import javax.swing.JFrame) 1.253 + (:import com.jme3.math.ColorRGBA) 1.254 + (:import com.jme3.scene.Node) 1.255 + (:import com.jme3.math.Vector3f)) 1.256 + 1.257 +(defn test-two-eyes 1.258 + "Testing vision: 1.259 + Tests the vision system by creating two views of the same rotating 1.260 + object from different angles and displaying both of those views in 1.261 + JFrames. 1.262 + 1.263 + You should see a rotating cube, and two windows, 1.264 + each displaying a different view of the cube." 1.265 + [] 1.266 + (let [candy 1.267 + (box 1 1 1 :physical? false :color ColorRGBA/Blue)] 1.268 + (world 1.269 + (doto (Node.) 1.270 + (.attachChild candy)) 1.271 + {} 1.272 + (fn [world] 1.273 + (let [cam (.clone (.getCamera world)) 1.274 + width (.getWidth cam) 1.275 + height (.getHeight cam)] 1.276 + (add-eye! world cam 1.277 + ;;no-op 1.278 + (comp (view-image) BufferedImage!) 1.279 + ) 1.280 + (add-eye! world 1.281 + (doto (.clone cam) 1.282 + (.setLocation (Vector3f. -10 0 0)) 1.283 + (.lookAt Vector3f/ZERO Vector3f/UNIT_Y)) 1.284 + ;;no-op 1.285 + (comp (view-image) BufferedImage!)) 1.286 + ;; This is here to restore the main view 1.287 + ;; after the other views have completed processing 1.288 + (add-eye! world (.getCamera world) no-op))) 1.289 + (fn [world tpf] 1.290 + (.rotate candy (* tpf 0.2) 0 0))))) 1.291 +#+end_src 1.292 + 1.293 +#+results: test-vision 1.294 +: #'cortex.test.vision/test-two-eyes 1.295 + 1.296 +The example code will create two videos of the same rotating object 1.297 +from different angles. It can be used both for stereoscopic vision 1.298 +simulation or for simulating multiple creatures, each with their own 1.299 +sense of vision. 1.300 + 1.301 +- As a neat bonus, this idea behind simulated vision also enables one 1.302 + to [[../../cortex/html/capture-video.html][capture live video feeds from jMonkeyEngine]]. 1.303 + 1.304 + 1.305 +* COMMENT code generation 1.306 +#+begin_src clojure :tangle ../src/cortex/vision.clj 1.307 +<<eyes>> 1.308 +#+end_src 1.309 + 1.310 +#+begin_src clojure :tangle ../src/cortex/test/vision.clj 1.311 +<<test-vision>> 1.312 +#+end_src