view org/gabor.org @ 480:ad76b8b05517

s.
author Robert McIntyre <rlm@mit.edu>
date Fri, 28 Mar 2014 23:17:10 -0400
parents 42ddfe406c0a
children
line wrap: on
line source
1 #+title: Fun with Gabor Filters
2 #+author: Robert McIntyre
3 #+email: rlm@mit.edu
4 #+description: gabor filters in clojure with opencv
5 #+keywords: computer vision, jMonkeyEngine3, clojure, opencv
6 #+SETUPFILE: ../../aurellem/org/setup.org
7 #+INCLUDE: ../../aurellem/org/level-0.org
8 #+babel: :mkdirp yes :noweb yes :exports both
11 Gabor filters were invented by the same guy who invented holograms.
13 They work well as edge detectors and are related to the human visual
14 system.
16 #+name: gabor
17 #+begin_src clojure
18 (ns cortex.gabor
19 (:import org.opencv.core.CvType)
20 (:import java.awt.image.BufferedImage)
21 (:import ij.ImagePlus)
22 (:import org.opencv.core.Mat)
23 (:use (cortex world sense util vision import))
24 (:import com.jme3.post.SceneProcessor)
25 (:import (com.jme3.util BufferUtils Screenshots))
26 (:import java.nio.ByteBuffer)
27 (:import java.awt.image.BufferedImage)
28 (:import (com.jme3.renderer ViewPort Camera))
29 (:import (com.jme3.math ColorRGBA Vector3f Matrix3f Vector2f))
30 (:import com.jme3.renderer.Renderer)
31 (:import com.jme3.app.Application)
32 (:import com.jme3.texture.FrameBuffer)
33 (:import (com.jme3.scene Node Spatial)))
36 (cortex.import/mega-import-jme3)
37 (use 'clojure.math.numeric-tower)
38 (defn load-opencv
39 "Load the opencv native library. Must be called before any OpenCV
40 stuff is used."
41 []
42 (clojure.lang.RT/loadLibrary "opencv_java249"))
44 (load-opencv)
46 (defn gabor-kernel
47 ([sigma wavelength theta]
48 (gabor-kernel sigma wavelength theta 1 0))
49 ([sigma wavelength]
50 (gabor-kernel sigma wavelength 0 1 0))
51 ([sigma wavelength theta aspect-ratio phase-offset]
53 ;; first, find the size of the kernel which is required
54 (let [square #(expt % 2)
55 rotated (fn [[x y]]
56 [(+ (* x (Math/cos theta)) (* y (Math/sin theta)))
57 (- (* y (Math/cos theta)) (* x (Math/sin theta)))])
59 gaussian (fn [[x y]]
60 (let [[x' y'] (rotated [x y])]
61 (Math/exp (- (/ (+ (square x')
62 (square (* aspect-ratio y')))
63 (* 2 (square sigma)))))))
64 sinusoid (fn [[x y]]
65 (let [[x' y'] (rotated [x y])]
66 (Math/cos
67 (+ (* 2 Math/PI (/ x' wavelength))
68 phase-offset))))
70 half-width
71 (let [std-dev-capture 5]
72 (max
73 (int (* std-dev-capture (/ sigma aspect-ratio)))
74 (int (* std-dev-capture sigma))
75 (int (* std-dev-capture (/ aspect-ratio sigma)))))
77 grid (let [axis (range (- half-width) (inc half-width))]
78 (for [y (reverse axis) x axis] (vector x y)))
80 scale (reduce + (map gaussian grid))
82 gabor (fn [[x y :as coord]]
83 (* (sinusoid coord) (gaussian coord) (/ scale)))
85 mat-width (+ 1 (* 2 half-width))
86 mat (Mat. mat-width mat-width CvType/CV_32F)]
88 (.put mat 0 0 (float-array (map gabor grid)))
89 mat)))
92 (defn draw-kernel! [kernel img-path]
93 (let [output img-path
94 size (.size kernel)
95 width (int (.width size))
96 height (int (.height size))
97 tmp-array (float-array (* width height))]
99 ;; read values from matrix.
100 (.get kernel 0 0 tmp-array)
102 ;; find overall dynamic range of the filter
103 (let [vals (vec tmp-array)
104 low (apply min vals)
105 high (apply max vals)
106 scaled-vals (map #(* 255 (- % low) (/ (- high low))) vals)
107 new-mat (Mat. height width CvType/CV_32F)]
108 (.put new-mat 0 0 (float-array scaled-vals))
109 (org.opencv.highgui.Highgui/imwrite output new-mat))))
111 ;; some cool examples
112 #+end_src
117 [[../images/gabor-50-10.png]]
119 #+begin_src clojure
120 (def img-base "/home/r/proj/cortex/images/")
122 (draw-kernel! (gabor-kernel 50 10 0 1 0)
123 (str img-base "gabor-50-10.png"))
124 #+end_src
127 [[../images/gabor-50-10-pi-over-4.png]]
129 #+begin_src clojure
130 (draw-kernel! (gabor-kernel 50 10 (/ Math/PI 4) 1 0)
131 (str img-base "gabor-50-10-pi-over-4.png"))
132 #+end_src
135 [[../images/gabor-50-10-pi-over-2.png]]
137 #+begin_src clojure
138 (draw-kernel! (gabor-kernel 50 10 (/ Math/PI 2) 1 0)
139 (str img-base "gabor-50-10-pi-over-2.png"))
140 #+end_src
143 [[../images/gabor-50-50.png]]
147 #+begin_src clojure
148 (draw-kernel! (gabor-kernel 50 50 0 1 0)
149 (str img-base "gabor-50-50.png"))
151 #+end_src
153 [[../images/gabor-50-10-0-3.png]]
155 #+begin_src clojure
156 (draw-kernel! (gabor-kernel 50 10 0 3 0)
157 (str img-base "gabor-50-10-0-3.png"))
158 #+end_src
162 [[../images/gabor-50-4-pi-over3-3.png]]
163 #+begin_src clojure
164 (draw-kernel! (gabor-kernel 50 4 (/ Math/PI 3) 3 0)
165 (str img-base "gabor-50-4-pi-over3-3.png"))
166 #+end_src
169 #+name: gabor-tail
170 #+begin_src clojure
171 (defn show-kernel [kernel]
172 (let [img-path "/home/r/proj/cortex/tmp/kernel.png"]
173 (draw-kernel! kernel img-path)
174 (view (ImagePlus. img-path))))
176 (defn print-kernel [kernel]
177 (println (.dump kernel)))
180 (import com.aurellem.capture.Capture)
182 (import java.io.File)
184 (def base "/home/r/proj/cortex/render/gabor-1/")
186 (defn brick-wall-game-run [record?]
187 (let [capture-dir (File. base "main")]
189 (.mkdir (File. base "main"))
190 (doto
191 (world
192 (doto (Node.) (.attachChild (floor*))
193 (.attachChild (brick-wall*))
194 )
195 {"key-f" (fn [game value]
196 (if (not value) (add-element game (brick-wall*))))
197 "key-space" (fire-cannon-ball )}
198 (fn [world]
199 (position-camera
200 world
201 (Vector3f. 1.382548, 4.0383573, 5.994235)
202 (Quaternion. 0.0013082094, 0.98581666,
203 -0.1676442, 0.0076932586))
205 ;;(speed-up world)
207 (if record?
208 (Capture/captureVideo
209 world capture-dir))
211 (add-camera! world (.getCamera world) no-op))
212 (fn [& _]))
213 (.start))))
215 (defn convolve-preview [kernel]
216 (let [input "/home/r/proj/cortex/render/gabor-1/main/0000032.png"
219 output "/home/r/ppp.png"
221 i (org.opencv.highgui.Highgui/imread input)
223 ;;kernel (gabor-kernel 10 1 (/ Math/PI 2) 10 0)
225 new-mat (Mat.)
227 ]
229 (org.opencv.imgproc.Imgproc/filter2D
230 i new-mat CvType/CV_32F kernel)
232 (org.opencv.highgui.Highgui/imwrite "/home/r/ppp.png" new-mat)
234 (view (ImagePlus. input))
235 (view (ImagePlus. output))))
237 (use 'clojure.java.shell)
240 (defn apply-gabor [kernel source dest]
241 (let [i (org.opencv.highgui.Highgui/imread source)
242 new-mat (Mat.)]
244 (println dest)
245 (if (not (.exists (File. dest)))
246 (do
247 (org.opencv.imgproc.Imgproc/filter2D
248 i new-mat CvType/CV_32F kernel)
249 (org.opencv.highgui.Highgui/imwrite dest new-mat)
250 (println "mogrify" "-modulate" "1000%" dest)
251 (sh "mogrify" "-modulate" "1000%" dest)))))
254 (import java.io.File)
256 (defn images [path]
257 (sort (rest (file-seq (File. path)))))
261 (defn pics [file]
262 (images (str base file)))
264 (defn generate-gabor-images [kernel name]
265 (draw-kernel! kernel (str base name ".png"))
267 (.mkdir (File. (str base name)))
269 (let [main (map #(.getCanonicalPath %) (pics "main"))
270 targets (map #(str base name "/" (format "%07d.png" %))
271 (range 0 (count main)))]
272 (dorun (pmap (partial apply-gabor kernel) main targets))))
275 (def banks
276 [[(gabor-kernel 2.8 3.5) "bank-1-1"]
277 [(gabor-kernel 2.8 3.5 (/ Math/PI 2)) "bank-1-1-rot"]
279 ;; [(gabor-kernel 3.6 4.6) "bank-1-2"]
280 ;; [(gabor-kernel 4.5 5.6) "bank-2-1"]
281 ;; [(gabor-kernel 6.3 7.9) "bank-3-1"]
282 ;; [(gabor-kernel 7.3 9.1) "bank-3-2"]
284 [(gabor-kernel 12.3 15.4) "bank-6-1"]
287 ;; [(gabor-kernel 17 21.2) "bank-8-1"]
288 ;; [(gabor-kernel 18.2 22.8) "bank-8-2"]
289 ])
292 (defn make-all-images []
293 (dorun (map (partial apply generate-gabor-images) banks)))
297 (defn compile-left-right []
298 (.mkdir (File. (str base "left-right")))
299 (let [main (pics "main")
300 left (pics "bank-1-1")
301 right (pics "bank-1-1-rot")
302 left-kernel (repeat 20000 (File. (str base "bank-1-1.png")))
303 right-kernel (repeat 20000 (File. (str base "bank-1-1-rot.png")))
304 targets (map
305 #(File. (str base "left-right/" (format "%07d.png" %)))
306 (range 0 (count main)))]
308 (dorun
309 (pmap
310 (comp
311 (fn [[main left right left-kernel right-kernel target]]
312 (println target)
313 (if (not (.exists (File. target)))
314 (sh "convert"
315 "-size" "1940x515" "xc:white"
316 main "-geometry" "+0+0" "-composite"
317 left "-geometry" "+650+0" "-composite"
318 right "-geometry" "+1300+0" "-composite"
319 left-kernel "-geometry" "+960+485" "-composite"
320 right-kernel "-geometry" "+1610+485" "-composite"
321 target)))
322 (fn [& args] (map #(.getCanonicalPath %) args)))
323 main left right left-kernel right-kernel targets))))
326 (defn compile-big-small []
327 (.mkdir (File. (str base "big-small")))
328 (let [main (pics "main")
329 left (pics "bank-1-1")
330 right (pics "bank-6-1")
331 small-kernel (repeat 20000 (File. (str base "bank-1-1.png")))
332 big-kernel (repeat 20000 (File. (str base "bank-6-1.png")))
333 targets (map
334 #(File. (str base "big-small/" (format "%07d.png" %)))
335 (range 0 (count main)))]
337 (dorun
338 (pmap
339 (comp
340 (fn [[main left right small-kernel big-kernel target]]
341 (println target)
342 (if (not (.exists (File. target)))
343 (sh "convert"
344 "-size" "1940x610" "xc:white"
345 main "-geometry" "+0+0" "-composite"
346 left "-geometry" "+650+0" "-composite"
347 right "-geometry" "+1300+0" "-composite"
348 small-kernel "-geometry" "+960+485" "-composite"
349 big-kernel "-geometry" "+1560+485" "-composite"
350 target)))
351 (fn [& args] (map #(.getCanonicalPath %) args)))
352 main left right small-kernel big-kernel targets))))
355 (defn regen-everything []
356 (make-all-images)
357 (compile-left-right)
358 (compile-big-small))
361 #+end_src
364 #+name: make-videos
365 #+begin_src makefile
366 scale:
367 ffmpeg -framerate 60 -i ./big-small/%07d.png -b:v 9000k\
368 -c:v theora -r 60 gabor-scale.ogg
370 rotation:
371 ffmpeg -framerate 60 -i ./left-right/%07d.png -b:v 9000k\
372 -c:v theora -r 60 gabor-rotation.ogg
374 all: rotation scale
376 clean:
377 rm gabor-rotation.org gabor-scale.ogg
378 #+end_src
381 #+begin_html
382 <div class="figure">
383 <video controls="controls">
384 <source src="../video/gabor-rotation.ogg" type="video/ogg"
385 preload="none" poster="../images/aurellem-1280x480.png" />
386 </video>
387 <br> <a href="http://youtu.be/bxujjO97B-U"> YouTube </a>
388 <p>Two gabor filters with different values of theta are compared. The
389 horizontally aligned one does better in this example.</p>
390 </div>
391 #+end_html
395 #+begin_html
396 <div class="figure">
397 <video controls="controls">
398 <source src="../video/gabor-scale.ogg" type="video/ogg"
399 preload="none" poster="../images/aurellem-1280x480.png" />
400 </video>
401 <br> <a href="http://youtu.be/-EsfA2ceWUk"> YouTube </a>
402 <p>Here we compare a gabor filter from bank 1 with one from bank 6.</p>
403 </div>
404 #+end_html
406 * Source Listing
407 - [[../src/cortex/gabor.clj][cortex.gabor]]
408 #+html: <ul> <li> <a href="../org/gabor.org">This org file</a> </li> </ul>
409 - [[http://hg.bortreb.com ][source-repository]]
413 * COMMENT Generate Source
414 #+begin_src clojure :tangle ../src/cortex/gabor.clj
415 <<gabor>>
416 <<gabor-tail>>
417 #+end_src
419 #+begin_src Makefile :tangle ../render/gabor-1/Makefile
420 <<make-videos>>
421 #+end_src