Mercurial > lasercutter
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))) |