1 // Copyright 2002, 2003, 2004 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.h: Abstract base class for the internal representations of images used
31 #include "exposure/exposure.h"
33 #define IMAGE_BAYER_NONE 0
36 * This constant indicates that some other default value should be filled in.
39 #define IMAGE_BAYER_DEFAULT 0x8
42 * Do not change these values without inspecting
43 * image_bayer_ale_real::r_*_offset().
45 #define IMAGE_BAYER_RGBG 0x4 /* binary 100 */
46 #define IMAGE_BAYER_GBGR 0x5 /* binary 101 */
47 #define IMAGE_BAYER_GRGB 0x6 /* binary 110 */
48 #define IMAGE_BAYER_BGRG 0x7 /* binary 111 */
50 class image
: protected exposure::listener
{
52 static double resident
;
53 unsigned int _dimx
, _dimy
, _depth
;
56 mutable exposure
*_exp
;
60 * Memoized function variables. We may want to change these even when
63 mutable int _apm_memo
;
64 mutable ale_real _apm
;
65 mutable int _accm_memo
;
67 mutable int _acm_memo
;
70 void avg_channel_clamped_magnitude_memo() const {
72 pixel_accum accumulator
;
80 accumulator
= pixel_accum(0, 0, 0);
82 for (i
= 0; i
< _dimy
; i
++)
83 for (j
= 0; j
< _dimx
; j
++) {
84 pixel value
= get_pixel(i
, j
);
86 for (k
= 0; k
< _depth
; k
++)
87 if (finite(value
[k
])) {
92 accumulator
[k
] += value
[k
];
97 accumulator
/= divisor
;
102 void avg_channel_magnitude_memo() const {
103 unsigned int i
, j
, k
;
104 pixel_accum accumulator
;
112 accumulator
= pixel_accum(0, 0, 0);
114 for (i
= 0; i
< _dimy
; i
++)
115 for (j
= 0; j
< _dimx
; j
++) {
116 pixel value
= get_pixel(i
, j
);
118 for (k
= 0; k
< _depth
; k
++)
119 if (finite(value
[k
])) {
120 accumulator
[k
] += value
[k
];
125 accumulator
/= divisor
;
131 void image_updated() {
138 static void set_resident(double r
) {
142 static double get_resident() {
146 image (unsigned int dimy
, unsigned int dimx
, unsigned int depth
,
147 const char *name
= "anonymous", exposure
*_exp
= NULL
,
148 unsigned int bayer
= IMAGE_BAYER_NONE
) {
155 _offset
= point(0, 0);
164 _exp
->add_listener(this, name
);
167 unsigned int get_bayer() const {
171 virtual char get_channels(int i
, int j
) const {
175 virtual unsigned int bayer_color(unsigned int i
, unsigned int j
) const {
179 double storage_size() const {
180 if (bayer
!= IMAGE_BAYER_NONE
)
181 return _dimx
* _dimy
* sizeof(ale_real
);
183 return 3 * _dimx
* _dimy
* sizeof(ale_real
);
186 exposure
&exp() const {
190 point
offset() const {
194 void set_offset(int i
, int j
) {
199 void set_offset(point p
) {
203 unsigned int width() const {
207 unsigned int height() const {
211 unsigned int depth() const {
215 virtual void set_pixel(unsigned int y
, unsigned int x
, spixel p
) = 0;
217 virtual spixel
get_pixel(unsigned int y
, unsigned int x
) const = 0;
219 virtual spixel
get_raw_pixel(unsigned int y
, unsigned int x
) const {
220 return ((const image
*)this)->get_pixel(y
, x
);
223 virtual void add_pixel(unsigned int y
, unsigned int x
, pixel p
) {
227 virtual void mul_pixel(unsigned int y
, unsigned int x
, pixel p
) {
231 virtual void div_pixel(unsigned int y
, unsigned int x
, pixel p
) {
235 virtual void add_chan(unsigned int y
, unsigned int x
, unsigned int k
, ale_real c
) {
239 virtual void div_chan(unsigned int y
, unsigned int x
, unsigned int k
, ale_real c
) {
243 virtual void set_chan(unsigned int y
, unsigned int x
, unsigned int k
, ale_sreal c
) = 0;
245 virtual ale_sreal
get_chan(unsigned int y
, unsigned int x
, unsigned int k
) const = 0;
247 ale_real
maxval() const {
248 ale_real result
= get_pixel(0, 0)[0];
250 for (unsigned int i
= 0; i
< _dimy
; i
++)
251 for (unsigned int j
= 0; j
< _dimx
; j
++) {
252 pixel p
= get_pixel(i
, j
);
254 for (unsigned int k
= 0; k
< _depth
; k
++)
255 if (p
[k
] > result
|| !finite(result
))
262 ale_real
minval() const {
263 ale_real result
= get_pixel(0, 0)[0];
265 for (unsigned int i
= 0; i
< _dimy
; i
++)
266 for (unsigned int j
= 0; j
< _dimx
; j
++) {
267 pixel p
= get_pixel(i
, j
);
269 for (unsigned int k
= 0; k
< _depth
; k
++)
270 if (p
[k
] < result
|| !finite(result
))
278 * Get the maximum difference among adjacent pixels.
281 pixel
get_max_diff(unsigned int i
, unsigned int j
) const {
282 assert(i
<= _dimy
- 1);
283 assert(j
<= _dimx
- 1);
285 pixel max
= get_pixel(i
, j
), min
= get_pixel(i
, j
);
287 for (int ii
= -1; ii
<= 1; ii
++)
288 for (int jj
= -1; jj
<= 1; jj
++) {
296 if ((unsigned int) iii
> _dimy
- 1)
298 if ((unsigned int) jjj
> _dimx
- 1)
301 pixel p
= get_pixel(iii
, jjj
);
303 for (int d
= 0; d
< 3; d
++) {
314 pixel
get_max_diff(point x
) const {
317 assert (x
[0] <= _dimy
- 1);
318 assert (x
[1] <= _dimx
- 1);
320 unsigned int i
= (unsigned int) round(x
[0]);
321 unsigned int j
= (unsigned int) round(x
[1]);
323 return get_max_diff(i
, j
);
326 int in_bounds(point x
) const {
329 || x
[0] > height() - 1
330 || x
[1] > width() - 1)
338 * Get a color value at a given position using bilinear interpolation between the
339 * four nearest pixels.
341 pixel
get_bl(point x
, int defined
= 0) const {
342 // fprintf(stderr, "get_bl x=%f %f\n", (double) x[0], (double) x[1]);
348 assert (x
[0] <= _dimy
- 1);
349 assert (x
[1] <= _dimx
- 1);
351 int lx
= (int) floor(x
[1]);
352 int hx
= (int) floor(x
[1]) + 1;
353 int ly
= (int) floor(x
[0]);
354 int hy
= (int) floor(x
[0]) + 1;
356 // fprintf(stderr, "get_bl l=%d %d h=%d %d\n", ly, lx, hy, hx);
361 neighbor
[0] = get_pixel(ly
, lx
);
362 neighbor
[1] = get_pixel(hy
% _dimy
, lx
);
363 neighbor
[2] = get_pixel(hy
% _dimy
, hx
% _dimx
);
364 neighbor
[3] = get_pixel(ly
, hx
% _dimx
);
366 // for (int d = 0; d < 4; d++)
367 // fprintf(stderr, "neighbor_%d=%f %f %f\n", d,
368 // (double) neighbor[d][0],
369 // (double) neighbor[d][1],
370 // (double) neighbor[d][2]);
372 factor
[0] = (ale_real
) (hx
- x
[1]) * (ale_real
) (hy
- x
[0]);
373 factor
[1] = (ale_real
) (hx
- x
[1]) * (ale_real
) (x
[0] - ly
);
374 factor
[2] = (ale_real
) (x
[1] - lx
) * (ale_real
) (x
[0] - ly
);
375 factor
[3] = (ale_real
) (x
[1] - lx
) * (ale_real
) (hy
- x
[0]);
377 // for (int d = 0; d < 4; d++)
378 // fprintf(stderr, "factor_%d=%f\n", d,
379 // (double) factor[d]);
382 * Use bilinear and/or geometric interpolation
386 result
= pixel(0, 0, 0);
388 for (int n
= 0; n
< 4; n
++)
389 result
+= factor
[n
] * neighbor
[n
];
393 * Calculating the geometric mean may be expensive on
394 * some platforms (e.g., those without floating-point
398 result
= pixel(1, 1, 1);
400 for (int n
= 0; n
< 4; n
++)
401 result
*= ppow(neighbor
[n
], factor
[n
]);
404 * Taking the minimum value may be cheaper than
405 * calculating a geometric mean.
408 result
= neighbor
[0];
410 for (int n
= 1; n
< 4; n
++)
411 for (int k
= 0; k
< 3; k
++)
412 if (neighbor
[n
][k
] < result
[k
])
413 result
[k
] = neighbor
[n
][k
];
417 // fprintf(stderr, "result=%f %f %f\n",
418 // (double) result[0],
419 // (double) result[1],
420 // (double) result[2]);
425 pixel
get_scaled_bl(point x
, ale_pos f
, int defined
= 0) const {
427 x
[0]/f
<= height() - 1
429 : (ale_pos
) (height() - 1),
430 x
[1]/f
<= width() - 1
432 : (ale_pos
) (width() - 1));
434 return get_bl(scaled
, defined
);
439 * Make a new image suitable for receiving scaled values.
441 virtual image
*scale_generator(int height
, int width
, int depth
, const char *name
) const = 0;
444 * Generate an image of medians within a given radius
447 image
*medians(int radius
) const {
449 assert (radius
>= 0);
451 image
*is
= scale_generator(height(), width(), depth(), "median");
454 for (unsigned int i
= 0; i
< height(); i
++)
455 for (unsigned int j
= 0; j
< width(); j
++) {
457 std::vector
<ale_real
> p
[3];
459 for (int ii
= -radius
; ii
<= radius
; ii
++)
460 for (int jj
= -radius
; jj
<= radius
; jj
++) {
464 if (in_bounds(point(iii
, jjj
)))
465 for (int k
= 0; k
< 3; k
++)
466 if (finite(get_pixel(iii
, jjj
)[k
]))
467 p
[k
].push_back(get_pixel(iii
, jjj
)[k
]);
470 is
->set_pixel(i
, j
, d2::pixel::undefined());
472 for (int k
= 0; k
< 3; k
++) {
473 std::sort(p
[k
].begin(), p
[k
].end());
475 unsigned int pkc
= p
[k
].size();
481 is
->set_chan(i
, j
, k
,
482 (p
[k
][pkc
/ 2] + p
[k
][pkc
/ 2 - 1]) / 2);
484 is
->set_chan(i
, j
, k
,
493 * Generate an image of differences of the first channel. The first
494 * coordinate differences are stored in the first channel, second in the
498 image
*fcdiffs() const {
499 image
*is
= scale_generator(height(), width(), depth(), "diff");
503 for (unsigned int i
= 0; i
< height(); i
++)
504 for (unsigned int j
= 0; j
< width(); j
++) {
508 && !finite(get_chan(i
, j
, 0))) {
510 is
->set_chan(i
, j
, 0, (get_chan(i
+ 1, j
, 0)
511 - get_chan(i
- 1, j
, 0)) / 2);
513 } else if (i
+ 1 < height()
515 && finite(get_chan(i
+ 1, j
, 0))
516 && finite(get_chan(i
- 1, j
, 0))) {
518 is
->set_chan(i
, j
, 0, ((get_chan(i
, j
, 0) - get_chan(i
- 1, j
, 0))
519 + (get_chan(i
+ 1, j
, 0) - get_chan(i
, j
, 0))) / 2);
521 } else if (i
+ 1 < height()
522 && finite(get_chan(i
+ 1, j
, 0))) {
524 is
->set_chan(i
, j
, 0, get_chan(i
+ 1, j
, 0) - get_chan(i
, j
, 0));
527 && finite(get_chan(i
- 1, j
, 0))) {
529 is
->set_chan(i
, j
, 0, get_chan(i
, j
, 0) - get_chan(i
- 1, j
, 0));
532 is
->set_chan(i
, j
, 0, 0);
537 && !finite(get_chan(i
, j
, 0))) {
539 is
->set_chan(i
, j
, 1, (get_chan(i
, j
+ 1, 0) - get_chan(i
, j
- 1, 0)) / 2);
541 } else if (j
+ 1 < width()
543 && finite(get_chan(i
, j
+ 1, 0))
544 && finite(get_chan(i
, j
- 1, 0))) {
546 is
->set_chan(i
, j
, 1, ((get_chan(i
, j
, 0) - get_chan(i
, j
- 1, 0))
547 + (get_chan(i
, j
+ 1, 0) - get_chan(i
, j
, 0))) / 2);
549 } else if (j
+ 1 < width() && finite(get_chan(i
, j
+ 1, 0))) {
551 is
->set_chan(i
, j
, 1, get_chan(i
, j
+ 1, 0) - get_chan(i
, j
, 0));
553 } else if (j
> 0 && finite(get_chan(i
, j
- 1, 0))) {
555 is
->set_chan(i
, j
, 1, get_chan(i
, j
, 0) - get_chan(i
, j
- 1, 0));
558 is
->set_chan(i
, j
, 1, 0);
566 * Generate an image of median (within a given radius) difference of the
570 image
*fcdiff_median(int radius
) const {
571 image
*diff
= fcdiffs();
575 image
*median
= diff
->medians(radius
);
585 * Scale by half. We use the following filter:
591 * At the edges, these values are normalized so that the sum of the
592 * weights of contributing pixels is 1.
594 class scale_by_half_threaded
: public thread::decompose_domain
{
598 void subdomain_algorithm(unsigned int thread
,
599 int i_min
, int i_max
, int j_min
, int j_max
) {
601 ale_real _0625
= (ale_real
) 0.0625;
602 ale_real _125
= (ale_real
) 0.125;
603 ale_real _25
= (ale_real
) 0.25;
604 ale_real _0
= (ale_real
) 0;
606 unsigned int ui_min
= (unsigned int) i_min
;
607 unsigned int ui_max
= (unsigned int) i_max
;
608 unsigned int uj_min
= (unsigned int) j_min
;
609 unsigned int uj_max
= (unsigned int) j_max
;
611 for (unsigned int i
= ui_min
; i
< ui_max
; i
++)
612 for (unsigned int j
= uj_min
; j
< uj_max
; j
++) {
615 ( ( ((i
> 0 && j
> 0)
616 ? iu
->get_pixel(2 * i
- 1, 2 * j
- 1) * _0625
619 ? iu
->get_pixel(2 * i
- 1, 2 * j
) * _125
621 + ((i
> 0 && j
< is
->width() - 1)
622 ? iu
->get_pixel(2 * i
- 1, 2 * j
+ 1) * _0625
625 ? iu
->get_pixel(2 * i
, 2 * j
- 1) * _125
627 + iu
->get_pixel(2 * i
, 2 * j
) * _25
628 + ((j
< is
->width() - 1)
629 ? iu
->get_pixel(2 * i
, 2 * j
+ 1) * _125
631 + ((i
< is
->height() - 1 && j
> 0)
632 ? iu
->get_pixel(2 * i
+ 1, 2 * j
- 1) * _0625
634 + ((i
< is
->height() - 1)
635 ? iu
->get_pixel(2 * i
+ 1, 2 * j
) * _125
637 + ((i
< is
->height() && j
< is
->width() - 1)
638 ? iu
->get_pixel(2 * i
+ 1, 2 * j
+ 1) * _0625
649 + ((i
> 0 && j
< is
->width() - 1)
656 + ((j
< is
->width() - 1)
659 + ((i
< is
->height() - 1 && j
> 0)
662 + ((i
< is
->height() - 1)
665 + ((i
< is
->height() && j
< is
->width() - 1)
672 scale_by_half_threaded(image
*_is
, const image
*_iu
)
673 : decompose_domain(0, _is
->height(),
680 image
*scale_by_half(const char *name
) const {
683 image
*is
= scale_generator(
684 (int) floor(height() * (double) f
),
685 (int) floor(width() * (double) f
), depth(), name
);
689 scale_by_half_threaded
sbht(is
, this);
692 is
->_offset
= point(_offset
[0] * f
, _offset
[1] * f
);
698 * Scale by half. This function uses externally-provided weights,
699 * multiplied by the following filter:
705 * Values are normalized so that the sum of the weights of contributing
708 image
*scale_by_half(const image
*weights
, const char *name
) const {
711 return scale_by_half(name
);
715 image
*is
= scale_generator(
716 (int) floor(height() * (double) f
),
717 (int) floor(width() * (double) f
), depth(), name
);
721 for (unsigned int i
= 0; i
< is
->height(); i
++)
722 for (unsigned int j
= 0; j
< is
->width(); j
++) {
726 ( ( ((i
> 0 && j
> 0)
727 ? (pixel
) get_pixel(2 * i
- 1, 2 * j
- 1)
728 * (pixel
) weights
->get_pixel(2 * i
- 1, 2 * j
- 1)
732 ? (pixel
) get_pixel(2 * i
- 1, 2 * j
)
733 * (pixel
) weights
->get_pixel(2 * i
- 1, 2 * j
)
736 + ((i
> 0 && j
< is
->width() - 1)
737 ? (pixel
) get_pixel(2 * i
- 1, 2 * j
+ 1)
738 * (pixel
) weights
->get_pixel(2 * i
- 1, 2 * j
+ 1)
742 ? (pixel
) get_pixel(2 * i
, 2 * j
- 1)
743 * (pixel
) weights
->get_pixel(2 * i
, 2 * j
- 1)
746 + get_pixel(2 * i
, 2 * j
)
747 * (pixel
) weights
->get_pixel(2 * i
, 2 * j
)
749 + ((j
< is
->width() - 1)
750 ? (pixel
) get_pixel(2 * i
, 2 * j
+ 1)
751 * (pixel
) weights
->get_pixel(2 * i
, 2 * j
+ 1)
754 + ((i
< is
->height() - 1 && j
> 0)
755 ? (pixel
) get_pixel(2 * i
+ 1, 2 * j
- 1)
756 * (pixel
) weights
->get_pixel(2 * i
+ 1, 2 * j
- 1)
759 + ((i
< is
->height() - 1)
760 ? (pixel
) get_pixel(2 * i
+ 1, 2 * j
)
761 * (pixel
) weights
->get_pixel(2 * i
+ 1, 2 * j
)
764 + ((i
< is
->height() && j
< is
->width() - 1)
765 ? (pixel
) get_pixel(2 * i
+ 1, 2 * j
+ 1)
766 * (pixel
) weights
->get_pixel(2 * i
+ 1, 2 * j
+ 1)
773 ? weights
->get_pixel(2 * i
- 1, 2 * j
- 1)
777 ? weights
->get_pixel(2 * i
- 1, 2 * j
)
780 + ((i
> 0 && j
< is
->width() - 1)
781 ? weights
->get_pixel(2 * i
- 1, 2 * j
+ 1)
785 ? weights
->get_pixel(2 * i
, 2 * j
- 1)
788 + weights
->get_pixel(2 * i
, 2 * j
)
790 + ((j
< is
->width() - 1)
791 ? weights
->get_pixel(2 * i
, 2 * j
+ 1)
794 + ((i
< is
->height() - 1 && j
> 0)
795 ? weights
->get_pixel(2 * i
+ 1, 2 * j
- 1)
798 + ((i
< is
->height() - 1)
799 ? weights
->get_pixel(2 * i
+ 1, 2 * j
)
802 + ((i
< is
->height() && j
< is
->width() - 1)
803 ? weights
->get_pixel(2 * i
+ 1, 2 * j
+ 1)
805 : pixel(0, 0, 0)) ) );
807 for (int k
= 0; k
< 3; k
++)
808 if (!finite(value
[k
]))
811 is
->set_pixel(i
, j
, value
);
814 is
->_offset
= point(_offset
[0] * f
, _offset
[1] * f
);
820 * Scale an image definition array by 1/2.
822 * ALE considers an image definition array as a special kind of image
823 * weight array (typedefs of which should appear below the definition
824 * of this class). ALE uses nonzero pixel values to mean 'defined' and
825 * zero values to mean 'undefined'. Through this interpretation, the
826 * image weight array implementation that ALE uses allows image weight
827 * arrays to also serve as image definition arrays.
829 * Whereas scaling of image weight arrays is not generally obvious in
830 * either purpose or method, ALE requires that image definition arrays
831 * be scalable. (Note that in the special case where weight is treated
832 * as certainty, using a geometric mean is probably correct.)
834 * We currently use a geometric mean to implement scaling of
838 class defined_scale_by_half_threaded
: public thread::decompose_domain
{
842 void subdomain_algorithm(unsigned int thread
,
843 int i_min
, int i_max
, int j_min
, int j_max
) {
846 ale_real _0625
= (ale_real
) 0.0625;
847 ale_real _125
= (ale_real
) 0.125;
848 ale_real _25
= (ale_real
) 0.25;
851 int ui_min
= (int) i_min
;
852 int ui_max
= (int) i_max
;
853 int uj_min
= (int) j_min
;
854 int uj_max
= (int) j_max
;
856 for (int i
= ui_min
; i
< ui_max
; i
++)
857 for (int j
= uj_min
; j
< uj_max
; j
++) {
862 * Calculate a geometric mean; this approach
863 * may be expensive on some platforms (e.g.,
864 * those without floating-point support in
870 ( ( ((i
> 0 && j
> 0)
871 ? ppow(iu
->get_pixel(2 * i
- 1, 2 * j
- 1), _0625
)
874 ? ppow(iu
->get_pixel(2 * i
- 1, 2 * j
), _125
)
876 * ((i
> 0 && j
< is
->width() - 1)
877 ? ppow(iu
->get_pixel(2 * i
- 1, 2 * j
+ 1), _0625
)
880 ? ppow(iu
->get_pixel(2 * i
, 2 * j
- 1), _125
)
882 * ppow(iu
->get_pixel(2 * i
, 2 * j
), _25
)
883 * ((j
< is
->width() - 1)
884 ? ppow(iu
->get_pixel(2 * i
, 2 * j
+ 1), _125
)
886 * ((i
< is
->height() - 1 && j
> 0)
887 ? ppow(iu
->get_pixel(2 * i
+ 1, 2 * j
- 1), _0625
)
889 * ((i
< is
->height() - 1)
890 ? ppow(iu
->get_pixel(2 * i
+ 1, 2 * j
), _125
)
892 * ((i
< is
->height() && j
< is
->width() - 1)
893 ? ppow(iu
->get_pixel(2 * i
+ 1, 2 * j
+ 1), _0625
)
897 pixel value
= iu
->get_pixel(2 * i
, 2 * j
);
899 for (int ii
= 2 * i
- 1; ii
<= 2 * i
+ 1; ii
++)
900 for (int jj
= 2 * j
- 1; jj
<= 2 * j
+ 1; jj
++) {
903 || ii
> (int) iu
->height() - 1
904 || jj
> (int) iu
->height() - 1)
907 pixel value2
= iu
->get_pixel(ii
, jj
);
909 for (int k
= 0; k
< 3; k
++)
910 if (value2
[k
] < value
[k
]
911 || !finite(value2
[k
])) /* propagate non-finites */
912 value
[k
] = value2
[k
];
918 for (int k
= 0; k
< 3; k
++)
919 if (!finite(value
[k
]))
922 is
->set_pixel(i
, j
, value
);
927 defined_scale_by_half_threaded(image
*_is
, const image
*_iu
)
928 : decompose_domain(0, _is
->height(),
935 image
*defined_scale_by_half(const char *name
) const {
938 image
*is
= scale_generator(
939 (int) floor(height() * (double) f
),
940 (int) floor(width() * (double) f
), depth(), name
);
944 defined_scale_by_half_threaded
dsbht(is
, this);
947 is
->_offset
= point(_offset
[0] * f
, _offset
[1] * f
);
953 * Return an image scaled by some factor != 1.0, using bilinear
956 image
*scale(ale_pos f
, const char *name
, int defined
= 0) const {
959 * We probably don't want to scale images by a factor of 1.0,
960 * or by non-positive values.
962 assert (f
!= 1.0 && f
> 0);
965 image
*is
= scale_generator(
966 (int) floor(height() * (double) f
),
967 (int) floor(width() * (double) f
), depth(), name
);
971 unsigned int i
, j
, k
;
973 for (i
= 0; i
< is
->height(); i
++)
974 for (j
= 0; j
< is
->width(); j
++)
975 for (k
= 0; k
< is
->depth(); k
++)
977 get_scaled_bl(point(i
, j
), f
, defined
));
979 is
->_offset
= point(_offset
[0] * f
, _offset
[1] * f
);
982 } else if (f
== 0.5) {
984 return scale_by_half(name
);
986 return defined_scale_by_half(name
);
988 image
*is
= scale(2*f
, name
, defined
);
989 image
*result
= is
->scale(0.5, name
, defined
);
997 * Extend the image area to the top, bottom, left, and right,
998 * initializing the new image areas with black pixels. Negative values
1001 virtual image
*_extend(int top
, int bottom
, int left
, int right
) = 0;
1003 static void extend(image
**i
, int top
, int bottom
, int left
, int right
) {
1004 image
*is
= (*i
)->_extend(top
, bottom
, left
, right
);
1015 image
*clone(const char *name
) const {
1016 image
*ic
= scale_generator(
1017 height(), width(), depth(), name
);
1021 for (unsigned int i
= 0; i
< height(); i
++)
1022 for (unsigned int j
= 0; j
< width(); j
++)
1027 ic
->_offset
= _offset
;
1029 ic
->_apm_memo
= _apm_memo
;
1030 ic
->_acm_memo
= _acm_memo
;
1031 ic
->_accm_memo
= _accm_memo
;
1040 * Calculate the average (mean) clamped magnitude of a channel across
1041 * all pixels in an image. The magnitude is clamped to the range of
1044 ale_real
avg_channel_clamped_magnitude(unsigned int k
) const {
1047 * This is a memoized function
1050 assert (k
< _depth
);
1052 avg_channel_clamped_magnitude_memo();
1056 pixel
avg_channel_clamped_magnitude() const {
1057 avg_channel_clamped_magnitude_memo();
1062 * Calculate the average (mean) magnitude of a channel across all
1063 * pixels in an image.
1065 ale_real
avg_channel_magnitude(unsigned int k
) const {
1068 * This is a memoized function
1071 assert (k
< _depth
);
1073 avg_channel_magnitude_memo();
1077 pixel
avg_channel_magnitude() const {
1078 avg_channel_magnitude_memo();
1083 * Calculate the average (mean) magnitude of a pixel (where magnitude
1084 * is defined as the mean of the channel values).
1086 ale_real
avg_pixel_magnitude() const {
1087 unsigned int i
, j
, k
;
1089 ale_accum accumulator
;
1090 ale_accum divisor
= 0;
1098 for (i
= 0; i
< _dimy
; i
++)
1099 for (j
= 0; j
< _dimx
; j
++) {
1100 pixel value
= get_pixel(i
, j
);
1102 for (k
= 0; k
< _depth
; k
++)
1103 if (finite(value
[k
])) {
1104 accumulator
+= value
[k
];
1109 accumulator
/= divisor
;