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
40 (start-position 0 start-position-p
)
42 "Return file-position in binary stream after first occurence of
43 keyword, or nil if the search is unsuccessful. Return nil if
44 start-position is explicitly nil."
45 (unless (and start-position-p
46 (null start-position
))
47 (unless start-position-p
(setf start-position
0))
48 (let* ((keyword-size (length keyword
))
49 (keyword-bytes (map 'vector
#'char-code keyword
))
51 (chunk (make-array (list (+ chunk-max-size
(1- keyword-size
)))
52 :element-type
'(unsigned-byte 8)))
53 (end-position-in-stream (if search-range
54 (+ start-position search-range
)
55 most-positive-fixnum
)))
57 for chunk-start-in-stream from start-position to end-position-in-stream by chunk-max-size
58 for chunk-size
= (progn (file-position stream chunk-start-in-stream
)
59 (read-sequence chunk stream
))
60 for end-in-chunk
= (min chunk-size
(- end-position-in-stream
61 chunk-start-in-stream
))
62 while
(plusp chunk-size
)
64 for i from
0 to end-in-chunk
65 for correct-characters
= (mismatch keyword-bytes chunk
68 do
(when (or (null correct-characters
)
69 (= correct-characters keyword-size
))
70 (return-from find-keyword-in-stream
71 (+ chunk-start-in-stream i keyword-size
))))))))
73 (defun find-keyword-value (path keyword
&optional start-position search-range
)
74 "Return value associated with keyword."
76 (find-keyword path keyword start-position search-range
)))
78 (with-open-file (stream path
)
79 (file-position stream start-of-value
)
80 (car (read-delimited-list #\
; stream))))))
82 (defun find-keyword (path keyword
&optional
(start-position 0) search-range
)
83 "Return file-position after keyword."
84 (with-open-file (stream path
:element-type
'unsigned-byte
)
85 (find-keyword-in-stream stream keyword start-position search-range
)))
87 (defun read-huffman-table (stream &optional start-position
)
88 "Return in a hash table a huffman table read from stream. Start
89 either at stream's file position or at start-position."
90 (let ((huffman-codes-start (if start-position
92 (file-position stream
))))
93 (file-position stream
(+ (* 511 4) huffman-codes-start
)) ; start of lengths
94 (let* ((lengths (make-list 511))
95 (huffman-table (make-hash-table :size
1000 :test
#'equal
)))
96 (read-sequence lengths stream
)
97 (file-position stream huffman-codes-start
)
99 for i from -
255 to
255
100 for length in lengths
101 for key
= (make-array (list length
) :element-type
'bit
)
102 for code
= (let ((raw (make-array '(4) :element-type
'unsigned-byte
))
104 (read-sequence raw stream
)
106 for raw-byte across raw
107 for code-position from
24 downto
0 by
8
108 do
(setf code-part
(dpb raw-byte
109 (byte 8 code-position
)
111 finally
(return code-part
)))
112 unless
(zerop length
)
114 for key-index from
0 below length
115 for code-index downfrom
(1- length
)
116 do
(setf (sbit key key-index
)
117 (ldb (byte 1 code-index
) code
)))
119 do
(setf (gethash key huffman-table
) i
))
122 (defun read-compressed-picture (stream start-position length
)
123 "Return a compressed picture in a bit array. Start either at
124 start-position or, if that is nil, at stream's file position."
125 (when start-position
(file-position stream start-position
))
126 (let ((raw (make-array (list length
) :element-type
'unsigned-byte
))
128 (make-array (list (* 8 length
)) :element-type
'bit
)))
129 (read-sequence raw stream
)
132 for byte-position from
0
134 for source-bit from
7 downto
0
135 for destination-bit from
0 to
7
136 do
(setf (sbit compressed-picture
138 (* 8 byte-position
)))
139 (ldb (byte 1 source-bit
) byte
)))
140 finally
(return compressed-picture
))))
142 (defun get-leading-byte (bit-array &optional
(start 0) &aux
(result 0))
143 "Return integer made of eight bits from bit-array."
145 for bit-array-index from start
146 for result-index from
7 downto
0
147 for result
= (dpb (sbit bit-array bit-array-index
)
148 (byte 1 result-index
) 0)
149 then
(dpb (sbit bit-array bit-array-index
) (byte 1 result-index
) result
)
150 finally
(return result
)))
153 (defun uncompress-picture (huffman-table compressed-picture
154 height width channels
&key reversep
)
155 "Return the Bayer pattern extracted from compressed-picture, turned
156 upside-down if reversep is t, in an (array color (height
157 width channels)), everything in channel 0."
158 (declare (optimize speed
)
159 (optimize (safety 0))
160 (type (unsigned-byte 16) height width
)
161 (type vector compressed-picture
))
162 (let* ((uncompressed-image
163 (png:make-image height width channels
8))
164 (uncompressed-image-vector
165 (make-array (list (* height width channels
))
167 :displaced-to uncompressed-image
))
168 (channel (if reversep
169 (1- channels
) ;becomes 0 by reversal
171 (compressed-picture-index 0)
174 for code of-type simple-bit-vector being the hash-key in huffman-table
175 minimize
(length code
)))
178 for code of-type simple-bit-vector being the hash-key in huffman-table
179 maximize
(length code
))))
180 (declare (type (signed-byte 48) compressed-picture-index
)
181 (type channels channels
))
183 for row from
0 below height
185 (setf (aref uncompressed-image row
0 channel
)
186 (get-leading-byte compressed-picture
187 (prog1 compressed-picture-index
188 (incf compressed-picture-index
8))))
189 (setf (aref uncompressed-image row
1 channel
)
190 (get-leading-byte compressed-picture
191 (prog1 compressed-picture-index
192 (incf compressed-picture-index
8))))
194 for column from
2 below width
195 for try-start of-type
(unsigned-byte 48) from compressed-picture-index
198 for key-length from min-key-length to max-key-length
199 for huffman-code
= (subseq compressed-picture
200 try-start
(+ try-start key-length
))
201 for pixel-delta-maybe
= (gethash huffman-code huffman-table
)
202 when pixel-delta-maybe
204 (setf (aref uncompressed-image row column channel
)
205 (- (aref uncompressed-image row
(- column
2) channel
)
206 (the fixnum pixel-delta-maybe
)))
207 and do
(incf try-start
(1- key-length
))
210 "Decoder out of step at row ~S, column ~S. Giving up."
213 (setf compressed-picture-index
(1+ try-start
))))
214 (when reversep
(reverse-displaced-vector uncompressed-image-vector
))
218 (defun uncompress-picture (huffman-table compressed-picture
219 height width channels
&key reversep
)
220 "Return the Bayer pattern extracted from compressed-picture, turned
221 upside-down if reversep is t, in an (array color (height
222 width channels)), everything in channel 0."
223 (declare (optimize speed
)
224 (optimize (safety 0))
225 (type (unsigned-byte 16) height width
)
226 (type vector compressed-picture
))
227 (let* ((uncompressed-image
228 (make-array (list height width channels
)
229 :element-type
'color
))
230 (uncompressed-image-vector
231 (make-array (list (* height width channels
))
233 :displaced-to uncompressed-image
))
235 (channel (if reversep
236 (1- channels
) ;becomes 0 by reversal
238 (compressed-picture-index 0)
241 for code of-type simple-bit-vector
242 being the hash-key in huffman-table
243 minimize
(length code
)))
246 for code of-type simple-bit-vector
247 being the hash-key in huffman-table
248 maximize
(length code
))))
249 (declare (type (signed-byte 48) compressed-picture-index
)
250 (type channels channels
))
252 for row from
0 below height
254 (setf (aref uncompressed-image row
0 channel
)
255 (get-leading-byte compressed-picture
256 (prog1 compressed-picture-index
257 (incf compressed-picture-index
8))))
258 (setf (aref uncompressed-image row
1 channel
)
259 (get-leading-byte compressed-picture
260 (prog1 compressed-picture-index
261 (incf compressed-picture-index
8))))
263 for column from
2 below width
264 for try-start of-type
(unsigned-byte 48) from compressed-picture-index
267 for key-length from min-key-length to max-key-length
268 for huffman-code
= (subseq compressed-picture
269 try-start
(+ try-start key-length
))
270 for pixel-delta-maybe
= (gethash huffman-code huffman-table
)
271 when pixel-delta-maybe
273 (setf (aref uncompressed-image row column channel
)
274 (- (aref uncompressed-image row
(- column
2) channel
)
275 (the fixnum pixel-delta-maybe
)))
276 and do
(incf try-start
(1- key-length
))
279 "Decoder out of step at row ~S, column ~S. Giving up."
282 (setf compressed-picture-index
(1+ try-start
))))
283 (when reversep
(reverse-displaced-vector uncompressed-image-vector
))
286 (defun fetch-picture (stream start-position length height width channels
288 "Return the Bayer pattern taken from stream in an (array
289 \(unsigned-byte l8) (height width channels)), everything in color
290 channel 0. Start at start-position or, if that is nil, at stream's
292 (when start-position
(file-position stream start-position
))
294 (make-array (list height width channels
)
295 :element-type
'color
))
297 (make-array (list (* height width channels
))
299 :displaced-to image
))
301 (make-array (list length
) :element-type
'unsigned-byte
)))
304 (read-sequence image-vector stream
))
306 (error "Not implemented: ~
307 fetch-picture for (uncompressed) truecolor images")
308 ;; (read-sequence raw-image stream)
310 ;; for pixel across raw-image and red from 0 by 3 do
311 ;; (setf (svref png-image-data red) pixel))
313 (when reversep
(reverse-displaced-vector image-vector
))
316 (defun reverse-displaced-vector (vector)
317 "Reverse elements of vector of unsigned-byte in-place."
319 for cell across
(reverse vector
)
321 do
(setf (aref vector i
) cell
)))
323 (defun complete-horizontally (image row column color
)
324 "Fake a color component of a pixel based its neighbors."
325 (declare (optimize (safety 0))
327 #-phoros-uses-cl-png
(type image image
)
328 (type image-dimension row column
))
329 (setf (aref image row column color
)
330 (round (+ (the color
(aref image row
(1- column
) color
))
331 (the color
(aref image row
(1+ column
) color
)))
334 (defun complete-vertically (image row column color
)
335 "Fake a color component of a pixel based its neighbors."
336 (declare (optimize (safety 0))
338 #-phoros-uses-cl-png
(type image image
)
339 (type image-dimension row column
))
340 (setf (aref image row column color
)
341 (round (+ (the color
(aref image
(1- row
) column color
))
342 (the color
(aref image
(1+ row
) column color
)))
345 (defun complete-squarely (image row column color
)
346 "Fake a color component of a pixel based its neighbors."
347 (declare (optimize (safety 0))
349 #-phoros-uses-cl-png
(type image image
)
350 (type image-dimension row column
))
351 (setf (aref image row column color
)
352 (round (+ (the color
(aref image row
(1- column
) color
))
353 (the color
(aref image row
(1+ column
) color
))
354 (the color
(aref image
(1- row
) column color
))
355 (the color
(aref image
(1+ row
) column color
)))
358 (defun complete-diagonally (image row column color
)
359 "Fake a color component of a pixel based its neighbors."
360 (declare (optimize (safety 0))
362 #-phoros-uses-cl-png
(type image image
)
363 (type image-dimension row column
))
364 (setf (aref image row column color
)
365 (round (+ (the color
(aref image
(1- row
) (1- column
) color
))
366 (the color
(aref image
(1- row
) (1+ column
) color
))
367 (the color
(aref image
(1+ row
) (1- column
) color
))
368 (the color
(aref image
(1+ row
) (1+ column
) color
)))
371 (defun height (image) (array-dimension image
0))
372 (defun width (image) (array-dimension image
1))
373 (defun channels (image) (array-dimension image
2))
376 (defun demosaic-image (image bayer-pattern color-raiser brightenp
)
377 "Demosaic color image whose color channel 0 is supposed to be
378 filled with a Bayer color pattern. Return demosaiced image.
379 bayer-pattern is an array of 24-bit RGB values (red occupying the
380 least significant byte), describing the upper left corner of the
381 image. Currently, only pixels 0, 1 on row 0 are taken into account.
382 And, it's currently not even an array but a vector due to limitations
383 in postmodern. For a grayscale image do nothing. Then, if brightenp
384 is t and the image is too dark, make it brighter.
386 (declare (optimize (safety 0))
389 (when (= 3 (channels image
))
390 (let ((lowest-row (- (height image
) 2))
391 (rightmost-column (- (width image
) 2))
392 (bayer-pattern-red #x0000ff
)
393 (bayer-pattern-green #x00ff00
)
394 (bayer-pattern-blue #xff0000
)
395 (red 0) (green 1) (blue 2) ;color coordinate in IMAGE array
396 (color-raiser-red (coerce (elt color-raiser
0)
397 '(single-float -
10.0s0
10.0s0
)))
398 (color-raiser-green (coerce (elt color-raiser
1)
399 '(single-float -
10.0s0
10.0s0
)))
400 (color-raiser-blue (coerce (elt color-raiser
2)
401 '(single-float -
10.0s0
10.0s0
)))
402 (pix-depth 255) ;may some day become a function argument
403 complete-even-row-even-column
404 complete-even-row-odd-column
405 complete-odd-row-even-column
406 complete-odd-row-odd-column
407 colorize-even-row-even-column
408 colorize-even-row-odd-column
409 colorize-odd-row-even-column
410 colorize-odd-row-odd-column
)
411 (declare (type image-dimension lowest-row rightmost-column
)
413 (flet ((complete-green-on-red-row (row column
)
414 (complete-horizontally image row column red
)
415 (complete-vertically image row column blue
))
416 (complete-green-on-blue-row (row column
)
417 (complete-horizontally image row column blue
)
418 (complete-vertically image row column red
))
419 (complete-red (row column
)
420 (complete-squarely image row column green
)
421 (complete-diagonally image row column blue
))
422 (complete-blue (row column
)
423 (complete-squarely image row column green
)
424 (complete-diagonally image row column red
))
425 (colorize-red (row column
)
426 (setf (aref image row column red
)
428 (round (* color-raiser-red
431 (colorize-green (row column
)
432 (setf (aref image row column green
)
434 (round (* color-raiser-green
437 (colorize-blue (row column
)
438 (setf (aref image row column blue
)
440 (round (* color-raiser-blue
442 row column red
)))))))
444 ((= (aref bayer-pattern
0) bayer-pattern-red
)
445 (setf colorize-even-row-even-column
#'colorize-red
)
446 (setf colorize-even-row-odd-column
#'colorize-green
)
447 (setf colorize-odd-row-even-column
#'colorize-green
)
448 (setf colorize-odd-row-odd-column
#'colorize-blue
)
449 (setf complete-even-row-even-column
#'complete-red
)
450 (setf complete-even-row-odd-column
#'complete-green-on-red-row
)
451 (setf complete-odd-row-even-column
#'complete-green-on-blue-row
)
452 (setf complete-odd-row-odd-column
#'complete-blue
))
453 ((= (aref bayer-pattern
0) bayer-pattern-blue
)
454 (setf colorize-even-row-even-column
#'colorize-blue
)
455 (setf colorize-even-row-odd-column
#'colorize-green
)
456 (setf colorize-odd-row-even-column
#'colorize-green
)
457 (setf colorize-odd-row-odd-column
#'colorize-red
)
458 (setf complete-even-row-even-column
#'complete-blue
)
459 (setf complete-even-row-odd-column
#'complete-green-on-blue-row
)
460 (setf complete-odd-row-even-column
#'complete-green-on-red-row
)
461 (setf complete-odd-row-odd-column
#'complete-red
))
462 ((= (aref bayer-pattern
0) bayer-pattern-green
)
464 ((= (aref bayer-pattern
1) bayer-pattern-red
)
465 (setf colorize-even-row-even-column
#'colorize-green
)
466 (setf colorize-even-row-odd-column
#'colorize-red
)
467 (setf colorize-odd-row-even-column
#'colorize-blue
)
468 (setf colorize-odd-row-odd-column
#'colorize-green
)
469 (setf complete-even-row-even-column
#'complete-green-on-red-row
)
470 (setf complete-even-row-odd-column
#'complete-red
)
471 (setf complete-odd-row-even-column
#'complete-blue
)
472 (setf complete-odd-row-odd-column
#'complete-green-on-blue-row
))
473 ((= (aref bayer-pattern
1) bayer-pattern-blue
)
474 (setf colorize-even-row-even-column
#'colorize-green
)
475 (setf colorize-even-row-odd-column
#'colorize-blue
)
476 (setf colorize-odd-row-even-column
#'colorize-red
)
477 (setf colorize-odd-row-odd-column
#'colorize-green
)
478 (setf complete-even-row-even-column
#'complete-green-on-blue-row
)
479 (setf complete-even-row-odd-column
#'complete-blue
)
480 (setf complete-odd-row-even-column
#'complete-red
)
481 (setf complete-odd-row-odd-column
#'complete-green-on-red-row
))
482 (t (error "Don't know how to deal with a bayer-pattern of ~A"
484 (t (error "Don't know how to deal with a bayer-pattern of ~A"
486 ;; Recover colors (so far everything is in channel 0)
488 for row from
0 below
(the image-dimension
(height image
)) by
2
491 from
0 below
(the image-dimension
(width image
)) by
2
492 do
(funcall colorize-even-row-even-column row column
))
495 from
1 below
(the image-dimension
(width image
)) by
2
496 do
(funcall colorize-even-row-odd-column row column
)))
498 for row from
1 below
(the image-dimension
(height image
)) by
2
501 from
0 below
(the image-dimension
(width image
)) by
2
502 do
(funcall colorize-odd-row-even-column row column
))
505 from
1 below
(the image-dimension
(width image
)) by
2
506 do
(funcall colorize-odd-row-odd-column row column
)))
509 for row from
2 to lowest-row by
2 do
511 for column from
2 to rightmost-column by
2 do
512 (funcall complete-even-row-even-column row column
))
514 for column from
1 to rightmost-column by
2 do
515 (funcall complete-even-row-odd-column row column
)))
517 for row from
1 to lowest-row by
2 do
519 for column from
2 to rightmost-column by
2 do
520 (funcall complete-odd-row-even-column row column
))
522 for column from
1 to rightmost-column by
2 do
523 (funcall complete-odd-row-odd-column row column
))))))
524 (when brightenp
(brighten-maybe image
))
528 (defun demosaic-image (image bayer-pattern color-raiser brightenp
)
529 "Demosaic color image whose color channel 0 is supposed to be
530 filled with a Bayer color pattern. Return demosaiced image.
531 bayer-pattern is an array of 24-bit RGB values (red occupying the
532 least significant byte), describing the upper left corner of the
533 image. Currently, only pixels 0, 1 on row 0 are taken into account.
534 And, it's currently not even an array but a vector due to limitations
535 in postmodern. For a grayscale image do nothing. Then, if brightenp
536 is t and the image is too dark, make it brighter.
537 We are using cl-png."
538 (declare (optimize speed
)
539 (optimize (safety 0))
541 (when (= 3 (png:image-channels image
))
542 (let ((lowest-row (- (png:image-height image
) 2))
543 (rightmost-column (- (png:image-width image
) 2))
544 (bayer-pattern-red #x0000ff
)
545 (bayer-pattern-green #x00ff00
)
546 (bayer-pattern-blue #xff0000
)
547 (red 0) (green 1) (blue 2) ;color coordinate in IMAGE array
548 (color-raiser-red (coerce (elt color-raiser
0) '(single-float -
10.0s0
10.0s0
)))
549 (color-raiser-green (coerce (elt color-raiser
1) '(single-float -
10.0s0
10.0s0
)))
550 (color-raiser-blue (coerce (elt color-raiser
2) '(single-float -
10.0s0
10.0s0
)))
551 (pix-depth 255) ;may some day become a function argument
552 complete-even-row-even-column
553 complete-even-row-odd-column
554 complete-odd-row-even-column
555 complete-odd-row-odd-column
556 colorize-even-row-even-column
557 colorize-even-row-odd-column
558 colorize-odd-row-even-column
559 colorize-odd-row-odd-column
)
560 (declare (type image-dimension lowest-row rightmost-column
))
561 (flet ((complete-green-on-red-row (row column
)
562 (complete-horizontally image row column red
)
563 (complete-vertically image row column blue
))
564 (complete-green-on-blue-row (row column
)
565 (complete-horizontally image row column blue
)
566 (complete-vertically image row column red
))
567 (complete-red (row column
)
568 (complete-squarely image row column green
)
569 (complete-diagonally image row column blue
))
570 (complete-blue (row column
)
571 (complete-squarely image row column green
)
572 (complete-diagonally image row column red
))
573 (colorize-red (row column
)
574 (setf (aref image row column red
)
576 (round (* color-raiser-red
577 (the color
(aref image
578 row column red
)))))))
579 (colorize-green (row column
)
580 (setf (aref image row column green
)
582 (round (* color-raiser-green
583 (the color
(aref image
584 row column red
)))))))
585 (colorize-blue (row column
)
586 (setf (aref image row column blue
)
588 (round (* color-raiser-blue
589 (the color
(aref image
590 row column red
))))))))
592 ((= (aref bayer-pattern
0) bayer-pattern-red
)
593 (setf colorize-even-row-even-column
#'colorize-red
)
594 (setf colorize-even-row-odd-column
#'colorize-green
)
595 (setf colorize-odd-row-even-column
#'colorize-green
)
596 (setf colorize-odd-row-odd-column
#'colorize-blue
)
597 (setf complete-even-row-even-column
#'complete-red
)
598 (setf complete-even-row-odd-column
#'complete-green-on-red-row
)
599 (setf complete-odd-row-even-column
#'complete-green-on-blue-row
)
600 (setf complete-odd-row-odd-column
#'complete-blue
))
601 ((= (aref bayer-pattern
0) bayer-pattern-blue
)
602 (setf colorize-even-row-even-column
#'colorize-blue
)
603 (setf colorize-even-row-odd-column
#'colorize-green
)
604 (setf colorize-odd-row-even-column
#'colorize-green
)
605 (setf colorize-odd-row-odd-column
#'colorize-red
)
606 (setf complete-even-row-even-column
#'complete-blue
)
607 (setf complete-even-row-odd-column
#'complete-green-on-blue-row
)
608 (setf complete-odd-row-even-column
#'complete-green-on-red-row
)
609 (setf complete-odd-row-odd-column
#'complete-red
))
610 ((= (aref bayer-pattern
0) bayer-pattern-green
)
612 ((=(aref bayer-pattern
1) bayer-pattern-red
)
613 (setf colorize-even-row-even-column
#'colorize-green
)
614 (setf colorize-even-row-odd-column
#'colorize-red
)
615 (setf colorize-odd-row-even-column
#'colorize-blue
)
616 (setf colorize-odd-row-odd-column
#'colorize-green
)
617 (setf complete-even-row-even-column
#'complete-green-on-red-row
)
618 (setf complete-even-row-odd-column
#'complete-red
)
619 (setf complete-odd-row-even-column
#'complete-blue
)
620 (setf complete-odd-row-odd-column
#'complete-green-on-blue-row
))
621 ((=(aref bayer-pattern
1) bayer-pattern-blue
)
622 (setf colorize-even-row-even-column
#'colorize-green
)
623 (setf colorize-even-row-odd-column
#'colorize-blue
)
624 (setf colorize-odd-row-even-column
#'colorize-red
)
625 (setf colorize-odd-row-odd-column
#'colorize-green
)
626 (setf complete-even-row-even-column
#'complete-green-on-blue-row
)
627 (setf complete-even-row-odd-column
#'complete-blue
)
628 (setf complete-odd-row-even-column
#'complete-red
)
629 (setf complete-odd-row-odd-column
#'complete-green-on-red-row
))
630 (t (error "Don't know how to deal with a bayer-pattern of ~A"
632 (t (error "Don't know how to deal with a bayer-pattern of ~A"
634 ;; Recover colors (so far everything is in channel 0)
635 (loop for row from
0 below
(the image-dimension
(height image
)) by
2
636 do
(loop for column from
0 below
(the image-dimension
(width image
)) by
2
637 do
(funcall colorize-even-row-even-column row column
))
638 (loop for column from
1 below
(the image-dimension
(width image
)) by
2
639 do
(funcall colorize-even-row-odd-column row column
)))
640 (loop for row from
1 below
(the image-dimension
(height image
)) by
2
641 do
(loop for column from
0 below
(the image-dimension
(width image
)) by
2
642 do
(funcall colorize-odd-row-even-column row column
))
643 (loop for column from
1 below
(the image-dimension
(width image
)) by
2
644 do
(funcall colorize-odd-row-odd-column row column
)))
647 for row from
2 to lowest-row by
2 do
649 for column from
2 to rightmost-column by
2 do
650 (funcall complete-even-row-even-column row column
))
652 for column from
1 to rightmost-column by
2 do
653 (funcall complete-even-row-odd-column row column
)))
655 for row from
1 to lowest-row by
2 do
657 for column from
2 to rightmost-column by
2 do
658 (funcall complete-odd-row-even-column row column
))
660 for column from
1 to rightmost-column by
2 do
661 (funcall complete-odd-row-odd-column row column
))))))
662 (when brightenp
(brighten-maybe image
))
666 (defun brighten-maybe (image)
667 "Make image brighter if it is too dark.
669 (declare (optimize speed
)
670 (optimize (safety 0))
672 (multiple-value-bind (brightest-value darkest-value
)
674 (declare (type color brightest-value darkest-value
))
675 (when (< (the color brightest-value
) 200)
676 (dotimes (y (the image-dimension
(height image
)))
677 (dotimes (x (the image-dimension
(width image
)))
678 (dotimes (c (the channels
(channels image
)))
679 (setf (aref image y x c
)
680 (floor (* (the color
(- (aref image y x c
)
683 (- brightest-value darkest-value
)))))))))
686 (defun brighten-maybe (image)
687 "Make image brighter if it is too dark.
688 We are using cl-png."
689 (declare (optimize speed
)
690 (optimize (safety 0)))
691 (multiple-value-bind (brightest-value darkest-value
)
693 (declare (type color brightest-value darkest-value
))
694 (when (< (the color brightest-value
) 200)
695 (let ((image-vector (make-array (list (* (height image
)
699 :displaced-to image
)))
701 for i from
0 below
(length image-vector
)
702 do
(setf (aref image-vector i
)
703 (floor (* (the color
(- (aref image-vector i
)
706 (- brightest-value darkest-value
))))))))
709 (defun brightness (image)
710 "Return brightest value and darkest value of image.
712 (declare (optimize speed
)
713 (optimize (safety 0))
715 (let ((brightest-value 0)
717 (declare (type color brightest-value darkest-value
))
718 (dotimes (y (the image-dimension
(height image
)))
719 (dotimes (x (the image-dimension
(width image
)))
720 (dotimes (c (the channels
(channels image
)))
721 (let ((intensity (aref image y x c
)))
722 (setf brightest-value
(max intensity brightest-value
))
723 (setf darkest-value
(min intensity darkest-value
))))))
724 (values brightest-value darkest-value
)))
727 (defun brightness (image)
728 "Return brightest value and darkest value of image. We are using
730 (declare (optimize speed
))
732 (make-array (list (* (height image
) (width image
) (channels image
)))
734 :displaced-to image
)))
736 for brightness across image-vector
737 maximize brightness into brightest-value
738 minimize brightness into darkest-value
739 finally
(return (values brightest-value
742 (defun* send-png
(output-stream path start
743 &key
(color-raiser #(1 1 1))
745 &mandatory-key bayer-pattern
)
746 "Read an image at position start in .pictures file at path and send
747 it to the binary output-stream. Return UNIX trigger-time of image.
748 If brightenp is t, have it brightened up if necessary. If reversep is
749 t, turn it upside-down. Bayer-pattern is applied after turning, which
751 ;; TODO: bayer-pattern should be applied to the unturned image
752 (let ((blob-start (find-keyword path
"PICTUREDATA_BEGIN" start
))
753 (blob-size (find-keyword-value path
"dataSize=" start
))
754 (huffman-table-size (* 511 (+ 1 4)))
755 (image-height (find-keyword-value path
"height=" start
))
756 (image-width (find-keyword-value path
"width=" start
))
757 (compression-mode (find-keyword-value path
"compressed=" start
))
758 (channels (find-keyword-value path
"channels=" start
))
759 (trigger-time (find-keyword-value path
"timeTrigger=" start
)))
760 (assert (member channels
'(1 3)) ()
761 "Don't know how to deal with ~D-channel pixels." channels
)
762 (with-open-file (input-stream path
:element-type
'unsigned-byte
)
765 (ecase compression-mode
766 ((2 1) ;compressed with individual/pre-built huffman table
767 (uncompress-picture (read-huffman-table input-stream
769 (read-compressed-picture
771 (+ blob-start huffman-table-size
)
772 (- blob-size huffman-table-size
))
773 image-height image-width channels
776 (fetch-picture input-stream blob-start blob-size
777 image-height image-width channels
778 :reversep reversep
)))
782 (write-image image output-stream
)))
786 (defun write-image (image stream
)
787 "Write image array (height, width, channel) to stream."
788 (zpng:write-png-stream
790 (make-instance 'zpng
:png
791 :height
(height image
)
793 :color-type
(getf '(1 :grayscale
3 :truecolor
)
795 :image-data
(make-array
796 (list (* (height image
) (width image
)
799 :displaced-to image
)))
803 (defun write-image (image stream
)
804 "Write image array (height, width, channel) to stream."
805 (png:encode image stream
))
807 (defun find-nth-picture (n path
)
808 "Find file-position of zero-indexed nth picture in in .pictures file
810 (let ((estimated-header-length
811 (- (find-keyword path
"PICTUREHEADER_END")
812 (find-keyword path
"PICTUREHEADER_BEGIN")
813 *picture-header-length-tolerance
*))) ; allow for variation in dataSize and a few other parameters
817 (find-keyword path
"PICTUREHEADER_BEGIN" 0) then
818 (find-keyword path
"PICTUREHEADER_BEGIN"
819 (+ picture-start picture-length estimated-header-length
))
820 for picture-length
= (find-keyword-value path
821 "dataSize=" picture-start
)
822 finally
(return (- picture-start
(length "PICTUREHEADER_BEGIN"))))))
824 (defun* send-nth-png
(n output-stream path
825 &key
(color-raiser #(1 1 1))
827 &mandatory-key bayer-pattern
)
828 "Read image number n (zero-indexed) in .pictures file at path and
829 send it to the binary output-stream. Return UNIX trigger-time of
831 (send-png output-stream path
(find-nth-picture n path
)
832 :bayer-pattern bayer-pattern
835 :color-raiser color-raiser
))
839 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
840 ;; collect 4 single color pixels into a three-color one
841 ;; enhance contrast of grayscale images