doc/web: Place examples section more visibly.
[Ale.git] / d2 / image_rw.h
bloba91db6283c67b07bbe74da60047b140c9475ee41
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 * 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 * Maximum cache size, in megabytes (2^20 * bytes), for images not most
99 * recently closed.
101 static double cache_size_max;
104 * Actual cache size.
106 static double cache_size;
109 * Number of cached files.
111 static unsigned int cache_count;
114 * Private methods to init and shut down the file reader.
118 * Initialize the image file handler
120 static void init_image() {
121 #ifdef USE_MAGICK
122 InitializeMagick("ale");
123 #endif
127 * Destroy the image file handler
129 static void destroy_image() {
130 #ifdef USE_MAGICK
131 DestroyMagick();
132 #endif
135 public:
138 * Methods to read and write image files
142 * Read an image from a file
144 static image *read_image(const char *filename, exposure *exp, const char *name = "file",
145 unsigned int bayer = IMAGE_BAYER_DEFAULT, int init_reference_gain = 0) {
146 if (bayer == IMAGE_BAYER_DEFAULT)
147 bayer = bayer_default;
149 if (is_eppm(filename)) {
150 return read_ppm(filename, exp, bayer, init_reference_gain);
153 #ifdef USE_MAGICK
155 * Patterned after http://www.imagemagick.org/www/api.html
156 * and http://www.imagemagick.org/www/smile.c
159 ExceptionInfo exception;
160 Image *mi;
161 ImageInfo *image_info;
162 image *im;
163 const PixelPacket *p;
165 unsigned int i, j;
167 ale_real black_level = exp->get_black_level();
169 GetExceptionInfo(&exception);
170 image_info = CloneImageInfo((ImageInfo *) NULL);
172 strncpy(image_info->filename, filename, MaxTextExtent);
173 mi = ReadImage(image_info, &exception);
174 if (exception.severity != UndefinedException) {
175 fprintf(stderr, "\n\n");
176 CatchException(&exception);
177 fprintf(stderr, "\n");
179 if (mi == (Image *) NULL)
180 exit(1);
182 if (bayer == IMAGE_BAYER_NONE)
183 im = new_image_ale_real(mi->rows, mi->columns, 3, name, exp);
184 else
185 im = new_image_bayer_ale_real(mi->rows, mi->columns, 3, bayer, name, exp);
187 for (i = 0; i < mi->rows; i++) {
188 p = AcquireImagePixels(mi, 0, i, mi->columns, 1, &exception);
190 if (exception.severity != UndefinedException)
191 CatchException(&exception);
192 if (p == NULL)
193 exit(1);
195 for (j = 0; j < mi->columns; j++) {
197 pixel input ( ale_real_from_int(p->red, MaxRGB),
198 ale_real_from_int(p->green, MaxRGB),
199 ale_real_from_int(p->blue, MaxRGB) );
201 pixel linear_input = (exp->linearize(input) - exp->get_multiplier() * black_level)
202 / (1 - black_level);
204 im->set_pixel(i, j, linear_input);
206 p++;
210 DestroyImage(mi);
211 DestroyImageInfo(image_info);
213 return im;
214 #else
215 return read_ppm(filename, exp, bayer);
216 #endif
220 * Initializer.
222 * Handle FILE_COUNT input files with names in array FILENAMES and
223 * output file OUTPUT_FILENAME. FILENAMES should be an array of char *
224 * that is never freed. OUTPUT_FILENAME should be a char * that is
225 * never freed.
227 * INPUT_EXPOSURE should be an array of FILE_COUNT exposure objects
228 * that is never freed. OUTPUT_EXPOSURE should be an exposure * that
229 * is never freed.
231 static void init(unsigned int _file_count, const char **_filenames,
232 const char *_output_filename, exposure **_input_exposure,
233 exposure *_output_exposure){
234 assert (_file_count > 0);
236 init_image();
238 filenames = _filenames;
239 file_count = _file_count;
240 output_filename = _output_filename;
241 input_exposure = _input_exposure;
242 output_exposure = _output_exposure;
244 images = (const image **)malloc(file_count * sizeof(image *));
245 bayer_specific = (unsigned int *)malloc(file_count * sizeof(unsigned int));
246 files_open = (int *)calloc(file_count, sizeof(int));
248 assert (images);
249 assert (bayer_specific);
250 assert (files_open);
252 if (!images || !files_open || !bayer_specific) {
253 fprintf(stderr, "Unable to allocate memory for images.\n");
254 exit(1);
257 for (unsigned int i = 0; i < file_count; i++)
258 bayer_specific[i] = IMAGE_BAYER_DEFAULT;
260 ui::get()->identify_output(output_filename);
263 static void ppm_plain() {
264 ppm_type = 2;
267 static void ppm_raw() {
268 ppm_type = 1;
271 static void ppm_auto() {
272 #ifdef USE_MAGICK
273 ppm_type = 0;
274 #else
275 fprintf(stderr, "\n\n*** Error: --auto flag not supported on this build. ***\n"
276 "*** (Hint: Rebuild with IMAGEMAGICK=1) ***\n\n");
277 exit(1);
278 #endif
281 static void set_default_bayer(unsigned int b) {
282 bayer_default = b;
285 static void set_specific_bayer(unsigned int index, unsigned int b) {
286 assert (bayer_specific);
287 bayer_specific[index] = b;
290 static void depth16() {
291 num_bits = 16;
292 mcv = 65535;
295 static void depth8() {
296 num_bits = 8;
297 mcv = 255;
300 static void set_cache(double size) {
301 cache_size_max = size;
304 static void destroy() {
305 assert (file_count > 0);
306 destroy_image();
309 static unsigned int count() {
310 assert (file_count > 0);
311 return file_count;
314 static const char *name(unsigned int image) {
315 assert (image < file_count);
317 return filenames[image];
320 static void def_nn(double _nn) {
321 nn_defined_radius = _nn;
324 static const char *output_name() {
325 assert (file_count > 0);
326 return output_filename;
330 * Write an image to a file
332 static void write_image(const char *filename, const image *im, exposure *exp = output_exposure, int rezero = 0, int exp_scale_override = 0) {
334 * Handle ALE-specific magical filenames.
337 if (!strcmp(filename, "dump:")) {
338 fprintf(stderr, "Image dump: ");
339 for (unsigned int i = 0; i < im->height(); i++)
340 for (unsigned int j = 0; j < im->width(); j++) {
341 pixel p = im->get_pixel(i, j);
342 fprintf(stderr, "(%d, %d): [%f %f %f] ", i, j, (double) p[0], (double) p[1], (double) p[2]);
344 fprintf(stderr, "\n");
346 return;
349 #ifdef USE_MAGICK
352 * Patterned after http://www.imagemagick.org/www/api.html
353 * and http://www.imagemagick.org/www/smile.c
356 ExceptionInfo exception;
357 Image *mi;
358 ImageInfo *image_info;
359 PixelPacket *p;
361 unsigned int i, j;
363 GetExceptionInfo(&exception);
364 image_info = CloneImageInfo((ImageInfo *) NULL);
365 strncpy(image_info->filename, filename, MaxTextExtent);
367 mi = AllocateImage(image_info);
368 if (mi == (Image *) NULL)
369 MagickError(ResourceLimitError,
370 "Unable to display image", "MemoryAllocationFailed");
372 mi->columns = im->width();
373 mi->rows = im->height();
376 * Set the output image depth
379 if (MaxRGB < 65535 || mcv < 65535)
380 mi->depth = 8;
381 else
382 mi->depth = 16;
384 if (MaxRGB < 65535 && mcv == 65535) {
385 fprintf(stderr, "\n\n*** Warning: 16 bit-per-channel file output was specified,\n");
386 fprintf(stderr, "*** but ImageMagick has not been compiled with support for this.\n");
387 fprintf(stderr, "*** Writing output using 8 bits per channel.\n");
391 * Set compression type
394 if (ppm_type == 2) {
395 mi->compression = NoCompression;
396 image_info->compression = NoCompression;
397 strncpy(mi->magick, "PNM", MaxTextExtent);
398 strncpy(image_info->magick, "PNM", MaxTextExtent);
399 } else if (ppm_type == 1) {
400 strncpy(mi->magick, "PNM", MaxTextExtent);
401 strncpy(image_info->magick, "PNM", MaxTextExtent);
405 * Automatic exposure adjustment (don't blow out highlights)
407 ale_real maxval = 1;
408 ale_real minval = (rezero ? im->minval() : (ale_real) 0);
409 if (minval > 0)
410 minval = 0;
411 pixel minval_pixel(minval, minval, minval);
414 if (exposure_scale || exp_scale_override) {
415 ale_real new_maxval = im->maxval();
417 if (new_maxval > maxval)
418 maxval = new_maxval;
422 * Write the image
425 for (i = 0; i < mi->rows; i++) {
426 p = SetImagePixels(mi, 0, i, mi->columns, 1);
427 if (p == NULL)
428 break;
430 for (j = 0; j < mi->columns; j++) {
432 pixel value = im->get_pixel(i, j);
435 * Get nearest-neighbor defined values.
437 * XXX: While this implementation is correct, it is inefficient
438 * for large radii. A better implementation would search
439 * perimeters of squares of ever-increasing radius, tracking
440 * the best-so-far data until the square perimeter exceeded the
441 * best-so-far radius.
444 for (int k = 0; k < 3; k++)
445 if (isnan(value[k]))
446 for (int radius = 1; radius <= nn_defined_radius; radius++) {
447 double nearest_radius_squared = (radius + 1) * (radius + 1);
448 for (int ii = -radius; ii <= radius; ii++)
449 for (int jj = -radius; jj <= radius; jj++) {
450 if (!im->in_bounds(point(i + ii, j + jj)))
451 continue;
452 if (ii * ii + jj * jj < nearest_radius_squared
453 && finite(im->get_pixel(i + ii, j + jj)[k])) {
454 value[k] = im->get_pixel(i + ii, j + jj)[k];
455 nearest_radius_squared = ii * ii + jj * jj;
458 if (nearest_radius_squared < (radius + 1) * (radius + 1))
459 break;
463 * Unlinearize
466 pixel unlinearized(exp->unlinearize((value - minval_pixel)
467 / (maxval - minval)));
469 unlinearized = unlinearized.clamp();
471 p->red = (Quantum) ale_real_to_int(unlinearized[0], MaxRGB);
472 p->green = (Quantum) ale_real_to_int(unlinearized[1], MaxRGB);
473 p->blue = (Quantum) ale_real_to_int(unlinearized[2], MaxRGB);
474 p++;
477 if (!SyncImagePixels(mi))
478 break;
481 if (!WriteImage(image_info, mi)) {
484 * Perhaps file type was unknown? Set to PNM by default.
487 strncpy(mi->magick, "PNM", MaxTextExtent);
488 strncpy(image_info->magick, "PNM", MaxTextExtent);
490 if (!WriteImage(image_info, mi)) {
491 fprintf(stderr, "\n\n");
492 CatchException(&mi->exception);
493 fprintf(stderr, "\n");
494 exit(1);
498 DestroyImage(mi);
499 DestroyImageInfo(image_info);
500 #else
501 write_ppm(filename, im, exp, mcv, ppm_type == 2, rezero, exposure_scale || exp_scale_override,
502 nn_defined_radius);
503 #endif
506 static void output(const image *i) {
507 assert (file_count > 0);
508 write_image(output_name(), i, output_exposure);
511 static void vise_write(const char *p, const char *s, const image *i) {
512 static int count = 0;
513 int length = strlen(p) + strlen(s) + 8;
514 char *output_string = (char *) malloc(length * sizeof(char));
516 snprintf(output_string, length, "%s%08d%s", p, count, s);
518 write_image(output_string, i, output_exposure);
520 count++;
523 static exposure &exp(int n) {
524 return *input_exposure[n];
527 static const exposure &const_exp(int n) {
528 return *input_exposure[n];
531 static exposure &exp() {
532 return *output_exposure;
535 static void exp_scale() {
536 exposure_scale = 1;
539 static void exp_noscale() {
540 exposure_scale = 0;
543 static const exposure &const_exp() {
544 return *output_exposure;
547 static const unsigned int bayer(unsigned int n) {
548 if (bayer_specific[n] == IMAGE_BAYER_DEFAULT)
549 return bayer_default;
550 else
551 return bayer_specific[n];
554 static const image *open(unsigned int n) {
555 assert (n < file_count);
556 assert (!files_open[n]);
558 files_open[n] = 1;
560 if (latest_close_num >= 0 && n == (unsigned int) latest_close_num) {
561 latest_close_num = -1;
562 return images[n];
565 if (n < cache_count)
566 return images[n];
568 ui::get()->loading_file();
569 image *i = read_image(filenames[n], input_exposure[n], "file", bayer(n), (n == 0));
571 images[n] = i;
573 return images[n];
576 static void open_all() {
577 for (unsigned int n = 0; n < file_count; n++)
578 open(n);
581 static const image *get_open(unsigned int n) {
582 assert (files_open[n]);
583 return images[n];
586 static image *copy(unsigned int n, const char *name) {
587 assert (n < file_count);
589 if (files_open[n])
590 return images[n]->clone(name);
591 else {
592 image *i = read_image(filenames[n], input_exposure[n], name, bayer(n), (n == 0));
593 return i;
597 static void close(unsigned int image) {
598 assert (image < file_count);
599 assert (files_open[image]);
601 files_open[image] = 0;
603 if (image < cache_count)
604 return;
606 if (image == cache_count) {
607 double image_size = ((double) images[image]->storage_size()) / pow(2, 20);
609 if (image_size + cache_size < cache_size_max) {
610 cache_size += image_size;
611 cache_count++;
612 ui::get()->cache(cache_size, cache_size_max);
613 return;
614 } else {
615 ui::get()->cache_status(0);
619 if (latest_close_num >= 0)
620 delete images[latest_close_num];
622 latest_close_num = image;
625 static void close_all() {
626 for (unsigned int n = 0; n < file_count; n++)
627 close(n);
632 #endif