1 ;;; PHOROS -- Photogrammetric Road Survey
2 ;;; Copyright (C) 2010, 2011, 2012 Bert Burgemeister
4 ;;; This program is free software; you can redistribute it and/or modify
5 ;;; it under the terms of the GNU General Public License as published by
6 ;;; the Free Software Foundation; either version 2 of the License, or
7 ;;; (at your option) any later version.
9 ;;; This program is distributed in the hope that it will be useful,
10 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
11 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 ;;; GNU General Public License for more details.
14 ;;; You should have received a copy of the GNU General Public License along
15 ;;; with this program; if not, write to the Free Software Foundation, Inc.,
16 ;;; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 ;;; See file phoros.asd for how to choose between two alternative
22 ;;; image creation libraries, zpng and cl-png.
24 (deftype image-dimension
() '(unsigned-byte 16))
26 (deftype color
() '(unsigned-byte 8))
28 (deftype channels
() '(unsigned-byte 8))
33 '(simple-array color
3))
35 (defparameter *picture-header-length-tolerance
* 20
36 "Amount of leeway for the length of a picture header in a .pictures
39 (defun find-keyword-in-stream (stream keyword
&optional start-position search-range
)
40 "Return file-position in binary stream after first occurence of
42 (unless start-position
(setf start-position
0))
43 (let ((end-position (if search-range
44 (+ start-position search-range
)
45 most-positive-fixnum
)))
48 (file-position stream start-position
)
49 (let ((chunk-size (length keyword
)))
51 for next-chunk
= (let ((result (make-array
53 :element-type
'unsigned-byte
)))
54 (read-sequence result stream
)
55 (coerce (map 'vector
#'code-char result
)
57 if
(string/= next-chunk keyword
) do
58 (let ((next-position (- (file-position stream
) chunk-size -
1)))
59 (if (< next-position end-position
)
60 (file-position stream next-position
)
61 (return-from find-keyword-in-stream
)))
62 else return
(file-position stream
))))
63 (end-of-file () nil
))))
65 (defun find-keyword-value (path keyword
&optional start-position search-range
)
66 "Return value associated with keyword."
68 (find-keyword path keyword start-position search-range
)))
70 (with-open-file (stream path
)
71 (file-position stream start-of-value
)
72 (car (read-delimited-list #\
; stream))))))
74 (defun find-keyword (path keyword
&optional start-position search-range
)
75 "Return file-position after keyword."
76 (with-open-file (stream path
:element-type
'unsigned-byte
)
77 (find-keyword-in-stream stream keyword start-position search-range
)))
79 (defun read-huffman-table (stream &optional start-position
)
80 "Return in a hash table a huffman table read from stream. Start
81 either at stream's file position or at start-position."
82 (let ((huffman-codes-start (if start-position
84 (file-position stream
))))
85 (file-position stream
(+ (* 511 4) huffman-codes-start
)) ; start of lengths
86 (let* ((lengths (make-list 511))
87 (huffman-table (make-hash-table :size
1000 :test
#'equal
)))
88 (read-sequence lengths stream
)
89 (file-position stream huffman-codes-start
)
91 for i from -
255 to
255
93 for key
= (make-array (list length
) :element-type
'bit
)
94 for code
= (let ((raw (make-array '(4) :element-type
'unsigned-byte
))
96 (read-sequence raw stream
)
98 for raw-byte across raw
99 for code-position from
24 downto
0 by
8
100 do
(setf code-part
(dpb raw-byte
101 (byte 8 code-position
)
103 finally
(return code-part
)))
104 unless
(zerop length
)
106 for key-index from
0 below length
107 for code-index downfrom
(1- length
)
108 do
(setf (sbit key key-index
)
109 (ldb (byte 1 code-index
) code
)))
111 do
(setf (gethash key huffman-table
) i
))
114 (defun read-compressed-picture (stream start-position length
)
115 "Return a compressed picture in a bit array. Start either at
116 start-position or, if that is nil, at stream's file position."
117 (when start-position
(file-position stream start-position
))
118 (let ((raw (make-array (list length
) :element-type
'unsigned-byte
))
120 (make-array (list (* 8 length
)) :element-type
'bit
)))
121 (read-sequence raw stream
)
124 for byte-position from
0
126 for source-bit from
7 downto
0
127 for destination-bit from
0 to
7
128 do
(setf (sbit compressed-picture
130 (* 8 byte-position
)))
131 (ldb (byte 1 source-bit
) byte
)))
132 finally
(return compressed-picture
))))
134 (defun get-leading-byte (bit-array &optional
(start 0) &aux
(result 0))
135 "Return integer made of eight bits from bit-array."
137 for bit-array-index from start
138 for result-index from
7 downto
0
139 for result
= (dpb (sbit bit-array bit-array-index
)
140 (byte 1 result-index
) 0)
141 then
(dpb (sbit bit-array bit-array-index
) (byte 1 result-index
) result
)
142 finally
(return result
)))
145 (defun uncompress-picture (huffman-table compressed-picture
146 height width channels
&key reversep
)
147 "Return the Bayer pattern extracted from compressed-picture, turned
148 upside-down if reversep is t, in an (array color (height
149 width channels)), everything in channel 0."
150 (declare (optimize speed
)
151 (optimize (safety 0))
152 (type (unsigned-byte 16) height width
)
153 (type vector compressed-picture
))
154 (let* ((uncompressed-image
155 (png:make-image height width channels
8))
156 (uncompressed-image-vector
157 (make-array (list (* height width channels
))
159 :displaced-to uncompressed-image
))
160 (channel (if reversep
161 (1- channels
) ;becomes 0 by reversal
163 (compressed-picture-index 0)
166 for code of-type simple-bit-vector being the hash-key in huffman-table
167 minimize
(length code
)))
170 for code of-type simple-bit-vector being the hash-key in huffman-table
171 maximize
(length code
))))
172 (declare (type (signed-byte 48) compressed-picture-index
)
173 (type channels channels
))
175 for row from
0 below height
177 (setf (aref uncompressed-image row
0 channel
)
178 (get-leading-byte compressed-picture
179 (prog1 compressed-picture-index
180 (incf compressed-picture-index
8))))
181 (setf (aref uncompressed-image row
1 channel
)
182 (get-leading-byte compressed-picture
183 (prog1 compressed-picture-index
184 (incf compressed-picture-index
8))))
186 for column from
2 below width
187 for try-start of-type
(unsigned-byte 48) from compressed-picture-index
190 for key-length from min-key-length to max-key-length
191 for huffman-code
= (subseq compressed-picture
192 try-start
(+ try-start key-length
))
193 for pixel-delta-maybe
= (gethash huffman-code huffman-table
)
194 when pixel-delta-maybe
196 (setf (aref uncompressed-image row column channel
)
197 (- (aref uncompressed-image row
(- column
2) channel
)
198 (the fixnum pixel-delta-maybe
)))
199 and do
(incf try-start
(1- key-length
))
202 "Decoder out of step at row ~S, column ~S. Giving up."
205 (setf compressed-picture-index
(1+ try-start
))))
206 (when reversep
(setf uncompressed-image-vector
207 (reverse uncompressed-image-vector
)))
211 (defun uncompress-picture (huffman-table compressed-picture
212 height width channels
&key reversep
)
213 "Return the Bayer pattern extracted from compressed-picture, turned
214 upside-down if reversep is t, in an (array color (height
215 width channels)), everything in channel 0."
216 (declare (optimize (safety 0))
217 (type (unsigned-byte 16) height width
)
218 (type vector compressed-picture
))
219 (let* ((uncompressed-image
220 (make-array (list height width channels
)
221 :element-type
'color
))
222 (uncompressed-image-vector
223 (make-array (list (* height width channels
))
225 :displaced-to uncompressed-image
))
227 (channel (if reversep
228 (1- channels
) ;becomes 0 by reversal
230 (compressed-picture-index 0)
233 for code of-type simple-bit-vector
234 being the hash-key in huffman-table
235 minimize
(length code
)))
238 for code of-type simple-bit-vector
239 being the hash-key in huffman-table
240 maximize
(length code
))))
241 (declare (type (signed-byte 48) compressed-picture-index
)
242 (type channels channels
))
244 for row from
0 below height
246 (setf (aref uncompressed-image row
0 channel
)
247 (get-leading-byte compressed-picture
248 (prog1 compressed-picture-index
249 (incf compressed-picture-index
8))))
250 (setf (aref uncompressed-image row
1 channel
)
251 (get-leading-byte compressed-picture
252 (prog1 compressed-picture-index
253 (incf compressed-picture-index
8))))
255 for column from
2 below width
256 for try-start of-type
(unsigned-byte 48) from compressed-picture-index
259 for key-length from min-key-length to max-key-length
260 for huffman-code
= (subseq compressed-picture
261 try-start
(+ try-start key-length
))
262 for pixel-delta-maybe
= (gethash huffman-code huffman-table
)
263 when pixel-delta-maybe
265 (setf (aref uncompressed-image row column channel
)
266 (- (aref uncompressed-image row
(- column
2) channel
)
267 (the fixnum pixel-delta-maybe
)))
268 and do
(incf try-start
(1- key-length
))
271 "Decoder out of step at row ~S, column ~S. Giving up."
274 (setf compressed-picture-index
(1+ try-start
))))
275 (when reversep
(setf uncompressed-image-vector
276 (reverse uncompressed-image-vector
)))
279 (defun fetch-picture (stream start-position length height width channels
281 "Return the Bayer pattern taken from stream in an (array
282 \(unsigned-byte l8) (height width channels)), everything in color
283 channel 0. Start at start-position or, if that is nil, at stream's
285 (when start-position
(file-position stream start-position
))
287 (make-array (list height width channels
)
288 :element-type
'color
))
290 (make-array (list (* height width channels
))
292 :displaced-to image
))
294 (make-array (list length
) :element-type
'unsigned-byte
)))
297 (read-sequence image-vector stream
))
299 (error "Not implemented: ~
300 fetch-picture for (uncompressed) truecolor images")
301 ;; (read-sequence raw-image stream)
303 ;; for pixel across raw-image and red from 0 by 3 do
304 ;; (setf (svref png-image-data red) pixel))
306 (when reversep
(setf image-vector
(reverse image-vector
)))
309 (defun complete-horizontally (image row column color
)
310 "Fake a color component of a pixel based its neighbors."
311 (declare (optimize (safety 0))
313 #-png
(type image image
)
314 (type image-dimension row column
))
315 (setf (aref image row column color
)
316 (round (+ (the color
(aref image row
(1- column
) color
))
317 (the color
(aref image row
(1+ column
) color
)))
320 (defun complete-vertically (image row column color
)
321 "Fake a color component of a pixel based its neighbors."
322 (declare (optimize (safety 0))
324 #-png
(type image image
)
325 (type image-dimension row column
))
326 (setf (aref image row column color
)
327 (round (+ (the color
(aref image
(1- row
) column color
))
328 (the color
(aref image
(1+ row
) column color
)))
331 (defun complete-squarely (image row column color
)
332 "Fake a color component of a pixel based its neighbors."
333 (declare (optimize (safety 0))
335 #-png
(type image image
)
336 (type image-dimension row column
))
337 (setf (aref image row column color
)
338 (round (+ (the color
(aref image row
(1- column
) color
))
339 (the color
(aref image row
(1+ column
) color
))
340 (the color
(aref image
(1- row
) column color
))
341 (the color
(aref image
(1+ row
) column color
)))
344 (defun complete-diagonally (image row column color
)
345 "Fake a color component of a pixel based its neighbors."
346 (declare (optimize (safety 0))
348 #-png
(type image image
)
349 (type image-dimension row column
))
350 (setf (aref image row column color
)
351 (round (+ (the color
(aref image
(1- row
) (1- column
) color
))
352 (the color
(aref image
(1- row
) (1+ column
) color
))
353 (the color
(aref image
(1+ row
) (1- column
) color
))
354 (the color
(aref image
(1+ row
) (1+ column
) color
)))
357 (defun height (image) (array-dimension image
0))
358 (defun width (image) (array-dimension image
1))
359 (defun channels (image) (array-dimension image
2))
362 (defun demosaic-image (image bayer-pattern color-raiser brightenp
)
363 "Demosaic color image whose color channel 0 is supposed to be
364 filled with a Bayer color pattern. Return demosaiced image.
365 bayer-pattern is an array of 24-bit RGB values (red occupying the
366 least significant byte), describing the upper left corner of the
367 image. Currently, only pixels 0, 1 on row 0 are taken into account.
368 And, it's currently not even an array but a vector due to limitations
369 in postmodern. For a grayscale image do nothing. Then, if brightenp
370 is t and the image is too dark, make it brighter.
372 (declare (optimize (safety 0))
375 (when (= 3 (channels image
))
376 (let ((lowest-row (- (height image
) 2))
377 (rightmost-column (- (width image
) 2))
378 (bayer-pattern-red #x0000ff
)
379 (bayer-pattern-green #x00ff00
)
380 (bayer-pattern-blue #xff0000
)
381 (red 0) (green 1) (blue 2) ;color coordinate in IMAGE array
382 (color-raiser-red (coerce (elt color-raiser
0)
383 '(single-float -
10.0s0
10.0s0
)))
384 (color-raiser-green (coerce (elt color-raiser
1)
385 '(single-float -
10.0s0
10.0s0
)))
386 (color-raiser-blue (coerce (elt color-raiser
2)
387 '(single-float -
10.0s0
10.0s0
)))
388 (pix-depth 255) ;may some day become a function argument
389 complete-even-row-even-column
390 complete-even-row-odd-column
391 complete-odd-row-even-column
392 complete-odd-row-odd-column
393 colorize-even-row-even-column
394 colorize-even-row-odd-column
395 colorize-odd-row-even-column
396 colorize-odd-row-odd-column
)
397 (declare (type image-dimension lowest-row rightmost-column
)
399 (flet ((complete-green-on-red-row (row column
)
400 (complete-horizontally image row column red
)
401 (complete-vertically image row column blue
))
402 (complete-green-on-blue-row (row column
)
403 (complete-horizontally image row column blue
)
404 (complete-vertically image row column red
))
405 (complete-red (row column
)
406 (complete-squarely image row column green
)
407 (complete-diagonally image row column blue
))
408 (complete-blue (row column
)
409 (complete-squarely image row column green
)
410 (complete-diagonally image row column red
))
411 (colorize-red (row column
)
412 (setf (aref image row column red
)
414 (round (* color-raiser-red
417 (colorize-green (row column
)
418 (setf (aref image row column green
)
420 (round (* color-raiser-green
423 (colorize-blue (row column
)
424 (setf (aref image row column blue
)
426 (round (* color-raiser-blue
428 row column red
)))))))
430 ((= (aref bayer-pattern
0) bayer-pattern-red
)
431 (setf colorize-even-row-even-column
#'colorize-red
)
432 (setf colorize-even-row-odd-column
#'colorize-green
)
433 (setf colorize-odd-row-even-column
#'colorize-green
)
434 (setf colorize-odd-row-odd-column
#'colorize-blue
)
435 (setf complete-even-row-even-column
#'complete-red
)
436 (setf complete-even-row-odd-column
#'complete-green-on-red-row
)
437 (setf complete-odd-row-even-column
#'complete-green-on-blue-row
)
438 (setf complete-odd-row-odd-column
#'complete-blue
))
439 ((= (aref bayer-pattern
0) bayer-pattern-blue
)
440 (setf colorize-even-row-even-column
#'colorize-blue
)
441 (setf colorize-even-row-odd-column
#'colorize-green
)
442 (setf colorize-odd-row-even-column
#'colorize-green
)
443 (setf colorize-odd-row-odd-column
#'colorize-red
)
444 (setf complete-even-row-even-column
#'complete-blue
)
445 (setf complete-even-row-odd-column
#'complete-green-on-blue-row
)
446 (setf complete-odd-row-even-column
#'complete-green-on-red-row
)
447 (setf complete-odd-row-odd-column
#'complete-red
))
448 ((= (aref bayer-pattern
0) bayer-pattern-green
)
450 ((= (aref bayer-pattern
1) bayer-pattern-red
)
451 (setf colorize-even-row-even-column
#'colorize-green
)
452 (setf colorize-even-row-odd-column
#'colorize-red
)
453 (setf colorize-odd-row-even-column
#'colorize-blue
)
454 (setf colorize-odd-row-odd-column
#'colorize-green
)
455 (setf complete-even-row-even-column
#'complete-green-on-red-row
)
456 (setf complete-even-row-odd-column
#'complete-red
)
457 (setf complete-odd-row-even-column
#'complete-blue
)
458 (setf complete-odd-row-odd-column
#'complete-green-on-blue-row
))
459 ((= (aref bayer-pattern
1) bayer-pattern-blue
)
460 (setf colorize-even-row-even-column
#'colorize-green
)
461 (setf colorize-even-row-odd-column
#'colorize-blue
)
462 (setf colorize-odd-row-even-column
#'colorize-red
)
463 (setf colorize-odd-row-odd-column
#'colorize-green
)
464 (setf complete-even-row-even-column
#'complete-green-on-blue-row
)
465 (setf complete-even-row-odd-column
#'complete-blue
)
466 (setf complete-odd-row-even-column
#'complete-red
)
467 (setf complete-odd-row-odd-column
#'complete-green-on-red-row
))
468 (t (error "Don't know how to deal with a bayer-pattern of ~A"
470 (t (error "Don't know how to deal with a bayer-pattern of ~A"
472 ;; Recover colors (so far everything is in channel 0)
474 for row from
0 below
(the image-dimension
(height image
)) by
2
477 from
0 below
(the image-dimension
(width image
)) by
2
478 do
(funcall colorize-even-row-even-column row column
))
481 from
1 below
(the image-dimension
(width image
)) by
2
482 do
(funcall colorize-even-row-odd-column row column
)))
484 for row from
1 below
(the image-dimension
(height image
)) by
2
487 from
0 below
(the image-dimension
(width image
)) by
2
488 do
(funcall colorize-odd-row-even-column row column
))
491 from
1 below
(the image-dimension
(width image
)) by
2
492 do
(funcall colorize-odd-row-odd-column row column
)))
495 for row from
2 to lowest-row by
2 do
497 for column from
2 to rightmost-column by
2 do
498 (funcall complete-even-row-even-column row column
))
500 for column from
1 to rightmost-column by
2 do
501 (funcall complete-even-row-odd-column row column
)))
503 for row from
1 to lowest-row by
2 do
505 for column from
2 to rightmost-column by
2 do
506 (funcall complete-odd-row-even-column row column
))
508 for column from
1 to rightmost-column by
2 do
509 (funcall complete-odd-row-odd-column row column
))))))
510 (when brightenp
(brighten-maybe image
))
514 (defun demosaic-image (image bayer-pattern color-raiser brightenp
)
515 "Demosaic color image whose color channel 0 is supposed to be
516 filled with a Bayer color pattern. Return demosaiced image.
517 bayer-pattern is an array of 24-bit RGB values (red occupying the
518 least significant byte), describing the upper left corner of the
519 image. Currently, only pixels 0, 1 on row 0 are taken into account.
520 And, it's currently not even an array but a vector due to limitations
521 in postmodern. For a grayscale image do nothing. Then, if brightenp
522 is t and the image is too dark, make it brighter.
523 We are using cl-png."
524 (declare (optimize speed
)
525 (optimize (safety 0))
527 (when (= 3 (png:image-channels image
))
528 (let ((lowest-row (- (png:image-height image
) 2))
529 (rightmost-column (- (png:image-width image
) 2))
530 (bayer-pattern-red #x0000ff
)
531 (bayer-pattern-green #x00ff00
)
532 (bayer-pattern-blue #xff0000
)
533 (red 0) (green 1) (blue 2) ;color coordinate in IMAGE array
534 (color-raiser-red (coerce (elt color-raiser
0) '(single-float -
10.0s0
10.0s0
)))
535 (color-raiser-green (coerce (elt color-raiser
1) '(single-float -
10.0s0
10.0s0
)))
536 (color-raiser-blue (coerce (elt color-raiser
2) '(single-float -
10.0s0
10.0s0
)))
537 (pix-depth 255) ;may some day become a function argument
538 complete-even-row-even-column
539 complete-even-row-odd-column
540 complete-odd-row-even-column
541 complete-odd-row-odd-column
542 colorize-even-row-even-column
543 colorize-even-row-odd-column
544 colorize-odd-row-even-column
545 colorize-odd-row-odd-column
)
546 (declare (type image-dimension lowest-row rightmost-column
))
547 (flet ((complete-green-on-red-row (row column
)
548 (complete-horizontally image row column red
)
549 (complete-vertically image row column blue
))
550 (complete-green-on-blue-row (row column
)
551 (complete-horizontally image row column blue
)
552 (complete-vertically image row column red
))
553 (complete-red (row column
)
554 (complete-squarely image row column green
)
555 (complete-diagonally image row column blue
))
556 (complete-blue (row column
)
557 (complete-squarely image row column green
)
558 (complete-diagonally image row column red
))
559 (colorize-red (row column
)
560 (setf (aref image row column red
)
562 (round (* color-raiser-red
563 (the color
(aref image
564 row column red
)))))))
565 (colorize-green (row column
)
566 (setf (aref image row column green
)
568 (round (* color-raiser-green
569 (the color
(aref image
570 row column red
)))))))
571 (colorize-blue (row column
)
572 (setf (aref image row column blue
)
574 (round (* color-raiser-blue
575 (the color
(aref image
576 row column red
))))))))
578 ((= (aref bayer-pattern
0) bayer-pattern-red
)
579 (setf colorize-even-row-even-column
#'colorize-red
)
580 (setf colorize-even-row-odd-column
#'colorize-green
)
581 (setf colorize-odd-row-even-column
#'colorize-green
)
582 (setf colorize-odd-row-odd-column
#'colorize-blue
)
583 (setf complete-even-row-even-column
#'complete-red
)
584 (setf complete-even-row-odd-column
#'complete-green-on-red-row
)
585 (setf complete-odd-row-even-column
#'complete-green-on-blue-row
)
586 (setf complete-odd-row-odd-column
#'complete-blue
))
587 ((= (aref bayer-pattern
0) bayer-pattern-blue
)
588 (setf colorize-even-row-even-column
#'colorize-blue
)
589 (setf colorize-even-row-odd-column
#'colorize-green
)
590 (setf colorize-odd-row-even-column
#'colorize-green
)
591 (setf colorize-odd-row-odd-column
#'colorize-red
)
592 (setf complete-even-row-even-column
#'complete-blue
)
593 (setf complete-even-row-odd-column
#'complete-green-on-blue-row
)
594 (setf complete-odd-row-even-column
#'complete-green-on-red-row
)
595 (setf complete-odd-row-odd-column
#'complete-red
))
596 ((= (aref bayer-pattern
0) bayer-pattern-green
)
598 ((=(aref bayer-pattern
1) bayer-pattern-red
)
599 (setf colorize-even-row-even-column
#'colorize-green
)
600 (setf colorize-even-row-odd-column
#'colorize-red
)
601 (setf colorize-odd-row-even-column
#'colorize-blue
)
602 (setf colorize-odd-row-odd-column
#'colorize-green
)
603 (setf complete-even-row-even-column
#'complete-green-on-red-row
)
604 (setf complete-even-row-odd-column
#'complete-red
)
605 (setf complete-odd-row-even-column
#'complete-blue
)
606 (setf complete-odd-row-odd-column
#'complete-green-on-blue-row
))
607 ((=(aref bayer-pattern
1) bayer-pattern-blue
)
608 (setf colorize-even-row-even-column
#'colorize-green
)
609 (setf colorize-even-row-odd-column
#'colorize-blue
)
610 (setf colorize-odd-row-even-column
#'colorize-red
)
611 (setf colorize-odd-row-odd-column
#'colorize-green
)
612 (setf complete-even-row-even-column
#'complete-green-on-blue-row
)
613 (setf complete-even-row-odd-column
#'complete-blue
)
614 (setf complete-odd-row-even-column
#'complete-red
)
615 (setf complete-odd-row-odd-column
#'complete-green-on-red-row
))
616 (t (error "Don't know how to deal with a bayer-pattern of ~A"
618 (t (error "Don't know how to deal with a bayer-pattern of ~A"
620 ;; Recover colors (so far everything is in channel 0)
621 (loop for row from
0 below
(the image-dimension
(height image
)) by
2
622 do
(loop for column from
0 below
(the image-dimension
(width image
)) by
2
623 do
(funcall colorize-even-row-even-column row column
))
624 (loop for column from
1 below
(the image-dimension
(width image
)) by
2
625 do
(funcall colorize-even-row-odd-column row column
)))
626 (loop for row from
1 below
(the image-dimension
(height image
)) by
2
627 do
(loop for column from
0 below
(the image-dimension
(width image
)) by
2
628 do
(funcall colorize-odd-row-even-column row column
))
629 (loop for column from
1 below
(the image-dimension
(width image
)) by
2
630 do
(funcall colorize-odd-row-odd-column row column
)))
633 for row from
2 to lowest-row by
2 do
635 for column from
2 to rightmost-column by
2 do
636 (funcall complete-even-row-even-column row column
))
638 for column from
1 to rightmost-column by
2 do
639 (funcall complete-even-row-odd-column row column
)))
641 for row from
1 to lowest-row by
2 do
643 for column from
2 to rightmost-column by
2 do
644 (funcall complete-odd-row-even-column row column
))
646 for column from
1 to rightmost-column by
2 do
647 (funcall complete-odd-row-odd-column row column
))))))
648 (when brightenp
(brighten-maybe image
))
652 (defun brighten-maybe (image)
653 "Make image brighter if it is too dark.
655 (declare (optimize speed
)
656 (optimize (safety 0))
658 (multiple-value-bind (brightest-value darkest-value
)
660 (declare (type color brightest-value darkest-value
))
661 (when (< (the color brightest-value
) 200)
662 (dotimes (y (the image-dimension
(height image
)))
663 (dotimes (x (the image-dimension
(width image
)))
664 (dotimes (c (the channels
(channels image
)))
665 (setf (aref image y x c
)
666 (floor (* (the color
(- (aref image y x c
)
669 (- brightest-value darkest-value
)))))))))
672 (defun brighten-maybe (image)
673 "Make image brighter if it is too dark.
674 We are using cl-png."
675 (declare (optimize speed
)
676 (optimize (safety 0)))
677 (multiple-value-bind (brightest-value darkest-value
)
679 (declare (type color brightest-value darkest-value
))
680 (when (< (the color brightest-value
) 200)
681 (let ((image-vector (make-array (list (* (height image
)
685 :displaced-to image
)))
687 for i from
0 below
(length image-vector
)
688 do
(setf (aref image-vector i
)
689 (floor (* (the color
(- (aref image-vector i
)
692 (- brightest-value darkest-value
))))))))
695 (defun brightness (image)
696 "Return brightest value and darkest value of image.
698 (declare (optimize speed
)
699 (optimize (safety 0))
701 (let ((brightest-value 0)
703 (declare (type color brightest-value darkest-value
))
704 (dotimes (y (the image-dimension
(height image
)))
705 (dotimes (x (the image-dimension
(width image
)))
706 (dotimes (c (the channels
(channels image
)))
707 (let ((intensity (aref image y x c
)))
708 (setf brightest-value
(max intensity brightest-value
))
709 (setf darkest-value
(min intensity darkest-value
))))))
710 (values brightest-value darkest-value
)))
713 (defun brightness (image)
714 "Return brightest value and darkest value of image. We are using
716 (declare (optimize speed
))
718 (make-array (list (* (height image
) (width image
) (channels image
)))
720 :displaced-to image
)))
722 for brightness across image-vector
723 maximize brightness into brightest-value
724 minimize brightness into darkest-value
725 finally
(return (values brightest-value
728 (defun* send-png
(output-stream path start
729 &key
(color-raiser #(1 1 1))
731 &mandatory-key bayer-pattern
)
732 "Read an image at position start in .pictures file at path and send
733 it to the binary output-stream. Return UNIX trigger-time of image.
734 If brightenp is t, have it brightened up if necessary. If reversep is
735 t, turn it upside-down. Bayer-pattern is applied after turning, which
737 ;; TODO: bayer-pattern should be applied to the unturned image
738 (let ((blob-start (find-keyword path
"PICTUREDATA_BEGIN" start
))
739 (blob-size (find-keyword-value path
"dataSize=" start
))
740 (huffman-table-size (* 511 (+ 1 4)))
741 (image-height (find-keyword-value path
"height=" start
))
742 (image-width (find-keyword-value path
"width=" start
))
743 (compression-mode (find-keyword-value path
"compressed=" start
))
744 (channels (find-keyword-value path
"channels=" start
))
745 (trigger-time (find-keyword-value path
"timeTrigger=" start
)))
746 (assert (member channels
'(1 3)) ()
747 "Don't know how to deal with ~D-channel pixels." channels
)
748 (with-open-file (input-stream path
:element-type
'unsigned-byte
)
751 (ecase compression-mode
752 ((2 1) ;compressed with individual/pre-built huffman table
753 (uncompress-picture (read-huffman-table input-stream
755 (read-compressed-picture
757 (+ blob-start huffman-table-size
)
758 (- blob-size huffman-table-size
))
759 image-height image-width channels
762 (fetch-picture input-stream blob-start blob-size
763 image-height image-width channels
764 :reversep reversep
)))
768 (write-image image output-stream
)))
772 (defun write-image (image stream
)
773 "Write image array (height, width, channel) to stream."
774 (zpng:write-png-stream
776 (make-instance 'zpng
:png
777 :height
(height image
)
779 :color-type
(getf '(1 :grayscale
3 :truecolor
)
781 :image-data
(make-array
782 (list (* (height image
) (width image
)
785 :displaced-to image
)))
789 (defun write-image (image stream
)
790 "Write image array (height, width, channel) to stream."
791 (png:encode image stream
))
793 (defun find-nth-picture (n path
)
794 "Find file-position of zero-indexed nth picture in in .pictures file
796 (let ((estimated-header-length
797 (- (find-keyword path
"PICTUREHEADER_END")
798 (find-keyword path
"PICTUREHEADER_BEGIN")
799 *picture-header-length-tolerance
*))) ; allow for variation in dataSize and a few other parameters
803 (find-keyword path
"PICTUREHEADER_BEGIN" 0) then
804 (find-keyword path
"PICTUREHEADER_BEGIN"
805 (+ picture-start picture-length estimated-header-length
))
806 for picture-length
= (find-keyword-value path
807 "dataSize=" picture-start
)
808 finally
(return (- picture-start
(length "PICTUREHEADER_BEGIN"))))))
810 (defun* send-nth-png
(n output-stream path
811 &key
(color-raiser #(1 1 1))
813 &mandatory-key bayer-pattern
)
814 "Read image number n (zero-indexed) in .pictures file at path and
815 send it to the binary output-stream. Return UNIX trigger-time of
817 (send-png output-stream path
(find-nth-picture n path
)
818 :bayer-pattern bayer-pattern
821 :color-raiser color-raiser
))
825 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
826 ;; collect 4 single color pixels into a three-color one
827 ;; enhance contrast of grayscale images