Bugfix: bayer-pattern for grayscale with libimread.so
[phoros.git] / image-reader.lisp
blob272a7003df38ff24e54f848a73896db17106b3df
1 ;;; PHOROS -- Photogrammetric Road Survey
2 ;;; Copyright (C) 2010, 2011, 2012, 2016 Bert Burgemeister
3 ;;;
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.
8 ;;;
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.
13 ;;;
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.
19 (in-package :img)
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))
34 #+phoros-uses-zpng
35 (deftype image ()
36 "We are using zpng."
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
41 file.")
43 (defun find-keyword-in-stream (stream keyword &optional
44 (start-position 0 start-position-p)
45 search-range)
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))
54 (chunk-max-size 300)
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)))
60 (loop
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)
67 do (loop
68 for i from 0 to end-in-chunk
69 for correct-characters = (mismatch keyword-bytes chunk
70 :start2 i
71 :end2 end-in-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."
79 (let ((start-of-value
80 (find-keyword path keyword start-position search-range)))
81 (when start-of-value
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
96 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)
103 (loop
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))
108 (code-part 0))
109 (read-sequence raw stream)
110 (loop
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)
115 code-part))
116 finally (return code-part)))
117 unless (zerop length)
118 do (loop
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))
125 huffman-table)))
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))
133 (compressed-picture
134 (make-array (list (* 8 length)) :element-type 'bit)))
135 (read-sequence raw stream)
136 (loop
137 for byte across raw
138 for byte-position from 0
139 do (loop
140 for source-bit from 7 downto 0
141 for destination-bit from 0 to 7
142 do (setf (sbit compressed-picture
143 (+ destination-bit
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."
151 (loop
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)))
159 #+phoros-uses-cl-png
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))
173 :element-type 'color
174 :displaced-to uncompressed-image))
175 (channel (if reversep
176 (1- channels) ;becomes 0 by reversal
178 (compressed-picture-index 0)
179 (min-key-length
180 (loop
181 for code of-type simple-bit-vector being the hash-key in huffman-table
182 minimize (length code)))
183 (max-key-length
184 (loop
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))
189 (loop
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))))
200 (loop
201 for column from 2 below width
202 for try-start of-type (unsigned-byte 48) from compressed-picture-index
204 (loop
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))
215 and return nil
216 finally (error
217 "Decoder out of step at row ~S, column ~S. Giving up."
218 row column))
219 finally
220 (setf compressed-picture-index (1+ try-start))))
221 (when reversep (reverse-displaced-vector uncompressed-image-vector))
222 uncompressed-image))
224 #+phoros-uses-zpng
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))
239 :element-type 'color
240 :displaced-to uncompressed-image))
242 (channel (if reversep
243 (1- channels) ;becomes 0 by reversal
245 (compressed-picture-index 0)
246 (min-key-length
247 (loop
248 for code of-type simple-bit-vector
249 being the hash-key in huffman-table
250 minimize (length code)))
251 (max-key-length
252 (loop
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))
258 (loop
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))))
269 (loop
270 for column from 2 below width
271 for try-start of-type (unsigned-byte 48) from compressed-picture-index
273 (loop
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))
284 and return nil
285 finally (error
286 "Decoder out of step at row ~S, column ~S. Giving up."
287 row column))
288 finally
289 (setf compressed-picture-index (1+ try-start))))
290 (when reversep (reverse-displaced-vector uncompressed-image-vector))
291 uncompressed-image))
293 #+(or phoros-uses-cl-png phoros-uses-zpng)
294 (defun fetch-picture (stream start-position length height width channels
295 &key reversep)
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
299 file position."
300 (when start-position (file-position stream start-position))
301 (let* ((image
302 (make-array (list height width channels)
303 :element-type 'color))
304 (image-vector
305 (make-array (list (* height width channels))
306 :element-type 'color
307 :displaced-to image))
308 (raw-image
309 (make-array (list length) :element-type 'unsigned-byte)))
310 (ecase channels
312 (read-sequence image-vector stream))
314 (error "Not implemented: ~
315 fetch-picture for (uncompressed) truecolor images")
316 ;; (read-sequence raw-image stream)
317 ;; (loop
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))
322 image))
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."
327 (loop
328 for cell across (reverse vector)
329 for i from 0
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))
336 (optimize speed)
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)))
342 2)))
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))
348 (optimize speed)
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)))
354 2)))
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))
360 (optimize speed)
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)))
368 4)))
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))
374 (optimize speed)
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)))
382 4)))
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))
391 #+phoros-uses-zpng
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.
401 We are using zpng."
402 (declare (optimize (safety 0))
403 (optimize speed)
404 (type image image))
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)
443 (min pix-depth
444 (round (* color-raiser-red
445 (aref image
446 row column red))))))
447 (colorize-green (row column)
448 (setf (aref image row column green)
449 (min pix-depth
450 (round (* color-raiser-green
451 (aref image
452 row column red))))))
453 (colorize-blue (row column)
454 (setf (aref image row column blue)
455 (min pix-depth
456 (round (* color-raiser-blue
457 (aref image
458 row column red)))))))
459 (cond
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)
479 (cond
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"
499 bayer-pattern))))
500 (t (error "Don't know how to deal with a bayer-pattern of ~A"
501 bayer-pattern)))
502 ;; Recover colors (so far everything is in channel 0)
503 (loop
504 for row from 0 below (the image-dimension (height image)) by 2
505 do (loop
506 for column
507 from 0 below (the image-dimension (width image)) by 2
508 do (funcall colorize-even-row-even-column row column))
509 (loop
510 for column
511 from 1 below (the image-dimension (width image)) by 2
512 do (funcall colorize-even-row-odd-column row column)))
513 (loop
514 for row from 1 below (the image-dimension (height image)) by 2
515 do (loop
516 for column
517 from 0 below (the image-dimension (width image)) by 2
518 do (funcall colorize-odd-row-even-column row column))
519 (loop
520 for column
521 from 1 below (the image-dimension (width image)) by 2
522 do (funcall colorize-odd-row-odd-column row column)))
523 ;; Demosaic
524 (loop
525 for row from 2 to lowest-row by 2 do
526 (loop
527 for column from 2 to rightmost-column by 2 do
528 (funcall complete-even-row-even-column row column))
529 (loop
530 for column from 1 to rightmost-column by 2 do
531 (funcall complete-even-row-odd-column row column)))
532 (loop
533 for row from 1 to lowest-row by 2 do
534 (loop
535 for column from 2 to rightmost-column by 2 do
536 (funcall complete-odd-row-even-column row column))
537 (loop
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))
541 image)
543 #+phoros-uses-cl-png
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))
556 (optimize speed))
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)
591 (min pix-depth
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)
597 (min pix-depth
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)
603 (min pix-depth
604 (round (* color-raiser-blue
605 (the color (aref image
606 row column red))))))))
607 (cond
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)
627 (cond
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"
647 bayer-pattern))))
648 (t (error "Don't know how to deal with a bayer-pattern of ~A"
649 bayer-pattern)))
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)))
661 ;; Demosaic
662 (loop
663 for row from 2 to lowest-row by 2 do
664 (loop
665 for column from 2 to rightmost-column by 2 do
666 (funcall complete-even-row-even-column row column))
667 (loop
668 for column from 1 to rightmost-column by 2 do
669 (funcall complete-even-row-odd-column row column)))
670 (loop
671 for row from 1 to lowest-row by 2 do
672 (loop
673 for column from 2 to rightmost-column by 2 do
674 (funcall complete-odd-row-even-column row column))
675 (loop
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))
679 image)
681 #+phoros-uses-zpng
682 (defun brighten-maybe (image)
683 "Make image brighter if it is too dark.
684 We are using zpng."
685 (declare (optimize speed)
686 (optimize (safety 0))
687 (type image image))
688 (multiple-value-bind (brightest-value darkest-value)
689 (brightness image)
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)
697 darkest-value))
698 255)
699 (- brightest-value darkest-value)))))))))
701 #+phoros-uses-cl-png
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)
708 (brightness image)
709 (declare (type color brightest-value darkest-value))
710 (when (< (the color brightest-value) 200)
711 (let ((image-vector (make-array (list (* (height image)
712 (width image)
713 (channels image)))
714 :element-type 'color
715 :displaced-to image)))
716 (loop
717 for i from 0 below (length image-vector)
718 do (setf (aref image-vector i)
719 (floor (* (the color (- (aref image-vector i)
720 darkest-value))
721 255)
722 (- brightest-value darkest-value))))))))
724 #+phoros-uses-zpng
725 (defun brightness (image)
726 "Return brightest value and darkest value of image.
727 We are using zpng."
728 (declare (optimize speed)
729 (optimize (safety 0))
730 (type image image))
731 (let ((brightest-value 0)
732 (darkest-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)))
742 #+phoros-uses-cl-png
743 (defun brightness (image)
744 "Return brightest value and darkest value of image. We are using
745 cl-png."
746 (declare (optimize speed))
747 (let ((image-vector
748 (make-array (list (* (height image) (width image) (channels image)))
749 :element-type 'color
750 :displaced-to image)))
751 (loop
752 for brightness across image-vector
753 maximize brightness into brightest-value
754 minimize brightness into darkest-value
755 finally (return (values brightest-value
756 darkest-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))
762 reversep brightenp
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
768 is a wart."
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)
781 (let ((image
782 (demosaic-image
783 (ecase compression-mode
784 ((2 1) ;compressed with individual/pre-built huffman table
785 (uncompress-picture (read-huffman-table input-stream
786 blob-start)
787 (read-compressed-picture
788 input-stream
789 (+ blob-start huffman-table-size)
790 (- blob-size huffman-table-size))
791 image-height image-width channels
792 :reversep reversep))
793 (0 ;uncompressed
794 (fetch-picture input-stream blob-start blob-size
795 image-height image-width channels
796 :reversep reversep)))
797 bayer-pattern
798 color-raiser
799 brightenp)))
800 (write-image image output-stream)))
801 trigger-time))
803 #+phoros-uses-imread.so
804 (cffi:defcstruct mem-encode
805 (buffer :pointer)
806 (size :int))
808 #+phoros-uses-imread.so
809 (defun* send-png (output-stream path start
810 &key (color-raiser #(1 1 1))
811 reversep brightenp
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
817 is a wart."
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)
830 (compressed
831 :unsigned-char (- blob-size huffman-table-size))
832 (uncompressed
833 :unsigned-char (* image-width image-height)))
834 (loop
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)))
837 (loop
838 for i from 0 to 2 do
839 (setf (cffi:mem-aref colr-raisr :double i)
840 (coerce (aref color-raiser i) 'double-float)))
841 (let ((png2mem-exit
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)
848 (loop
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))))
852 ((= 1 png2mem-exit)
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."
856 bayer-pattern))
857 ((= 5 png2mem-exit)
858 (error "Unknown compression mode ~A in ~A."
859 compression-mode path))
860 ((= 6 png2mem-exit)
861 (error "Don't know how to deal with ~D-channel pixels." channels))
862 ((= 11 png2mem-exit)
863 (error "PNG error: create_write_struct()."))
864 ((= 12 png2mem-exit)
865 (error "PNG error: create_info_struct()"))
866 ((= 13 png2mem-exit)
867 (error "Error during PNG setup."))
868 ((= 21 png2mem-exit)
869 (error "Error while writing PNG row."))
870 ((= 31 png2mem-exit)
871 (error "Couldn't allocate memory for huffman table."))
873 (error "Can't unpack image.")))))
874 trigger-time))
876 #+phoros-uses-zpng
877 (defun write-image (image stream)
878 "Write image array (height, width, channel) to stream."
879 (zpng:write-png-stream
880 (zpng:copy-png
881 (make-instance 'zpng:png
882 :height (height image)
883 :width (width image)
884 :color-type (getf '(1 :grayscale 3 :truecolor)
885 (channels image))
886 :image-data (make-array
887 (list (* (height image) (width image)
888 (channels image)))
889 :element-type 'color
890 :displaced-to image)))
891 stream))
893 #+phoros-uses-cl-png
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
900 at path."
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
905 (loop
906 for i from 0 to n
907 for picture-start =
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))
917 reversep brightenp
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
921 image."
922 (send-png output-stream path (find-nth-picture n path)
923 :bayer-pattern bayer-pattern
924 :reversep reversep
925 :brightenp brightenp
926 :color-raiser color-raiser))
929 ;; TODO: (perhaps)
930 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
931 ;; collect 4 single color pixels into a three-color one
932 ;; enhance contrast of grayscale images