rlm@1
|
1 (ns clojureDemo.VisionCore)
|
rlm@1
|
2
|
rlm@1
|
3 (import '(java.awt Rectangle Robot Toolkit) )
|
rlm@1
|
4 (import '(java.awt.image BufferedImage) )
|
rlm@1
|
5 (import '(java.awt Graphics2D Panel))
|
rlm@1
|
6 (import '(java.io File) )
|
rlm@1
|
7 (import '(javax.imageio ImageIO) )
|
rlm@1
|
8 (import '(javax.swing JFrame))
|
rlm@1
|
9 (import '(org.apache.commons.io FileUtils))
|
rlm@1
|
10 (import clojure.lang.LazySeq)
|
rlm@1
|
11 (import '(name.audet.samuel.javacv.jna highgui cv cxcore))
|
rlm@1
|
12 (import '(name.audet.samuel.javacv CanvasFrame))
|
rlm@1
|
13 (import '(name.audet.samuel.javacv.jna cxcore$IplImage))
|
rlm@1
|
14 (import '(name.audet.samuel.javacv.jna highgui$CvCapture$PointerByReference))
|
rlm@1
|
15 (import '(name.audet.samuel.javacv.jna highgui$CvVideoWriter$PointerByReference))
|
rlm@1
|
16 (import '(name.audet.samuel.javacv.jna cxcore$IplImage$PointerByReference))
|
rlm@1
|
17 (import '(name.audet.samuel.javacv.jna cxcore$IplImage))
|
rlm@1
|
18 (import '(name.audet.samuel.javacv JavaCvErrorCallback))
|
rlm@1
|
19 (.redirectError (JavaCvErrorCallback.))
|
rlm@1
|
20
|
rlm@1
|
21 (use 'clojure.contrib.repl-utils)
|
rlm@1
|
22
|
rlm@1
|
23 (def -inf Double/NEGATIVE_INFINITY)
|
rlm@1
|
24 (def inf Double/POSITIVE_INFINITY)
|
rlm@1
|
25
|
rlm@1
|
26
|
rlm@1
|
27
|
rlm@1
|
28 (def lian (File. "/home/r/Desktop/source-videos/lian1.mpeg"))
|
rlm@1
|
29 (def look (File. "/home/r/Desktop/source-videos/dramatic_look.flv"))
|
rlm@1
|
30
|
rlm@1
|
31 (def target (File. "/home/r/Desktop/output-vision/"))
|
rlm@1
|
32
|
rlm@1
|
33
|
rlm@1
|
34 ;this is still a work in progress, I'll come back to it later when I understand
|
rlm@1
|
35 ;jna more thoroughly. the important abstraction here is
|
rlm@1
|
36 ;video-seq, which gives a lazy sequence of Intel Image Processing library images.
|
rlm@1
|
37
|
rlm@1
|
38 (defn naturals [] (iterate inc 0))
|
rlm@1
|
39
|
rlm@1
|
40 (def ext "jpg")
|
rlm@1
|
41 ;see below for the rationale for this choice of extention.
|
rlm@1
|
42
|
rlm@1
|
43 (def cache-location "/home/r/Desktop/vision-cache/")
|
rlm@1
|
44
|
rlm@1
|
45 (defn close-capture
|
rlm@1
|
46 [capture]
|
rlm@1
|
47 (highgui/cvReleaseCapture (highgui$CvCapture$PointerByReference. capture)))
|
rlm@1
|
48
|
rlm@1
|
49 (defn close-writer
|
rlm@1
|
50 [writer] (highgui/cvReleaseVideoWriter (highgui$CvVideoWriter$PointerByReference. writer)))
|
rlm@1
|
51
|
rlm@1
|
52 (defn- cache-path
|
rlm@1
|
53 [video]
|
rlm@1
|
54 (File. cache-location (.getName video)))
|
rlm@1
|
55
|
rlm@1
|
56 (defn- already-cached
|
rlm@1
|
57 "this is the simplest and most retarded way to do it"
|
rlm@1
|
58 [video]
|
rlm@1
|
59 (.exists (cache-path video)))
|
rlm@1
|
60
|
rlm@1
|
61 (defn- write-frame
|
rlm@1
|
62 [capture target-dir n]
|
rlm@1
|
63 (let [image (highgui/cvQueryFrame capture)]
|
rlm@1
|
64 (if (nil? image) false
|
rlm@1
|
65 (highgui/cvSaveImage (str (File. target-dir (str n "." ext))) image))))
|
rlm@1
|
66
|
rlm@1
|
67 (defn- write-frame-bad
|
rlm@1
|
68 [capture target-dir n]
|
rlm@1
|
69 (println (str "saving frame: " n))
|
rlm@1
|
70 (let [image (highgui/cvQueryFrame capture)]
|
rlm@1
|
71 (if (nil? image) false
|
rlm@1
|
72 ( ImageIO/write (.getBufferedImage image) ext (File. target-dir (str n "." ext))))))
|
rlm@1
|
73
|
rlm@1
|
74 (defn- write-frames
|
rlm@1
|
75 [video target-dir]
|
rlm@1
|
76 (let [capture (highgui/cvCreateFileCapture (.getPath video))]
|
rlm@1
|
77 (dorun
|
rlm@1
|
78 (for [n (naturals) :while (write-frame capture target-dir n) ] nil ))
|
rlm@1
|
79 (highgui/cvReleaseCapture (highgui$CvCapture$PointerByReference. capture))))
|
rlm@1
|
80
|
rlm@1
|
81 (defn- cache-frames
|
rlm@1
|
82 [cache-location video]
|
rlm@1
|
83 (time
|
rlm@1
|
84 (do
|
rlm@1
|
85 (println "\"caching entire video structure... this will take a while... go get a snack or something :)\"")
|
rlm@1
|
86 (FileUtils/deleteDirectory (cache-path video))
|
rlm@1
|
87 (FileUtils/forceMkdir (cache-path video))
|
rlm@1
|
88 (write-frames video (cache-path video)))))
|
rlm@1
|
89
|
rlm@1
|
90 (defn cache
|
rlm@1
|
91 [video]
|
rlm@1
|
92 (if (already-cached video) nil (cache-frames cache-location video)))
|
rlm@1
|
93
|
rlm@1
|
94 (defn video-len
|
rlm@1
|
95 [video] (cache video)
|
rlm@1
|
96 (alength (.list (cache-path video))))
|
rlm@1
|
97 (def video-len (memoize video-len))
|
rlm@1
|
98
|
rlm@1
|
99 (defn video-data
|
rlm@1
|
100 "since the opencv version is so absolutely unreliable..."
|
rlm@1
|
101 [video]
|
rlm@1
|
102 (let
|
rlm@1
|
103 [capture (highgui/cvCreateFileCapture (.getPath video))
|
rlm@1
|
104 info {:length (video-len video)
|
rlm@1
|
105 :width (highgui/cvGetCaptureProperty capture highgui/CV_CAP_PROP_FRAME_WIDTH)
|
rlm@1
|
106 :height (highgui/cvGetCaptureProperty capture highgui/CV_CAP_PROP_FRAME_HEIGHT)
|
rlm@1
|
107 :fps (highgui/cvGetCaptureProperty capture highgui/CV_CAP_PROP_FPS)
|
rlm@1
|
108 :codec (highgui/cvGetCaptureProperty capture highgui/CV_CAP_PROP_FOURCC)}]
|
rlm@1
|
109 (close-capture capture)
|
rlm@1
|
110 info))
|
rlm@1
|
111 (def video-data (memoize video-data))
|
rlm@1
|
112
|
rlm@1
|
113 (defn- video-frame
|
rlm@1
|
114 [video n]
|
rlm@1
|
115 (cache video)
|
rlm@1
|
116 (let
|
rlm@1
|
117 [c++-managed (highgui/cvLoadImage (str (File. (cache-path video) (str n "." ext))) highgui/CV_LOAD_IMAGE_COLOR)
|
rlm@1
|
118 jvm-managed (.clone c++-managed)]
|
rlm@1
|
119 ;this bit with the cloning is so I can deal with Garbage Collection once and for all.
|
rlm@1
|
120 ;the cpp-managed image must be manually Garbage Collected, but it's clone is managed by
|
rlm@1
|
121 ;the JVM's Garbage Collector. By getting rid of the c++ part right here and now, no
|
rlm@1
|
122 ;other function has to worry about manual garbage collection ever again.
|
rlm@1
|
123 ;Unfortunately, this doesn't seem to work for certain types of files. It's not file-size
|
rlm@1
|
124 ;which is the issue, but something involving the image header.
|
rlm@1
|
125 (cxcore/cvReleaseImage (.pointerByReference c++-managed))
|
rlm@1
|
126 jvm-managed
|
rlm@1
|
127 ))
|
rlm@1
|
128
|
rlm@1
|
129
|
rlm@1
|
130 (defn- video-frame-buffered
|
rlm@1
|
131 "takes one frame from a video in constant time"
|
rlm@1
|
132 [video n]
|
rlm@1
|
133 (cache video)
|
rlm@1
|
134 (ImageIO/read (File. (cache-path video) (str n "." ext))))
|
rlm@1
|
135
|
rlm@1
|
136
|
rlm@1
|
137 (defn- dumb-write
|
rlm@1
|
138 [video n writer]
|
rlm@1
|
139 (let
|
rlm@1
|
140 [c++-managed (highgui/cvLoadImage (str (File. (cache-path video) (str n ext))) highgui/CV_LOAD_IMAGE_COLOR)]
|
rlm@1
|
141 (highgui/cvWriteFrame writer c++-managed)
|
rlm@1
|
142 (cxcore/cvReleaseImage (cxcore$IplImage$PointerByReference. c++-managed))))
|
rlm@1
|
143
|
rlm@1
|
144 (defn video-seq
|
rlm@1
|
145 "makes a lazy sequence of IPL images"
|
rlm@1
|
146 ;additionally, I want to pass metadata around with the sequence.
|
rlm@1
|
147 [video] (cache video)
|
rlm@1
|
148 (map #(video-frame video %) (range (video-len video))))
|
rlm@1
|
149
|
rlm@1
|
150
|
rlm@1
|
151
|
rlm@1
|
152
|
rlm@1
|
153
|
rlm@1
|
154
|
rlm@1
|
155 (comment
|
rlm@1
|
156
|
rlm@1
|
157 ; I initially decided to use .sr because it loads the fastest out of all the
|
rlm@1
|
158 ; formats opencv supports, under a simple benchmark of reading/writing
|
rlm@1
|
159 ; a blank file of each type 100 times.
|
rlm@1
|
160
|
rlm@1
|
161 ;I just kept changing the file extention at the REPL to generate these times.
|
rlm@1
|
162 (def file "test.tiff")
|
rlm@1
|
163 (do
|
rlm@1
|
164 (time (dotimes [_ 100] (highgui/cvSaveImage (str cache-location file) ipl)))
|
rlm@1
|
165 (time (dotimes [_ 100] (highgui/cvLoadImage (str cache-location file)))))
|
rlm@1
|
166
|
rlm@1
|
167 ; Write Read
|
rlm@1
|
168 (jpg 4404.000955 msecs 3397.8564 msecs)
|
rlm@1
|
169 (jpeg 4376.138853 msecs 3482.990118 msecs)
|
rlm@1
|
170 (jpeg 4253.721501 msecs 3414.004122 msecs)
|
rlm@1
|
171 (bmp 3488.281695 msecs 786.883035 msecs)
|
rlm@1
|
172 (dib 3589.010247 msecs 685.681985 msecs)
|
rlm@1
|
173 (jpe 4288.541679 msecs 3359.819425 msecs)
|
rlm@1
|
174 (png 10127.648557 msecs 3786.184994 msecs)
|
rlm@1
|
175 (pbm 3880.794141 msecs 917.737667 msecs)
|
rlm@1
|
176 (pgm 3879.710445 msecs 894.78237 msecs)
|
rlm@1
|
177 (ppm 3938.319148 msecs 1014.412766 msecs)
|
rlm@1
|
178 (sr 3510.893891 msecs 676.502596 msecs)
|
rlm@1
|
179 (dib 3434.654784 msecs 737.495844 msecs)
|
rlm@1
|
180 (bmp 3354.956726 msecs 783.353025 msecs)
|
rlm@1
|
181 (ras 3351.400751 msecs 722.548007 msecs)
|
rlm@1
|
182 (tiff 3657.893326 msecs 1361.576798 msecs)
|
rlm@1
|
183 (tif 3594.753736 msecs 1254.568533 msecs)
|
rlm@1
|
184
|
rlm@1
|
185 ;Ah, but now it's time for some more tests.
|
rlm@1
|
186 ;I started using
|
rlm@1
|
187 (def ext ".sr")
|
rlm@1
|
188 ;, and an empty cache, and ran
|
rlm@1
|
189 (cache lian)
|
rlm@1
|
190 "caching entire video structure... this will take a while... go get a snack or something :)"
|
rlm@1
|
191 "Elapsed time: 56486.816728 msecs"
|
rlm@1
|
192 (time (dorun (video-seq lian)))
|
rlm@1
|
193 "Elapsed time: 120515.66221 msecs"
|
rlm@1
|
194 (time (dorun (video-seq lian)))
|
rlm@1
|
195 "Elapsed time: 122867.82989 msecs" ;good agreement with times
|
rlm@1
|
196
|
rlm@1
|
197 ;*erased vision cache with*
|
rlm@1
|
198 ;*rm -rf ~/Desktop/vision-cache *
|
rlm@1
|
199 (def ext ".bmp")
|
rlm@1
|
200 (cache lian)
|
rlm@1
|
201 "Elapsed time: 59613.624691 msecs"
|
rlm@1
|
202 (time (dorun (video-seq lian)))
|
rlm@1
|
203 "Elapsed time: 123850.390784 msecs"
|
rlm@1
|
204
|
rlm@1
|
205 ;same process except with
|
rlm@1
|
206 (def ext ".jpg")
|
rlm@1
|
207 (cache lian)
|
rlm@1
|
208 "Elapsed time: 139964.031921 msecs"
|
rlm@1
|
209 (time (dorun (video-seq lian)))
|
rlm@1
|
210 "Elapsed time: 127740.50204 msecs"
|
rlm@1
|
211
|
rlm@1
|
212 ;I find this quite shocking --- the jpg's do take longer to cache,
|
rlm@1
|
213 ;but the processing time is almost the same!
|
rlm@1
|
214
|
rlm@1
|
215 ;since lian is 434 MB as a bunch of jpg files, and 3.7 GB as .sr files,
|
rlm@1
|
216 ;I'll go with the jpgs.
|
rlm@1
|
217
|
rlm@1
|
218
|
rlm@1
|
219 ;; writing files
|
rlm@1
|
220
|
rlm@1
|
221 "JPG"
|
rlm@1
|
222 (time (write-video sarah (File. "/home/r/Desktop/clojure2.avi")))
|
rlm@1
|
223 "Elapsed time: 371541.024455 msecs"
|
rlm@1
|
224
|
rlm@1
|
225 "BMP"
|
rlm@1
|
226 (time (write-video sarah (File. "/home/r/Desktop/clojure3.avi")))
|
rlm@1
|
227 "Elapsed time: 382568.502361 msecs"
|
rlm@1
|
228
|
rlm@1
|
229 )
|