comparison src/laser/rasterize.clj @ 15:8ad629298649

major refactoring
author Robert McIntyre <rlm@mit.edu>
date Sun, 29 Aug 2010 19:02:26 -0400
parents db745c95aabd
children 52f544d05414
comparison
equal deleted inserted replaced
14:db745c95aabd 15:8ad629298649
1 (ns laser.rasterize) 1 (ns laser.rasterize
2 2 (:use [rlm
3 (import '(java.io File)) 3 image-utils
4 (import '(org.apache.commons.io FileUtils)) 4 map-utils]
5 (import '(javax.imageio ImageIO) ) 5 [clojure.contrib
6 (import '(javax.swing JFrame)) 6 [str-utils :only [str-join re-gsub]]
7 (import '(java.awt Color BorderLayout)) 7 [seq :only [indexed]]
8 (import '(ij ImagePlus IJ)) 8 [math]])
9 (import '(java.lang Math)) 9 (:import [ij ImagePlus IJ]))
10 (import '(java.awt Graphics2D Panel)) 10
11 (import '(ij Macro)) 11 ;(import '(java.io File))
12 12 ;(import '(org.apache.commons.io FileUtils))
13 (import '(java.io BufferedReader InputStreamReader)) 13 ;(import '(javax.imageio ImageIO) )
14 (import '(java.awt.image BufferedImage)) 14
15 15
16 ;(use 'clojure.contrib.str-utils) 16 (set! *print-length* 20)
17 ;(use 'clojure.contrib.seq-utils)
18 ;(use 'clojure.contrib.combinatorics)
19 ;(use 'clojure.contrib.duck-streams)
20
21 ;(use 'clojure.contrib.repl-utils)
22
23 ;(set! *print-length* 20)
24
25
26
27
28 (def feed 120) 17 (def feed 120)
29 (def dpi [500, 500]) 18 (def dpi [500, 500])
30 19
31 20
32 21 ;;; this process is divided into two tasks,
33 22 ;;; creating the raster g-code, which sweeps back and forth
34 (defn preserve-meta [f] 23 ;;; and creating the gmask, which turns the laser on and off.
35 (fn [& x] (with-meta 24
36 (apply f x) 25
37 (meta (last x))))) 26 ;;; we'll be using frame-hashes, which represent picutres as
38 27 ;;; a 3D vector field over 2D space, with the vectors representing
39 (defmulti frame-hash-multi class) 28 ;;; the rgb values at that particulat point.
40 29
41 30 (defn select-row
42 (defmethod frame-hash-multi ImagePlus 31 "returns a frame hash that is just a single line at the chosen y"
43 [image+] 32 [y window]
44 (with-meta 33 (filter-keys (comp (partial = y) last) window))
45 (let [buf (.. image+ getBufferedImage) 34
46 color (.getColorModel buf)] 35 (defn make-rows [pic]
47 (apply hash-map 36 (map (partial sort #(< (first %1) (first %2)))
48 (interleave 37 (partition-by last
49 (doall (for [x (range (.getWidth image+)) y (range (.getHeight image+))] 38 (sort (fn [[x1 y1][x2 y2]] (> y2 y1))
50 (vector x y))) 39 (map first (filter-vals (partial = black) pic))))))
51 (doall (for [x (range (.getWidth image+)) y (range (.getHeight image+))] 40
52 (let [data (.getRGB buf x y)] 41
53 (hash-map :r (bit-shift-right (bit-and 0xff0000 data) 16) 42 ;;; generate rastering g-code
54 :g (bit-shift-right (bit-and 0x00ff00 data) 8)
55 :b (bit-and 0x0000ff data))))))))
56 {:width (.getWidth image+) :height (.getHeight image+)}))
57
58
59 (defmethod frame-hash-multi String
60 [image-name]
61 (let [image+ (ImagePlus. image-name)]
62 (frame-hash-multi image+)))
63
64
65 (defn frame-hash
66 "yields a convienent representation for the pixles in an image.
67 Because of the size of the structvre generated, this must only be used
68 in a transient way so that java can do it's garbage collection."
69 [something]
70 (frame-hash-multi something))
71
72 ;(def frame-hash (preserve-meta frame-hash))
73
74
75
76
77 (def white {:r 255, :g 255, :b 255})
78 (def black {:r 0, :g 0, :b 0})
79
80
81
82 (defn rgb-euclidian
83 [{r1 :r g1 :g b1 :b} {r2 :r g2 :g b2 :b} ]
84 (expt (+ (expt (- r1 r2) 2)
85 (expt (- g1 g2) 2)
86 (expt (- b1 b2) 2)) 0.5))
87
88 (defn b&w
89 "turn everything strictly black or white"
90 [window]
91 (with-meta
92 (zipmap
93 (keys window)
94 (map (fn [rgb]
95 (if (> (rgb-euclidian rgb white) (rgb-euclidian rgb black))
96 black white))
97 (vals window))) (meta window)))
98
99
100 43
101 (defn raster-preamble [] 44 (defn raster-preamble []
102 (str-join \newline 45 (str-join \newline
103 ["M63 P0\nG61" 46 ["M63 P0\nG61"
104 (str \F feed) 47 (str "F" feed)
105 "M101" 48 "M101"
106 "M3 S1\n"])) 49 "M3 S1\n"]))
107 50
108 (defn raster-epilogue [] 51 (defn raster-epilogue []
109 (str-join \newline 52 (str-join \newline
110 ["M63 P0" 53 ["M63 P0"
111 "M5" 54 "M5"
112 "M2\n"])) 55 "M2\n"]))
113 56
114
115 (defn raster-comment [string] 57 (defn raster-comment [string]
116 (str "(" (re-gsub #"[()]" "" string) ")")) 58 (str "(" (re-gsub #"[()]" "" string) ")"))
117
118 (defn filter-keys [fun m]
119 (select-keys m (filter fun (keys m))))
120
121 (def filter-keys (preserve-meta filter-keys))
122
123 (defn filter-vals [fun m]
124 (into {} (filter (comp fun val) m)))
125
126 (def filter-vals (preserve-meta filter-vals))
127
128 (defn frame-hash->bufferedImage
129 [frame-hash]
130 (let [data (meta frame-hash)
131 image (BufferedImage. (:width data) (:height data) BufferedImage/TYPE_INT_BGR)]
132
133 (doall (for [element frame-hash]
134 (let [coord (key element)
135 rgb (val element)
136 packed-RGB
137 (+ (bit-shift-left (:r rgb) 16)
138 (bit-shift-left (:g rgb) 8)
139 (:b rgb))]
140 (.setRGB image (first coord) (last coord) packed-RGB))))
141 image))
142
143 (defmulti display "Creates a JFrame and displays a buffered image" class)
144
145 (defn- makePanel [image] (proxy [Panel] [] (paint [g] (.drawImage g image 0 0 nil))))
146
147 (defn select-row [x window]
148 (filter-keys (comp (partial = x) first) window))
149
150
151
152 (defmethod display
153 BufferedImage [image]
154 (let [panel (makePanel image)
155 frame (JFrame. "Oh Yeah!")]
156 (.add frame panel)
157 (.pack frame)
158 (.setVisible frame true )
159 (.setSize frame(.getWidth image) (.getHeight image))))
160
161 (defmethod display
162 ImagePlus [image]
163 (display (.getBufferedImage image)))
164
165 (defmethod display
166 clojure.lang.PersistentHashMap [frame-hash]
167 (display (frame-hash->bufferedImage frame-hash)))
168
169 (defmethod display
170 clojure.lang.PersistentArrayMap [frame-hash]
171 (display (frame-hash->bufferedImage frame-hash)))
172
173
174
175 59
176 60
177 ;this is a sequence of rows 61 ;this is a sequence of rows
178 62
179 ;(defn span [row] 63 ;(defn span [row]
199 (float (* y1 (/ y-dpi)))) 83 (float (* y1 (/ y-dpi))))
200 84
201 (format "G1 X%.3f Y%.3f\n" 85 (format "G1 X%.3f Y%.3f\n"
202 (float (* x2 (/ x-dpi))) 86 (float (* x2 (/ x-dpi)))
203 (float (* y2 (/ y-dpi))))))) 87 (float (* y2 (/ y-dpi)))))))
88
89
90 (defn generate-gcode [pic]
91 (str (raster-preamble)
92 (str-join ""
93 (map
94 (fn [[index row]]
95 (row->gcode dpi (even? index) row))
96 (indexed (make-rows pic))))
97 (raster-epilogue)))
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
204 116
205 (defn gather-row [row] 117 (defn gather-row [row]
206 (let [base [[(first (first row)) (first (first row))]]] 118 (let [base [[(first (first row)) (first (first row))]]]
207 ; (println base) 119 ; (println base)
208 (reduce 120 (reduce
226 138
227 139
228 140
229 141
230 (defn row->gmask [[x-dpi y-dpi] forward? row] 142 (defn row->gmask [[x-dpi y-dpi] forward? row]
231 ; (println forward?)
232 (let [start (float (* (/ x-dpi) (first (first 143 (let [start (float (* (/ x-dpi) (first (first
233 (if forward? 144 (if forward?
234 (reverse row) row)))))] 145 (reverse row) row)))))]
235
236 (let [preamble (if-not forward? 146 (let [preamble (if-not forward?
237 (format "0 0 0 %.3f\n" start) 147 (format "0 0 0 %.3f\n" start)
238 (format "0 0 1 %.3f\n" start)) 148 (format "0 0 1 %.3f\n" start))
239 body 149 body
240 (for [[x y] 150 (for [[x y]
241 (if forward? 151 (if forward?
242 (reverse (gather-row row)) 152 (reverse (gather-row row))
243 (gather-row row))] 153 (gather-row row))]
244 (let [x (float (* x (/ x-dpi))) 154 (let [x (float (* x (/ x-dpi)))
245 y (float (* y (/ x-dpi))) 155 y (float (* y (/ x-dpi)))]
246 ;; x (+ x 0.159)];; shift by a small margin. 156 ;; x (+ x 0.159)];; shift by a small margin.
247 (if-not forward? 157 (if-not forward?
248 (str (format "0 0 1 %.3f\n" x) 158 (str (format "0 0 1 %.3f\n" x)
249 (format "0 1 1 %.3f\n" y)) 159 (format "0 1 1 %.3f\n" y))
250 160
252 (format "0 1 0 %.3f\n" x)))))] 162 (format "0 1 0 %.3f\n" x)))))]
253 163
254 (str preamble (str-join "" body))))) 164 (str preamble (str-join "" body)))))
255 165
256 166
257
258 (defn make-rows [pic]
259 (map (partial sort #(< (first %1) (first %2)))
260 (partition-by last
261 (sort (fn [[x1 y1][x2 y2]] (> y2 y1))
262 (map first (filter-vals (partial = black) pic))))))
263
264
265
266 (defn generate-gmask [pic] 167 (defn generate-gmask [pic]
267
268 (str "1 0 0 0\n" 168 (str "1 0 0 0\n"
269 (str-join "" (map (fn [[index row]] 169 (str-join "" (map (fn [[index row]]
270 (row->gmask dpi (even? index) row)) 170 (row->gmask dpi (even? index) row))
271 (indexed (make-rows pic)))))) 171 (indexed (make-rows pic))))))
272 172
273 173
274 ;; 1 0 0 0 174
275 ;; 0 0 1 2.881 175
276 ;; 0 0 0 2.881 176 ;;;; testing
277 ;; 0 1 0 2.863 177
278 ;; 0 0 0 2.769 178 (defn generate-files [pic]
279 ;; 0 1 0 2.751 179 (println "made-image")
280 ;; 0 0 0 2.729 180 (spit "/home/r/kevin/out.ngc" (generate-gcode pic))
281 ;; 0 1 0 2.617 181 (println "/home/r/kevin/out.ngc")
282 ;; 0 0 0 2.593 182 (spit "/home/r/kevin/out.gmask" (generate-gmask pic))
283 ;; 0 1 0 2.561 183 (println "/home/r/kevin/out.gmask")
284 ;; 0 0 0 2.463 184 pic)
285 ;; 0 1 0 2.445 185
286 ;; 0 0 0 2.385 186 (defn update-state []
287 ;; 0 1 0 2.317 187 (def sing "/home/r/kevin/sing.png")
288 ;; 0 0 0 2.253 188 (def pic (frame-hash (ImagePlus. sing)))
289 ;; 0 1 0 2.233 189 (def pic (b&w pic)))
290 ;; 0 0 0 2.177 190
291 191 (defn compare-gen-fn [n f cmp]
292 192 (let [theirs (re-split #"\n" (slurp cmp))
293 193 ours (re-split #"\n" (f pic))]
294 (defn generate-gcode [pic] 194 (println (format "%1$-25s%2$s" "OURS" "THEIRS"))
295 (str (raster-preamble) 195 (println "_______________________________________")
296 196 (dorun (map (fn [[us them]] (println
297 (str-join "" 197 (format "%1$-25s%2$s" us them)))
298 (map 198 (take n (partition 2 (interleave ours theirs)))))))
299 (fn [index row] 199
300 (partial row->gcode dpi (even? index)) (indexed (make-rows pic)))) 200 (defn compare-gcode [n]
301 (raster-epilogue)))) 201 (compare-gen-fn n generate-gcode "/home/r/kevin/reference.ngc"))
302 202 (defn compare-gmask [n]
303 203 (compare-gen-fn n generate-gmask "/home/r/kevin/reference.gmask"))
304
305 ; (str-join "" (map (partial row->gcode dpi) (make-rows pic)))
306 ; (raster-epilogue)))
307 204
308 205
309 206
310 (defn rotate [degrees #^ImagePlus image] 207
311 (.rotate (.getChannelProcessor image) degrees) 208
312 image)
313
314 (defn map-keys [f m]
315 (into {} (map (fn [[key val]] [(f key) val]) m)))
316
317
318
319 (defn invert-frame-hash [pic]
320 (map-keys (fn [[x y]] [x (- (:height (meta pic)) y 1)]) pic ))
321
322
323 (defn generate-files [pic]
324 (let [image (invert-frame-hash (b&w (frame-hash (rotate 180 (ImagePlus. pic)))))]
325 (spit "/home/r/kevin/out.ngc" (generate-gcode image))
326 (spit "/home/r/kevin/out.gmask" (generate-gmask image))
327 image))
328
329
330
331 (defn update-state []
332 (def sing "/home/r/lasercutter/graster/signer4laser2x1.png")
333
334 (def pic (frame-hash (let [image (ImagePlus. sing)]
335 (.rotate (.getChannelProcessor image) 180)
336 image)))
337
338 (def pic (b&w pic)))
339
340
341
342
343
344
345
346
347
348
349
350 ;;;; testing
351
352 (defn init []
353 (let [stuff
354
355 (bound-fn []
356
357 (do
358 (println "hi everyone")
359 (def img "/home/r/kevin/sing.png")
360 (def pic (frame-hash (let [image (ImagePlus. img)]
361 (.rotate (.getChannelProcessor image) 180)
362 image)))
363
364
365 (def test-image
366 (invert-frame-hash (b&w (frame-hash (rotate 180 (ImagePlus. img))))))
367
368 (defn test-gmask []
369 (println (str-join "" (take 170 (generate-gmask test-image)))))
370
371 (println "ALL variables initialized!")
372
373 ))]
374 (.start
375 (Thread. stuff))))
376
377
378
379 (defn thread-test []
380
381 (let [temp *out*]
382 (.start
383 (Thread.
384 (fn []
385 (with-bindings {#'*out* temp}
386 (Thread/sleep 5000)
387 (println "hi")))))))
388
389
390 (comment
391 (do
392 (require 'rlm.quick)
393 (ns laser.rasterize)
394 (rlm.quick/dirty)
395 (use :reload-all 'laser.rasterize)
396 (undef map-keys)
397 (use :reload-all 'laser.rasterize)))