annotate src/clojureDemo/VisionCore.clj @ 15:8ad629298649

major refactoring
author Robert McIntyre <rlm@mit.edu>
date Sun, 29 Aug 2010 19:02:26 -0400
parents 6d9bdaf919f7
children
rev   line source
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 )