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 unsigned int _dimx
, _dimy
, _depth
;
55 mutable exposure
*_exp
;
59 * Memoized function variables. We may want to change these even when
62 mutable int _apm_memo
;
63 mutable ale_real _apm
;
64 mutable int _accm_memo
;
66 mutable int _acm_memo
;
69 void avg_channel_clamped_magnitude_memo() const {
71 pixel_accum accumulator
;
79 accumulator
= pixel_accum(0, 0, 0);
81 for (i
= 0; i
< _dimy
; i
++)
82 for (j
= 0; j
< _dimx
; j
++) {
83 pixel value
= get_pixel(i
, j
);
85 for (k
= 0; k
< _depth
; k
++)
86 if (finite(value
[k
])) {
91 accumulator
[k
] += value
[k
];
96 accumulator
/= divisor
;
101 void avg_channel_magnitude_memo() const {
102 unsigned int i
, j
, k
;
103 pixel_accum accumulator
;
111 accumulator
= pixel_accum(0, 0, 0);
113 for (i
= 0; i
< _dimy
; i
++)
114 for (j
= 0; j
< _dimx
; j
++) {
115 pixel value
= get_pixel(i
, j
);
117 for (k
= 0; k
< _depth
; k
++)
118 if (finite(value
[k
])) {
119 accumulator
[k
] += value
[k
];
124 accumulator
/= divisor
;
130 void image_updated() {
137 image (unsigned int dimy
, unsigned int dimx
, unsigned int depth
,
138 char *name
= "anonymous", exposure
*_exp
= NULL
,
139 unsigned int bayer
= IMAGE_BAYER_NONE
) {
146 _offset
= point(0, 0);
155 _exp
->add_listener(this, name
);
158 unsigned int get_bayer() const {
162 virtual char get_channels(int i
, int j
) const {
166 double storage_size() const {
167 if (bayer
!= IMAGE_BAYER_NONE
)
168 return _dimx
* _dimy
* sizeof(ale_real
);
170 return 3 * _dimx
* _dimy
* sizeof(ale_real
);
173 exposure
&exp() const {
177 point
offset() const {
181 void set_offset(int i
, int j
) {
186 void set_offset(point p
) {
190 unsigned int width() const {
194 unsigned int height() const {
198 unsigned int depth() const {
202 virtual spixel
&pix(unsigned int y
, unsigned int x
) = 0;
204 virtual const spixel
&pix(unsigned int y
, unsigned int x
) const = 0;
206 virtual ale_real
&chan(unsigned int y
, unsigned int x
, unsigned int k
) = 0;
208 virtual const ale_real
&chan(unsigned int y
, unsigned int x
, unsigned int k
) const = 0;
210 virtual void set_pixel(unsigned int y
, unsigned int x
, spixel p
) {
214 virtual pixel
get_pixel(unsigned int y
, unsigned int x
) const {
215 return ((const image
*)this)->pix(y
, x
);
218 virtual pixel
get_raw_pixel(unsigned int y
, unsigned int x
) const {
219 return ((const image
*)this)->get_pixel(y
, x
);
222 ale_real
maxval() const {
223 ale_real result
= get_pixel(0, 0)[0];
225 for (unsigned int i
= 0; i
< _dimy
; i
++)
226 for (unsigned int j
= 0; j
< _dimx
; j
++) {
227 pixel p
= get_pixel(i
, j
);
229 for (unsigned int k
= 0; k
< _depth
; k
++)
230 if (p
[k
] > result
|| !finite(result
))
237 ale_real
minval() const {
238 ale_real result
= get_pixel(0, 0)[0];
240 for (unsigned int i
= 0; i
< _dimy
; i
++)
241 for (unsigned int j
= 0; j
< _dimx
; j
++) {
242 pixel p
= get_pixel(i
, j
);
244 for (unsigned int k
= 0; k
< _depth
; k
++)
245 if (p
[k
] < result
|| !finite(result
))
253 * Get the maximum difference among adjacent pixels.
256 pixel
get_max_diff(unsigned int i
, unsigned int j
) const {
257 assert(i
<= _dimy
- 1);
258 assert(j
<= _dimx
- 1);
260 pixel max
= get_pixel(i
, j
), min
= get_pixel(i
, j
);
262 for (int ii
= -1; ii
<= 1; ii
++)
263 for (int jj
= -1; jj
<= 1; jj
++) {
271 if ((unsigned int) iii
> _dimy
- 1)
273 if ((unsigned int) jjj
> _dimx
- 1)
276 pixel p
= get_pixel(iii
, jjj
);
278 for (int d
= 0; d
< 3; d
++) {
289 pixel
get_max_diff(point x
) const {
292 assert (x
[0] <= _dimy
- 1);
293 assert (x
[1] <= _dimx
- 1);
295 unsigned int i
= (unsigned int) round(x
[0]);
296 unsigned int j
= (unsigned int) round(x
[1]);
298 return get_max_diff(i
, j
);
301 int in_bounds(point x
) const {
304 || x
[0] > height() - 1
305 || x
[1] > width() - 1)
313 * Get a color value at a given position using bilinear interpolation between the
314 * four nearest pixels.
316 pixel
get_bl(point x
, int defined
= 0) const {
322 assert (x
[0] <= _dimy
- 1);
323 assert (x
[1] <= _dimx
- 1);
325 int lx
= (int) floor(x
[1]);
326 int hx
= (int) floor(x
[1]) + 1;
327 int ly
= (int) floor(x
[0]);
328 int hy
= (int) floor(x
[0]) + 1;
333 neighbor
[0] = get_pixel(ly
, lx
);
334 neighbor
[1] = get_pixel(hy
% _dimy
, lx
);
335 neighbor
[2] = get_pixel(hy
% _dimy
, hx
% _dimx
);
336 neighbor
[3] = get_pixel(ly
, hx
% _dimx
);
338 factor
[0] = (hx
- x
[1]) * (hy
- x
[0]);
339 factor
[1] = (hx
- x
[1]) * (x
[0] - ly
);
340 factor
[2] = (x
[1] - lx
) * (x
[0] - ly
);
341 factor
[3] = (x
[1] - lx
) * (hy
- x
[0]);
344 * Use bilinear and/or geometric interpolation
348 result
= pixel(0, 0, 0);
350 for (int n
= 0; n
< 4; n
++)
351 result
+= factor
[n
] * neighbor
[n
];
353 result
= pixel(1, 1, 1);
355 for (int n
= 0; n
< 4; n
++)
356 result
*= ppow(neighbor
[n
], factor
[n
]);
362 pixel
get_scaled_bl(point x
, ale_pos f
, int defined
= 0) const {
364 x
[0]/f
<= height() - 1
367 x
[1]/f
<= width() - 1
371 return get_bl(scaled
, defined
);
376 * Make a new image suitable for receiving scaled values.
378 virtual image
*scale_generator(int height
, int width
, int depth
, char *name
) const = 0;
381 * Generate an image of medians within a given radius
384 image
*medians(int radius
) const {
386 assert (radius
>= 0);
388 image
*is
= scale_generator(height(), width(), depth(), "median");
391 for (unsigned int i
= 0; i
< height(); i
++)
392 for (unsigned int j
= 0; j
< width(); j
++) {
394 std::vector
<ale_real
> p
[3];
396 for (int ii
= -radius
; ii
<= radius
; ii
++)
397 for (int jj
= -radius
; jj
<= radius
; jj
++) {
401 if (in_bounds(point(iii
, jjj
)))
402 for (int k
= 0; k
< 3; k
++)
403 if (finite(get_pixel(iii
, jjj
)[k
]))
404 p
[k
].push_back(get_pixel(iii
, jjj
)[k
]);
407 is
->pix(i
, j
) = d2::pixel::undefined();
409 for (int k
= 0; k
< 3; k
++) {
410 std::sort(p
[k
].begin(), p
[k
].end());
412 unsigned int pkc
= p
[k
].size();
418 is
->chan(i
, j
, k
) = (p
[k
][pkc
/ 2] + p
[k
][pkc
/ 2 - 1]) / 2;
420 is
->chan(i
, j
, k
) = p
[k
][pkc
/ 2];
428 * Generate an image of differences of the first channel. The first
429 * coordinate differences are stored in the first channel, second in the
433 image
*fcdiffs() const {
434 image
*is
= scale_generator(height(), width(), depth(), "diff");
438 for (unsigned int i
= 0; i
< height(); i
++)
439 for (unsigned int j
= 0; j
< width(); j
++) {
443 && !finite(chan(i
, j
, 0))) {
445 is
->chan(i
, j
, 0) = (chan(i
+ 1, j
, 0) - chan(i
- 1, j
, 0)) / 2;
447 } else if (i
+ 1 < height()
449 && finite(chan(i
+ 1, j
, 0))
450 && finite(chan(i
- 1, j
, 0))) {
452 is
->chan(i
, j
, 0) = ((chan(i
, j
, 0) - chan(i
- 1, j
, 0))
453 + (chan(i
+ 1, j
, 0) - chan(i
, j
, 0))) / 2;
455 } else if (i
+ 1 < height()
456 && finite(chan(i
+ 1, j
, 0))) {
458 is
->chan(i
, j
, 0) = chan(i
+ 1, j
, 0) - chan(i
, j
, 0);
461 && finite(chan(i
- 1, j
, 0))) {
463 is
->chan(i
, j
, 0) = chan(i
, j
, 0) - chan(i
- 1, j
, 0);
466 is
->chan(i
, j
, 0) = 0;
471 && !finite(chan(i
, j
, 0))) {
473 is
->chan(i
, j
, 1) = (chan(i
, j
+ 1, 0) - chan(i
, j
- 1, 0)) / 2;
475 } else if (j
+ 1 < width()
477 && finite(chan(i
, j
+ 1, 0))
478 && finite(chan(i
, j
- 1, 0))) {
480 is
->chan(i
, j
, 1) = ((chan(i
, j
, 0) - chan(i
, j
- 1, 0))
481 + (chan(i
, j
+ 1, 0) - chan(i
, j
, 0))) / 2;
483 } else if (j
+ 1 < width() && finite(chan(i
, j
+ 1, 0))) {
485 is
->chan(i
, j
, 1) = chan(i
, j
+ 1, 0) - chan(i
, j
, 0);
487 } else if (j
> 0 && finite(chan(i
, j
- 1, 0))) {
489 is
->chan(i
, j
, 1) = chan(i
, j
, 0) - chan(i
, j
- 1, 0);
492 is
->chan(i
, j
, 1) = 0;
500 * Generate an image of median (within a given radius) difference of the
504 image
*fcdiff_median(int radius
) const {
505 image
*diff
= fcdiffs();
509 image
*median
= diff
->medians(radius
);
519 * Scale by half. We use the following filter:
525 * At the edges, these values are normalized so that the sum of the
526 * weights of contributing pixels is 1.
528 class scale_by_half_threaded
: public thread::decompose_domain
{
532 void subdomain_algorithm(unsigned int thread
,
533 int i_min
, int i_max
, int j_min
, int j_max
) {
535 unsigned int ui_min
= (unsigned int) i_min
;
536 unsigned int ui_max
= (unsigned int) i_max
;
537 unsigned int uj_min
= (unsigned int) j_min
;
538 unsigned int uj_max
= (unsigned int) j_max
;
540 for (unsigned int i
= ui_min
; i
< ui_max
; i
++)
541 for (unsigned int j
= uj_min
; j
< uj_max
; j
++) {
544 ( ( ((i
> 0 && j
> 0)
545 ? iu
->get_pixel(2 * i
- 1, 2 * j
- 1) * (ale_real
) 0.0625
548 ? iu
->get_pixel(2 * i
- 1, 2 * j
) * 0.125
550 + ((i
> 0 && j
< is
->width() - 1)
551 ? iu
->get_pixel(2 * i
- 1, 2 * j
+ 1) * 0.0625
554 ? iu
->get_pixel(2 * i
, 2 * j
- 1) * 0.125
556 + iu
->get_pixel(2 * i
, 2 * j
) * 0.25
557 + ((j
< is
->width() - 1)
558 ? iu
->get_pixel(2 * i
, 2 * j
+ 1) * 0.125
560 + ((i
< is
->height() - 1 && j
> 0)
561 ? iu
->get_pixel(2 * i
+ 1, 2 * j
- 1) * 0.0625
563 + ((i
< is
->height() - 1)
564 ? iu
->get_pixel(2 * i
+ 1, 2 * j
) * 0.125
566 + ((i
< is
->height() && j
< is
->width() - 1)
567 ? iu
->get_pixel(2 * i
+ 1, 2 * j
+ 1) * 0.0625
578 + ((i
> 0 && j
< is
->width() - 1)
585 + ((j
< is
->width() - 1)
588 + ((i
< is
->height() - 1 && j
> 0)
591 + ((i
< is
->height() - 1)
594 + ((i
< is
->height() && j
< is
->width() - 1)
601 scale_by_half_threaded(image
*_is
, const image
*_iu
)
602 : decompose_domain(0, _is
->height(),
609 image
*scale_by_half(char *name
) const {
612 image
*is
= scale_generator(
613 (int) floor(height() * f
),
614 (int) floor(width() * f
), depth(), name
);
618 scale_by_half_threaded
sbht(is
, this);
621 is
->_offset
= point(_offset
[0] * f
, _offset
[1] * f
);
627 * Scale by half. This function uses externally-provided weights,
628 * multiplied by the following filter:
634 * Values are normalized so that the sum of the weights of contributing
637 image
*scale_by_half(const image
*weights
, char *name
) const {
640 return scale_by_half(name
);
644 image
*is
= scale_generator(
645 (int) floor(height() * f
),
646 (int) floor(width() * f
), depth(), name
);
650 for (unsigned int i
= 0; i
< is
->height(); i
++)
651 for (unsigned int j
= 0; j
< is
->width(); j
++) {
655 ( ( ((i
> 0 && j
> 0)
656 ? get_pixel(2 * i
- 1, 2 * j
- 1)
657 * weights
->get_pixel(2 * i
- 1, 2 * j
- 1)
661 ? get_pixel(2 * i
- 1, 2 * j
)
662 * weights
->get_pixel(2 * i
- 1, 2 * j
)
665 + ((i
> 0 && j
< is
->width() - 1)
666 ? get_pixel(2 * i
- 1, 2 * j
+ 1)
667 * weights
->get_pixel(2 * i
- 1, 2 * j
+ 1)
671 ? get_pixel(2 * i
, 2 * j
- 1)
672 * weights
->get_pixel(2 * i
, 2 * j
- 1)
675 + get_pixel(2 * i
, 2 * j
)
676 * weights
->get_pixel(2 * i
, 2 * j
)
678 + ((j
< is
->width() - 1)
679 ? get_pixel(2 * i
, 2 * j
+ 1)
680 * weights
->get_pixel(2 * i
, 2 * j
+ 1)
683 + ((i
< is
->height() - 1 && j
> 0)
684 ? get_pixel(2 * i
+ 1, 2 * j
- 1)
685 * weights
->get_pixel(2 * i
+ 1, 2 * j
- 1)
688 + ((i
< is
->height() - 1)
689 ? get_pixel(2 * i
+ 1, 2 * j
)
690 * weights
->get_pixel(2 * i
+ 1, 2 * j
)
693 + ((i
< is
->height() && j
< is
->width() - 1)
694 ? get_pixel(2 * i
+ 1, 2 * j
+ 1)
695 * weights
->get_pixel(2 * i
+ 1, 2 * j
+ 1)
702 ? weights
->get_pixel(2 * i
- 1, 2 * j
- 1)
706 ? weights
->get_pixel(2 * i
- 1, 2 * j
)
709 + ((i
> 0 && j
< is
->width() - 1)
710 ? weights
->get_pixel(2 * i
- 1, 2 * j
+ 1)
714 ? weights
->get_pixel(2 * i
, 2 * j
- 1)
717 + weights
->get_pixel(2 * i
, 2 * j
)
719 + ((j
< is
->width() - 1)
720 ? weights
->get_pixel(2 * i
, 2 * j
+ 1)
723 + ((i
< is
->height() - 1 && j
> 0)
724 ? weights
->get_pixel(2 * i
+ 1, 2 * j
- 1)
727 + ((i
< is
->height() - 1)
728 ? weights
->get_pixel(2 * i
+ 1, 2 * j
)
731 + ((i
< is
->height() && j
< is
->width() - 1)
732 ? weights
->get_pixel(2 * i
+ 1, 2 * j
+ 1)
734 : pixel(0, 0, 0)) ) );
736 for (int k
= 0; k
< 3; k
++)
737 if (!finite(value
[k
]))
740 is
->set_pixel(i
, j
, value
);
743 is
->_offset
= point(_offset
[0] * f
, _offset
[1] * f
);
749 * Scale an image definition array by 1/2.
751 * ALE considers an image definition array as a special kind of image
752 * weight array (typedefs of which should appear below the definition
753 * of this class). ALE uses nonzero pixel values to mean 'defined' and
754 * zero values to mean 'undefined'. Through this interpretation, the
755 * image weight array implementation that ALE uses allows image weight
756 * arrays to also serve as image definition arrays.
758 * Whereas scaling of image weight arrays is not generally obvious in
759 * either purpose or method, ALE requires that image definition arrays
760 * be scalable. (Note that in the special case where weight is treated
761 * as certainty, using a geometric mean is probably correct.)
763 * We currently use a geometric mean to implement scaling of
767 class defined_scale_by_half_threaded
: public thread::decompose_domain
{
771 void subdomain_algorithm(unsigned int thread
,
772 int i_min
, int i_max
, int j_min
, int j_max
) {
774 unsigned int ui_min
= (unsigned int) i_min
;
775 unsigned int ui_max
= (unsigned int) i_max
;
776 unsigned int uj_min
= (unsigned int) j_min
;
777 unsigned int uj_max
= (unsigned int) j_max
;
779 for (unsigned int i
= ui_min
; i
< ui_max
; i
++)
780 for (unsigned int j
= uj_min
; j
< uj_max
; j
++) {
784 ( ( ((i
> 0 && j
> 0)
785 ? ppow(iu
->get_pixel(2 * i
- 1, 2 * j
- 1), 0.0625)
788 ? ppow(iu
->get_pixel(2 * i
- 1, 2 * j
), 0.125)
790 * ((i
> 0 && j
< is
->width() - 1)
791 ? ppow(iu
->get_pixel(2 * i
- 1, 2 * j
+ 1), 0.0625)
794 ? ppow(iu
->get_pixel(2 * i
, 2 * j
- 1), 0.125)
796 * ppow(iu
->get_pixel(2 * i
, 2 * j
), 0.25)
797 * ((j
< is
->width() - 1)
798 ? ppow(iu
->get_pixel(2 * i
, 2 * j
+ 1), 0.125)
800 * ((i
< is
->height() - 1 && j
> 0)
801 ? ppow(iu
->get_pixel(2 * i
+ 1, 2 * j
- 1), 0.0625)
803 * ((i
< is
->height() - 1)
804 ? ppow(iu
->get_pixel(2 * i
+ 1, 2 * j
), 0.125)
806 * ((i
< is
->height() && j
< is
->width() - 1)
807 ? ppow(iu
->get_pixel(2 * i
+ 1, 2 * j
+ 1), 0.0625)
811 for (int k
= 0; k
< 3; k
++)
812 if (!finite(value
[k
]))
815 is
->set_pixel(i
, j
, value
);
820 defined_scale_by_half_threaded(image
*_is
, const image
*_iu
)
821 : decompose_domain(0, _is
->height(),
828 image
*defined_scale_by_half(char *name
) const {
831 image
*is
= scale_generator(
832 (int) floor(height() * f
),
833 (int) floor(width() * f
), depth(), name
);
837 defined_scale_by_half_threaded
dsbht(is
, this);
840 is
->_offset
= point(_offset
[0] * f
, _offset
[1] * f
);
846 * Return an image scaled by some factor != 1.0, using bilinear
849 image
*scale(ale_pos f
, char *name
, int defined
= 0) const {
852 * We probably don't want to scale images by a factor of 1.0,
853 * or by non-positive values.
855 assert (f
!= 1.0 && f
> 0);
858 image
*is
= scale_generator(
859 (int) floor(height() * f
),
860 (int) floor(width() * f
), depth(), name
);
864 unsigned int i
, j
, k
;
866 for (i
= 0; i
< is
->height(); i
++)
867 for (j
= 0; j
< is
->width(); j
++)
868 for (k
= 0; k
< is
->depth(); k
++)
870 get_scaled_bl(point(i
, j
), f
, defined
));
872 is
->_offset
= point(_offset
[0] * f
, _offset
[1] * f
);
875 } else if (f
== 0.5) {
877 return scale_by_half(name
);
879 return defined_scale_by_half(name
);
881 image
*is
= scale(2*f
, name
, defined
);
882 image
*result
= is
->scale(0.5, name
, defined
);
890 * Extend the image area to the top, bottom, left, and right,
891 * initializing the new image areas with black pixels. Negative values
894 virtual void extend(int top
, int bottom
, int left
, int right
) = 0;
899 image
*clone(char *name
) const {
900 image
*ic
= scale_generator(
901 height(), width(), depth(), name
);
905 for (unsigned int i
= 0; i
< height(); i
++)
906 for (unsigned int j
= 0; j
< width(); j
++)
911 ic
->_offset
= _offset
;
913 ic
->_apm_memo
= _apm_memo
;
914 ic
->_acm_memo
= _acm_memo
;
915 ic
->_accm_memo
= _accm_memo
;
924 * Calculate the average (mean) clamped magnitude of a channel across
925 * all pixels in an image. The magnitude is clamped to the range of
928 ale_real
avg_channel_clamped_magnitude(unsigned int k
) const {
931 * This is a memoized function
936 avg_channel_clamped_magnitude_memo();
940 pixel
avg_channel_clamped_magnitude() const {
941 avg_channel_clamped_magnitude_memo();
946 * Calculate the average (mean) magnitude of a channel across all
947 * pixels in an image.
949 ale_real
avg_channel_magnitude(unsigned int k
) const {
952 * This is a memoized function
957 avg_channel_magnitude_memo();
961 pixel
avg_channel_magnitude() const {
962 avg_channel_magnitude_memo();
967 * Calculate the average (mean) magnitude of a pixel (where magnitude
968 * is defined as the mean of the channel values).
970 ale_real
avg_pixel_magnitude() const {
971 unsigned int i
, j
, k
;
973 ale_accum accumulator
;
974 ale_accum divisor
= 0;
982 for (i
= 0; i
< _dimy
; i
++)
983 for (j
= 0; j
< _dimx
; j
++) {
984 pixel value
= get_pixel(i
, j
);
986 for (k
= 0; k
< _depth
; k
++)
987 if (finite(value
[k
])) {
988 accumulator
+= value
[k
];
993 accumulator
/= divisor
;