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__
30 #include "image_ale_real.h"
31 #include "image_bayer_ale_real.h"
33 #include "exposure/exposure.h"
34 #include "exposure/exposure_default.h"
39 * Private data members
45 * 0 = No type selected
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() {
111 InitializeMagick("ale");
116 * Destroy the image file handler
118 static void destroy_image() {
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
);
144 * Patterned after http://www.imagemagick.org/www/api.html
145 * and http://www.imagemagick.org/www/smile.c
148 ExceptionInfo exception
;
150 ImageInfo
*image_info
;
152 const PixelPacket
*p
;
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
)
171 if (bayer
== IMAGE_BAYER_NONE
)
172 im
= new image_ale_real(mi
->rows
, mi
->columns
, 3, name
, exp
);
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
);
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
));
196 DestroyImageInfo(image_info
);
200 return read_ppm(filename
, exp
, bayer
);
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
212 * INPUT_EXPOSURE should be an array of FILE_COUNT exposure objects
213 * that is never freed. OUTPUT_EXPOSURE should be an exposure * that
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);
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));
234 assert (bayer_specific
);
237 if (!images
|| !files_open
|| !bayer_specific
) {
238 fprintf(stderr
, "Unable to allocate memory for images.\n");
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",
250 static void ppm_plain() {
254 static void ppm_raw() {
258 static void ppm_auto() {
262 fprintf(stderr
, "\n\n*** Error: --auto flag not supported on this build. ***\n"
263 "*** (Hint: Rebuild with IMAGEMAGICK=1) ***\n\n");
268 static void set_default_bayer(unsigned int 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() {
282 static void depth8() {
287 static void destroy() {
288 assert (file_count
> 0);
292 static unsigned int count() {
293 assert (file_count
> 0);
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");
335 * Patterned after http://www.imagemagick.org/www/api.html
336 * and http://www.imagemagick.org/www/smile.c
339 ExceptionInfo exception
;
341 ImageInfo
*image_info
;
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)
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
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)
391 ale_real minval
= (rezero
? im
->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
)
408 for (i
= 0; i
< mi
->rows
; i
++) {
409 p
= SetImagePixels(mi
, 0, i
, mi
->columns
, 1);
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
++)
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
)))
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))
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
);
460 if (!SyncImagePixels(mi
))
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");
482 DestroyImageInfo(image_info
);
484 write_ppm(filename
, im
, exp
, mcv
, ppm_type
== 2, rezero
, exposure_scale
|| exp_scale_override
,
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
);
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() {
522 static void exp_noscale() {
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
;
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;
547 image
*i
= read_image(filenames
[n
], input_exposure
[n
], "file", bayer(n
), (n
== 0));
555 static void open_all() {
556 for (unsigned int n
= 0; n
< file_count
; n
++)
560 static const image
*get_open(unsigned int n
) {
561 assert (files_open
[n
]);
565 static image
*copy(unsigned int n
, char *name
) {
566 assert (n
< file_count
);
569 return images
[n
]->clone(name
);
571 image
*i
= read_image(filenames
[n
], input_exposure
[n
], name
, bayer(n
), (n
== 0));
576 static void close(unsigned int image
) {
577 assert (image
< file_count
);
578 assert (files_open
[image
]);
580 if (latest_close_num
>= 0)
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
++)