Mercurial > cortex
diff org/vision.org @ 215:f283c62bd212
fixed long standing problem with orientation of eyes in blender, fleshed out text in vision.org
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Fri, 10 Feb 2012 02:19:24 -0700 |
parents | 01d3e9855ef9 |
children | f5ea63245b3b |
line wrap: on
line diff
1.1 --- a/org/vision.org Thu Feb 09 09:04:17 2012 -0700 1.2 +++ b/org/vision.org Fri Feb 10 02:19:24 2012 -0700 1.3 @@ -160,21 +160,50 @@ 1.4 1.5 #+name: add-eye 1.6 #+begin_src clojure 1.7 +(in-ns 'cortex.vision) 1.8 + 1.9 +(import com.jme3.math.Vector3f) 1.10 + 1.11 +(def blender-rotation-correction 1.12 + (doto (Quaternion.) 1.13 + (.fromRotationMatrix 1.14 + (doto (Matrix3f.) 1.15 + (.setColumn 0 1.16 + (Vector3f. 1 0 0)) 1.17 + (.setColumn 1 1.18 + (Vector3f. 0 -1 0)) 1.19 + (.setColumn 2 1.20 + (Vector3f. 0 0 -1))) 1.21 + 1.22 + (doto (Matrix3f.) 1.23 + (.setColumn 0 1.24 + (Vector3f. 1.25 + 1.26 + 1.27 (defn add-eye! 1.28 "Create a Camera centered on the current position of 'eye which 1.29 follows the closest physical node in 'creature and sends visual 1.30 - data to 'continuation." 1.31 + data to 'continuation. The camera will point in the X direction and 1.32 + use the Z vector as up as determined by the rotation of these 1.33 + vectors in blender coordinate space. Use XZY rotation for the node 1.34 + in blender." 1.35 [#^Node creature #^Spatial eye] 1.36 (let [target (closest-node creature eye) 1.37 [cam-width cam-height] (eye-dimensions eye) 1.38 - cam (Camera. cam-width cam-height)] 1.39 + cam (Camera. cam-width cam-height) 1.40 + rot (.getWorldRotation eye)] 1.41 (.setLocation cam (.getWorldTranslation eye)) 1.42 - (.setRotation cam (.getWorldRotation eye)) 1.43 + (.lookAtDirection cam (.mult rot Vector3f/UNIT_X) 1.44 + ;; this part is consistent with using Z in 1.45 + ;; blender as the UP vector. 1.46 + (.mult rot Vector3f/UNIT_Y)) 1.47 + 1.48 + (println-repl "eye unit-z ->" (.mult rot Vector3f/UNIT_Z)) 1.49 + (println-repl "eye unit-y ->" (.mult rot Vector3f/UNIT_Y)) 1.50 + (println-repl "eye unit-x ->" (.mult rot Vector3f/UNIT_X)) 1.51 (.setFrustumPerspective 1.52 - cam 45 (/ (.getWidth cam) (.getHeight cam)) 1.53 - 1 1000) 1.54 - (bind-sense target cam) 1.55 - cam)) 1.56 + cam 45 (/ (.getWidth cam) (.getHeight cam)) 1 1000) 1.57 + (bind-sense target cam) cam)) 1.58 #+end_src 1.59 1.60 Here, the camera is created based on metadata on the eye-node and 1.61 @@ -286,7 +315,8 @@ 1.62 "Return the children of the creature's \"eyes\" node.") 1.63 #+end_src 1.64 1.65 -Then, 1.66 +Then, add the camera created by =(add-eye!)= to the simulation by 1.67 +creating a new viewport. 1.68 1.69 #+begin_src clojure 1.70 (defn add-camera! 1.71 @@ -302,12 +332,26 @@ 1.72 (.setBackgroundColor ColorRGBA/Black) 1.73 (.addProcessor (vision-pipeline continuation)) 1.74 (.attachScene (.getRootNode world))))) 1.75 +#+end_src 1.76 1.77 1.78 +The continuation function registers the viewport with the simulation 1.79 +the first time it is called, and uses the CPU to extract the 1.80 +appropriate pixels from the rendered image and weight them by each 1.81 +sensors sensitivity. I have the option to do this filtering in native 1.82 +code for a slight gain in speed. I could also do it in the GPU for a 1.83 +massive gain in speed. =(vision-kernel)= generates a list of such 1.84 +continuation functions, one for each channel of the eye. 1.85 1.86 +#+begin_src clojure 1.87 +(in-ns 'cortex.vision) 1.88 1.89 +(defrecord attached-viewport [vision-fn viewport-fn] 1.90 + clojure.lang.IFn 1.91 + (invoke [this world] (vision-fn world)) 1.92 + (applyTo [this args] (apply vision-fn args))) 1.93 1.94 -(defn vision-fn 1.95 +(defn vision-kernel 1.96 "Returns a list of functions, each of which will return a color 1.97 channel's worth of visual information when called inside a running 1.98 simulation." 1.99 @@ -335,20 +379,47 @@ 1.100 (let [whites (white-coordinates image) 1.101 topology (vec (collapse whites)) 1.102 mask (color-channel-presets key key)] 1.103 - (fn [world] 1.104 - (register-eye! world) 1.105 - (vector 1.106 - topology 1.107 - (vec 1.108 - (for [[x y] whites] 1.109 - (bit-and 1.110 - mask (.getRGB @vision-image x y)))))))) 1.111 - retinal-map)))) 1.112 + (attached-viewport. 1.113 + (fn [world] 1.114 + (register-eye! world) 1.115 + (vector 1.116 + topology 1.117 + (vec 1.118 + (for [[x y] whites] 1.119 + (bit-and 1.120 + mask (.getRGB @vision-image x y)))))) 1.121 + register-eye!))) 1.122 + retinal-map)))) 1.123 1.124 +(defn gen-fix-display 1.125 + "Create a function to call to restore a simulation's display when it 1.126 + is disrupted by a Viewport." 1.127 + [] 1.128 + (runonce 1.129 + (fn [world] 1.130 + (add-camera! world (.getCamera world) no-op)))) 1.131 1.132 -;; TODO maybe should add a viewport-manipulation function to 1.133 -;; automatically change viewport settings, attach shadow filters, etc. 1.134 +#+end_src 1.135 1.136 +Note that since each of the functions generated by =(vision-kernel)= 1.137 +shares the same =(register-eye!)= function, the eye will be registered 1.138 +only once the first time any of the functions from the list returned 1.139 +by =(vision-kernel)= is called. Each of the functions returned by 1.140 +=(vision-kernel)= also allows access to the =Viewport= through which 1.141 +it recieves images. 1.142 + 1.143 +The in-game display can be disrupted by all the viewports that the 1.144 +functions greated by =(vision-kernel)= add. This doesn't affect the 1.145 +simulation or the simulated senses, but can be annoying. 1.146 +=(gen-fix-display)= restores the in-simulation display. 1.147 + 1.148 +** Vision! 1.149 + 1.150 +All the hard work has been done, all that remains is to apply 1.151 +=(vision-kernel)= to each eye in the creature and gather the results 1.152 +into one list of functions. 1.153 + 1.154 +#+begin_src clojure 1.155 (defn vision! 1.156 "Returns a function which returns visual sensory data when called 1.157 inside a running simulation" 1.158 @@ -356,8 +427,16 @@ 1.159 (reduce 1.160 concat 1.161 (for [eye (eyes creature)] 1.162 - (vision-fn creature eye)))) 1.163 + (vision-kernel creature eye)))) 1.164 +#+end_src 1.165 1.166 +** Visualization of Vision 1.167 + 1.168 +It's vital to have a visual representation for each sense. Here I use 1.169 +=(view-sense)= to construct a function that will create a display for 1.170 +visual data. 1.171 + 1.172 +#+begin_src clojure 1.173 (defn view-vision 1.174 "Creates a function which accepts a list of visual sensor-data and 1.175 displays each element of the list to the screen." 1.176 @@ -371,31 +450,20 @@ 1.177 (.setRGB image ((coords i) 0) ((coords i) 1) 1.178 (sensor-data i)))) 1.179 image)))) 1.180 - 1.181 #+end_src 1.182 1.183 +* Tests 1.184 1.185 -Note the use of continuation passing style for connecting the eye to a 1.186 -function to process the output. You can create any number of eyes, and 1.187 -each of them will see the world from their own =Camera=. Once every 1.188 -frame, the rendered image is copied to a =BufferedImage=, and that 1.189 -data is sent off to the continuation function. Moving the =Camera= 1.190 -which was used to create the eye will change what the eye sees. 1.191 +** Basic Test 1.192 1.193 -* Example 1.194 +This is a basic test for the vision system. It only tests the 1.195 +vision-pipeline and does not deal with loadig eyes from a blender 1.196 +file. The code creates two videos of the same rotating cube from 1.197 +different angles. 1.198 1.199 -#+name: test-vision 1.200 +#+name: test-1 1.201 #+begin_src clojure 1.202 -(ns cortex.test.vision 1.203 - (:use (cortex world util vision)) 1.204 - (:import java.awt.image.BufferedImage) 1.205 - (:import javax.swing.JPanel) 1.206 - (:import javax.swing.SwingUtilities) 1.207 - (:import java.awt.Dimension) 1.208 - (:import javax.swing.JFrame) 1.209 - (:import com.jme3.math.ColorRGBA) 1.210 - (:import com.jme3.scene.Node) 1.211 - (:import com.jme3.math.Vector3f)) 1.212 +(in-ns 'cortex.test.vision) 1.213 1.214 (defn test-two-eyes 1.215 "Testing vision: 1.216 @@ -417,15 +485,18 @@ 1.217 width (.getWidth cam) 1.218 height (.getHeight cam)] 1.219 (add-camera! world cam 1.220 - ;;no-op 1.221 - (comp (view-image) BufferedImage!) 1.222 - ) 1.223 + (comp 1.224 + (view-image 1.225 + (File. "/home/r/proj/cortex/render/vision/1")) 1.226 + BufferedImage!)) 1.227 (add-camera! world 1.228 (doto (.clone cam) 1.229 (.setLocation (Vector3f. -10 0 0)) 1.230 (.lookAt Vector3f/ZERO Vector3f/UNIT_Y)) 1.231 - ;;no-op 1.232 - (comp (view-image) BufferedImage!)) 1.233 + (comp 1.234 + (view-image 1.235 + (File. "/home/r/proj/cortex/render/vision/2")) 1.236 + BufferedImage!)) 1.237 ;; This is here to restore the main view 1.238 ;; after the other views have completed processing 1.239 (add-camera! world (.getCamera world) no-op))) 1.240 @@ -433,6 +504,98 @@ 1.241 (.rotate candy (* tpf 0.2) 0 0))))) 1.242 #+end_src 1.243 1.244 +#+begin_html 1.245 +<div class="figure"> 1.246 +<video controls="controls" width="755"> 1.247 + <source src="../video/spinning-cube.ogg" type="video/ogg" 1.248 + preload="none" poster="../images/aurellem-1280x480.png" /> 1.249 +</video> 1.250 +<p>A rotating cube viewed from two different perspectives.</p> 1.251 +</div> 1.252 +#+end_html 1.253 + 1.254 +Creating multiple eyes like this can be used for stereoscopic vision 1.255 +simulation in a single creature or for simulating multiple creatures, 1.256 +each with their own sense of vision. 1.257 + 1.258 +** Adding Vision to the Worm 1.259 + 1.260 +To the worm from the last post, we add a new node that describes its 1.261 +eyes. 1.262 + 1.263 +#+attr_html: width=755 1.264 +#+caption: The worm with newly added empty nodes describing a single eye. 1.265 +[[../images/worm-with-eye.png]] 1.266 + 1.267 +The node highlighted in yellow is the root level "eyes" node. It has 1.268 +a single node, highlighted in orange, which describes a single 1.269 +eye. This is the "eye" node. The two nodes which are not highlighted describe the single joint 1.270 +of the worm. 1.271 + 1.272 +The metadata of the eye-node is: 1.273 + 1.274 +#+begin_src clojure :results verbatim :exports both 1.275 +(cortex.sense/meta-data 1.276 + (.getChild 1.277 + (.getChild (cortex.test.body/worm) 1.278 + "eyes") "eye") "eye") 1.279 +#+end_src 1.280 + 1.281 +#+results: 1.282 +: "(let [retina \"Models/test-creature/retina-small.png\"] 1.283 +: {:all retina :red retina :green retina :blue retina})" 1.284 + 1.285 +This is the approximation to the human eye described earlier. 1.286 + 1.287 +#+begin_src clojure 1.288 +(in-ns 'cortex.test.vision) 1.289 + 1.290 +(import com.aurellem.capture.Capture) 1.291 + 1.292 +(defn test-worm-vision [] 1.293 + (let [the-worm (doto (worm)(body!)) 1.294 + vision (vision! the-worm) 1.295 + vision-display (view-vision) 1.296 + fix-display (gen-fix-display) 1.297 + me (sphere 0.5 :color ColorRGBA/Blue :physical? false) 1.298 + x-axis 1.299 + (box 1 0.01 0.01 :physical? false :color ColorRGBA/Red 1.300 + :position (Vector3f. 0 -5 0)) 1.301 + y-axis 1.302 + (box 0.01 1 0.01 :physical? false :color ColorRGBA/Green 1.303 + :position (Vector3f. 0 -5 0)) 1.304 + z-axis 1.305 + (box 0.01 0.01 1 :physical? false :color ColorRGBA/Blue 1.306 + :position (Vector3f. 0 -5 0))] 1.307 + 1.308 + (world (nodify [(floor) the-worm x-axis y-axis z-axis me]) 1.309 + standard-debug-controls 1.310 + (fn [world] 1.311 + (light-up-everything world) 1.312 + ;; add a view from the worm's perspective 1.313 + (add-camera! 1.314 + world 1.315 + (add-eye! the-worm 1.316 + (.getChild 1.317 + (.getChild the-worm "eyes") "eye")) 1.318 + (comp 1.319 + (view-image 1.320 + (File. "/home/r/proj/cortex/render/worm-vision/worm-view")) 1.321 + BufferedImage!)) 1.322 + (set-gravity world Vector3f/ZERO) 1.323 + (Capture/captureVideo 1.324 + world 1.325 + (File. "/home/r/proj/cortex/render/worm-vision/main-view"))) 1.326 + (fn [world _ ] 1.327 + (.setLocalTranslation me (.getLocation (.getCamera world))) 1.328 + (vision-display 1.329 + (map #(% world) vision) 1.330 + (File. "/home/r/proj/cortex/render/worm-vision")) 1.331 + (fix-display world))))) 1.332 +#+end_src 1.333 + 1.334 +* Headers 1.335 + 1.336 #+name: vision-header 1.337 #+begin_src clojure 1.338 (ns cortex.vision 1.339 @@ -456,10 +619,23 @@ 1.340 (:import (com.jme3.scene Node Spatial))) 1.341 #+end_src 1.342 1.343 -The example code will create two videos of the same rotating object 1.344 -from different angles. It can be used both for stereoscopic vision 1.345 -simulation or for simulating multiple creatures, each with their own 1.346 -sense of vision. 1.347 +#+name: test-header 1.348 +#+begin_src clojure 1.349 +(ns cortex.test.vision 1.350 + (:use (cortex world sense util body vision)) 1.351 + (:use cortex.test.body) 1.352 + (:import java.awt.image.BufferedImage) 1.353 + (:import javax.swing.JPanel) 1.354 + (:import javax.swing.SwingUtilities) 1.355 + (:import java.awt.Dimension) 1.356 + (:import javax.swing.JFrame) 1.357 + (:import com.jme3.math.ColorRGBA) 1.358 + (:import com.jme3.scene.Node) 1.359 + (:import com.jme3.math.Vector3f) 1.360 + (:import java.io.File)) 1.361 +#+end_src 1.362 + 1.363 + 1.364 1.365 - As a neat bonus, this idea behind simulated vision also enables one 1.366 to [[../../cortex/html/capture-video.html][capture live video feeds from jMonkeyEngine]]. 1.367 @@ -471,5 +647,6 @@ 1.368 #+end_src 1.369 1.370 #+begin_src clojure :tangle ../src/cortex/test/vision.clj 1.371 -<<test-vision>> 1.372 +<<test-header>> 1.373 +<<test-1>> 1.374 #+end_src