d2::image::get_*bl(): Always return a single value.
[Ale.git] / d2 / ppm.h
blob85e7b2b2234f965749a04855276cc1bf927c87d1
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.
25 #ifndef __ppm_h__
26 #define __ppm_h__
28 #include "image_ale_real.h"
29 #include "image_bayer_ale_real.h"
30 #include "exposure/exposure.h"
33 * Extended attributes
36 struct extended_t {
37 int is_extended;
38 ale_real black_level;
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. */
43 extended_t() {
44 is_extended = 0;
45 black_level = 0;
46 aperture = 0;
47 shutter = 0;
48 gain = 0;
52 static inline void error_ppm(const char *filename) {
53 fprintf(stderr,
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",
57 filename);
58 exit(1);
61 static inline int digest_comment(FILE *f, const char *filename, extended_t *extended) {
62 int next = '#';
63 int value;
64 double fvalue, fvalue2;
66 while (next != '\n' && next != '\r' && next != EOF) {
67 while (next == ' ' || next == '\t' || next == '#') {
68 next = fgetc(f);
69 if (feof(f))
70 error_ppm(filename);
73 if (ungetc(next, f) == EOF) {
74 assert(0);
75 fprintf(stderr, "Unable to ungetc().");
76 exit(1);
79 fvalue2 = 1;
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)
92 next = fgetc(f);
94 next = fgetc(f);
97 return next;
100 static inline void eat_comments(FILE *f, const char *filename, extended_t *extended) {
101 int next = ' ';
103 while (next == ' ' || next == '\n' || next == '\t' || next == '#' || next == '\r') {
104 next = fgetc(f);
105 if (next == '#')
106 next = digest_comment(f, filename, extended);
107 if (feof(f))
108 error_ppm(filename);
111 if (ungetc(next, f) == EOF) {
112 assert(0);
113 fprintf(stderr, "Unable to ungetc().");
114 exit(1);
118 static inline int is_eppm(const char *filename) {
119 char m1, m2, m3, m4;
120 int n;
121 extended_t extended;
122 FILE *f = fopen(filename, "rb");
124 if (f == NULL)
125 return 0;
127 /* Magic */
129 eat_comments(f, filename, &extended); /* XXX - should we eat comments here? */
130 n = fscanf(f, "%c%c%c%c", &m1, &m2, &m3, &m4);
132 fclose(f);
134 if (n != 4 || m1 != 'P' || (m2 != '6' && m2 != '3') || m3 != '#' || m4 != 'E')
135 return 0;
137 return 1;
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;
142 image *im;
143 unsigned char m1, m2, val;
144 int m3, m4;
145 int ival;
146 int w, h, mcv;
147 int n;
148 struct extended_t extended;
149 FILE *f = fopen(filename, "rb");
151 if (f == NULL) {
152 fprintf(stderr, "\n\nUnable to open '%s'.\n\n", filename);
153 exit(1);
156 assert(f);
158 /* Magic */
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'))
164 error_ppm(filename);
166 assert(n == 2 && m1 == 'P' && (m2 == '6' || m2 == '3'));
168 /* Extended flag */
170 m3 = fgetc(f);
172 if (m3 == '#') {
173 m4 = fgetc(f);
174 if (m4 == 'E')
175 extended.is_extended = 1;
176 else while (m4 != EOF && m4 != '\n' && m4 != '\r')
177 m4 = fgetc(f);
178 } else if (ungetc(m3, f) == EOF) {
179 assert(0);
180 fprintf(stderr, "Unable to ungetc().");
181 exit(1);
184 /* Width */
186 eat_comments(f, filename, &extended);
187 n = fscanf(f, " %d", &w);
188 assert(n == 1);
190 if (n != 1)
191 error_ppm(filename);
193 /* Height */
195 eat_comments(f, filename, &extended);
196 n = fscanf(f, "%d", &h);
197 assert(n == 1);
199 if (n != 1)
200 error_ppm(filename);
202 /* Maximum component value */
204 eat_comments(f, filename, &extended);
205 n = fscanf(f, "%d", &mcv);
206 assert(n == 1);
207 assert(mcv <= 65535 || m2 == '3');
209 if (extended.black_level == 0) {
210 extended.black_level = e->get_black_level();
211 } else {
212 extended.black_level /= mcv;
215 if (n != 1 || (mcv > 65535 && m2 == '6'))
216 error_ppm(filename);
218 /* Make a new image */
220 if (bayer == IMAGE_BAYER_NONE)
221 im = new image_ale_real(h, w, 3, "file", e);
222 else
223 im = new image_bayer_ale_real(h, w, 3, bayer, "file", e);
225 assert (im);
227 /* Trailing whitespace */
229 if (fgetc(f) == EOF) {
230 assert(0);
231 error_ppm(filename);
234 /* Pixels */
236 for (i = 0; i < im->height(); i++)
237 for (j = 0; j < im->width(); j++) {
238 pixel p;
239 for (k = 0; k < im->depth(); k++) {
241 if (m2 == '6') {
243 /* Binary data */
245 n = fscanf(f, "%c", &val);
246 assert (n == 1);
248 if (n != 1)
249 error_ppm(filename);
251 ival = val;
253 if (mcv > 255) {
254 n = fscanf(f, "%c", &val);
255 assert(n == 1);
257 if (n != 1)
258 error_ppm(filename);
260 ival = (ival << 8) | val;
263 } else {
265 /* ASCII data */
267 eat_comments(f, filename, &extended);
269 n = fscanf(f, "%d", &ival);
271 assert (n == 1);
272 if (n != 1)
273 error_ppm(filename);
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)
298 extended.gain = 1;
300 double combined_gain = (1 / pow(extended.aperture, 2))
301 * extended.shutter
302 * extended.gain;
304 if (init_reference_gain)
305 exposure::set_gain_reference(combined_gain);
306 else
307 e->set_gain_multiplier(exposure::get_gain_reference()
308 / combined_gain);
312 /* Done */
314 fclose(f);
316 return im;
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");
324 if (f == NULL) {
325 fprintf(stderr, "\n\nUnable to open '%s'.\n\n", filename);
326 exit(1);
329 assert(f);
332 * Output a plain (ASCII) or raw (binary) PPM file
335 /* Magic */
337 if (plain)
338 fprintf(f, "P3 ");
339 else
340 fprintf(f, "P6 ");
342 /* Width */
344 fprintf(f, "%d ", im->width());
346 /* Height */
348 fprintf(f, "%d ", im->height());
350 /* Maximum component value */
352 fprintf(f, "%d\n", mcv);
354 /* Automatic exposure adjustment information */
356 ale_real maxval = 1;
357 ale_real minval = (rezero ? im->minval() : 0);
358 if (minval > 0)
359 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)
366 maxval = new_maxval;
369 /* Pixels */
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++)
386 if (isnan(value[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)))
392 continue;
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))
400 break;
403 pixel exposure_adjust = (value - minval_pixel)
404 / (maxval - minval);
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]);
413 if (plain) {
414 fprintf(f, "%d ", output_value);
415 } else {
416 if (mcv > 255)
417 fprintf(f, "%c", output_value >> 8);
418 fprintf(f, "%c", 0xff & output_value);
422 if (plain)
423 fprintf(f, "\n");
427 /* Done */
429 fclose(f);
432 #endif