doc/web: Place examples section more visibly.
[Ale.git] / d2 / image.h
blob9ece0be10a105d15752907203f76fbfadcfd2600
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
23 * by ALE.
26 #ifndef __image_h__
27 #define __image_h__
29 #include "point.h"
30 #include "pixel.h"
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 {
51 protected:
52 static double resident;
53 unsigned int _dimx, _dimy, _depth;
54 point _offset;
55 const char *name;
56 mutable exposure *_exp;
57 unsigned int bayer;
58 private:
60 * Memoized function variables. We may want to change these even when
61 * *this is constant.
63 mutable int _apm_memo;
64 mutable ale_real _apm;
65 mutable int _accm_memo;
66 mutable pixel _accm;
67 mutable int _acm_memo;
68 mutable pixel _acm;
70 void avg_channel_clamped_magnitude_memo() const {
71 unsigned int i, j, k;
72 pixel_accum accumulator;
73 pixel_accum divisor;
75 if (_accm_memo)
76 return;
78 _accm_memo = 1;
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])) {
88 if (value[k] > 1)
89 value[k] = 1;
90 if (value[k] < 0)
91 value[k] = 0;
92 accumulator[k] += value[k];
93 divisor[k] += 1;
97 accumulator /= divisor;
99 _accm = accumulator;
102 void avg_channel_magnitude_memo() const {
103 unsigned int i, j, k;
104 pixel_accum accumulator;
105 pixel_accum divisor;
107 if (_acm_memo)
108 return;
110 _acm_memo = 1;
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];
121 divisor[k] += 1;
125 accumulator /= divisor;
127 _acm = accumulator;
130 protected:
131 void image_updated() {
132 _apm_memo = 0;
133 _acm_memo = 0;
134 _accm_memo = 0;
137 public:
138 static void set_resident(double r) {
139 resident = r;
142 static double get_resident() {
143 return 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) {
150 assert (depth == 3);
151 _depth = 3;
153 _dimx = dimx;
154 _dimy = dimy;
155 _offset = point(0, 0);
156 _apm_memo = 0;
157 _acm_memo = 0;
158 _accm_memo = 0;
159 this->name = name;
160 this->_exp = _exp;
161 this->bayer = bayer;
163 if (_exp != NULL)
164 _exp->add_listener(this, name);
167 unsigned int get_bayer() const {
168 return bayer;
171 virtual char get_channels(int i, int j) const {
172 return 0x7;
175 virtual unsigned int bayer_color(unsigned int i, unsigned int j) const {
176 assert(0);
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 {
187 return *_exp;
190 point offset() const {
191 return _offset;
194 void set_offset(int i, int j) {
195 _offset[0] = i;
196 _offset[1] = j;
199 void set_offset(point p) {
200 _offset = p;
203 unsigned int width() const {
204 return _dimx;
207 unsigned int height() const {
208 return _dimy;
211 unsigned int depth() const {
212 return _depth;
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) {
224 assert(0);
227 virtual void mul_pixel(unsigned int y, unsigned int x, pixel p) {
228 assert(0);
231 virtual void div_pixel(unsigned int y, unsigned int x, pixel p) {
232 assert(0);
235 virtual void add_chan(unsigned int y, unsigned int x, unsigned int k, ale_real c) {
236 assert(0);
239 virtual void div_chan(unsigned int y, unsigned int x, unsigned int k, ale_real c) {
240 assert(0);
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))
256 result = p[k];
259 return 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))
271 result = p[k];
274 return 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++) {
289 int iii = i + ii;
290 int jjj = j + jj;
292 if (iii < 0)
293 continue;
294 if (jjj < 0)
295 continue;
296 if ((unsigned int) iii > _dimy - 1)
297 continue;
298 if ((unsigned int) jjj > _dimx - 1)
299 continue;
301 pixel p = get_pixel(iii, jjj);
303 for (int d = 0; d < 3; d++) {
304 if (p[d] > max[d])
305 max[d] = p[d];
306 if (p[d] < min[d])
307 min[d] = p[d];
311 return max - min;
314 pixel get_max_diff(point x) const {
315 assert (x[0] >= 0);
316 assert (x[1] >= 0);
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 {
327 if (x[0] < 0
328 || x[1] < 0
329 || x[0] > height() - 1
330 || x[1] > width() - 1)
331 return 0;
332 if (!x.defined())
333 return 0;
334 return 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]);
344 pixel result;
346 assert (x[0] >= 0);
347 assert (x[1] >= 0);
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);
358 pixel neighbor[4];
359 ale_real factor[4];
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
385 if (defined == 0) {
386 result = pixel(0, 0, 0);
388 for (int n = 0; n < 4; n++)
389 result += factor[n] * neighbor[n];
390 } else {
391 #if 0
393 * Calculating the geometric mean may be expensive on
394 * some platforms (e.g., those without floating-point
395 * support.
398 result = pixel(1, 1, 1);
400 for (int n = 0; n < 4; n++)
401 result *= ppow(neighbor[n], factor[n]);
402 #else
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];
414 #endif
417 // fprintf(stderr, "result=%f %f %f\n",
418 // (double) result[0],
419 // (double) result[1],
420 // (double) result[2]);
422 return result;
425 pixel get_scaled_bl(point x, ale_pos f, int defined = 0) const {
426 point scaled(
427 x[0]/f <= height() - 1
428 ? (x[0]/f)
429 : (ale_pos) (height() - 1),
430 x[1]/f <= width() - 1
431 ? (x[1]/f)
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");
452 assert(is);
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++) {
461 int iii = i + ii;
462 int jjj = j + 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();
477 if (pkc == 0)
478 continue;
480 if (pkc % 2 == 0)
481 is->set_chan(i, j, k,
482 (p[k][pkc / 2] + p[k][pkc / 2 - 1]) / 2);
483 else
484 is->set_chan(i, j, k,
485 p[k][pkc / 2]);
489 return is;
493 * Generate an image of differences of the first channel. The first
494 * coordinate differences are stored in the first channel, second in the
495 * second channel.
498 image *fcdiffs() const {
499 image *is = scale_generator(height(), width(), depth(), "diff");
501 assert(is);
503 for (unsigned int i = 0; i < height(); i++)
504 for (unsigned int j = 0; j < width(); j++) {
506 if (i + 1 < height()
507 && i > 0
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()
514 && i > 0
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));
526 } else if (i > 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));
531 } else {
532 is->set_chan(i, j, 0, 0);
535 if (j + 1 < width()
536 && j > 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()
542 && j > 0
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));
557 } else {
558 is->set_chan(i, j, 1, 0);
562 return is;
566 * Generate an image of median (within a given radius) difference of the
567 * first channel.
570 image *fcdiff_median(int radius) const {
571 image *diff = fcdiffs();
573 assert(diff);
575 image *median = diff->medians(radius);
577 assert(median);
579 delete diff;
581 return median;
585 * Scale by half. We use the following filter:
587 * 1/16 1/8 1/16
588 * 1/8 1/4 1/8
589 * 1/16 1/8 1/16
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 {
595 image *is;
596 const image *iu;
597 protected:
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++) {
613 is->set_pixel(i, j,
615 ( ( ((i > 0 && j > 0)
616 ? iu->get_pixel(2 * i - 1, 2 * j - 1) * _0625
617 : pixel(0, 0, 0))
618 + ((i > 0)
619 ? iu->get_pixel(2 * i - 1, 2 * j) * _125
620 : pixel(0, 0, 0))
621 + ((i > 0 && j < is->width() - 1)
622 ? iu->get_pixel(2 * i - 1, 2 * j + 1) * _0625
623 : pixel(0, 0, 0))
624 + ((j > 0)
625 ? iu->get_pixel(2 * i, 2 * j - 1) * _125
626 : pixel(0, 0, 0))
627 + iu->get_pixel(2 * i, 2 * j) * _25
628 + ((j < is->width() - 1)
629 ? iu->get_pixel(2 * i, 2 * j + 1) * _125
630 : pixel(0, 0, 0))
631 + ((i < is->height() - 1 && j > 0)
632 ? iu->get_pixel(2 * i + 1, 2 * j - 1) * _0625
633 : pixel(0, 0, 0))
634 + ((i < is->height() - 1)
635 ? iu->get_pixel(2 * i + 1, 2 * j) * _125
636 : pixel(0, 0, 0))
637 + ((i < is->height() && j < is->width() - 1)
638 ? iu->get_pixel(2 * i + 1, 2 * j + 1) * _0625
639 : pixel(0, 0, 0)))
643 ( ((i > 0 && j > 0)
644 ? _0625
645 : _0)
646 + ((i > 0)
647 ? _125
648 : _0)
649 + ((i > 0 && j < is->width() - 1)
650 ? _0625
651 : _0)
652 + ((j > 0)
653 ? _125
654 : _0)
655 + _25
656 + ((j < is->width() - 1)
657 ? _125
658 : _0)
659 + ((i < is->height() - 1 && j > 0)
660 ? _0625
661 : _0)
662 + ((i < is->height() - 1)
663 ? _125
664 : _0)
665 + ((i < is->height() && j < is->width() - 1)
666 ? _0625
667 : _0) ) ) );
671 public:
672 scale_by_half_threaded(image *_is, const image *_iu)
673 : decompose_domain(0, _is->height(),
674 0, _is->width()) {
675 is = _is;
676 iu = _iu;
680 image *scale_by_half(const char *name) const {
681 ale_pos f = 0.5;
683 image *is = scale_generator(
684 (int) floor(height() * (double) f),
685 (int) floor(width() * (double) f), depth(), name);
687 assert(is);
689 scale_by_half_threaded sbht(is, this);
690 sbht.run();
692 is->_offset = point(_offset[0] * f, _offset[1] * f);
694 return is;
698 * Scale by half. This function uses externally-provided weights,
699 * multiplied by the following filter:
701 * 1/16 1/8 1/16
702 * 1/8 1/4 1/8
703 * 1/16 1/8 1/16
705 * Values are normalized so that the sum of the weights of contributing
706 * pixels is 1.
708 image *scale_by_half(const image *weights, const char *name) const {
710 if (weights == NULL)
711 return scale_by_half(name);
713 ale_pos f = 0.5;
715 image *is = scale_generator(
716 (int) floor(height() * (double) f),
717 (int) floor(width() * (double) f), depth(), name);
719 assert(is);
721 for (unsigned int i = 0; i < is->height(); i++)
722 for (unsigned int j = 0; j < is->width(); j++) {
724 pixel value = pixel
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)
729 * (ale_real) 0.0625
730 : pixel(0, 0, 0))
731 + ((i > 0)
732 ? (pixel) get_pixel(2 * i - 1, 2 * j)
733 * (pixel) weights->get_pixel(2 * i - 1, 2 * j)
734 * 0.125
735 : pixel(0, 0, 0))
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)
739 * 0.0625
740 : pixel(0, 0, 0))
741 + ((j > 0)
742 ? (pixel) get_pixel(2 * i, 2 * j - 1)
743 * (pixel) weights->get_pixel(2 * i, 2 * j - 1)
744 * 0.125
745 : pixel(0, 0, 0))
746 + get_pixel(2 * i, 2 * j)
747 * (pixel) weights->get_pixel(2 * i, 2 * j)
748 * 0.25
749 + ((j < is->width() - 1)
750 ? (pixel) get_pixel(2 * i, 2 * j + 1)
751 * (pixel) weights->get_pixel(2 * i, 2 * j + 1)
752 * 0.125
753 : pixel(0, 0, 0))
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)
757 * 0.0625
758 : pixel(0, 0, 0))
759 + ((i < is->height() - 1)
760 ? (pixel) get_pixel(2 * i + 1, 2 * j)
761 * (pixel) weights->get_pixel(2 * i + 1, 2 * j)
762 * 0.125
763 : pixel(0, 0, 0))
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)
767 * 0.0625
768 : pixel(0, 0, 0)))
772 ( ((i > 0 && j > 0)
773 ? weights->get_pixel(2 * i - 1, 2 * j - 1)
774 * 0.0625
775 : pixel(0, 0, 0))
776 + ((i > 0)
777 ? weights->get_pixel(2 * i - 1, 2 * j)
778 * 0.125
779 : pixel(0, 0, 0))
780 + ((i > 0 && j < is->width() - 1)
781 ? weights->get_pixel(2 * i - 1, 2 * j + 1)
782 * 0.0625
783 : pixel(0, 0, 0))
784 + ((j > 0)
785 ? weights->get_pixel(2 * i, 2 * j - 1)
786 * 0.125
787 : pixel(0, 0, 0))
788 + weights->get_pixel(2 * i, 2 * j)
789 * 0.25
790 + ((j < is->width() - 1)
791 ? weights->get_pixel(2 * i, 2 * j + 1)
792 * 0.125
793 : pixel(0, 0, 0))
794 + ((i < is->height() - 1 && j > 0)
795 ? weights->get_pixel(2 * i + 1, 2 * j - 1)
796 * 0.0625
797 : pixel(0, 0, 0))
798 + ((i < is->height() - 1)
799 ? weights->get_pixel(2 * i + 1, 2 * j)
800 * 0.125
801 : pixel(0, 0, 0))
802 + ((i < is->height() && j < is->width() - 1)
803 ? weights->get_pixel(2 * i + 1, 2 * j + 1)
804 * 0.0625
805 : pixel(0, 0, 0)) ) );
807 for (int k = 0; k < 3; k++)
808 if (!finite(value[k]))
809 value[k] = 0;
811 is->set_pixel(i, j, value);
814 is->_offset = point(_offset[0] * f, _offset[1] * f);
816 return is;
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
835 * definition arrays.
838 class defined_scale_by_half_threaded : public thread::decompose_domain {
839 image *is;
840 const image *iu;
841 protected:
842 void subdomain_algorithm(unsigned int thread,
843 int i_min, int i_max, int j_min, int j_max) {
845 #if 0
846 ale_real _0625 = (ale_real) 0.0625;
847 ale_real _125 = (ale_real) 0.125;
848 ale_real _25 = (ale_real) 0.25;
849 #endif
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++) {
859 #if 0
862 * Calculate a geometric mean; this approach
863 * may be expensive on some platforms (e.g.,
864 * those without floating-point support in
865 * hardware).
868 pixel value = pixel
870 ( ( ((i > 0 && j > 0)
871 ? ppow(iu->get_pixel(2 * i - 1, 2 * j - 1), _0625)
872 : pixel(0, 0, 0))
873 * ((i > 0)
874 ? ppow(iu->get_pixel(2 * i - 1, 2 * j), _125)
875 : pixel(0, 0, 0))
876 * ((i > 0 && j < is->width() - 1)
877 ? ppow(iu->get_pixel(2 * i - 1, 2 * j + 1), _0625)
878 : pixel(0, 0, 0))
879 * ((j > 0)
880 ? ppow(iu->get_pixel(2 * i, 2 * j - 1), _125)
881 : pixel(0, 0, 0))
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)
885 : pixel(0, 0, 0))
886 * ((i < is->height() - 1 && j > 0)
887 ? ppow(iu->get_pixel(2 * i + 1, 2 * j - 1), _0625)
888 : pixel(0, 0, 0))
889 * ((i < is->height() - 1)
890 ? ppow(iu->get_pixel(2 * i + 1, 2 * j), _125)
891 : pixel(0, 0, 0))
892 * ((i < is->height() && j < is->width() - 1)
893 ? ppow(iu->get_pixel(2 * i + 1, 2 * j + 1), _0625)
894 : pixel(0, 0, 0))));
895 #else
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++) {
901 if (ii < 0
902 || jj < 0
903 || ii > (int) iu->height() - 1
904 || jj > (int) iu->height() - 1)
905 continue;
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];
915 #endif
918 for (int k = 0; k < 3; k++)
919 if (!finite(value[k]))
920 value[k] = 0;
922 is->set_pixel(i, j, value);
926 public:
927 defined_scale_by_half_threaded(image *_is, const image *_iu)
928 : decompose_domain(0, _is->height(),
929 0, _is->width()) {
930 is = _is;
931 iu = _iu;
935 image *defined_scale_by_half(const char *name) const {
936 ale_pos f = 0.5;
938 image *is = scale_generator(
939 (int) floor(height() * (double) f),
940 (int) floor(width() * (double) f), depth(), name);
942 assert(is);
944 defined_scale_by_half_threaded dsbht(is, this);
945 dsbht.run();
947 is->_offset = point(_offset[0] * f, _offset[1] * f);
949 return is;
953 * Return an image scaled by some factor != 1.0, using bilinear
954 * interpolation.
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);
964 if (f > 1.0) {
965 image *is = scale_generator(
966 (int) floor(height() * (double) f),
967 (int) floor(width() * (double) f), depth(), name);
969 assert(is);
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++)
976 is->set_pixel(i, j,
977 get_scaled_bl(point(i, j), f, defined));
979 is->_offset = point(_offset[0] * f, _offset[1] * f);
981 return is;
982 } else if (f == 0.5) {
983 if (defined == 0)
984 return scale_by_half(name);
985 else
986 return defined_scale_by_half(name);
987 } else {
988 image *is = scale(2*f, name, defined);
989 image *result = is->scale(0.5, name, defined);
990 delete is;
991 return result;
997 * Extend the image area to the top, bottom, left, and right,
998 * initializing the new image areas with black pixels. Negative values
999 * shrink the image.
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);
1006 if (is != NULL) {
1007 delete (*i);
1008 *i = is;
1013 * Clone
1015 image *clone(const char *name) const {
1016 image *ic = scale_generator(
1017 height(), width(), depth(), name);
1019 assert(ic);
1021 for (unsigned int i = 0; i < height(); i++)
1022 for (unsigned int j = 0; j < width(); j++)
1023 ic->set_pixel(i, j,
1024 get_pixel(i, j));
1027 ic->_offset = _offset;
1029 ic->_apm_memo = _apm_memo;
1030 ic->_acm_memo = _acm_memo;
1031 ic->_accm_memo = _accm_memo;
1032 ic->_apm = _apm;
1033 ic->_acm = _acm;
1034 ic->_accm = _accm;
1036 return ic;
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
1042 * real inputs.
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();
1053 return _accm[k];
1056 pixel avg_channel_clamped_magnitude() const {
1057 avg_channel_clamped_magnitude_memo();
1058 return _accm;
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();
1074 return _acm[k];
1077 pixel avg_channel_magnitude() const {
1078 avg_channel_magnitude_memo();
1079 return _acm;
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;
1092 if (_apm_memo)
1093 return _apm;
1095 _apm_memo = 1;
1096 accumulator = 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];
1105 divisor += 1;
1109 accumulator /= divisor;
1111 _apm = accumulator;
1113 return _apm;
1116 virtual ~image() {
1120 #endif