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 3 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, use a version of ALE with\n"
56 "*** ImageMagick support enabled.)\n\n",
61 static inline int digest_comment(FILE *f
, const char *filename
, extended_t
*extended
) {
64 double fvalue
, fvalue2
;
66 while (next
!= '\n' && next
!= '\r' && next
!= EOF
) {
67 while (next
== ' ' || next
== '\t' || next
== '#') {
73 if (ungetc(next
, f
) == EOF
) {
75 fprintf(stderr
, "Unable to ungetc().");
81 if (extended
->is_extended
&& fscanf(f
, "Black-level: %d", &value
) == 1)
82 extended
->black_level
= value
;
83 else if (extended
->is_extended
&& fscanf(f
, "ISO: %lf", &fvalue
) == 1)
84 extended
->gain
= fvalue
/ 100;
85 else if (extended
->is_extended
&& fscanf(f
, "Gain: %lf", &fvalue
) == 1)
86 extended
->gain
= fvalue
;
87 else if (extended
->is_extended
&& fscanf(f
, "Aperture: %lf", &fvalue
) == 1)
88 extended
->aperture
= fvalue
;
89 else if (extended
->is_extended
&& fscanf(f
, "Shutter: %lf/%lf", &fvalue
, &fvalue2
) > 0)
90 extended
->shutter
= fvalue
/ fvalue2
;
91 else if (next
!= '\n' && next
!= '\r' && next
!= EOF
)
100 static inline void eat_comments(FILE *f
, const char *filename
, extended_t
*extended
) {
103 while (next
== ' ' || next
== '\n' || next
== '\t' || next
== '#' || next
== '\r') {
106 next
= digest_comment(f
, filename
, extended
);
111 if (ungetc(next
, f
) == EOF
) {
113 fprintf(stderr
, "Unable to ungetc().");
118 static inline int is_eppm(const char *filename
) {
122 FILE *f
= fopen(filename
, "rb");
129 eat_comments(f
, filename
, &extended
); /* XXX - should we eat comments here? */
130 n
= fscanf(f
, "%c%c%c%c", &m1
, &m2
, &m3
, &m4
);
134 if (n
!= 4 || m1
!= 'P' || (m2
!= '6' && m2
!= '3') || m3
!= '#' || m4
!= 'E')
140 static inline image
*read_ppm(const char *filename
, exposure
*e
, unsigned int bayer
, int init_reference_gain
= 0) {
141 unsigned int i
, j
, k
;
143 unsigned char m1
, m2
, val
;
148 struct extended_t extended
;
149 FILE *f
= fopen(filename
, "rb");
152 fprintf(stderr
, "\n\nUnable to open '%s'.\n\n", filename
);
160 eat_comments(f
, filename
, &extended
); /* XXX - should we eat comments here? */
161 n
= fscanf(f
, "%c%c", &m1
, &m2
);
163 if (n
!= 2 || m1
!= 'P' || (m2
!= '6' && m2
!= '3'))
166 assert(n
== 2 && m1
== 'P' && (m2
== '6' || m2
== '3'));
175 extended
.is_extended
= 1;
176 else while (m4
!= EOF
&& m4
!= '\n' && m4
!= '\r')
178 } else if (ungetc(m3
, f
) == EOF
) {
180 fprintf(stderr
, "Unable to ungetc().");
186 eat_comments(f
, filename
, &extended
);
187 n
= fscanf(f
, " %d", &w
);
195 eat_comments(f
, filename
, &extended
);
196 n
= fscanf(f
, "%d", &h
);
202 /* Maximum component value */
204 eat_comments(f
, filename
, &extended
);
205 n
= fscanf(f
, "%d", &mcv
);
207 assert(mcv
<= 65535 || m2
== '3');
209 if (extended
.black_level
== 0) {
210 extended
.black_level
= e
->get_black_level();
212 extended
.black_level
/= mcv
;
215 if (n
!= 1 || (mcv
> 65535 && m2
== '6'))
218 /* Make a new image */
220 if (bayer
== IMAGE_BAYER_NONE
)
221 im
= new image_ale_real(h
, w
, 3, "file", e
);
223 im
= new image_bayer_ale_real(h
, w
, 3, bayer
, "file", e
);
227 /* Trailing whitespace */
229 if (fgetc(f
) == EOF
) {
236 for (i
= 0; i
< im
->height(); i
++)
237 for (j
= 0; j
< im
->width(); j
++) {
239 for (k
= 0; k
< im
->depth(); k
++) {
245 n
= fscanf(f
, "%c", &val
);
254 n
= fscanf(f
, "%c", &val
);
260 ival
= (ival
<< 8) | val
;
267 eat_comments(f
, filename
, &extended
);
269 n
= fscanf(f
, "%d", &ival
);
276 p
[k
] = ((ale_real
) ival
) / ((ale_real
) mcv
);
280 pixel p_linear
= (e
->linearize(p
) - e
->get_multiplier() * extended
.black_level
)
281 / (1 - extended
.black_level
);
283 im
->set_pixel(i
, j
, p_linear
);
286 /* Handle exposure and gain */
288 if (extended
.is_extended
) {
289 if (extended
.aperture
!= 0
290 || extended
.shutter
!= 0
291 || extended
.gain
!= 0) {
293 if (extended
.aperture
== 0)
294 extended
.aperture
= 1;
295 if (extended
.shutter
== 0)
296 extended
.shutter
= 1;
297 if (extended
.gain
== 0)
300 double combined_gain
= (1 / pow(extended
.aperture
, 2))
304 if (init_reference_gain
)
305 exposure::set_gain_reference(combined_gain
);
307 e
->set_gain_multiplier(exposure::get_gain_reference()
319 static inline void write_ppm(const char *filename
, const image
*im
, exposure
*e
,
320 unsigned int mcv
, int plain
, int rezero
, int exposure_scale
, double nn_defined_radius
) {
321 unsigned int i
, j
, k
;
322 FILE *f
= fopen(filename
, "wb");
325 fprintf(stderr
, "\n\nUnable to open '%s'.\n\n", filename
);
332 * Output a plain (ASCII) or raw (binary) PPM file
344 fprintf(f
, "%d ", im
->width());
348 fprintf(f
, "%d ", im
->height());
350 /* Maximum component value */
352 fprintf(f
, "%d\n", mcv
);
354 /* Automatic exposure adjustment information */
357 ale_real minval
= (rezero
? im
->minval() : 0);
360 pixel
minval_pixel(minval
, minval
, minval
);
362 if (exposure_scale
) {
363 ale_real new_maxval
= im
->maxval();
365 if (new_maxval
> maxval
)
371 for (i
= 0; i
< im
->height(); i
++)
372 for (j
= 0; j
< im
->width(); j
++) {
373 pixel value
= im
->get_pixel(i
, j
);
376 * Get nearest-neighbor defined values.
378 * XXX: While this implementation is correct, it is inefficient
379 * for large radii. A better implementation would search
380 * perimeters of squares of ever-increasing radius, tracking
381 * the best-so-far data until the square perimeter exceeded the
382 * best-so-far radius.
385 for (k
= 0; k
< 3; k
++)
387 for (int radius
= 1; radius
<= nn_defined_radius
; radius
++) {
388 double nearest_radius_squared
= (radius
+ 1) * (radius
+ 1);
389 for (int ii
= -radius
; ii
<= radius
; ii
++)
390 for (int jj
= -radius
; jj
<= radius
; jj
++) {
391 if (!im
->in_bounds(point(i
+ ii
, j
+ jj
)))
393 if (ii
* ii
+ jj
* jj
< nearest_radius_squared
394 && finite(im
->get_pixel(i
+ ii
, j
+ jj
)[k
])) {
395 value
[k
] = im
->get_pixel(i
+ ii
, j
+ jj
)[k
];
396 nearest_radius_squared
= ii
* ii
+ jj
* jj
;
399 if (nearest_radius_squared
< (radius
+ 1) * (radius
+ 1))
403 pixel exposure_adjust
= (value
- minval_pixel
)
405 pixel unlinearized
= (e
->unlinearize(exposure_adjust
)).clamp();
407 pixel rescaled
= unlinearized
* (ale_real
) mcv
;
409 for (k
= 0; k
< im
->depth(); k
++) {
411 uint16_t output_value
= (uint16_t) round(rescaled
[k
]);
414 fprintf(f
, "%d ", output_value
);
417 fprintf(f
, "%c", output_value
>> 8);
418 fprintf(f
, "%c", 0xff & output_value
);