d2::align: Make a structure for follow semantics variables, and use a static instance...
[Ale.git] / d2 / image_rw.h
blobca71d23d0d2685ac9537be577ba776a05c498572
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 * image_rw.h: Read and write images.
26 #ifndef __image_rw_h__
27 #define __image_rw_h__
29 #include "image.h"
30 #include "image_ale_real.h"
31 #include "image_bayer_ale_real.h"
32 #include "ppm.h"
33 #include "exposure/exposure.h"
34 #include "exposure/exposure_default.h"
36 class image_rw {
39 * Private data members
43 * PPM type
45 * 0 = No type selected
46 * 1 = PPM Raw
47 * 2 = PPM Plain
49 static int ppm_type;
52 * Bit depth
54 static unsigned int num_bits;
55 static unsigned int mcv;
58 * Nearest-neighbor defined value radius.
60 static double nn_defined_radius;
63 * Input and output exposure models
65 static exposure **input_exposure;
66 static exposure *output_exposure;
67 static int exposure_scale;
70 * Default bayer pattern
72 static unsigned int bayer_default;
75 * Image-specific bayer patterns.
77 static unsigned int *bayer_specific;
80 * Pointer to the output filename
82 static const char *output_filename;
85 * Variables relating to input image files and image data structures.
87 static const char **filenames;
88 static unsigned int file_count;
89 static const image **images;
90 static int *files_open;
93 * The most recently closed image number.
95 static int latest_close_num;
98 * A cache of the data associated with the most recently closed image.
100 static const image *latest_close;
103 * Private methods to init and shut down the file reader.
107 * Initialize the image file handler
109 static void init_image() {
110 #ifdef USE_MAGICK
111 InitializeMagick("ale");
112 #endif
116 * Destroy the image file handler
118 static void destroy_image() {
119 #ifdef USE_MAGICK
120 DestroyMagick();
121 #endif
124 public:
127 * Methods to read and write image files
131 * Read an image from a file
133 static image *read_image(const char *filename, exposure *exp, char *name = "file",
134 unsigned int bayer = IMAGE_BAYER_DEFAULT, int init_reference_gain = 0) {
135 if (bayer == IMAGE_BAYER_DEFAULT)
136 bayer = bayer_default;
138 if (is_eppm(filename)) {
139 return read_ppm(filename, exp, bayer, init_reference_gain);
142 #ifdef USE_MAGICK
144 * Patterned after http://www.imagemagick.org/www/api.html
145 * and http://www.imagemagick.org/www/smile.c
148 ExceptionInfo exception;
149 Image *mi;
150 ImageInfo *image_info;
151 image *im;
152 const PixelPacket *p;
154 unsigned int i, j;
156 ale_real black_level = exp->get_black_level() * MaxRGB;
158 GetExceptionInfo(&exception);
159 image_info = CloneImageInfo((ImageInfo *) NULL);
161 strncpy(image_info->filename, filename, MaxTextExtent);
162 mi = ReadImage(image_info, &exception);
163 if (exception.severity != UndefinedException) {
164 fprintf(stderr, "\n\n");
165 CatchException(&exception);
166 fprintf(stderr, "\n");
168 if (mi == (Image *) NULL)
169 exit(1);
171 if (bayer == IMAGE_BAYER_NONE)
172 im = new image_ale_real(mi->rows, mi->columns, 3, name, exp);
173 else
174 im = new image_bayer_ale_real(mi->rows, mi->columns, 3, bayer, name, exp);
176 for (i = 0; i < mi->rows; i++) {
177 p = AcquireImagePixels(mi, 0, i, mi->columns, 1, &exception);
179 if (exception.severity != UndefinedException)
180 CatchException(&exception);
181 if (p == NULL)
182 exit(1);
184 for (j = 0; j < mi->columns; j++) {
185 pixel input( (ale_real) (p->red - black_level) / (MaxRGB - black_level),
186 (ale_real) (p->green - black_level) / (MaxRGB - black_level),
187 (ale_real) (p->blue - black_level) / (MaxRGB - black_level) );
189 im->set_pixel(i, j, exp->linearize(input));
191 p++;
195 DestroyImage(mi);
196 DestroyImageInfo(image_info);
198 return im;
199 #else
200 return read_ppm(filename, exp, bayer);
201 #endif
205 * Initializer.
207 * Handle FILE_COUNT input files with names in array FILENAMES and
208 * output file OUTPUT_FILENAME. FILENAMES should be an array of char *
209 * that is never freed. OUTPUT_FILENAME should be a char * that is
210 * never freed.
212 * INPUT_EXPOSURE should be an array of FILE_COUNT exposure objects
213 * that is never freed. OUTPUT_EXPOSURE should be an exposure * that
214 * is never freed.
216 static void init(unsigned int _file_count, const char **_filenames,
217 const char *_output_filename, exposure **_input_exposure,
218 exposure *_output_exposure){
219 assert (_file_count > 0);
221 init_image();
223 filenames = _filenames;
224 file_count = _file_count;
225 output_filename = _output_filename;
226 input_exposure = _input_exposure;
227 output_exposure = _output_exposure;
229 images = (const image **)malloc(file_count * sizeof(image *));
230 bayer_specific = (unsigned int *)malloc(file_count * sizeof(unsigned int));
231 files_open = (int *)calloc(file_count, sizeof(int));
233 assert (images);
234 assert (bayer_specific);
235 assert (files_open);
237 if (!images || !files_open || !bayer_specific) {
238 fprintf(stderr, "Unable to allocate memory for images.\n");
239 exit(1);
242 for (unsigned int i = 0; i < file_count; i++)
243 bayer_specific[i] = IMAGE_BAYER_DEFAULT;
245 fprintf(stderr, "Output file will be '%s'.\n",
246 output_filename);
250 static void ppm_plain() {
251 ppm_type = 2;
254 static void ppm_raw() {
255 ppm_type = 1;
258 static void ppm_auto() {
259 #ifdef USE_MAGICK
260 ppm_type = 0;
261 #else
262 fprintf(stderr, "\n\n*** Error: --auto flag not supported on this build. ***\n"
263 "*** (Hint: Rebuild with IMAGEMAGICK=1) ***\n\n");
264 exit(1);
265 #endif
268 static void set_default_bayer(unsigned int b) {
269 bayer_default = b;
272 static void set_specific_bayer(unsigned int index, unsigned int b) {
273 assert (bayer_specific);
274 bayer_specific[index] = b;
277 static void depth16() {
278 num_bits = 16;
279 mcv = 65535;
282 static void depth8() {
283 num_bits = 8;
284 mcv = 255;
287 static void destroy() {
288 assert (file_count > 0);
289 destroy_image();
292 static unsigned int count() {
293 assert (file_count > 0);
294 return file_count;
297 static const char *name(unsigned int image) {
298 assert (image < file_count);
300 return filenames[image];
303 static void def_nn(double _nn) {
304 nn_defined_radius = _nn;
307 static const char *output_name() {
308 assert (file_count > 0);
309 return output_filename;
313 * Write an image to a file
315 static void write_image(const char *filename, const image *im, exposure *exp = output_exposure, int rezero = 0, int exp_scale_override = 0) {
317 * Handle ALE-specific magical filenames.
320 if (!strcmp(filename, "dump:")) {
321 fprintf(stderr, "Image dump: ");
322 for (unsigned int i = 0; i < im->height(); i++)
323 for (unsigned int j = 0; j < im->width(); j++) {
324 pixel p = im->pix(i, j);
325 fprintf(stderr, "(%d, %d): [%f %f %f] ", i, j, p[0], p[1], p[2]);
327 fprintf(stderr, "\n");
329 return;
332 #ifdef USE_MAGICK
335 * Patterned after http://www.imagemagick.org/www/api.html
336 * and http://www.imagemagick.org/www/smile.c
339 ExceptionInfo exception;
340 Image *mi;
341 ImageInfo *image_info;
342 PixelPacket *p;
344 unsigned int i, j;
346 GetExceptionInfo(&exception);
347 image_info = CloneImageInfo((ImageInfo *) NULL);
348 strncpy(image_info->filename, filename, MaxTextExtent);
350 mi = AllocateImage(image_info);
351 if (mi == (Image *) NULL)
352 MagickError(ResourceLimitError,
353 "Unable to display image", "MemoryAllocationFailed");
355 mi->columns = im->width();
356 mi->rows = im->height();
359 * Set the output image depth
362 if (MaxRGB < 65535 || mcv < 65535)
363 mi->depth = 8;
364 else
365 mi->depth = 16;
367 if (MaxRGB < 65535 && mcv == 65535) {
368 fprintf(stderr, "\n\n*** Warning: 16 bit-per-channel file output was specified,\n");
369 fprintf(stderr, "*** but ImageMagick has not been compiled with support for this.\n");
370 fprintf(stderr, "*** Writing output using 8 bits per channel.\n");
374 * Set compression type
377 if (ppm_type == 2) {
378 mi->compression = NoCompression;
379 image_info->compression = NoCompression;
380 strncpy(mi->magick, "PNM", MaxTextExtent);
381 strncpy(image_info->magick, "PNM", MaxTextExtent);
382 } else if (ppm_type == 1) {
383 strncpy(mi->magick, "PNM", MaxTextExtent);
384 strncpy(image_info->magick, "PNM", MaxTextExtent);
388 * Automatic exposure adjustment (don't blow out highlights)
390 ale_real maxval = 1;
391 ale_real minval = (rezero ? im->minval() : 0);
392 if (minval > 0)
393 minval = 0;
394 pixel minval_pixel(minval, minval, minval);
397 if (exposure_scale || exp_scale_override) {
398 ale_real new_maxval = im->maxval();
400 if (new_maxval > maxval)
401 maxval = new_maxval;
405 * Write the image
408 for (i = 0; i < mi->rows; i++) {
409 p = SetImagePixels(mi, 0, i, mi->columns, 1);
410 if (p == NULL)
411 break;
413 for (j = 0; j < mi->columns; j++) {
415 pixel value = im->get_pixel(i, j);
418 * Get nearest-neighbor defined values.
420 * XXX: While this implementation is correct, it is inefficient
421 * for large radii. A better implementation would search
422 * perimeters of squares of ever-increasing radius, tracking
423 * the best-so-far data until the square perimeter exceeded the
424 * best-so-far radius.
427 for (int k = 0; k < 3; k++)
428 if (isnan(value[k]))
429 for (int radius = 1; radius <= nn_defined_radius; radius++) {
430 double nearest_radius_squared = (radius + 1) * (radius + 1);
431 for (int ii = -radius; ii <= radius; ii++)
432 for (int jj = -radius; jj <= radius; jj++) {
433 if (!im->in_bounds(point(i + ii, j + jj)))
434 continue;
435 if (ii * ii + jj * jj < nearest_radius_squared
436 && finite(im->get_pixel(i + ii, j + jj)[k])) {
437 value[k] = im->get_pixel(i + ii, j + jj)[k];
438 nearest_radius_squared = ii * ii + jj * jj;
441 if (nearest_radius_squared < (radius + 1) * (radius + 1))
442 break;
446 * Unlinearize
449 pixel unlinearized(exp->unlinearize((value - minval_pixel)
450 / (maxval - minval)));
452 unlinearized = unlinearized.clamp();
454 p->red = (Quantum) round(unlinearized[0] * MaxRGB);
455 p->green = (Quantum) round(unlinearized[1] * MaxRGB);
456 p->blue = (Quantum) round(unlinearized[2] * MaxRGB);
457 p++;
460 if (!SyncImagePixels(mi))
461 break;
464 if (!WriteImage(image_info, mi)) {
467 * Perhaps file type was unknown? Set to PNM by default.
470 strncpy(mi->magick, "PNM", MaxTextExtent);
471 strncpy(image_info->magick, "PNM", MaxTextExtent);
473 if (!WriteImage(image_info, mi)) {
474 fprintf(stderr, "\n\n");
475 CatchException(&mi->exception);
476 fprintf(stderr, "\n");
477 exit(1);
481 DestroyImage(mi);
482 DestroyImageInfo(image_info);
483 #else
484 write_ppm(filename, im, exp, mcv, ppm_type == 2, rezero, exposure_scale || exp_scale_override,
485 nn_defined_radius);
486 #endif
489 static void output(const image *i) {
490 assert (file_count > 0);
491 write_image(output_name(), i, output_exposure);
494 static void vise_write(const char *p, const char *s, const image *i) {
495 static int count = 0;
496 int length = strlen(p) + strlen(s) + 8;
497 char *output_string = (char *) malloc(length * sizeof(char));
499 snprintf(output_string, length, "%s%08d%s", p, count, s);
501 write_image(output_string, i, output_exposure);
503 count++;
506 static exposure &exp(int n) {
507 return *input_exposure[n];
510 static const exposure &const_exp(int n) {
511 return *input_exposure[n];
514 static exposure &exp() {
515 return *output_exposure;
518 static void exp_scale() {
519 exposure_scale = 1;
522 static void exp_noscale() {
523 exposure_scale = 0;
526 static const exposure &const_exp() {
527 return *output_exposure;
530 static const unsigned int bayer(unsigned int n) {
531 if (bayer_specific[n] == IMAGE_BAYER_DEFAULT)
532 return bayer_default;
533 else
534 return bayer_specific[n];
537 static const image *open(unsigned int n) {
538 assert (n < file_count);
539 assert (!files_open[n]);
541 if (latest_close_num >= 0 && n == (unsigned int) latest_close_num) {
542 images[n] = latest_close;
543 latest_close_num = -1;
544 files_open[n] = 1;
545 } else {
547 image *i = read_image(filenames[n], input_exposure[n], "file", bayer(n), (n == 0));
549 images[n] = i;
550 files_open[n] = 1;
552 return images[n];
555 static void open_all() {
556 for (unsigned int n = 0; n < file_count; n++)
557 open(n);
560 static const image *get_open(unsigned int n) {
561 assert (files_open[n]);
562 return images[n];
565 static image *copy(unsigned int n, char *name) {
566 assert (n < file_count);
568 if (files_open[n])
569 return images[n]->clone(name);
570 else {
571 image *i = read_image(filenames[n], input_exposure[n], name, bayer(n), (n == 0));
572 return i;
576 static void close(unsigned int image) {
577 assert (image < file_count);
578 assert (files_open[image]);
580 if (latest_close_num >= 0)
581 delete latest_close;
582 latest_close = images[image];
583 latest_close_num = image;
585 files_open[image] = 0;
588 static void close_all() {
589 for (unsigned int n = 0; n < file_count; n++)
590 close(n);
595 #endif