1 ;;; PHOROS -- Photogrammetric Road Survey
2 ;;; Copyright (C) 2010, 2011, 2012, 2016, 2017 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 (defparameter *picture-header-length-tolerance
* 20
22 "Amount of leeway for the length of a picture header in a .pictures
25 (defun find-keyword-in-stream (stream keyword
&optional
26 (start-position 0 start-position-p
)
28 "Return file-position in binary stream after first occurence of
29 keyword, or nil if the search is unsuccessful. Return nil if
30 start-position is explicitly nil."
31 (unless (and start-position-p
32 (null start-position
))
33 (unless start-position-p
(setf start-position
0))
34 (let* ((keyword-size (length keyword
))
35 (keyword-bytes (map 'vector
#'char-code keyword
))
37 (chunk (make-array (list (+ chunk-max-size
(1- keyword-size
)))
38 :element-type
'(unsigned-byte 8)))
39 (end-position-in-stream (if search-range
40 (+ start-position search-range
)
41 most-positive-fixnum
)))
43 for chunk-start-in-stream from start-position to end-position-in-stream by chunk-max-size
44 for chunk-size
= (let ((*readtable
* (copy-readtable)))
45 (setf (readtable-case *readtable
*) :preserve
)
46 (file-position stream chunk-start-in-stream
)
47 (read-sequence chunk stream
))
48 for end-in-chunk
= (min chunk-size
(- end-position-in-stream
49 chunk-start-in-stream
))
50 while
(plusp chunk-size
)
52 for i from
0 to end-in-chunk
53 for correct-characters
= (mismatch keyword-bytes chunk
56 do
(when (or (null correct-characters
)
57 (= correct-characters keyword-size
))
58 (return-from find-keyword-in-stream
59 (+ chunk-start-in-stream i keyword-size
))))))))
61 (defun find-keyword-value (path keyword
&optional start-position search-range
)
62 "Return value associated with keyword."
64 (find-keyword path keyword start-position search-range
)))
66 (with-open-file (stream path
)
67 (let ((*readtable
* (copy-readtable)))
68 (setf (readtable-case *readtable
*) :preserve
)
69 (file-position stream start-of-value
)
70 (car (read-delimited-list #\
; stream)))))))
72 (defun find-keyword (path keyword
&optional
(start-position 0) search-range
)
73 "Return file-position after keyword."
74 (with-open-file (stream path
:element-type
'unsigned-byte
)
75 (find-keyword-in-stream stream keyword start-position search-range
)))
77 (cffi:defcstruct mem-encode
81 (defun* send-png
(output-stream path start
82 &key
(color-raiser #(1 1 1))
84 &mandatory-key bayer-pattern
)
85 "Read an image at position start in .pictures file at path and send
86 it to the binary output-stream. Return UNIX trigger-time of image.
87 If brightenp is t, have it brightened up if necessary. If reversep is
88 t, turn it upside-down. Bayer-pattern is applied after turning, which
90 ;; TODO: bayer-pattern should be applied to the unturned image
91 (let ((blob-start (find-keyword path
"PICTUREDATA_BEGIN" start
))
92 (blob-size (find-keyword-value path
"dataSize=" start
))
93 (image-height (find-keyword-value path
"height=" start
))
94 (image-width (find-keyword-value path
"width=" start
))
95 (compression-mode (find-keyword-value path
"compressed=" start
))
96 (channels (find-keyword-value path
"channels=" start
))
97 (trigger-time (find-keyword-value path
"timeTrigger=" start
))
99 (cffi:with-foreign-objects
((baypat :int
3)
100 (colr-raisr :double
3)
101 (mem-png 'mem-encode
))
103 for i from
0 below
(min 4 (first (array-dimensions bayer-pattern
))) do
104 (setf (cffi:mem-aref baypat
:int i
) (aref bayer-pattern i
)))
107 (setf (cffi:mem-aref colr-raisr
:double i
)
108 (coerce (aref color-raiser i
) 'double-float
)))
110 (imread:png2mem
(namestring path
) blob-start blob-size
111 image-width image-height channels
112 baypat demosaic-fast compression-mode
113 mem-png reversep brightenp colr-raisr
)))
114 (cond ((zerop png2mem-exit
)
115 (cffi:with-foreign-slots
((buffer size
) mem-png mem-encode
)
117 for i from
0 below size do
118 (write-byte (cffi:mem-aref buffer
:unsigned-char i
) output-stream
))
119 (unless (cffi:null-pointer-p buffer
) (cffi:foreign-free buffer
))))
121 (error "Input file ~A not found." path
))
122 ((or (= 2 png2mem-exit
) (= 3 png2mem-exit
))
123 (error "Don't know how to deal with a bayer-pattern of ~A."
126 (error "Unknown compression mode ~A in ~A."
127 compression-mode path
))
129 (error "Don't know how to deal with ~D-channel pixels." channels
))
131 (error "JPEG decompression error."))
133 (error "JPEG discarded. It was bigger than expected."))
135 (error "JPEG reversing not implemented."))
137 (error "JPEG brightening not implemented."))
139 (error "Couldn't allocate memory for uncompressed image."))
141 (error "Couldn't allocate buffer for image data input."))
143 (error "PNG error: create_write_struct()."))
145 (error "PNG error: create_info_struct()"))
147 (error "Error during PNG setup."))
149 (error "Error while writing PNG row."))
151 (error "Couldn't allocate memory for huffman table."))
153 (error "Huffman decoder out of step."))
155 (error "Can't unpack image.")))))
158 (defun find-nth-picture (n path
)
159 "Find file-position of zero-indexed nth picture in in .pictures file
161 (let ((estimated-header-length
162 (- (find-keyword path
"PICTUREHEADER_END")
163 (find-keyword path
"PICTUREHEADER_BEGIN")
164 *picture-header-length-tolerance
*))) ; allow for variation in dataSize and a few other parameters
168 (find-keyword path
"PICTUREHEADER_BEGIN" 0) then
169 (find-keyword path
"PICTUREHEADER_BEGIN"
170 (+ picture-start picture-length estimated-header-length
))
171 for picture-length
= (find-keyword-value path
172 "dataSize=" picture-start
)
173 finally
(return (- picture-start
(length "PICTUREHEADER_BEGIN"))))))
175 (defun* send-nth-png
(n output-stream path
176 &key
(color-raiser #(1 1 1))
178 &mandatory-key bayer-pattern
)
179 "Read image number n (zero-indexed) in .pictures file at path and
180 send it to the binary output-stream. Return UNIX trigger-time of
182 (send-png output-stream path
(find-nth-picture n path
)
183 :bayer-pattern bayer-pattern
186 :color-raiser color-raiser
))
190 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
191 ;; collect 4 single color pixels into a three-color one
192 ;; enhance contrast of grayscale images