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_weighted_median.h: Image representing a weighted median of inputs.
25 #ifndef __image_weighted_median_h__
26 #define __image_weighted_median_h__
28 #include "exposure/exposure.h"
32 class image_weighted_median
: public image_weighted_avg
{
36 * Array 'colors' stores image colors, sorted by intensity for each
37 * channel at each pixel location.
39 * Array 'weights' stores the weights associated with each color, where
40 * the weights are represented cumulatively, so that for weights and
46 * The (cumulative) representation would be:
51 * XXX: This storage approach may have poor cache characteristics.
52 * It might be better to localize elements having identical spatial
57 unsigned int capacity
;
60 image_weighted_median (unsigned int dimy
, unsigned int dimx
, unsigned int
61 depth
, int capacity
= -1, const char *name
= "anonymous")
62 : image_weighted_avg(dimy
, dimx
, depth
, name
) {
65 this->capacity
= image_rw::count();
66 } else if (capacity
>= 0) {
67 this->capacity
= (unsigned int) capacity
;
71 colors
= (image
**) malloc(this->capacity
* sizeof(image
*));
72 weights
= (image
**) malloc(this->capacity
* sizeof(image
*));
77 if (!colors
|| !weights
) {
78 fprintf(stderr
, "Could not allocate memory for image data.\n");
82 for (unsigned int f
= 0; f
< this->capacity
; f
++) {
83 colors
[f
] = new_image_ale_real(dimy
, dimx
, depth
);
84 weights
[f
] = new_image_ale_real(dimy
, dimx
, depth
);
89 if (!colors
[f
] || !weights
[f
]) {
90 fprintf(stderr
, "Could not allocate memory for image data.\n");
96 virtual ~image_weighted_median() {
97 for (unsigned int f
= 0; f
< capacity
; f
++) {
107 * Extend the image area to the top, bottom, left, and right,
108 * initializing the new image areas with black pixels. Negative values
111 image
*_extend(int top
, int bottom
, int left
, int right
) {
113 for (unsigned int f
= 0; f
< capacity
; f
++) {
114 extend(&colors
[f
], top
, bottom
, left
, right
);
115 extend(&weights
[f
], top
, bottom
, left
, right
);
118 _dimx
= colors
[0]->width();
119 _dimy
= colors
[0]->height();
120 _offset
= colors
[0]->offset();
125 int accumulate_norender(int i
, int j
) {
130 * Perform insertion sort on the arrays, where sort is by color.
132 * XXX: This does a poor job of handling multiple contributions from
133 * the same frame, especially when the number of frames is 1.
135 void accumulate(int i
, int j
, int f
, pixel new_value
, pixel new_weight
) {
136 for (unsigned int k
= 0; k
< 3; k
++) {
138 if (fabs(new_weight
[k
]) > ale_real_weight_floor
)
139 new_value
[k
] /= new_weight
[k
];
144 * XXX: This initialization should not be necessary.
147 for (unsigned int ff
= 0; ff
< capacity
; ff
++)
148 weights
[ff
]->set_chan(i
, j
, k
, 0);
150 assert (finite(new_weight
[k
]));
152 if (new_weight
[k
] <= 0)
155 for (unsigned int ff
= 0; ff
< capacity
; ff
++) {
156 assert (ff
<= (unsigned int) f
);
157 if (ff
== capacity
- 1) {
158 colors
[ff
]->set_chan(i
, j
, k
, new_value
[k
]);
159 weights
[ff
]->set_chan(i
, j
, k
,
160 weights
[ff
]->get_chan(i
, j
, k
) + new_weight
[k
]);
163 if ((ff
== 0 && weights
[ff
]->get_chan(i
, j
, k
) == 0)
164 || (ff
> 0 && weights
[ff
]->get_chan(i
, j
, k
) == weights
[ff
- 1]->get_chan(i
, j
, k
))) {
165 colors
[ff
]->set_chan(i
, j
, k
, new_value
[k
]);
166 for (unsigned int fff
= ff
; fff
< capacity
; fff
++)
167 weights
[fff
]->set_chan(i
, j
, k
,
168 weights
[fff
]->get_chan(i
, j
, k
) + new_weight
[k
]);
171 if (colors
[ff
]->get_chan(i
, j
, k
) == (ale_sreal
) new_value
[k
]) {
172 for (unsigned int fff
= ff
; fff
< capacity
; fff
++)
173 weights
[fff
]->set_chan(i
, j
, k
,
174 weights
[fff
]->get_chan(i
, j
, k
) + new_weight
[k
]);
177 if (colors
[ff
]->get_chan(i
, j
, k
) > (ale_sreal
) new_value
[k
]) {
178 for (unsigned int fff
= capacity
- 1; fff
> ff
; fff
--) {
179 weights
[fff
]->set_chan(i
, j
, k
, weights
[fff
- 1]->get_pixel(i
, j
)[k
] + new_weight
[k
]);
180 colors
[fff
]->set_chan(i
, j
, k
, colors
[fff
- 1]->get_pixel(i
, j
)[k
]);
182 colors
[ff
]->set_chan(i
, j
, k
, new_value
[k
]);
183 weights
[ff
]->set_chan(i
, j
, k
, new_weight
[k
]);
185 weights
[ff
]->set_chan(i
, j
, k
,
186 weights
[ff
]->get_chan(i
, j
, k
)
187 + weights
[ff
- 1]->get_chan(i
, j
, k
));
196 * XXX: This is inefficient in cases where only one channel is desired.
198 spixel
get_pixel(unsigned int y
, unsigned int x
) const {
201 for (int k
= 0; k
< 3; k
++) {
202 ale_real midpoint
= weights
[capacity
- 1]->get_chan(y
, x
, k
) / 2;
205 return pixel::zero();
212 unsigned int h
= capacity
- 1;
213 unsigned int m
= h
/ 2;
216 if ((ale_real
) weights
[m
]->get_chan(y
, x
, k
) < midpoint
)
223 if ((ale_real
) weights
[l
]->get_chan(y
, x
, k
) < midpoint
)
225 if ((ale_real
) weights
[l
]->get_chan(y
, x
, k
) > midpoint
)
226 result
[k
] = colors
[l
]->get_chan(y
, x
, k
);
227 else if ((ale_real
) weights
[l
]->get_chan(y
, x
, k
) == midpoint
)
228 result
[k
] = (colors
[l
]->get_chan(y
, x
, k
)
229 + colors
[l
+ 1]->get_chan(y
, x
, k
)) / 2;
238 image
*get_weights() {
239 return weights
[capacity
- 1];
242 image
*get_colors() {