1 ;;; PHOROS -- Photogrammetric Road Survey
2 ;;; Copyright (C) 2010, 2011, 2012, 2016 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 three alternative
22 ;;; image creation libraries, zpng cl-png, and phoros's own imread.so.
25 #+(or phoros-uses-cl-png phoros-uses-zpng
)
26 (deftype image-dimension
() '(unsigned-byte 16))
28 #+(or phoros-uses-cl-png phoros-uses-zpng
)
29 (deftype color
() '(unsigned-byte 8))
31 #+(or phoros-uses-cl-png phoros-uses-zpng
)
32 (deftype channels
() '(unsigned-byte 8))
37 '(simple-array color
3))
39 (defparameter *picture-header-length-tolerance
* 20
40 "Amount of leeway for the length of a picture header in a .pictures
43 (defun find-keyword-in-stream (stream keyword
&optional
44 (start-position 0 start-position-p
)
46 "Return file-position in binary stream after first occurence of
47 keyword, or nil if the search is unsuccessful. Return nil if
48 start-position is explicitly nil."
49 (unless (and start-position-p
50 (null start-position
))
51 (unless start-position-p
(setf start-position
0))
52 (let* ((keyword-size (length keyword
))
53 (keyword-bytes (map 'vector
#'char-code keyword
))
55 (chunk (make-array (list (+ chunk-max-size
(1- keyword-size
)))
56 :element-type
'(unsigned-byte 8)))
57 (end-position-in-stream (if search-range
58 (+ start-position search-range
)
59 most-positive-fixnum
)))
61 for chunk-start-in-stream from start-position to end-position-in-stream by chunk-max-size
62 for chunk-size
= (progn (file-position stream chunk-start-in-stream
)
63 (read-sequence chunk stream
))
64 for end-in-chunk
= (min chunk-size
(- end-position-in-stream
65 chunk-start-in-stream
))
66 while
(plusp chunk-size
)
68 for i from
0 to end-in-chunk
69 for correct-characters
= (mismatch keyword-bytes chunk
72 do
(when (or (null correct-characters
)
73 (= correct-characters keyword-size
))
74 (return-from find-keyword-in-stream
75 (+ chunk-start-in-stream i keyword-size
))))))))
77 (defun find-keyword-value (path keyword
&optional start-position search-range
)
78 "Return value associated with keyword."
80 (find-keyword path keyword start-position search-range
)))
82 (with-open-file (stream path
)
83 (file-position stream start-of-value
)
84 (car (read-delimited-list #\
; stream))))))
86 (defun find-keyword (path keyword
&optional
(start-position 0) search-range
)
87 "Return file-position after keyword."
88 (with-open-file (stream path
:element-type
'unsigned-byte
)
89 (find-keyword-in-stream stream keyword start-position search-range
)))
91 #+(or phoros-uses-cl-png phoros-uses-zpng
)
92 (defun read-huffman-table (stream &optional start-position
)
93 "Return in a hash table a huffman table read from stream. Start
94 either at stream's file position or at start-position."
95 (let ((huffman-codes-start (if start-position
97 (file-position stream
))))
98 (file-position stream
(+ (* 511 4) huffman-codes-start
)) ; start of lengths
99 (let* ((lengths (make-list 511))
100 (huffman-table (make-hash-table :size
1000 :test
#'equal
)))
101 (read-sequence lengths stream
)
102 (file-position stream huffman-codes-start
)
104 for i from -
255 to
255
105 for length in lengths
106 for key
= (make-array (list length
) :element-type
'bit
)
107 for code
= (let ((raw (make-array '(4) :element-type
'unsigned-byte
))
109 (read-sequence raw stream
)
111 for raw-byte across raw
112 for code-position from
24 downto
0 by
8
113 do
(setf code-part
(dpb raw-byte
114 (byte 8 code-position
)
116 finally
(return code-part
)))
117 unless
(zerop length
)
119 for key-index from
0 below length
120 for code-index downfrom
(1- length
)
121 do
(setf (sbit key key-index
)
122 (ldb (byte 1 code-index
) code
)))
124 do
(setf (gethash key huffman-table
) i
))
127 #+(or phoros-uses-cl-png phoros-uses-zpng
)
128 (defun read-compressed-picture (stream start-position length
)
129 "Return a compressed picture in a bit array. Start either at
130 start-position or, if that is nil, at stream's file position."
131 (when start-position
(file-position stream start-position
))
132 (let ((raw (make-array (list length
) :element-type
'unsigned-byte
))
134 (make-array (list (* 8 length
)) :element-type
'bit
)))
135 (read-sequence raw stream
)
138 for byte-position from
0
140 for source-bit from
7 downto
0
141 for destination-bit from
0 to
7
142 do
(setf (sbit compressed-picture
144 (* 8 byte-position
)))
145 (ldb (byte 1 source-bit
) byte
)))
146 finally
(return compressed-picture
))))
148 #+(or phoros-uses-cl-png phoros-uses-zpng
)
149 (defun get-leading-byte (bit-array &optional
(start 0) &aux
(result 0))
150 "Return integer made of eight bits from bit-array."
152 for bit-array-index from start
153 for result-index from
7 downto
0
154 for result
= (dpb (sbit bit-array bit-array-index
)
155 (byte 1 result-index
) 0)
156 then
(dpb (sbit bit-array bit-array-index
) (byte 1 result-index
) result
)
157 finally
(return result
)))
160 (defun uncompress-picture (huffman-table compressed-picture
161 height width channels
&key reversep
)
162 "Return the Bayer pattern extracted from compressed-picture, turned
163 upside-down if reversep is t, in an (array color (height
164 width channels)), everything in channel 0."
165 (declare (optimize speed
)
166 (optimize (safety 0))
167 (type (unsigned-byte 16) height width
)
168 (type vector compressed-picture
))
169 (let* ((uncompressed-image
170 (png:make-image height width channels
8))
171 (uncompressed-image-vector
172 (make-array (list (* height width channels
))
174 :displaced-to uncompressed-image
))
175 (channel (if reversep
176 (1- channels
) ;becomes 0 by reversal
178 (compressed-picture-index 0)
181 for code of-type simple-bit-vector being the hash-key in huffman-table
182 minimize
(length code
)))
185 for code of-type simple-bit-vector being the hash-key in huffman-table
186 maximize
(length code
))))
187 (declare (type (signed-byte 48) compressed-picture-index
)
188 (type channels channels
))
190 for row from
0 below height
192 (setf (aref uncompressed-image row
0 channel
)
193 (get-leading-byte compressed-picture
194 (prog1 compressed-picture-index
195 (incf compressed-picture-index
8))))
196 (setf (aref uncompressed-image row
1 channel
)
197 (get-leading-byte compressed-picture
198 (prog1 compressed-picture-index
199 (incf compressed-picture-index
8))))
201 for column from
2 below width
202 for try-start of-type
(unsigned-byte 48) from compressed-picture-index
205 for key-length from min-key-length to max-key-length
206 for huffman-code
= (subseq compressed-picture
207 try-start
(+ try-start key-length
))
208 for pixel-delta-maybe
= (gethash huffman-code huffman-table
)
209 when pixel-delta-maybe
211 (setf (aref uncompressed-image row column channel
)
212 (- (aref uncompressed-image row
(- column
2) channel
)
213 (the fixnum pixel-delta-maybe
)))
214 and do
(incf try-start
(1- key-length
))
217 "Decoder out of step at row ~S, column ~S. Giving up."
220 (setf compressed-picture-index
(1+ try-start
))))
221 (when reversep
(reverse-displaced-vector uncompressed-image-vector
))
225 (defun uncompress-picture (huffman-table compressed-picture
226 height width channels
&key reversep
)
227 "Return the Bayer pattern extracted from compressed-picture, turned
228 upside-down if reversep is t, in an (array color (height
229 width channels)), everything in channel 0."
230 (declare (optimize speed
)
231 (optimize (safety 0))
232 (type (unsigned-byte 16) height width
)
233 (type vector compressed-picture
))
234 (let* ((uncompressed-image
235 (make-array (list height width channels
)
236 :element-type
'color
))
237 (uncompressed-image-vector
238 (make-array (list (* height width channels
))
240 :displaced-to uncompressed-image
))
242 (channel (if reversep
243 (1- channels
) ;becomes 0 by reversal
245 (compressed-picture-index 0)
248 for code of-type simple-bit-vector
249 being the hash-key in huffman-table
250 minimize
(length code
)))
253 for code of-type simple-bit-vector
254 being the hash-key in huffman-table
255 maximize
(length code
))))
256 (declare (type (signed-byte 48) compressed-picture-index
)
257 (type channels channels
))
259 for row from
0 below height
261 (setf (aref uncompressed-image row
0 channel
)
262 (get-leading-byte compressed-picture
263 (prog1 compressed-picture-index
264 (incf compressed-picture-index
8))))
265 (setf (aref uncompressed-image row
1 channel
)
266 (get-leading-byte compressed-picture
267 (prog1 compressed-picture-index
268 (incf compressed-picture-index
8))))
270 for column from
2 below width
271 for try-start of-type
(unsigned-byte 48) from compressed-picture-index
274 for key-length from min-key-length to max-key-length
275 for huffman-code
= (subseq compressed-picture
276 try-start
(+ try-start key-length
))
277 for pixel-delta-maybe
= (gethash huffman-code huffman-table
)
278 when pixel-delta-maybe
280 (setf (aref uncompressed-image row column channel
)
281 (- (aref uncompressed-image row
(- column
2) channel
)
282 (the fixnum pixel-delta-maybe
)))
283 and do
(incf try-start
(1- key-length
))
286 "Decoder out of step at row ~S, column ~S. Giving up."
289 (setf compressed-picture-index
(1+ try-start
))))
290 (when reversep
(reverse-displaced-vector uncompressed-image-vector
))
293 #+(or phoros-uses-cl-png phoros-uses-zpng
)
294 (defun fetch-picture (stream start-position length height width channels
296 "Return the Bayer pattern taken from stream in an (array
297 \(unsigned-byte l8) (height width channels)), everything in color
298 channel 0. Start at start-position or, if that is nil, at stream's
300 (when start-position
(file-position stream start-position
))
302 (make-array (list height width channels
)
303 :element-type
'color
))
305 (make-array (list (* height width channels
))
307 :displaced-to image
))
309 (make-array (list length
) :element-type
'unsigned-byte
)))
312 (read-sequence image-vector stream
))
314 (error "Not implemented: ~
315 fetch-picture for (uncompressed) truecolor images")
316 ;; (read-sequence raw-image stream)
318 ;; for pixel across raw-image and red from 0 by 3 do
319 ;; (setf (svref png-image-data red) pixel))
321 (when reversep
(reverse-displaced-vector image-vector
))
324 #+(or phoros-uses-cl-png phoros-uses-zpng
)
325 (defun reverse-displaced-vector (vector)
326 "Reverse elements of vector of unsigned-byte in-place."
328 for cell across
(reverse vector
)
330 do
(setf (aref vector i
) cell
)))
332 #+(or phoros-uses-cl-png phoros-uses-zpng
)
333 (defun complete-horizontally (image row column color
)
334 "Fake a color component of a pixel based its neighbors."
335 (declare (optimize (safety 0))
337 #-phoros-uses-cl-png
(type image image
)
338 (type image-dimension row column
))
339 (setf (aref image row column color
)
340 (round (+ (the color
(aref image row
(1- column
) color
))
341 (the color
(aref image row
(1+ column
) color
)))
344 #+(or phoros-uses-cl-png phoros-uses-zpng
)
345 (defun complete-vertically (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
(1- row
) column color
))
353 (the color
(aref image
(1+ row
) column color
)))
356 #+(or phoros-uses-cl-png phoros-uses-zpng
)
357 (defun complete-squarely (image row column color
)
358 "Fake a color component of a pixel based its neighbors."
359 (declare (optimize (safety 0))
361 #-phoros-uses-cl-png
(type image image
)
362 (type image-dimension row column
))
363 (setf (aref image row column color
)
364 (round (+ (the color
(aref image row
(1- column
) color
))
365 (the color
(aref image row
(1+ column
) color
))
366 (the color
(aref image
(1- row
) column color
))
367 (the color
(aref image
(1+ row
) column color
)))
370 #+(or phoros-uses-cl-png phoros-uses-zpng
)
371 (defun complete-diagonally (image row column color
)
372 "Fake a color component of a pixel based its neighbors."
373 (declare (optimize (safety 0))
375 #-phoros-uses-cl-png
(type image image
)
376 (type image-dimension row column
))
377 (setf (aref image row column color
)
378 (round (+ (the color
(aref image
(1- row
) (1- column
) color
))
379 (the color
(aref image
(1- row
) (1+ column
) color
))
380 (the color
(aref image
(1+ row
) (1- column
) color
))
381 (the color
(aref image
(1+ row
) (1+ column
) color
)))
384 #+(or phoros-uses-cl-png phoros-uses-zpng
)
385 (defun height (image) (array-dimension image
0))
386 #+(or phoros-uses-cl-png phoros-uses-zpng
)
387 (defun width (image) (array-dimension image
1))
388 #+(or phoros-uses-cl-png phoros-uses-zpng
)
389 (defun channels (image) (array-dimension image
2))
392 (defun demosaic-image (image bayer-pattern color-raiser brightenp
)
393 "Demosaic color image whose color channel 0 is supposed to be
394 filled with a Bayer color pattern. Return demosaiced image.
395 bayer-pattern is an array of 24-bit RGB values (red occupying the
396 least significant byte), describing the upper left corner of the
397 image. Currently, only pixels 0, 1 on row 0 are taken into account.
398 And, it's currently not even an array but a vector due to limitations
399 in postmodern. For a grayscale image do nothing. Then, if brightenp
400 is t and the image is too dark, make it brighter.
402 (declare (optimize (safety 0))
405 (when (= 3 (channels image
))
406 (let ((lowest-row (- (height image
) 2))
407 (rightmost-column (- (width image
) 2))
408 (bayer-pattern-red #x0000ff
)
409 (bayer-pattern-green #x00ff00
)
410 (bayer-pattern-blue #xff0000
)
411 (red 0) (green 1) (blue 2) ;color coordinate in IMAGE array
412 (color-raiser-red (coerce (elt color-raiser
0)
413 '(single-float -
10.0s0
10.0s0
)))
414 (color-raiser-green (coerce (elt color-raiser
1)
415 '(single-float -
10.0s0
10.0s0
)))
416 (color-raiser-blue (coerce (elt color-raiser
2)
417 '(single-float -
10.0s0
10.0s0
)))
418 (pix-depth 255) ;may some day become a function argument
419 complete-even-row-even-column
420 complete-even-row-odd-column
421 complete-odd-row-even-column
422 complete-odd-row-odd-column
423 colorize-even-row-even-column
424 colorize-even-row-odd-column
425 colorize-odd-row-even-column
426 colorize-odd-row-odd-column
)
427 (declare (type image-dimension lowest-row rightmost-column
)
429 (flet ((complete-green-on-red-row (row column
)
430 (complete-horizontally image row column red
)
431 (complete-vertically image row column blue
))
432 (complete-green-on-blue-row (row column
)
433 (complete-horizontally image row column blue
)
434 (complete-vertically image row column red
))
435 (complete-red (row column
)
436 (complete-squarely image row column green
)
437 (complete-diagonally image row column blue
))
438 (complete-blue (row column
)
439 (complete-squarely image row column green
)
440 (complete-diagonally image row column red
))
441 (colorize-red (row column
)
442 (setf (aref image row column red
)
444 (round (* color-raiser-red
447 (colorize-green (row column
)
448 (setf (aref image row column green
)
450 (round (* color-raiser-green
453 (colorize-blue (row column
)
454 (setf (aref image row column blue
)
456 (round (* color-raiser-blue
458 row column red
)))))))
460 ((= (aref bayer-pattern
0) bayer-pattern-red
)
461 (setf colorize-even-row-even-column
#'colorize-red
)
462 (setf colorize-even-row-odd-column
#'colorize-green
)
463 (setf colorize-odd-row-even-column
#'colorize-green
)
464 (setf colorize-odd-row-odd-column
#'colorize-blue
)
465 (setf complete-even-row-even-column
#'complete-red
)
466 (setf complete-even-row-odd-column
#'complete-green-on-red-row
)
467 (setf complete-odd-row-even-column
#'complete-green-on-blue-row
)
468 (setf complete-odd-row-odd-column
#'complete-blue
))
469 ((= (aref bayer-pattern
0) bayer-pattern-blue
)
470 (setf colorize-even-row-even-column
#'colorize-blue
)
471 (setf colorize-even-row-odd-column
#'colorize-green
)
472 (setf colorize-odd-row-even-column
#'colorize-green
)
473 (setf colorize-odd-row-odd-column
#'colorize-red
)
474 (setf complete-even-row-even-column
#'complete-blue
)
475 (setf complete-even-row-odd-column
#'complete-green-on-blue-row
)
476 (setf complete-odd-row-even-column
#'complete-green-on-red-row
)
477 (setf complete-odd-row-odd-column
#'complete-red
))
478 ((= (aref bayer-pattern
0) bayer-pattern-green
)
480 ((= (aref bayer-pattern
1) bayer-pattern-red
)
481 (setf colorize-even-row-even-column
#'colorize-green
)
482 (setf colorize-even-row-odd-column
#'colorize-red
)
483 (setf colorize-odd-row-even-column
#'colorize-blue
)
484 (setf colorize-odd-row-odd-column
#'colorize-green
)
485 (setf complete-even-row-even-column
#'complete-green-on-red-row
)
486 (setf complete-even-row-odd-column
#'complete-red
)
487 (setf complete-odd-row-even-column
#'complete-blue
)
488 (setf complete-odd-row-odd-column
#'complete-green-on-blue-row
))
489 ((= (aref bayer-pattern
1) bayer-pattern-blue
)
490 (setf colorize-even-row-even-column
#'colorize-green
)
491 (setf colorize-even-row-odd-column
#'colorize-blue
)
492 (setf colorize-odd-row-even-column
#'colorize-red
)
493 (setf colorize-odd-row-odd-column
#'colorize-green
)
494 (setf complete-even-row-even-column
#'complete-green-on-blue-row
)
495 (setf complete-even-row-odd-column
#'complete-blue
)
496 (setf complete-odd-row-even-column
#'complete-red
)
497 (setf complete-odd-row-odd-column
#'complete-green-on-red-row
))
498 (t (error "Don't know how to deal with a bayer-pattern of ~A"
500 (t (error "Don't know how to deal with a bayer-pattern of ~A"
502 ;; Recover colors (so far everything is in channel 0)
504 for row from
0 below
(the image-dimension
(height image
)) by
2
507 from
0 below
(the image-dimension
(width image
)) by
2
508 do
(funcall colorize-even-row-even-column row column
))
511 from
1 below
(the image-dimension
(width image
)) by
2
512 do
(funcall colorize-even-row-odd-column row column
)))
514 for row from
1 below
(the image-dimension
(height image
)) by
2
517 from
0 below
(the image-dimension
(width image
)) by
2
518 do
(funcall colorize-odd-row-even-column row column
))
521 from
1 below
(the image-dimension
(width image
)) by
2
522 do
(funcall colorize-odd-row-odd-column row column
)))
525 for row from
2 to lowest-row by
2 do
527 for column from
2 to rightmost-column by
2 do
528 (funcall complete-even-row-even-column row column
))
530 for column from
1 to rightmost-column by
2 do
531 (funcall complete-even-row-odd-column row column
)))
533 for row from
1 to lowest-row by
2 do
535 for column from
2 to rightmost-column by
2 do
536 (funcall complete-odd-row-even-column row column
))
538 for column from
1 to rightmost-column by
2 do
539 (funcall complete-odd-row-odd-column row column
))))))
540 (when brightenp
(brighten-maybe image
))
544 (defun demosaic-image (image bayer-pattern color-raiser brightenp
)
545 "Demosaic color image whose color channel 0 is supposed to be
546 filled with a Bayer color pattern. Return demosaiced image.
547 bayer-pattern is an array of 24-bit RGB values (red occupying the
548 least significant byte), describing the upper left corner of the
549 image. Currently, only pixels 0, 1 on row 0 are taken into account.
550 And, it's currently not even an array but a vector due to limitations
551 in postmodern. For a grayscale image do nothing. Then, if brightenp
552 is t and the image is too dark, make it brighter.
553 We are using cl-png."
554 (declare (optimize speed
)
555 (optimize (safety 0))
557 (when (= 3 (png:image-channels image
))
558 (let ((lowest-row (- (png:image-height image
) 2))
559 (rightmost-column (- (png:image-width image
) 2))
560 (bayer-pattern-red #x0000ff
)
561 (bayer-pattern-green #x00ff00
)
562 (bayer-pattern-blue #xff0000
)
563 (red 0) (green 1) (blue 2) ;color coordinate in IMAGE array
564 (color-raiser-red (coerce (elt color-raiser
0) '(single-float -
10.0s0
10.0s0
)))
565 (color-raiser-green (coerce (elt color-raiser
1) '(single-float -
10.0s0
10.0s0
)))
566 (color-raiser-blue (coerce (elt color-raiser
2) '(single-float -
10.0s0
10.0s0
)))
567 (pix-depth 255) ;may some day become a function argument
568 complete-even-row-even-column
569 complete-even-row-odd-column
570 complete-odd-row-even-column
571 complete-odd-row-odd-column
572 colorize-even-row-even-column
573 colorize-even-row-odd-column
574 colorize-odd-row-even-column
575 colorize-odd-row-odd-column
)
576 (declare (type image-dimension lowest-row rightmost-column
))
577 (flet ((complete-green-on-red-row (row column
)
578 (complete-horizontally image row column red
)
579 (complete-vertically image row column blue
))
580 (complete-green-on-blue-row (row column
)
581 (complete-horizontally image row column blue
)
582 (complete-vertically image row column red
))
583 (complete-red (row column
)
584 (complete-squarely image row column green
)
585 (complete-diagonally image row column blue
))
586 (complete-blue (row column
)
587 (complete-squarely image row column green
)
588 (complete-diagonally image row column red
))
589 (colorize-red (row column
)
590 (setf (aref image row column red
)
592 (round (* color-raiser-red
593 (the color
(aref image
594 row column red
)))))))
595 (colorize-green (row column
)
596 (setf (aref image row column green
)
598 (round (* color-raiser-green
599 (the color
(aref image
600 row column red
)))))))
601 (colorize-blue (row column
)
602 (setf (aref image row column blue
)
604 (round (* color-raiser-blue
605 (the color
(aref image
606 row column red
))))))))
608 ((= (aref bayer-pattern
0) bayer-pattern-red
)
609 (setf colorize-even-row-even-column
#'colorize-red
)
610 (setf colorize-even-row-odd-column
#'colorize-green
)
611 (setf colorize-odd-row-even-column
#'colorize-green
)
612 (setf colorize-odd-row-odd-column
#'colorize-blue
)
613 (setf complete-even-row-even-column
#'complete-red
)
614 (setf complete-even-row-odd-column
#'complete-green-on-red-row
)
615 (setf complete-odd-row-even-column
#'complete-green-on-blue-row
)
616 (setf complete-odd-row-odd-column
#'complete-blue
))
617 ((= (aref bayer-pattern
0) bayer-pattern-blue
)
618 (setf colorize-even-row-even-column
#'colorize-blue
)
619 (setf colorize-even-row-odd-column
#'colorize-green
)
620 (setf colorize-odd-row-even-column
#'colorize-green
)
621 (setf colorize-odd-row-odd-column
#'colorize-red
)
622 (setf complete-even-row-even-column
#'complete-blue
)
623 (setf complete-even-row-odd-column
#'complete-green-on-blue-row
)
624 (setf complete-odd-row-even-column
#'complete-green-on-red-row
)
625 (setf complete-odd-row-odd-column
#'complete-red
))
626 ((= (aref bayer-pattern
0) bayer-pattern-green
)
628 ((=(aref bayer-pattern
1) bayer-pattern-red
)
629 (setf colorize-even-row-even-column
#'colorize-green
)
630 (setf colorize-even-row-odd-column
#'colorize-red
)
631 (setf colorize-odd-row-even-column
#'colorize-blue
)
632 (setf colorize-odd-row-odd-column
#'colorize-green
)
633 (setf complete-even-row-even-column
#'complete-green-on-red-row
)
634 (setf complete-even-row-odd-column
#'complete-red
)
635 (setf complete-odd-row-even-column
#'complete-blue
)
636 (setf complete-odd-row-odd-column
#'complete-green-on-blue-row
))
637 ((=(aref bayer-pattern
1) bayer-pattern-blue
)
638 (setf colorize-even-row-even-column
#'colorize-green
)
639 (setf colorize-even-row-odd-column
#'colorize-blue
)
640 (setf colorize-odd-row-even-column
#'colorize-red
)
641 (setf colorize-odd-row-odd-column
#'colorize-green
)
642 (setf complete-even-row-even-column
#'complete-green-on-blue-row
)
643 (setf complete-even-row-odd-column
#'complete-blue
)
644 (setf complete-odd-row-even-column
#'complete-red
)
645 (setf complete-odd-row-odd-column
#'complete-green-on-red-row
))
646 (t (error "Don't know how to deal with a bayer-pattern of ~A"
648 (t (error "Don't know how to deal with a bayer-pattern of ~A"
650 ;; Recover colors (so far everything is in channel 0)
651 (loop for row from
0 below
(the image-dimension
(height image
)) by
2
652 do
(loop for column from
0 below
(the image-dimension
(width image
)) by
2
653 do
(funcall colorize-even-row-even-column row column
))
654 (loop for column from
1 below
(the image-dimension
(width image
)) by
2
655 do
(funcall colorize-even-row-odd-column row column
)))
656 (loop for row from
1 below
(the image-dimension
(height image
)) by
2
657 do
(loop for column from
0 below
(the image-dimension
(width image
)) by
2
658 do
(funcall colorize-odd-row-even-column row column
))
659 (loop for column from
1 below
(the image-dimension
(width image
)) by
2
660 do
(funcall colorize-odd-row-odd-column row column
)))
663 for row from
2 to lowest-row by
2 do
665 for column from
2 to rightmost-column by
2 do
666 (funcall complete-even-row-even-column row column
))
668 for column from
1 to rightmost-column by
2 do
669 (funcall complete-even-row-odd-column row column
)))
671 for row from
1 to lowest-row by
2 do
673 for column from
2 to rightmost-column by
2 do
674 (funcall complete-odd-row-even-column row column
))
676 for column from
1 to rightmost-column by
2 do
677 (funcall complete-odd-row-odd-column row column
))))))
678 (when brightenp
(brighten-maybe image
))
682 (defun brighten-maybe (image)
683 "Make image brighter if it is too dark.
685 (declare (optimize speed
)
686 (optimize (safety 0))
688 (multiple-value-bind (brightest-value darkest-value
)
690 (declare (type color brightest-value darkest-value
))
691 (when (< (the color brightest-value
) 200)
692 (dotimes (y (the image-dimension
(height image
)))
693 (dotimes (x (the image-dimension
(width image
)))
694 (dotimes (c (the channels
(channels image
)))
695 (setf (aref image y x c
)
696 (floor (* (the color
(- (aref image y x c
)
699 (- brightest-value darkest-value
)))))))))
702 (defun brighten-maybe (image)
703 "Make image brighter if it is too dark.
704 We are using cl-png."
705 (declare (optimize speed
)
706 (optimize (safety 0)))
707 (multiple-value-bind (brightest-value darkest-value
)
709 (declare (type color brightest-value darkest-value
))
710 (when (< (the color brightest-value
) 200)
711 (let ((image-vector (make-array (list (* (height image
)
715 :displaced-to image
)))
717 for i from
0 below
(length image-vector
)
718 do
(setf (aref image-vector i
)
719 (floor (* (the color
(- (aref image-vector i
)
722 (- brightest-value darkest-value
))))))))
725 (defun brightness (image)
726 "Return brightest value and darkest value of image.
728 (declare (optimize speed
)
729 (optimize (safety 0))
731 (let ((brightest-value 0)
733 (declare (type color brightest-value darkest-value
))
734 (dotimes (y (the image-dimension
(height image
)))
735 (dotimes (x (the image-dimension
(width image
)))
736 (dotimes (c (the channels
(channels image
)))
737 (let ((intensity (aref image y x c
)))
738 (setf brightest-value
(max intensity brightest-value
))
739 (setf darkest-value
(min intensity darkest-value
))))))
740 (values brightest-value darkest-value
)))
743 (defun brightness (image)
744 "Return brightest value and darkest value of image. We are using
746 (declare (optimize speed
))
748 (make-array (list (* (height image
) (width image
) (channels image
)))
750 :displaced-to image
)))
752 for brightness across image-vector
753 maximize brightness into brightest-value
754 minimize brightness into darkest-value
755 finally
(return (values brightest-value
759 #+(or phoros-uses-cl-png phoros-uses-zpng
)
760 (defun* send-png
(output-stream path start
761 &key
(color-raiser #(1 1 1))
763 &mandatory-key bayer-pattern
)
764 "Read an image at position start in .pictures file at path and send
765 it to the binary output-stream. Return UNIX trigger-time of image.
766 If brightenp is t, have it brightened up if necessary. If reversep is
767 t, turn it upside-down. Bayer-pattern is applied after turning, which
769 ;; TODO: bayer-pattern should be applied to the unturned image
770 (let ((blob-start (find-keyword path
"PICTUREDATA_BEGIN" start
))
771 (blob-size (find-keyword-value path
"dataSize=" start
))
772 (huffman-table-size (* 511 (+ 1 4)))
773 (image-height (find-keyword-value path
"height=" start
))
774 (image-width (find-keyword-value path
"width=" start
))
775 (compression-mode (find-keyword-value path
"compressed=" start
))
776 (channels (find-keyword-value path
"channels=" start
))
777 (trigger-time (find-keyword-value path
"timeTrigger=" start
)))
778 (assert (member channels
'(1 3)) ()
779 "Don't know how to deal with ~D-channel pixels." channels
)
780 (with-open-file (input-stream path
:element-type
'unsigned-byte
)
783 (ecase compression-mode
784 ((2 1) ;compressed with individual/pre-built huffman table
785 (uncompress-picture (read-huffman-table input-stream
787 (read-compressed-picture
789 (+ blob-start huffman-table-size
)
790 (- blob-size huffman-table-size
))
791 image-height image-width channels
794 (fetch-picture input-stream blob-start blob-size
795 image-height image-width channels
796 :reversep reversep
)))
800 (write-image image output-stream
)))
803 #+phoros-uses-imread.so
804 (cffi:defcstruct mem-encode
808 #+phoros-uses-imread.so
809 (defun* send-png
(output-stream path start
810 &key
(color-raiser #(1 1 1))
812 &mandatory-key bayer-pattern
)
813 "Read an image at position start in .pictures file at path and send
814 it to the binary output-stream. Return UNIX trigger-time of image.
815 If brightenp is t, have it brightened up if necessary. If reversep is
816 t, turn it upside-down. Bayer-pattern is applied after turning, which
818 ;; TODO: bayer-pattern should be applied to the unturned image
819 (let ((blob-start (find-keyword path
"PICTUREDATA_BEGIN" start
))
820 (blob-size (find-keyword-value path
"dataSize=" start
))
821 (huffman-table-size (* 511 (+ 1 4)))
822 (image-height (find-keyword-value path
"height=" start
))
823 (image-width (find-keyword-value path
"width=" start
))
824 (compression-mode (find-keyword-value path
"compressed=" start
))
825 (channels (find-keyword-value path
"channels=" start
))
826 (trigger-time (find-keyword-value path
"timeTrigger=" start
)))
827 (cffi:with-foreign-objects
((baypat :int
3)
828 (colr-raisr :double
3)
829 (mem-png 'mem-encode
)
831 :unsigned-char
(- blob-size huffman-table-size
))
833 :unsigned-char
(* image-width image-height
)))
835 for i from
0 below
(min 4 (first (array-dimensions bayer-pattern
))) do
836 (setf (cffi:mem-aref baypat
:int i
) (aref bayer-pattern i
)))
839 (setf (cffi:mem-aref colr-raisr
:double i
)
840 (coerce (aref color-raiser i
) 'double-float
)))
842 (imread:png2mem
(namestring path
) blob-start
(- blob-size huffman-table-size
)
843 image-width image-height channels baypat compression-mode
844 uncompressed compressed mem-png
845 (if reversep
1 0) (if brightenp
1 0) colr-raisr
)))
846 (cond ((zerop png2mem-exit
)
847 (cffi:with-foreign-slots
((buffer size
) mem-png mem-encode
)
849 for i from
0 below size do
850 (write-byte (cffi:mem-aref buffer
:unsigned-char i
) output-stream
))
851 (unless (cffi:null-pointer-p buffer
) (cffi:foreign-free buffer
))))
853 (error "Input file ~A not found." path
))
854 ((or (= 2 png2mem-exit
) (= 3 png2mem-exit
))
855 (error "Don't know how to deal with a bayer-pattern of ~A."
858 (error "Unknown compression mode ~A in ~A."
859 compression-mode path
))
861 (error "Don't know how to deal with ~D-channel pixels." channels
))
863 (error "PNG error: create_write_struct()."))
865 (error "PNG error: create_info_struct()"))
867 (error "Error during PNG setup."))
869 (error "Error while writing PNG row."))
871 (error "Couldn't allocate memory for huffman table."))
873 (error "Can't unpack image.")))))
877 (defun write-image (image stream
)
878 "Write image array (height, width, channel) to stream."
879 (zpng:write-png-stream
881 (make-instance 'zpng
:png
882 :height
(height image
)
884 :color-type
(getf '(1 :grayscale
3 :truecolor
)
886 :image-data
(make-array
887 (list (* (height image
) (width image
)
890 :displaced-to image
)))
894 (defun write-image (image stream
)
895 "Write image array (height, width, channel) to stream."
896 (png:encode image stream
))
898 (defun find-nth-picture (n path
)
899 "Find file-position of zero-indexed nth picture in in .pictures file
901 (let ((estimated-header-length
902 (- (find-keyword path
"PICTUREHEADER_END")
903 (find-keyword path
"PICTUREHEADER_BEGIN")
904 *picture-header-length-tolerance
*))) ; allow for variation in dataSize and a few other parameters
908 (find-keyword path
"PICTUREHEADER_BEGIN" 0) then
909 (find-keyword path
"PICTUREHEADER_BEGIN"
910 (+ picture-start picture-length estimated-header-length
))
911 for picture-length
= (find-keyword-value path
912 "dataSize=" picture-start
)
913 finally
(return (- picture-start
(length "PICTUREHEADER_BEGIN"))))))
915 (defun* send-nth-png
(n output-stream path
916 &key
(color-raiser #(1 1 1))
918 &mandatory-key bayer-pattern
)
919 "Read image number n (zero-indexed) in .pictures file at path and
920 send it to the binary output-stream. Return UNIX trigger-time of
922 (send-png output-stream path
(find-nth-picture n path
)
923 :bayer-pattern bayer-pattern
926 :color-raiser color-raiser
))
930 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
931 ;; collect 4 single color pixels into a three-color one
932 ;; enhance contrast of grayscale images