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 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.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 exposure
&exp() const {
166 point
offset() const {
170 void set_offset(int i
, int j
) {
175 void set_offset(point p
) {
179 unsigned int width() const {
183 unsigned int height() const {
187 unsigned int depth() const {
191 virtual spixel
&pix(unsigned int y
, unsigned int x
) = 0;
193 virtual const spixel
&pix(unsigned int y
, unsigned int x
) const = 0;
195 virtual ale_real
&chan(unsigned int y
, unsigned int x
, unsigned int k
) = 0;
197 virtual const ale_real
&chan(unsigned int y
, unsigned int x
, unsigned int k
) const = 0;
199 virtual void set_pixel(unsigned int y
, unsigned int x
, spixel p
) {
203 virtual pixel
get_pixel(unsigned int y
, unsigned int x
) const {
204 return ((const image
*)this)->pix(y
, x
);
207 ale_real
maxval() const {
208 ale_real result
= get_pixel(0, 0)[0];
210 for (unsigned int i
= 0; i
< _dimy
; i
++)
211 for (unsigned int j
= 0; j
< _dimx
; j
++) {
212 pixel p
= get_pixel(i
, j
);
214 for (unsigned int k
= 0; k
< _depth
; k
++)
215 if (p
[k
] > result
|| !finite(result
))
222 ale_real
minval() 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
))
238 * Get the maximum difference among adjacent pixels.
241 pixel
get_max_diff(unsigned int i
, unsigned int j
) const {
244 assert(i
<= _dimy
- 1);
245 assert(j
<= _dimx
- 1);
247 pixel max
= get_pixel(i
, j
), min
= get_pixel(i
, j
);
249 for (int ii
= -1; ii
<= 1; ii
++)
250 for (int jj
= -1; jj
<= 1; jj
++) {
258 if ((unsigned int) iii
> _dimy
- 1)
260 if ((unsigned int) jjj
> _dimx
- 1)
263 pixel p
= get_pixel(iii
, jjj
);
265 for (int d
= 0; d
< 3; d
++) {
276 pixel
get_max_diff(point x
) const {
279 assert (x
[0] <= _dimy
- 1);
280 assert (x
[1] <= _dimx
- 1);
282 unsigned int i
= (unsigned int) round(x
[0]);
283 unsigned int j
= (unsigned int) round(x
[1]);
285 return get_max_diff(i
, j
);
289 * Get a color value at a given position using bilinear interpolation between the
290 * four nearest pixels. Result values:
292 * result[0] == pixel value
293 * result[1] == pixel confidence
295 void get_bl(point x
, pixel result
[2]) const {
299 assert (x
[0] <= _dimy
- 1);
300 assert (x
[1] <= _dimx
- 1);
302 int lx
= (int) floor(x
[1]);
303 int hx
= (int) floor(x
[1]) + 1;
304 int ly
= (int) floor(x
[0]);
305 int hy
= (int) floor(x
[0]) + 1;
310 neighbor
[0] = get_pixel(ly
, lx
);
311 neighbor
[1] = get_pixel(hy
% _dimy
, lx
);
312 neighbor
[2] = get_pixel(hy
% _dimy
, hx
% _dimx
);
313 neighbor
[3] = get_pixel(ly
, hx
% _dimx
);
315 factor
[0] = (hx
- x
[1]) * (hy
- x
[0]);
316 factor
[1] = (hx
- x
[1]) * (x
[0] - ly
);
317 factor
[2] = (x
[1] - lx
) * (x
[0] - ly
);
318 factor
[3] = (x
[1] - lx
) * (hy
- x
[0]);
321 * Use bilinear interpolation for pixel value
324 result
[0] = pixel(0, 0, 0);
326 for (int n
= 0; n
< 4; n
++)
327 result
[0] += factor
[n
] * neighbor
[n
];
331 * Take the minimum confidence
335 result
[1] = _exp
->confidence(neighbor
[0]);
337 for (int n
= 1; n
< 4; n
++)
340 = _exp
->confidence(neighbor
[n
]);
342 for (int k
= 0; k
< 3; k
++)
343 if (confidence
[k
] < result
[1][k
])
344 result
[1][k
] = confidence
[k
];
347 result
[1] = pixel(1, 1, 1);
351 * Use bilinear interpolation for confidence
355 result
[1] = pixel(0, 0, 0);
356 for (int n
= 0; n
< 4; n
++)
357 result
[1] += factor
[n
] * _exp
->confidence(neighbor
[n
]);
359 result
[1] = pixel(1, 1, 1);
364 int in_bounds(point x
) const {
367 || x
[0] > height() - 1
368 || x
[1] > width() - 1)
375 pixel
get_bl(point x
) const {
383 void get_scaled_bl(point x
, ale_pos f
, pixel result
[2]) const {
385 x
[0]/f
<= height() - 1
388 x
[1]/f
<= width() - 1
392 get_bl(scaled
, result
);
395 pixel
get_scaled_bl(point x
, ale_pos f
) const {
398 get_scaled_bl(x
, f
, result
);
405 * Make a new image suitable for receiving scaled values.
407 virtual image
*scale_generator(int height
, int width
, int depth
, char *name
) const = 0;
410 * Generate an image of medians within a given radius
413 image
*medians(int radius
) const {
415 assert (radius
>= 0);
417 image
*is
= scale_generator(height(), width(), depth(), "median");
420 for (unsigned int i
= 0; i
< height(); i
++)
421 for (unsigned int j
= 0; j
< width(); j
++) {
423 std::vector
<ale_real
> p
[3];
425 for (int ii
= -radius
; ii
<= radius
; ii
++)
426 for (int jj
= -radius
; jj
<= radius
; jj
++) {
430 if (in_bounds(point(iii
, jjj
)))
431 for (int k
= 0; k
< 3; k
++)
432 if (finite(get_pixel(iii
, jjj
)[k
]))
433 p
[k
].push_back(get_pixel(iii
, jjj
)[k
]);
436 is
->pix(i
, j
) = d2::pixel::undefined();
438 for (int k
= 0; k
< 3; k
++) {
439 std::sort(p
[k
].begin(), p
[k
].end());
441 unsigned int pkc
= p
[k
].size();
447 is
->chan(i
, j
, k
) = (p
[k
][pkc
/ 2] + p
[k
][pkc
/ 2 - 1]) / 2;
449 is
->chan(i
, j
, k
) = p
[k
][pkc
/ 2];
457 * Generate an image of differences of the first channel. The first
458 * coordinate differences are stored in the first channel, second in the
462 image
*fcdiffs() const {
463 image
*is
= scale_generator(height(), width(), depth(), "diff");
467 for (unsigned int i
= 0; i
< height(); i
++)
468 for (unsigned int j
= 0; j
< width(); j
++) {
472 && !finite(chan(i
, j
, 0))) {
474 is
->chan(i
, j
, 0) = (chan(i
+ 1, j
, 0) - chan(i
- 1, j
, 0)) / 2;
476 } else if (i
+ 1 < height()
478 && finite(chan(i
+ 1, j
, 0))
479 && finite(chan(i
- 1, j
, 0))) {
481 is
->chan(i
, j
, 0) = ((chan(i
, j
, 0) - chan(i
- 1, j
, 0))
482 + (chan(i
+ 1, j
, 0) - chan(i
, j
, 0))) / 2;
484 } else if (i
+ 1 < height()
485 && finite(chan(i
+ 1, j
, 0))) {
487 is
->chan(i
, j
, 0) = chan(i
+ 1, j
, 0) - chan(i
, j
, 0);
490 && finite(chan(i
- 1, j
, 0))) {
492 is
->chan(i
, j
, 0) = chan(i
, j
, 0) - chan(i
- 1, j
, 0);
495 is
->chan(i
, j
, 0) = 0;
500 && !finite(chan(i
, j
, 0))) {
502 is
->chan(i
, j
, 1) = (chan(i
, j
+ 1, 0) - chan(i
, j
- 1, 0)) / 2;
504 } else if (j
+ 1 < width()
506 && finite(chan(i
, j
+ 1, 0))
507 && finite(chan(i
, j
- 1, 0))) {
509 is
->chan(i
, j
, 1) = ((chan(i
, j
, 0) - chan(i
, j
- 1, 0))
510 + (chan(i
, j
+ 1, 0) - chan(i
, j
, 0))) / 2;
512 } else if (j
+ 1 < width() && finite(chan(i
, j
+ 1, 0))) {
514 is
->chan(i
, j
, 1) = chan(i
, j
+ 1, 0) - chan(i
, j
, 0);
516 } else if (j
> 0 && finite(chan(i
, j
- 1, 0))) {
518 is
->chan(i
, j
, 1) = chan(i
, j
, 0) - chan(i
, j
- 1, 0);
521 is
->chan(i
, j
, 1) = 0;
529 * Generate an image of median (within a given radius) difference of the
533 image
*fcdiff_median(int radius
) const {
534 image
*diff
= fcdiffs();
538 image
*median
= diff
->medians(radius
);
548 * Scale by half. We use the following filter:
554 * At the edges, these values are normalized so that the sum of the
555 * weights of contributing pixels is 1.
557 image
*scale_by_half(char *name
) const {
560 image
*is
= scale_generator(
561 (int) floor(height() * f
),
562 (int) floor(width() * f
), depth(), name
);
566 for (unsigned int i
= 0; i
< is
->height(); i
++)
567 for (unsigned int j
= 0; j
< is
->width(); j
++)
571 ( ( ((i
> 0 && j
> 0)
572 ? get_pixel(2 * i
- 1, 2 * j
- 1) * (ale_real
) 0.0625
575 ? get_pixel(2 * i
- 1, 2 * j
) * 0.125
577 + ((i
> 0 && j
< is
->width() - 1)
578 ? get_pixel(2 * i
- 1, 2 * j
+ 1) * 0.0625
581 ? get_pixel(2 * i
, 2 * j
- 1) * 0.125
583 + get_pixel(2 * i
, 2 * j
) * 0.25
584 + ((j
< is
->width() - 1)
585 ? get_pixel(2 * i
, 2 * j
+ 1) * 0.125
587 + ((i
< is
->height() - 1 && j
> 0)
588 ? get_pixel(2 * i
+ 1, 2 * j
- 1) * 0.0625
590 + ((i
< is
->height() - 1)
591 ? get_pixel(2 * i
+ 1, 2 * j
) * 0.125
593 + ((i
< is
->height() && j
< is
->width() - 1)
594 ? get_pixel(2 * i
+ 1, 2 * j
+ 1) * 0.0625
605 + ((i
> 0 && j
< is
->width() - 1)
612 + ((j
< is
->width() - 1)
615 + ((i
< is
->height() - 1 && j
> 0)
618 + ((i
< is
->height() - 1)
621 + ((i
< is
->height() && j
< is
->width() - 1)
625 is
->_offset
= point(_offset
[0] * f
, _offset
[1] * f
);
631 * Scale by half. This function uses externally-provided weights,
632 * multiplied by the following filter:
638 * Values are normalized so that the sum of the weights of contributing
641 image
*scale_by_half(const image
*weights
, char *name
) const {
644 return scale_by_half(name
);
648 image
*is
= scale_generator(
649 (int) floor(height() * f
),
650 (int) floor(width() * f
), depth(), name
);
654 for (unsigned int i
= 0; i
< is
->height(); i
++)
655 for (unsigned int j
= 0; j
< is
->width(); j
++) {
659 ( ( ((i
> 0 && j
> 0)
660 ? get_pixel(2 * i
- 1, 2 * j
- 1)
661 * weights
->get_pixel(2 * i
- 1, 2 * j
- 1)
665 ? get_pixel(2 * i
- 1, 2 * j
)
666 * weights
->get_pixel(2 * i
- 1, 2 * j
)
669 + ((i
> 0 && j
< is
->width() - 1)
670 ? get_pixel(2 * i
- 1, 2 * j
+ 1)
671 * weights
->get_pixel(2 * i
- 1, 2 * j
+ 1)
675 ? get_pixel(2 * i
, 2 * j
- 1)
676 * weights
->get_pixel(2 * i
, 2 * j
- 1)
679 + get_pixel(2 * i
, 2 * j
)
680 * weights
->get_pixel(2 * i
, 2 * j
)
682 + ((j
< is
->width() - 1)
683 ? get_pixel(2 * i
, 2 * j
+ 1)
684 * weights
->get_pixel(2 * i
, 2 * j
+ 1)
687 + ((i
< is
->height() - 1 && j
> 0)
688 ? get_pixel(2 * i
+ 1, 2 * j
- 1)
689 * weights
->get_pixel(2 * i
+ 1, 2 * j
- 1)
692 + ((i
< is
->height() - 1)
693 ? get_pixel(2 * i
+ 1, 2 * j
)
694 * weights
->get_pixel(2 * i
+ 1, 2 * j
)
697 + ((i
< is
->height() && j
< is
->width() - 1)
698 ? get_pixel(2 * i
+ 1, 2 * j
+ 1)
699 * weights
->get_pixel(2 * i
+ 1, 2 * j
+ 1)
706 ? weights
->get_pixel(2 * i
- 1, 2 * j
- 1)
710 ? weights
->get_pixel(2 * i
- 1, 2 * j
)
713 + ((i
> 0 && j
< is
->width() - 1)
714 ? weights
->get_pixel(2 * i
- 1, 2 * j
+ 1)
718 ? weights
->get_pixel(2 * i
, 2 * j
- 1)
721 + weights
->get_pixel(2 * i
, 2 * j
)
723 + ((j
< is
->width() - 1)
724 ? weights
->get_pixel(2 * i
, 2 * j
+ 1)
727 + ((i
< is
->height() - 1 && j
> 0)
728 ? weights
->get_pixel(2 * i
+ 1, 2 * j
- 1)
731 + ((i
< is
->height() - 1)
732 ? weights
->get_pixel(2 * i
+ 1, 2 * j
)
735 + ((i
< is
->height() && j
< is
->width() - 1)
736 ? weights
->get_pixel(2 * i
+ 1, 2 * j
+ 1)
738 : pixel(0, 0, 0)) ) );
740 for (int k
= 0; k
< 3; k
++)
741 if (!finite(value
[k
]))
744 is
->set_pixel(i
, j
, value
);
747 is
->_offset
= point(_offset
[0] * f
, _offset
[1] * f
);
753 * Return an image scaled by some factor != 1.0, using bilinear
756 image
*scale(ale_pos f
, char *name
) const {
759 * We probably don't want to scale images by a factor of 1.0,
760 * or by non-positive values.
762 assert (f
!= 1.0 && f
> 0);
765 image
*is
= scale_generator(
766 (int) floor(height() * f
),
767 (int) floor(width() * f
), depth(), name
);
771 unsigned int i
, j
, k
;
773 for (i
= 0; i
< is
->height(); i
++)
774 for (j
= 0; j
< is
->width(); j
++)
775 for (k
= 0; k
< is
->depth(); k
++)
777 get_scaled_bl(point(i
, j
), f
));
780 } else if (f
== 0.5) {
781 return scale_by_half(name
);
783 image
*is
= scale(2*f
, name
);
784 image
*result
= is
->scale(0.5, name
);
792 * Scale an image definition array by 1/2.
794 * ALE considers an image definition array as a special kind of image
795 * weight array (typedefs of which should appear below the definition
796 * of this class). ALE uses nonzero pixel values to mean 'defined' and
797 * zero values to mean 'undefined'. Through this interpretation, the
798 * image weight array implementation that ALE uses allows image weight
799 * arrays to also serve as image definition arrays.
801 * Whereas scaling of image weight arrays is not generally obvious in
802 * either purpose or method, ALE requires that image definition arrays
803 * be scalable, and the method we implement here is a fairly obvious
804 * one. In particular, if any source pixel contributing to the value of
805 * a scaled target pixel has an undefined value, then the scaled target
806 * pixel is undefined (zero). Otherwise, it is defined (non-zero).
808 * Since there are many possible ways of implementing this function, we
809 * choose an easy way and simply multiply the numerical values of the
810 * source pixels to obtain the value of the scaled target pixel.
812 * XXX: we consider all pixels within a 9-pixel square to contribute.
813 * There are other approaches. For example, edge pixels could be
814 * considered to have six contributing pixels and corner pixels four
815 * contributing pixels. To use this convention, change the ': 0' text
816 * in the code below to ': 1'.
819 image
*defined_scale_by_half(char *name
) const {
822 image
*is
= scale_generator(
823 (int) floor(height() * f
),
824 (int) floor(width() * f
), depth(), name
);
828 for (unsigned int i
= 0; i
< is
->height(); i
++)
829 for (unsigned int j
= 0; j
< is
->width(); j
++)
834 ? get_pixel(2 * i
- 1, 2 * j
- 1)
837 ? get_pixel(2 * i
- 1, 2 * j
)
839 * ((i
> 0 && j
< is
->width() - 1)
840 ? get_pixel(2 * i
- 1, 2 * j
+ 1)
843 ? get_pixel(2 * i
, 2 * j
- 1)
845 * get_pixel(2 * i
, 2 * j
)
846 * ((j
< is
->width() - 1)
847 ? get_pixel(2 * i
, 2 * j
+ 1)
849 * ((i
< is
->height() - 1 && j
> 0)
850 ? get_pixel(2 * i
+ 1, 2 * j
- 1)
852 * ((i
< is
->height() - 1)
853 ? get_pixel(2 * i
+ 1, 2 * j
)
855 * ((i
< is
->height() && j
< is
->width() - 1)
856 ? get_pixel(2 * i
+ 1, 2 * j
+ 1)
859 is
->_offset
= point(_offset
[0] * f
, _offset
[1] * f
);
865 * Extend the image area to the top, bottom, left, and right,
866 * initializing the new image areas with black pixels. Negative values
869 virtual void extend(int top
, int bottom
, int left
, int right
) = 0;
874 image
*clone(char *name
) const {
875 image
*ic
= scale_generator(
876 height(), width(), depth(), name
);
880 for (unsigned int i
= 0; i
< height(); i
++)
881 for (unsigned int j
= 0; j
< width(); j
++)
886 ic
->_offset
= _offset
;
888 ic
->_apm_memo
= _apm_memo
;
889 ic
->_acm_memo
= _acm_memo
;
890 ic
->_accm_memo
= _accm_memo
;
899 * Calculate the average (mean) clamped magnitude of a channel across
900 * all pixels in an image. The magnitude is clamped to the range of
903 ale_real
avg_channel_clamped_magnitude(unsigned int k
) const {
906 * This is a memoized function
911 avg_channel_clamped_magnitude_memo();
915 pixel
avg_channel_clamped_magnitude() const {
916 avg_channel_clamped_magnitude_memo();
921 * Calculate the average (mean) magnitude of a channel across all
922 * pixels in an image.
924 ale_real
avg_channel_magnitude(unsigned int k
) const {
927 * This is a memoized function
932 avg_channel_magnitude_memo();
936 pixel
avg_channel_magnitude() const {
937 avg_channel_magnitude_memo();
942 * Calculate the average (mean) magnitude of a pixel (where magnitude
943 * is defined as the mean of the channel values).
945 ale_real
avg_pixel_magnitude() const {
946 unsigned int i
, j
, k
;
948 ale_accum accumulator
;
949 ale_accum divisor
= 0;
957 for (i
= 0; i
< _dimy
; i
++)
958 for (j
= 0; j
< _dimx
; j
++) {
959 pixel value
= get_pixel(i
, j
);
961 for (k
= 0; k
< _depth
; k
++)
962 if (finite(value
[k
])) {
963 accumulator
+= value
[k
];
968 accumulator
/= divisor
;