Mercurial > cortex
changeset 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 |
files | .hgignore assets/Models/test-creature/worm.blend images/worm-with-eye.png org/body.org org/sense.org org/vision.org |
diffstat | 6 files changed, 288 insertions(+), 87 deletions(-) [+] |
line wrap: on
line diff
1.1 --- a/.hgignore Thu Feb 09 09:04:17 2012 -0700 1.2 +++ b/.hgignore Fri Feb 10 02:19:24 2012 -0700 1.3 @@ -7,6 +7,10 @@ 1.4 render* 1.5 sources* 1.6 *.hdr 1.7 +libbulletjme64.so 1.8 +liblwjgl64.so 1.9 +libopenal64.so 1.10 + 1.11 1.12 syntax: regexp 1.13 ^.*blend\d$ 1.14 \ No newline at end of file
2.1 Binary file assets/Models/test-creature/worm.blend has changed
3.1 Binary file images/worm-with-eye.png has changed
4.1 --- a/org/body.org Thu Feb 09 09:04:17 2012 -0700 4.2 +++ b/org/body.org Fri Feb 10 02:19:24 2012 -0700 4.3 @@ -491,13 +491,15 @@ 4.4 #+begin_src clojure 4.5 (in-ns 'cortex.test.body) 4.6 4.7 +(defn worm [] 4.8 + (load-blender-model 4.9 + "Models/test-creature/worm.blend")) 4.10 + 4.11 (defn worm-1 [] 4.12 (let [timer (RatchetTimer. 60)] 4.13 (world 4.14 (nodify 4.15 - [(doto 4.16 - (load-blender-model 4.17 - "Models/test-creature/worm.blend") 4.18 + [(doto (worm) 4.19 (body!)) 4.20 (floor)]) 4.21 (merge standard-debug-controls debug-control)
5.1 --- a/org/sense.org Thu Feb 09 09:04:17 2012 -0700 5.2 +++ b/org/sense.org Fri Feb 10 02:19:24 2012 -0700 5.3 @@ -271,15 +271,27 @@ 5.4 its own JFrame." 5.5 [sense-display-kernel] 5.6 (let [windows (atom [])] 5.7 - (fn [data] 5.8 - (if (> (count data) (count @windows)) 5.9 - (reset! 5.10 - windows (map (fn [_] (view-image)) (range (count data))))) 5.11 - (dorun 5.12 - (map 5.13 - (fn [display datum] 5.14 - (display (sense-display-kernel datum))) 5.15 - @windows data))))) 5.16 + (fn this 5.17 + ([data] 5.18 + (this data nil)) 5.19 + ([data save-to] 5.20 + (if (> (count data) (count @windows)) 5.21 + (reset! 5.22 + windows 5.23 + (doall 5.24 + (map 5.25 + (fn [idx] 5.26 + (if save-to 5.27 + (let [dir (File. save-to (str idx))] 5.28 + (.mkdir dir) 5.29 + (view-image dir)) 5.30 + (view-image))) (range (count data)))))) 5.31 + (dorun 5.32 + (map 5.33 + (fn [display datum] 5.34 + (display (sense-display-kernel datum))) 5.35 + @windows data)))))) 5.36 + 5.37 5.38 (defn points->image 5.39 "Take a collection of points and visuliaze it as a BufferedImage." 5.40 @@ -464,33 +476,35 @@ 5.41 5.42 *** Combine Frames with ImageMagick 5.43 #+begin_src clojure :results silent 5.44 -(in-ns 'user) 5.45 -(import java.io.File) 5.46 -(use 'clojure.contrib.shell-out) 5.47 -(let 5.48 - [idx (atom -1) 5.49 - left (rest 5.50 - (sort 5.51 - (file-seq (File. "/home/r/proj/cortex/render/bind-sense0/")))) 5.52 - right (rest 5.53 +(ns cortex.video.magick 5.54 + (:import java.io.File) 5.55 + (:use clojure.contrib.shell-out)) 5.56 + 5.57 +(defn combine-images [] 5.58 + (let 5.59 + [idx (atom -1) 5.60 + left (rest 5.61 + (sort 5.62 + (file-seq (File. "/home/r/proj/cortex/render/bind-sense0/")))) 5.63 + right (rest 5.64 + (sort 5.65 + (file-seq 5.66 + (File. "/home/r/proj/cortex/render/bind-sense1/")))) 5.67 + sub (rest 5.68 (sort 5.69 (file-seq 5.70 - (File. "/home/r/proj/cortex/render/bind-sense1/")))) 5.71 - sub (rest 5.72 - (sort 5.73 - (file-seq 5.74 - (File. "/home/r/proj/cortex/render/bind-senseB/")))) 5.75 - sub* (concat sub (repeat 1000 (last sub)))] 5.76 - (dorun 5.77 - (map 5.78 - (fn [im-1 im-2 sub] 5.79 - (sh "convert" (.getCanonicalPath im-1) 5.80 - (.getCanonicalPath im-2) "+append" 5.81 - (.getCanonicalPath sub) "-append" 5.82 - (.getCanonicalPath 5.83 - (File. "/home/r/proj/cortex/render/bind-sense/" 5.84 - (format "%07d.png" (swap! idx inc)))))) 5.85 - left right sub*))) 5.86 + (File. "/home/r/proj/cortex/render/bind-senseB/")))) 5.87 + sub* (concat sub (repeat 1000 (last sub)))] 5.88 + (dorun 5.89 + (map 5.90 + (fn [im-1 im-2 sub] 5.91 + (sh "convert" (.getCanonicalPath im-1) 5.92 + (.getCanonicalPath im-2) "+append" 5.93 + (.getCanonicalPath sub) "-append" 5.94 + (.getCanonicalPath 5.95 + (File. "/home/r/proj/cortex/render/bind-sense/" 5.96 + (format "%07d.png" (swap! idx inc)))))) 5.97 + left right sub*)))) 5.98 #+end_src 5.99 5.100 *** Encode Frames with ffmpeg 5.101 @@ -559,3 +573,7 @@ 5.102 <<test-header>> 5.103 <<test>> 5.104 #+end_src 5.105 + 5.106 +#+begin_src clojure :tangle ../src/cortex/video/magick.clj 5.107 +<<magick>> 5.108 +#+end_src
6.1 --- a/org/vision.org Thu Feb 09 09:04:17 2012 -0700 6.2 +++ b/org/vision.org Fri Feb 10 02:19:24 2012 -0700 6.3 @@ -160,21 +160,50 @@ 6.4 6.5 #+name: add-eye 6.6 #+begin_src clojure 6.7 +(in-ns 'cortex.vision) 6.8 + 6.9 +(import com.jme3.math.Vector3f) 6.10 + 6.11 +(def blender-rotation-correction 6.12 + (doto (Quaternion.) 6.13 + (.fromRotationMatrix 6.14 + (doto (Matrix3f.) 6.15 + (.setColumn 0 6.16 + (Vector3f. 1 0 0)) 6.17 + (.setColumn 1 6.18 + (Vector3f. 0 -1 0)) 6.19 + (.setColumn 2 6.20 + (Vector3f. 0 0 -1))) 6.21 + 6.22 + (doto (Matrix3f.) 6.23 + (.setColumn 0 6.24 + (Vector3f. 6.25 + 6.26 + 6.27 (defn add-eye! 6.28 "Create a Camera centered on the current position of 'eye which 6.29 follows the closest physical node in 'creature and sends visual 6.30 - data to 'continuation." 6.31 + data to 'continuation. The camera will point in the X direction and 6.32 + use the Z vector as up as determined by the rotation of these 6.33 + vectors in blender coordinate space. Use XZY rotation for the node 6.34 + in blender." 6.35 [#^Node creature #^Spatial eye] 6.36 (let [target (closest-node creature eye) 6.37 [cam-width cam-height] (eye-dimensions eye) 6.38 - cam (Camera. cam-width cam-height)] 6.39 + cam (Camera. cam-width cam-height) 6.40 + rot (.getWorldRotation eye)] 6.41 (.setLocation cam (.getWorldTranslation eye)) 6.42 - (.setRotation cam (.getWorldRotation eye)) 6.43 + (.lookAtDirection cam (.mult rot Vector3f/UNIT_X) 6.44 + ;; this part is consistent with using Z in 6.45 + ;; blender as the UP vector. 6.46 + (.mult rot Vector3f/UNIT_Y)) 6.47 + 6.48 + (println-repl "eye unit-z ->" (.mult rot Vector3f/UNIT_Z)) 6.49 + (println-repl "eye unit-y ->" (.mult rot Vector3f/UNIT_Y)) 6.50 + (println-repl "eye unit-x ->" (.mult rot Vector3f/UNIT_X)) 6.51 (.setFrustumPerspective 6.52 - cam 45 (/ (.getWidth cam) (.getHeight cam)) 6.53 - 1 1000) 6.54 - (bind-sense target cam) 6.55 - cam)) 6.56 + cam 45 (/ (.getWidth cam) (.getHeight cam)) 1 1000) 6.57 + (bind-sense target cam) cam)) 6.58 #+end_src 6.59 6.60 Here, the camera is created based on metadata on the eye-node and 6.61 @@ -286,7 +315,8 @@ 6.62 "Return the children of the creature's \"eyes\" node.") 6.63 #+end_src 6.64 6.65 -Then, 6.66 +Then, add the camera created by =(add-eye!)= to the simulation by 6.67 +creating a new viewport. 6.68 6.69 #+begin_src clojure 6.70 (defn add-camera! 6.71 @@ -302,12 +332,26 @@ 6.72 (.setBackgroundColor ColorRGBA/Black) 6.73 (.addProcessor (vision-pipeline continuation)) 6.74 (.attachScene (.getRootNode world))))) 6.75 +#+end_src 6.76 6.77 6.78 +The continuation function registers the viewport with the simulation 6.79 +the first time it is called, and uses the CPU to extract the 6.80 +appropriate pixels from the rendered image and weight them by each 6.81 +sensors sensitivity. I have the option to do this filtering in native 6.82 +code for a slight gain in speed. I could also do it in the GPU for a 6.83 +massive gain in speed. =(vision-kernel)= generates a list of such 6.84 +continuation functions, one for each channel of the eye. 6.85 6.86 +#+begin_src clojure 6.87 +(in-ns 'cortex.vision) 6.88 6.89 +(defrecord attached-viewport [vision-fn viewport-fn] 6.90 + clojure.lang.IFn 6.91 + (invoke [this world] (vision-fn world)) 6.92 + (applyTo [this args] (apply vision-fn args))) 6.93 6.94 -(defn vision-fn 6.95 +(defn vision-kernel 6.96 "Returns a list of functions, each of which will return a color 6.97 channel's worth of visual information when called inside a running 6.98 simulation." 6.99 @@ -335,20 +379,47 @@ 6.100 (let [whites (white-coordinates image) 6.101 topology (vec (collapse whites)) 6.102 mask (color-channel-presets key key)] 6.103 - (fn [world] 6.104 - (register-eye! world) 6.105 - (vector 6.106 - topology 6.107 - (vec 6.108 - (for [[x y] whites] 6.109 - (bit-and 6.110 - mask (.getRGB @vision-image x y)))))))) 6.111 - retinal-map)))) 6.112 + (attached-viewport. 6.113 + (fn [world] 6.114 + (register-eye! world) 6.115 + (vector 6.116 + topology 6.117 + (vec 6.118 + (for [[x y] whites] 6.119 + (bit-and 6.120 + mask (.getRGB @vision-image x y)))))) 6.121 + register-eye!))) 6.122 + retinal-map)))) 6.123 6.124 +(defn gen-fix-display 6.125 + "Create a function to call to restore a simulation's display when it 6.126 + is disrupted by a Viewport." 6.127 + [] 6.128 + (runonce 6.129 + (fn [world] 6.130 + (add-camera! world (.getCamera world) no-op)))) 6.131 6.132 -;; TODO maybe should add a viewport-manipulation function to 6.133 -;; automatically change viewport settings, attach shadow filters, etc. 6.134 +#+end_src 6.135 6.136 +Note that since each of the functions generated by =(vision-kernel)= 6.137 +shares the same =(register-eye!)= function, the eye will be registered 6.138 +only once the first time any of the functions from the list returned 6.139 +by =(vision-kernel)= is called. Each of the functions returned by 6.140 +=(vision-kernel)= also allows access to the =Viewport= through which 6.141 +it recieves images. 6.142 + 6.143 +The in-game display can be disrupted by all the viewports that the 6.144 +functions greated by =(vision-kernel)= add. This doesn't affect the 6.145 +simulation or the simulated senses, but can be annoying. 6.146 +=(gen-fix-display)= restores the in-simulation display. 6.147 + 6.148 +** Vision! 6.149 + 6.150 +All the hard work has been done, all that remains is to apply 6.151 +=(vision-kernel)= to each eye in the creature and gather the results 6.152 +into one list of functions. 6.153 + 6.154 +#+begin_src clojure 6.155 (defn vision! 6.156 "Returns a function which returns visual sensory data when called 6.157 inside a running simulation" 6.158 @@ -356,8 +427,16 @@ 6.159 (reduce 6.160 concat 6.161 (for [eye (eyes creature)] 6.162 - (vision-fn creature eye)))) 6.163 + (vision-kernel creature eye)))) 6.164 +#+end_src 6.165 6.166 +** Visualization of Vision 6.167 + 6.168 +It's vital to have a visual representation for each sense. Here I use 6.169 +=(view-sense)= to construct a function that will create a display for 6.170 +visual data. 6.171 + 6.172 +#+begin_src clojure 6.173 (defn view-vision 6.174 "Creates a function which accepts a list of visual sensor-data and 6.175 displays each element of the list to the screen." 6.176 @@ -371,31 +450,20 @@ 6.177 (.setRGB image ((coords i) 0) ((coords i) 1) 6.178 (sensor-data i)))) 6.179 image)))) 6.180 - 6.181 #+end_src 6.182 6.183 +* Tests 6.184 6.185 -Note the use of continuation passing style for connecting the eye to a 6.186 -function to process the output. You can create any number of eyes, and 6.187 -each of them will see the world from their own =Camera=. Once every 6.188 -frame, the rendered image is copied to a =BufferedImage=, and that 6.189 -data is sent off to the continuation function. Moving the =Camera= 6.190 -which was used to create the eye will change what the eye sees. 6.191 +** Basic Test 6.192 6.193 -* Example 6.194 +This is a basic test for the vision system. It only tests the 6.195 +vision-pipeline and does not deal with loadig eyes from a blender 6.196 +file. The code creates two videos of the same rotating cube from 6.197 +different angles. 6.198 6.199 -#+name: test-vision 6.200 +#+name: test-1 6.201 #+begin_src clojure 6.202 -(ns cortex.test.vision 6.203 - (:use (cortex world util vision)) 6.204 - (:import java.awt.image.BufferedImage) 6.205 - (:import javax.swing.JPanel) 6.206 - (:import javax.swing.SwingUtilities) 6.207 - (:import java.awt.Dimension) 6.208 - (:import javax.swing.JFrame) 6.209 - (:import com.jme3.math.ColorRGBA) 6.210 - (:import com.jme3.scene.Node) 6.211 - (:import com.jme3.math.Vector3f)) 6.212 +(in-ns 'cortex.test.vision) 6.213 6.214 (defn test-two-eyes 6.215 "Testing vision: 6.216 @@ -417,15 +485,18 @@ 6.217 width (.getWidth cam) 6.218 height (.getHeight cam)] 6.219 (add-camera! world cam 6.220 - ;;no-op 6.221 - (comp (view-image) BufferedImage!) 6.222 - ) 6.223 + (comp 6.224 + (view-image 6.225 + (File. "/home/r/proj/cortex/render/vision/1")) 6.226 + BufferedImage!)) 6.227 (add-camera! world 6.228 (doto (.clone cam) 6.229 (.setLocation (Vector3f. -10 0 0)) 6.230 (.lookAt Vector3f/ZERO Vector3f/UNIT_Y)) 6.231 - ;;no-op 6.232 - (comp (view-image) BufferedImage!)) 6.233 + (comp 6.234 + (view-image 6.235 + (File. "/home/r/proj/cortex/render/vision/2")) 6.236 + BufferedImage!)) 6.237 ;; This is here to restore the main view 6.238 ;; after the other views have completed processing 6.239 (add-camera! world (.getCamera world) no-op))) 6.240 @@ -433,6 +504,98 @@ 6.241 (.rotate candy (* tpf 0.2) 0 0))))) 6.242 #+end_src 6.243 6.244 +#+begin_html 6.245 +<div class="figure"> 6.246 +<video controls="controls" width="755"> 6.247 + <source src="../video/spinning-cube.ogg" type="video/ogg" 6.248 + preload="none" poster="../images/aurellem-1280x480.png" /> 6.249 +</video> 6.250 +<p>A rotating cube viewed from two different perspectives.</p> 6.251 +</div> 6.252 +#+end_html 6.253 + 6.254 +Creating multiple eyes like this can be used for stereoscopic vision 6.255 +simulation in a single creature or for simulating multiple creatures, 6.256 +each with their own sense of vision. 6.257 + 6.258 +** Adding Vision to the Worm 6.259 + 6.260 +To the worm from the last post, we add a new node that describes its 6.261 +eyes. 6.262 + 6.263 +#+attr_html: width=755 6.264 +#+caption: The worm with newly added empty nodes describing a single eye. 6.265 +[[../images/worm-with-eye.png]] 6.266 + 6.267 +The node highlighted in yellow is the root level "eyes" node. It has 6.268 +a single node, highlighted in orange, which describes a single 6.269 +eye. This is the "eye" node. The two nodes which are not highlighted describe the single joint 6.270 +of the worm. 6.271 + 6.272 +The metadata of the eye-node is: 6.273 + 6.274 +#+begin_src clojure :results verbatim :exports both 6.275 +(cortex.sense/meta-data 6.276 + (.getChild 6.277 + (.getChild (cortex.test.body/worm) 6.278 + "eyes") "eye") "eye") 6.279 +#+end_src 6.280 + 6.281 +#+results: 6.282 +: "(let [retina \"Models/test-creature/retina-small.png\"] 6.283 +: {:all retina :red retina :green retina :blue retina})" 6.284 + 6.285 +This is the approximation to the human eye described earlier. 6.286 + 6.287 +#+begin_src clojure 6.288 +(in-ns 'cortex.test.vision) 6.289 + 6.290 +(import com.aurellem.capture.Capture) 6.291 + 6.292 +(defn test-worm-vision [] 6.293 + (let [the-worm (doto (worm)(body!)) 6.294 + vision (vision! the-worm) 6.295 + vision-display (view-vision) 6.296 + fix-display (gen-fix-display) 6.297 + me (sphere 0.5 :color ColorRGBA/Blue :physical? false) 6.298 + x-axis 6.299 + (box 1 0.01 0.01 :physical? false :color ColorRGBA/Red 6.300 + :position (Vector3f. 0 -5 0)) 6.301 + y-axis 6.302 + (box 0.01 1 0.01 :physical? false :color ColorRGBA/Green 6.303 + :position (Vector3f. 0 -5 0)) 6.304 + z-axis 6.305 + (box 0.01 0.01 1 :physical? false :color ColorRGBA/Blue 6.306 + :position (Vector3f. 0 -5 0))] 6.307 + 6.308 + (world (nodify [(floor) the-worm x-axis y-axis z-axis me]) 6.309 + standard-debug-controls 6.310 + (fn [world] 6.311 + (light-up-everything world) 6.312 + ;; add a view from the worm's perspective 6.313 + (add-camera! 6.314 + world 6.315 + (add-eye! the-worm 6.316 + (.getChild 6.317 + (.getChild the-worm "eyes") "eye")) 6.318 + (comp 6.319 + (view-image 6.320 + (File. "/home/r/proj/cortex/render/worm-vision/worm-view")) 6.321 + BufferedImage!)) 6.322 + (set-gravity world Vector3f/ZERO) 6.323 + (Capture/captureVideo 6.324 + world 6.325 + (File. "/home/r/proj/cortex/render/worm-vision/main-view"))) 6.326 + (fn [world _ ] 6.327 + (.setLocalTranslation me (.getLocation (.getCamera world))) 6.328 + (vision-display 6.329 + (map #(% world) vision) 6.330 + (File. "/home/r/proj/cortex/render/worm-vision")) 6.331 + (fix-display world))))) 6.332 +#+end_src 6.333 + 6.334 +* Headers 6.335 + 6.336 #+name: vision-header 6.337 #+begin_src clojure 6.338 (ns cortex.vision 6.339 @@ -456,10 +619,23 @@ 6.340 (:import (com.jme3.scene Node Spatial))) 6.341 #+end_src 6.342 6.343 -The example code will create two videos of the same rotating object 6.344 -from different angles. It can be used both for stereoscopic vision 6.345 -simulation or for simulating multiple creatures, each with their own 6.346 -sense of vision. 6.347 +#+name: test-header 6.348 +#+begin_src clojure 6.349 +(ns cortex.test.vision 6.350 + (:use (cortex world sense util body vision)) 6.351 + (:use cortex.test.body) 6.352 + (:import java.awt.image.BufferedImage) 6.353 + (:import javax.swing.JPanel) 6.354 + (:import javax.swing.SwingUtilities) 6.355 + (:import java.awt.Dimension) 6.356 + (:import javax.swing.JFrame) 6.357 + (:import com.jme3.math.ColorRGBA) 6.358 + (:import com.jme3.scene.Node) 6.359 + (:import com.jme3.math.Vector3f) 6.360 + (:import java.io.File)) 6.361 +#+end_src 6.362 + 6.363 + 6.364 6.365 - As a neat bonus, this idea behind simulated vision also enables one 6.366 to [[../../cortex/html/capture-video.html][capture live video feeds from jMonkeyEngine]]. 6.367 @@ -471,5 +647,6 @@ 6.368 #+end_src 6.369 6.370 #+begin_src clojure :tangle ../src/cortex/test/vision.clj 6.371 -<<test-vision>> 6.372 +<<test-header>> 6.373 +<<test-1>> 6.374 #+end_src