1 // Copyright 2002 David Hilvert <dhilvert@auricle.dyndns.org>,
2 // <dhilvert@ugcs.caltech.edu>
4 /* This file is part of the Anti-Lamenessing Engine.
6 The Anti-Lamenessing Engine is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 The Anti-Lamenessing Engine is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with the Anti-Lamenessing Engine; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 * ppm.h: Read and write PPM files.
28 #include "image_ale_real.h"
29 #include "image_bayer_ale_real.h"
30 #include "exposure/exposure.h"
39 ale_real aperture
; /* 1 == f/1.0, 1.4 == f/1.4, etc. */
40 ale_real shutter
; /* 1 == 1 sec, 0.5 == 1/2 sec, etc. */
41 ale_real gain
; /* 1 == ISO 100, 2 == ISO 200, etc. */
52 static inline void error_ppm(const char *filename
) {
54 "\n\n*** '%s' doesn't look like a PPM file.\n"
55 "\n*** To handle other file types, compile ALE with\n"
56 "*** ImageMagick support ('make IMAGEMAGICK=1').\n"
57 "*** (To do this, you must have a source distribution\n"
58 "*** of ALE, and you must have ImageMagick installed.)\n\n",
63 static inline int digest_comment(FILE *f
, const char *filename
, extended_t
*extended
) {
66 double fvalue
, fvalue2
;
68 while (next
!= '\n' && next
!= '\r' && next
!= EOF
) {
69 while (next
== ' ' || next
== '\t' || next
== '#') {
75 if (ungetc(next
, f
) == EOF
) {
77 fprintf(stderr
, "Unable to ungetc().");
83 if (extended
->is_extended
&& fscanf(f
, "Black-level: %d", &value
) == 1)
84 extended
->black_level
= value
;
85 else if (extended
->is_extended
&& fscanf(f
, "ISO: %lf", &fvalue
) == 1)
86 extended
->gain
= fvalue
/ 100;
87 else if (extended
->is_extended
&& fscanf(f
, "Gain: %lf", &fvalue
) == 1)
88 extended
->gain
= fvalue
;
89 else if (extended
->is_extended
&& fscanf(f
, "Aperture: %lf", &fvalue
) == 1)
90 extended
->aperture
= fvalue
;
91 else if (extended
->is_extended
&& fscanf(f
, "Shutter: %lf/%lf", &fvalue
, &fvalue2
) > 0)
92 extended
->shutter
= fvalue
/ fvalue2
;
93 else if (next
!= '\n' && next
!= '\r' && next
!= EOF
)
102 static inline void eat_comments(FILE *f
, const char *filename
, extended_t
*extended
) {
105 while (next
== ' ' || next
== '\n' || next
== '\t' || next
== '#' || next
== '\r') {
108 next
= digest_comment(f
, filename
, extended
);
113 if (ungetc(next
, f
) == EOF
) {
115 fprintf(stderr
, "Unable to ungetc().");
120 static inline int is_eppm(const char *filename
) {
124 FILE *f
= fopen(filename
, "rb");
131 eat_comments(f
, filename
, &extended
); /* XXX - should we eat comments here? */
132 n
= fscanf(f
, "%c%c%c%c", &m1
, &m2
, &m3
, &m4
);
136 if (n
!= 4 || m1
!= 'P' || (m2
!= '6' && m2
!= '3') || m3
!= '#' || m4
!= 'E')
142 static inline image
*read_ppm(const char *filename
, exposure
*e
, unsigned int bayer
, int init_reference_gain
= 0) {
143 unsigned int i
, j
, k
;
145 unsigned char m1
, m2
, val
;
150 struct extended_t extended
;
151 FILE *f
= fopen(filename
, "rb");
154 fprintf(stderr
, "\n\nUnable to open '%s'.\n\n", filename
);
162 eat_comments(f
, filename
, &extended
); /* XXX - should we eat comments here? */
163 n
= fscanf(f
, "%c%c", &m1
, &m2
);
165 if (n
!= 2 || m1
!= 'P' || (m2
!= '6' && m2
!= '3'))
168 assert(n
== 2 && m1
== 'P' && (m2
== '6' || m2
== '3'));
177 extended
.is_extended
= 1;
178 else while (m4
!= EOF
&& m4
!= '\n' && m4
!= '\r')
180 } else if (ungetc(m3
, f
) == EOF
) {
182 fprintf(stderr
, "Unable to ungetc().");
188 eat_comments(f
, filename
, &extended
);
189 n
= fscanf(f
, " %d", &w
);
197 eat_comments(f
, filename
, &extended
);
198 n
= fscanf(f
, "%d", &h
);
204 /* Maximum component value */
206 eat_comments(f
, filename
, &extended
);
207 n
= fscanf(f
, "%d", &mcv
);
209 assert(mcv
<= 65535 || m2
== '3');
211 if (n
!= 1 || (mcv
> 65535 && m2
== '6'))
214 /* Make a new image */
216 if (bayer
== IMAGE_BAYER_NONE
)
217 im
= new image_ale_real(h
, w
, 3, "file", e
);
219 im
= new image_bayer_ale_real(h
, w
, 3, bayer
, "file", e
);
223 /* Trailing whitespace */
225 if (fgetc(f
) == EOF
) {
232 for (i
= 0; i
< im
->height(); i
++)
233 for (j
= 0; j
< im
->width(); j
++) {
235 for (k
= 0; k
< im
->depth(); k
++) {
241 n
= fscanf(f
, "%c", &val
);
250 n
= fscanf(f
, "%c", &val
);
256 ival
= (ival
<< 8) | val
;
263 eat_comments(f
, filename
, &extended
);
265 n
= fscanf(f
, "%d", &ival
);
272 p
[k
] = (ale_real
) (ival
- (extended
.is_extended
? extended
.black_level
: 0))
273 / (ale_real
) (mcv
- (extended
.is_extended
? extended
.black_level
: 0));
276 im
->set_pixel(i
, j
, e
->linearize(p
));
279 /* Handle exposure and gain */
281 if (extended
.is_extended
) {
282 double combined_gain
= (1 / pow(extended
.aperture
, 2))
285 if (init_reference_gain
)
286 exposure::set_gain_reference(combined_gain
);
288 e
->set_gain_multiplier(exposure::get_gain_reference()
299 static inline void write_ppm(const char *filename
, const image
*im
, exposure
*e
,
300 unsigned int mcv
, int plain
, int rezero
, int exposure_scale
, double nn_defined_radius
) {
301 unsigned int i
, j
, k
;
302 FILE *f
= fopen(filename
, "wb");
305 fprintf(stderr
, "\n\nUnable to open '%s'.\n\n", filename
);
312 * Output a plain (ASCII) or raw (binary) PPM file
324 fprintf(f
, "%d ", im
->width());
328 fprintf(f
, "%d ", im
->height());
330 /* Maximum component value */
332 fprintf(f
, "%d\n", mcv
);
334 /* Automatic exposure adjustment information */
337 ale_real minval
= (rezero
? im
->minval() : 0);
340 pixel
minval_pixel(minval
, minval
, minval
);
342 if (exposure_scale
) {
343 ale_real new_maxval
= im
->maxval();
345 if (new_maxval
> maxval
)
351 for (i
= 0; i
< im
->height(); i
++)
352 for (j
= 0; j
< im
->width(); j
++) {
353 pixel value
= im
->get_pixel(i
, j
);
356 * Get nearest-neighbor defined values.
358 * XXX: While this implementation is correct, it is inefficient
359 * for large radii. A better implementation would search
360 * perimeters of squares of ever-increasing radius, tracking
361 * the best-so-far data until the square perimeter exceeded the
362 * best-so-far radius.
365 for (k
= 0; k
< 3; k
++)
367 for (int radius
= 1; radius
<= nn_defined_radius
; radius
++) {
368 double nearest_radius_squared
= (radius
+ 1) * (radius
+ 1);
369 for (int ii
= -radius
; ii
<= radius
; ii
++)
370 for (int jj
= -radius
; jj
<= radius
; jj
++) {
371 if (!im
->in_bounds(point(i
+ ii
, j
+ jj
)))
373 if (ii
* ii
+ jj
* jj
< nearest_radius_squared
374 && finite(im
->get_pixel(i
+ ii
, j
+ jj
)[k
])) {
375 value
[k
] = im
->get_pixel(i
+ ii
, j
+ jj
)[k
];
376 nearest_radius_squared
= ii
* ii
+ jj
* jj
;
379 if (nearest_radius_squared
< (radius
+ 1) * (radius
+ 1))
383 pixel exposure_adjust
= (value
- minval_pixel
)
385 pixel unlinearized
= (e
->unlinearize(exposure_adjust
)).clamp();
387 pixel rescaled
= unlinearized
* (ale_real
) mcv
;
389 for (k
= 0; k
< im
->depth(); k
++) {
391 uint16_t output_value
= (uint16_t) round(rescaled
[k
]);
394 fprintf(f
, "%d ", output_value
);
397 fprintf(f
, "%c", output_value
>> 8);
398 fprintf(f
, "%c", 0xff & output_value
);