rlm@1: (ns clojureDemo.VisionCore) rlm@1: rlm@1: (import '(java.awt Rectangle Robot Toolkit) ) rlm@1: (import '(java.awt.image BufferedImage) ) rlm@1: (import '(java.awt Graphics2D Panel)) rlm@1: (import '(java.io File) ) rlm@1: (import '(javax.imageio ImageIO) ) rlm@1: (import '(javax.swing JFrame)) rlm@1: (import '(org.apache.commons.io FileUtils)) rlm@1: (import clojure.lang.LazySeq) rlm@1: (import '(name.audet.samuel.javacv.jna highgui cv cxcore)) rlm@1: (import '(name.audet.samuel.javacv CanvasFrame)) rlm@1: (import '(name.audet.samuel.javacv.jna cxcore$IplImage)) rlm@1: (import '(name.audet.samuel.javacv.jna highgui$CvCapture$PointerByReference)) rlm@1: (import '(name.audet.samuel.javacv.jna highgui$CvVideoWriter$PointerByReference)) rlm@1: (import '(name.audet.samuel.javacv.jna cxcore$IplImage$PointerByReference)) rlm@1: (import '(name.audet.samuel.javacv.jna cxcore$IplImage)) rlm@1: (import '(name.audet.samuel.javacv JavaCvErrorCallback)) rlm@1: (.redirectError (JavaCvErrorCallback.)) rlm@1: rlm@1: (use 'clojure.contrib.repl-utils) rlm@1: rlm@1: (def -inf Double/NEGATIVE_INFINITY) rlm@1: (def inf Double/POSITIVE_INFINITY) rlm@1: rlm@1: rlm@1: rlm@1: (def lian (File. "/home/r/Desktop/source-videos/lian1.mpeg")) rlm@1: (def look (File. "/home/r/Desktop/source-videos/dramatic_look.flv")) rlm@1: rlm@1: (def target (File. "/home/r/Desktop/output-vision/")) rlm@1: rlm@1: rlm@1: ;this is still a work in progress, I'll come back to it later when I understand rlm@1: ;jna more thoroughly. the important abstraction here is rlm@1: ;video-seq, which gives a lazy sequence of Intel Image Processing library images. rlm@1: rlm@1: (defn naturals [] (iterate inc 0)) rlm@1: rlm@1: (def ext "jpg") rlm@1: ;see below for the rationale for this choice of extention. rlm@1: rlm@1: (def cache-location "/home/r/Desktop/vision-cache/") rlm@1: rlm@1: (defn close-capture rlm@1: [capture] rlm@1: (highgui/cvReleaseCapture (highgui$CvCapture$PointerByReference. capture))) rlm@1: rlm@1: (defn close-writer rlm@1: [writer] (highgui/cvReleaseVideoWriter (highgui$CvVideoWriter$PointerByReference. writer))) rlm@1: rlm@1: (defn- cache-path rlm@1: [video] rlm@1: (File. cache-location (.getName video))) rlm@1: rlm@1: (defn- already-cached rlm@1: "this is the simplest and most retarded way to do it" rlm@1: [video] rlm@1: (.exists (cache-path video))) rlm@1: rlm@1: (defn- write-frame rlm@1: [capture target-dir n] rlm@1: (let [image (highgui/cvQueryFrame capture)] rlm@1: (if (nil? image) false rlm@1: (highgui/cvSaveImage (str (File. target-dir (str n "." ext))) image)))) rlm@1: rlm@1: (defn- write-frame-bad rlm@1: [capture target-dir n] rlm@1: (println (str "saving frame: " n)) rlm@1: (let [image (highgui/cvQueryFrame capture)] rlm@1: (if (nil? image) false rlm@1: ( ImageIO/write (.getBufferedImage image) ext (File. target-dir (str n "." ext)))))) rlm@1: rlm@1: (defn- write-frames rlm@1: [video target-dir] rlm@1: (let [capture (highgui/cvCreateFileCapture (.getPath video))] rlm@1: (dorun rlm@1: (for [n (naturals) :while (write-frame capture target-dir n) ] nil )) rlm@1: (highgui/cvReleaseCapture (highgui$CvCapture$PointerByReference. capture)))) rlm@1: rlm@1: (defn- cache-frames rlm@1: [cache-location video] rlm@1: (time rlm@1: (do rlm@1: (println "\"caching entire video structure... this will take a while... go get a snack or something :)\"") rlm@1: (FileUtils/deleteDirectory (cache-path video)) rlm@1: (FileUtils/forceMkdir (cache-path video)) rlm@1: (write-frames video (cache-path video))))) rlm@1: rlm@1: (defn cache rlm@1: [video] rlm@1: (if (already-cached video) nil (cache-frames cache-location video))) rlm@1: rlm@1: (defn video-len rlm@1: [video] (cache video) rlm@1: (alength (.list (cache-path video)))) rlm@1: (def video-len (memoize video-len)) rlm@1: rlm@1: (defn video-data rlm@1: "since the opencv version is so absolutely unreliable..." rlm@1: [video] rlm@1: (let rlm@1: [capture (highgui/cvCreateFileCapture (.getPath video)) rlm@1: info {:length (video-len video) rlm@1: :width (highgui/cvGetCaptureProperty capture highgui/CV_CAP_PROP_FRAME_WIDTH) rlm@1: :height (highgui/cvGetCaptureProperty capture highgui/CV_CAP_PROP_FRAME_HEIGHT) rlm@1: :fps (highgui/cvGetCaptureProperty capture highgui/CV_CAP_PROP_FPS) rlm@1: :codec (highgui/cvGetCaptureProperty capture highgui/CV_CAP_PROP_FOURCC)}] rlm@1: (close-capture capture) rlm@1: info)) rlm@1: (def video-data (memoize video-data)) rlm@1: rlm@1: (defn- video-frame rlm@1: [video n] rlm@1: (cache video) rlm@1: (let rlm@1: [c++-managed (highgui/cvLoadImage (str (File. (cache-path video) (str n "." ext))) highgui/CV_LOAD_IMAGE_COLOR) rlm@1: jvm-managed (.clone c++-managed)] rlm@1: ;this bit with the cloning is so I can deal with Garbage Collection once and for all. rlm@1: ;the cpp-managed image must be manually Garbage Collected, but it's clone is managed by rlm@1: ;the JVM's Garbage Collector. By getting rid of the c++ part right here and now, no rlm@1: ;other function has to worry about manual garbage collection ever again. rlm@1: ;Unfortunately, this doesn't seem to work for certain types of files. It's not file-size rlm@1: ;which is the issue, but something involving the image header. rlm@1: (cxcore/cvReleaseImage (.pointerByReference c++-managed)) rlm@1: jvm-managed rlm@1: )) rlm@1: rlm@1: rlm@1: (defn- video-frame-buffered rlm@1: "takes one frame from a video in constant time" rlm@1: [video n] rlm@1: (cache video) rlm@1: (ImageIO/read (File. (cache-path video) (str n "." ext)))) rlm@1: rlm@1: rlm@1: (defn- dumb-write rlm@1: [video n writer] rlm@1: (let rlm@1: [c++-managed (highgui/cvLoadImage (str (File. (cache-path video) (str n ext))) highgui/CV_LOAD_IMAGE_COLOR)] rlm@1: (highgui/cvWriteFrame writer c++-managed) rlm@1: (cxcore/cvReleaseImage (cxcore$IplImage$PointerByReference. c++-managed)))) rlm@1: rlm@1: (defn video-seq rlm@1: "makes a lazy sequence of IPL images" rlm@1: ;additionally, I want to pass metadata around with the sequence. rlm@1: [video] (cache video) rlm@1: (map #(video-frame video %) (range (video-len video)))) rlm@1: rlm@1: rlm@1: rlm@1: rlm@1: rlm@1: rlm@1: (comment rlm@1: rlm@1: ; I initially decided to use .sr because it loads the fastest out of all the rlm@1: ; formats opencv supports, under a simple benchmark of reading/writing rlm@1: ; a blank file of each type 100 times. rlm@1: rlm@1: ;I just kept changing the file extention at the REPL to generate these times. rlm@1: (def file "test.tiff") rlm@1: (do rlm@1: (time (dotimes [_ 100] (highgui/cvSaveImage (str cache-location file) ipl))) rlm@1: (time (dotimes [_ 100] (highgui/cvLoadImage (str cache-location file))))) rlm@1: rlm@1: ; Write Read rlm@1: (jpg 4404.000955 msecs 3397.8564 msecs) rlm@1: (jpeg 4376.138853 msecs 3482.990118 msecs) rlm@1: (jpeg 4253.721501 msecs 3414.004122 msecs) rlm@1: (bmp 3488.281695 msecs 786.883035 msecs) rlm@1: (dib 3589.010247 msecs 685.681985 msecs) rlm@1: (jpe 4288.541679 msecs 3359.819425 msecs) rlm@1: (png 10127.648557 msecs 3786.184994 msecs) rlm@1: (pbm 3880.794141 msecs 917.737667 msecs) rlm@1: (pgm 3879.710445 msecs 894.78237 msecs) rlm@1: (ppm 3938.319148 msecs 1014.412766 msecs) rlm@1: (sr 3510.893891 msecs 676.502596 msecs) rlm@1: (dib 3434.654784 msecs 737.495844 msecs) rlm@1: (bmp 3354.956726 msecs 783.353025 msecs) rlm@1: (ras 3351.400751 msecs 722.548007 msecs) rlm@1: (tiff 3657.893326 msecs 1361.576798 msecs) rlm@1: (tif 3594.753736 msecs 1254.568533 msecs) rlm@1: rlm@1: ;Ah, but now it's time for some more tests. rlm@1: ;I started using rlm@1: (def ext ".sr") rlm@1: ;, and an empty cache, and ran rlm@1: (cache lian) rlm@1: "caching entire video structure... this will take a while... go get a snack or something :)" rlm@1: "Elapsed time: 56486.816728 msecs" rlm@1: (time (dorun (video-seq lian))) rlm@1: "Elapsed time: 120515.66221 msecs" rlm@1: (time (dorun (video-seq lian))) rlm@1: "Elapsed time: 122867.82989 msecs" ;good agreement with times rlm@1: rlm@1: ;*erased vision cache with* rlm@1: ;*rm -rf ~/Desktop/vision-cache * rlm@1: (def ext ".bmp") rlm@1: (cache lian) rlm@1: "Elapsed time: 59613.624691 msecs" rlm@1: (time (dorun (video-seq lian))) rlm@1: "Elapsed time: 123850.390784 msecs" rlm@1: rlm@1: ;same process except with rlm@1: (def ext ".jpg") rlm@1: (cache lian) rlm@1: "Elapsed time: 139964.031921 msecs" rlm@1: (time (dorun (video-seq lian))) rlm@1: "Elapsed time: 127740.50204 msecs" rlm@1: rlm@1: ;I find this quite shocking --- the jpg's do take longer to cache, rlm@1: ;but the processing time is almost the same! rlm@1: rlm@1: ;since lian is 434 MB as a bunch of jpg files, and 3.7 GB as .sr files, rlm@1: ;I'll go with the jpgs. rlm@1: rlm@1: rlm@1: ;; writing files rlm@1: rlm@1: "JPG" rlm@1: (time (write-video sarah (File. "/home/r/Desktop/clojure2.avi"))) rlm@1: "Elapsed time: 371541.024455 msecs" rlm@1: rlm@1: "BMP" rlm@1: (time (write-video sarah (File. "/home/r/Desktop/clojure3.avi"))) rlm@1: "Elapsed time: 382568.502361 msecs" rlm@1: rlm@1: )