d2::align: Cast with lrint() for lod count, as plain (unsigned int) seems to be broke...
[Ale.git] / d2 / align.h
blob15c6cfccf3f86e91a36481e69780f9c178691daf
1 // Copyright 2002, 2004, 2007 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 * align.h: Handle alignment of frames.
25 #ifndef __d2align_h__
26 #define __d2align_h__
28 #include "filter.h"
29 #include "transformation.h"
30 #include "image.h"
31 #include "point.h"
32 #include "render.h"
33 #include "tfile.h"
34 #include "image_rw.h"
36 class align {
37 private:
40 * Private data members
43 static ale_pos scale_factor;
46 * Original frame transformation
48 static transformation orig_t;
51 * Keep data older than latest
53 static int _keep;
54 static transformation *kept_t;
55 static int *kept_ok;
58 * Transformation file handlers
61 static tload_t *tload;
62 static tsave_t *tsave;
65 * Control point variables
68 static const point **cp_array;
69 static unsigned int cp_count;
72 * Reference rendering to align against
75 static render *reference;
76 static filter::scaled_filter *interpolant;
77 static const image *reference_image;
78 static const image *reference_defined;
81 * Per-pixel alignment weight map
84 static const image *weight_map;
87 * Frequency-dependent alignment weights
90 static double horiz_freq_cut;
91 static double vert_freq_cut;
92 static double avg_freq_cut;
93 static const char *fw_output;
96 * Algorithmic alignment weighting
99 static const char *wmx_exec;
100 static const char *wmx_file;
101 static const char *wmx_defs;
104 * Consolidated alignment weights
106 * The final value of alignment_weights_const is the canonical weight
107 * set. If alignment_weights_const is set but alignment_weights is
108 * not, then the memory is not ours, and the object should not be
109 * modified or deleted.
112 static image *alignment_weights;
113 static const image *alignment_weights_const;
116 * Latest transformation.
119 static transformation latest_t;
122 * Flag indicating whether the latest transformation
123 * resulted in a match.
126 static int latest_ok;
129 * Frame number most recently aligned.
132 static int latest;
135 * Exposure registration
137 * 0. Preserve the original exposure of images.
139 * 1. Match exposure between images.
141 * 2. Use only image metadata for registering exposure.
144 static int _exp_register;
147 * Alignment class.
149 * 0. Translation only. Only adjust the x and y position of images.
150 * Do not rotate input images or perform projective transformations.
152 * 1. Euclidean transformations only. Adjust the x and y position
153 * of images and the orientation of the image about the image center.
155 * 2. Perform general projective transformations. See the file gpt.h
156 * for more information about general projective transformations.
159 static int alignment_class;
162 * Default initial alignment type.
164 * 0. Identity transformation.
166 * 1. Most recently accepted frame's final transformation.
169 static int default_initial_alignment_type;
172 * Projective group behavior
174 * 0. Perturb in output coordinates.
176 * 1. Perturb in source coordinates
179 static int perturb_type;
182 * Alignment element
184 * This structure contains variables necessary for handling a
185 * multi-alignment element. The change between the non-default old
186 * initial alignment and old final alignment is used to adjust the
187 * non-default current initial alignment. If either the old or new
188 * initial alignment is a default alignment, the old --follow semantics
189 * are preserved.
192 struct element_t {
193 int is_default, old_is_default;
194 int is_primary;
195 int old_lod;
196 transformation old_initial_alignment;
197 transformation old_final_alignment;
198 transformation default_initial_alignment;
199 const image *input_frame;
201 public:
202 element_t() {
203 is_default = 1;
204 input_frame = NULL;
209 * Alignment for failed frames -- default or optimal?
211 * A frame that does not meet the match threshold can be assigned the
212 * best alignment found, or can be assigned its alignment default.
215 static int is_fail_default;
218 * Alignment code.
220 * 0. Align images with an error contribution from each color channel.
222 * 1. Align images with an error contribution only from the green channel.
223 * Other color channels do not affect alignment.
225 * 2. Align images using a summation of channels. May be useful when dealing
226 * with images that have high frequency color ripples due to color aliasing.
229 static int channel_alignment_type;
232 * Error metric exponent
235 static float metric_exponent;
238 * Match threshold
241 static float match_threshold;
244 * Perturbation lower and upper bounds.
247 static ale_pos perturb_lower;
248 static int perturb_lower_percent;
249 static ale_pos perturb_upper;
250 static int perturb_upper_percent;
253 * Maximum level-of-detail scale factor is 2^lod_max/perturb.
256 static int lod_max;
259 * Maximum rotational perturbation
262 static ale_pos rot_max;
265 * Barrel distortion alignment multiplier
268 static ale_pos bda_mult;
271 * Barrel distortion maximum adjustment rate
274 static ale_pos bda_rate;
277 * Alignment match sum
280 static ale_accum match_sum;
283 * Alignment match count.
286 static int match_count;
289 * Certainty weight flag
291 * 0. Don't use certainty weights for alignment.
293 * 1. Use certainty weights for alignment.
296 static int certainty_weights;
299 * Global search parameter
301 * 0. Local: Local search only.
302 * 1. Inner: Alignment reference image inner region
303 * 2. Outer: Alignment reference image outer region
304 * 3. All: Alignment reference image inner and outer regions.
305 * 4. Central: Inner if possible; else, best of inner and outer.
306 * 5. Points: Align by control points.
309 static int _gs;
312 * Multi-alignment cardinality.
315 static unsigned int _ma_card;
318 * Multi-alignment contiguity.
321 static double _ma_cont;
324 * Minimum overlap for global searches
327 static ale_pos _gs_mo;
328 static int gs_mo_percent;
331 * Exclusion regions
334 static exclusion *ax_parameters;
335 static int ax_count;
338 * Types for scale clusters.
341 struct nl_scale_cluster {
342 const image *accum_max;
343 const image *accum_min;
344 const image *defined_max;
345 const image *defined_min;
346 const image *aweight_max;
347 const image *aweight_min;
348 exclusion *ax_parameters;
350 ale_pos input_scale;
351 const image *input_max;
352 const image *input_min;
355 struct scale_cluster {
356 const image *accum;
357 const image *defined;
358 const image *aweight;
359 exclusion *ax_parameters;
361 ale_pos input_scale;
362 const image *input;
364 nl_scale_cluster *nl_scale_clusters;
368 * Check for exclusion region coverage in the reference
369 * array.
371 static int ref_excluded(int i, int j, point offset, exclusion *params, int param_count) {
372 for (int idx = 0; idx < param_count; idx++)
373 if (params[idx].type == exclusion::RENDER
374 && i + offset[0] >= params[idx].x[0]
375 && i + offset[0] <= params[idx].x[1]
376 && j + offset[1] >= params[idx].x[2]
377 && j + offset[1] <= params[idx].x[3])
378 return 1;
380 return 0;
384 * Check for exclusion region coverage in the input
385 * array.
387 static int input_excluded(ale_pos ti, ale_pos tj, exclusion *params, int param_count) {
388 for (int idx = 0; idx < param_count; idx++)
389 if (params[idx].type == exclusion::FRAME
390 && ti >= params[idx].x[0]
391 && ti <= params[idx].x[1]
392 && tj >= params[idx].x[2]
393 && tj <= params[idx].x[3])
394 return 1;
396 return 0;
400 * Overlap function. Determines the number of pixels in areas where
401 * the arrays overlap. Uses the reference array's notion of pixel
402 * positions.
404 static unsigned int overlap(struct scale_cluster c, transformation t, int ax_count) {
405 assert (reference_image);
407 unsigned int result = 0;
409 point offset = c.accum->offset();
411 for (unsigned int i = 0; i < c.accum->height(); i++)
412 for (unsigned int j = 0; j < c.accum->width(); j++) {
414 if (ref_excluded(i, j, offset, c.ax_parameters, ax_count))
415 continue;
418 * Transform
421 struct point q;
423 q = (c.input_scale < 1.0 && interpolant == NULL)
424 ? t.scaled_inverse_transform(
425 point(i + offset[0], j + offset[1]))
426 : t.unscaled_inverse_transform(
427 point(i + offset[0], j + offset[1]));
429 ale_pos ti = q[0];
430 ale_pos tj = q[1];
433 * Check that the transformed coordinates are within
434 * the boundaries of array c.input, and check that the
435 * weight value in the accumulated array is nonzero,
436 * unless we know it is nonzero by virtue of the fact
437 * that it falls within the region of the original
438 * frame (e.g. when we're not increasing image
439 * extents). Also check for frame exclusion.
442 if (input_excluded(ti, tj, c.ax_parameters, ax_count))
443 continue;
445 if (ti >= 0
446 && ti <= c.input->height() - 1
447 && tj >= 0
448 && tj <= c.input->width() - 1
449 && c.defined->get_pixel(i, j)[0] != 0)
450 result++;
453 return result;
457 * Calculate the region associated with the current multi-alignment
458 * element.
460 static void calculate_element_region(transformation *t, scale_cluster si,
461 int local_ax_count) {
463 unsigned int i_max = si.accum->height();
464 unsigned int j_max = si.accum->width();
465 point offset = si.accum->offset();
467 if (si.input_scale < 1.0 && interpolant == NULL)
468 t->begin_calculate_scaled_region(i_max, j_max, offset);
469 else
470 t->begin_calculate_unscaled_region(i_max, j_max, offset);
472 for (unsigned int i = 0; i < i_max; i++)
473 for (unsigned int j = 0; j < j_max; j++) {
475 if (ref_excluded(i, j, offset, si.ax_parameters, local_ax_count))
476 continue;
478 point q;
480 while ((q = t->get_query_point((int) (i + offset[0]),
481 (int) (j + offset[1]))).defined()) {
483 ale_pos ti = q[0];
484 ale_pos tj = q[1];
486 if (input_excluded(ti, tj, si.ax_parameters, ax_count))
487 continue;
489 if (ti >= 0
490 && ti <= si.input->height() - 1
491 && tj >= 0
492 && tj <= si.input->width() - 1
493 && si.defined->get_pixel(i, j)[0] != 0) {
495 assert(0);
500 t->end_calculate_region();
504 * Not-quite-symmetric difference function. Determines the difference in areas
505 * where the arrays overlap. Uses the reference array's notion of pixel positions.
507 * For the purposes of determining the difference, this function divides each
508 * pixel value by the corresponding image's average pixel magnitude, unless we
509 * are:
511 * a) Extending the boundaries of the image, or
513 * b) following the previous frame's transform
515 * If we are doing monte-carlo pixel sampling for alignment, we
516 * typically sample a subset of available pixels; otherwise, we sample
517 * all pixels.
521 class diff_stat_t {
523 struct run {
525 transformation offset;
526 ale_pos perturb;
528 ale_accum result;
529 ale_accum divisor;
531 point max, min;
532 ale_accum centroid[2], centroid_divisor;
533 ale_accum de_centroid[2], de_centroid_v, de_sum;
535 void init() {
537 result = 0;
538 divisor = 0;
540 min = point::posinf();
541 max = point::neginf();
543 centroid[0] = 0;
544 centroid[1] = 0;
545 centroid_divisor = 0;
547 de_centroid[0] = 0;
548 de_centroid[1] = 0;
550 de_centroid_v = 0;
552 de_sum = 0;
555 void init(transformation _offset, ale_pos _perturb) {
556 offset = _offset;
557 perturb = _perturb;
558 init();
562 * Required for STL sanity.
564 run() : offset() {
565 init();
568 run(transformation _offset, ale_pos _perturb) : offset() {
569 init(_offset, _perturb);
572 void add(const run &_run) {
573 result += _run.result;
574 divisor += _run.divisor;
576 for (int d = 0; d < 2; d++) {
577 if (min[d] > _run.min[d])
578 min[d] = _run.min[d];
579 if (max[d] < _run.max[d])
580 max[d] = _run.max[d];
581 centroid[d] += _run.centroid[d];
582 de_centroid[d] += _run.de_centroid[d];
585 centroid_divisor += _run.centroid_divisor;
586 de_centroid_v += _run.de_centroid_v;
587 de_sum += _run.de_sum;
590 run(const run &_run) : offset() {
593 * Initialize
595 init(_run.offset, _run.perturb);
598 * Add
600 add(_run);
603 run &operator=(const run &_run) {
606 * Initialize
608 init(_run.offset, _run.perturb);
611 * Add
613 add(_run);
615 return *this;
618 ~run() {
621 ale_accum get_error() const {
622 return pow(result / divisor, 1/metric_exponent);
625 void sample(int f, scale_cluster c, int i, int j, point t, point u,
626 const run &comparison) {
628 pixel pa = c.accum->get_pixel(i, j);
630 pixel p[2];
631 pixel weight[2];
632 ale_accum this_result[2] = { 0, 0 };
633 ale_accum this_divisor[2] = { 0, 0 };
636 * XXX: It's not clear that certainty is being
637 * handled sanely here.
640 if (interpolant != NULL)
641 interpolant->filtered(i, j, &p[0], &weight[0], 1, f);
642 else {
643 pixel result[2];
645 c.input->get_bl(t, result);
646 p[0] = result[0];
647 weight[0] = result[1];
650 if (u.defined()) {
651 pixel result[2];
652 c.input->get_bl(u, result);
653 p[1] = result[0];
654 weight[1] = result[1];
659 * Handle certainty.
662 if (certainty_weights == 0) {
663 weight[0] = pixel(1, 1, 1);
664 weight[1] = pixel(1, 1, 1);
667 if (c.aweight != NULL) {
668 weight[0] *= c.aweight->get_pixel(i, j);
669 weight[1] *= c.aweight->get_pixel(i, j);
673 * Update sampling area statistics
676 if (min[0] > i)
677 min[0] = i;
678 if (min[1] > j)
679 min[1] = j;
680 if (max[0] < i)
681 max[0] = i;
682 if (max[1] < j)
683 max[1] = j;
685 centroid[0] += (weight[0][0] + weight[0][1] + weight[0][2]) * i;
686 centroid[1] += (weight[0][0] + weight[0][1] + weight[0][2]) * j;
687 centroid_divisor += (weight[0][0] + weight[0][1] + weight[0][2]);
690 * Determine alignment type.
693 for (int m = 0; m < (u.defined() ? 2 : 1); m++)
694 if (channel_alignment_type == 0) {
696 * Align based on all channels.
700 for (int k = 0; k < 3; k++) {
701 ale_real achan = pa[k];
702 ale_real bchan = p[m][k];
704 this_result[m] += weight[m][k] * pow(fabs(achan - bchan), metric_exponent);
705 this_divisor[m] += weight[m][k] * pow(achan > bchan ? achan : bchan, metric_exponent);
707 } else if (channel_alignment_type == 1) {
709 * Align based on the green channel.
712 ale_real achan = pa[1];
713 ale_real bchan = p[m][1];
715 this_result[m] = weight[m][1] * pow(fabs(achan - bchan), metric_exponent);
716 this_divisor[m] = weight[m][1] * pow(achan > bchan ? achan : bchan, metric_exponent);
717 } else if (channel_alignment_type == 2) {
719 * Align based on the sum of all channels.
722 ale_real asum = 0;
723 ale_real bsum = 0;
724 ale_real wsum = 0;
726 for (int k = 0; k < 3; k++) {
727 asum += pa[k];
728 bsum += p[m][k];
729 wsum += weight[m][k] / 3;
732 this_result[m] = wsum * pow(fabs(asum - bsum), metric_exponent);
733 this_divisor[m] = wsum * pow(asum > bsum ? asum : bsum, metric_exponent);
736 if (u.defined()) {
737 // ale_accum de = fabs(this_result[0] / this_divisor[0]
738 // - this_result[1] / this_divisor[1]);
739 ale_accum de = fabs(this_result[0] - this_result[1]);
741 de_centroid[0] += de * i;
742 de_centroid[1] += de * j;
744 de_centroid_v += de * t.lengthto(u);
746 de_sum += de;
749 result += (this_result[0]);
750 divisor += (this_divisor[0]);
753 void rescale(ale_pos scale) {
754 offset.rescale(scale);
756 de_centroid[0] *= scale;
757 de_centroid[1] *= scale;
758 de_centroid_v *= scale;
761 point get_centroid() {
762 point result = point(centroid[0] / centroid_divisor, centroid[1] / centroid_divisor);
764 assert (finite(centroid[0])
765 && finite(centroid[1])
766 && (result.defined() || centroid_divisor == 0));
768 return result;
771 point get_error_centroid() {
772 point result = point(de_centroid[0] / de_sum, de_centroid[1] / de_sum);
773 return result;
777 ale_pos get_error_perturb() {
778 ale_pos result = de_centroid_v / de_sum;
780 return result;
786 * When non-empty, runs.front() is best, runs.back() is
787 * testing.
790 std::vector<run> runs;
793 * old_runs stores the latest available perturbation set for
794 * each multi-alignment element.
797 typedef std::pair<unsigned int, unsigned int> run_index;
798 std::map<run_index, run> old_runs;
800 static void *diff_subdomain(void *args);
802 struct subdomain_args {
803 struct scale_cluster c;
804 std::vector<run> runs;
805 int ax_count;
806 int f;
807 int i_min, i_max, j_min, j_max;
808 int subdomain;
811 int get_current_index() const {
812 assert(runs.size());
813 return runs[0].offset.get_current_index();
816 struct scale_cluster si;
817 int ax_count;
818 int frame;
820 std::vector<ale_pos> perturb_multipliers;
822 public:
823 void diff(struct scale_cluster c, ale_pos perturb,
824 transformation t,
825 int _ax_count, int f) {
827 if (runs.size() == 2)
828 runs.pop_back();
830 runs.push_back(run(t, perturb));
832 si = c;
833 ax_count = _ax_count;
834 frame = f;
836 ui::get()->d2_align_sample_start();
838 if (interpolant != NULL)
839 interpolant->set_parameters(t, c.input, c.accum->offset());
841 int N;
842 #ifdef USE_PTHREAD
843 N = thread::count();
845 pthread_t *threads = (pthread_t *) malloc(sizeof(pthread_t) * N);
846 pthread_attr_t *thread_attr = (pthread_attr_t *) malloc(sizeof(pthread_attr_t) * N);
848 #else
849 N = 1;
850 #endif
852 subdomain_args *args = new subdomain_args[N];
854 for (int ti = 0; ti < N; ti++) {
855 args[ti].c = c;
856 args[ti].runs = runs;
857 args[ti].ax_count = ax_count;
858 args[ti].f = f;
859 args[ti].i_min = (c.accum->height() * ti) / N;
860 args[ti].i_max = (c.accum->height() * (ti + 1)) / N;
861 args[ti].j_min = 0;
862 args[ti].j_max = c.accum->width();
863 args[ti].subdomain = ti;
864 #ifdef USE_PTHREAD
865 pthread_attr_init(&thread_attr[ti]);
866 pthread_attr_setdetachstate(&thread_attr[ti], PTHREAD_CREATE_JOINABLE);
867 pthread_create(&threads[ti], &thread_attr[ti], diff_subdomain, &args[ti]);
868 #else
869 diff_subdomain(&args[ti]);
870 #endif
873 for (int ti = 0; ti < N; ti++) {
874 #ifdef USE_PTHREAD
875 pthread_join(threads[ti], NULL);
876 #endif
877 runs.back().add(args[ti].runs.back());
880 delete[] args;
882 ui::get()->d2_align_sample_stop();
886 private:
887 void rediff() {
888 std::vector<transformation> t_array;
889 std::vector<ale_pos> p_array;
891 for (unsigned int r = 0; r < runs.size(); r++) {
892 t_array.push_back(runs[r].offset);
893 p_array.push_back(runs[r].perturb);
896 runs.clear();
898 for (unsigned int r = 0; r < t_array.size(); r++)
899 diff(si, p_array[r], t_array[r], ax_count, frame);
903 public:
904 int better() {
905 assert(runs.size() >= 2);
906 assert(runs[0].offset.scale() == runs[1].offset.scale());
908 return (runs[1].get_error() < runs[0].get_error()
909 || (!finite(runs[0].get_error()) && finite(runs[1].get_error())));
912 diff_stat_t() : runs(), old_runs(), perturb_multipliers() {
915 run_index get_run_index(unsigned int perturb_index) {
916 return run_index(get_current_index(), perturb_index);
919 run &get_run(unsigned int perturb_index) {
920 run_index index = get_run_index(perturb_index);
922 assert(old_runs.count(index));
923 return old_runs[index];
926 void rescale(ale_pos scale, scale_cluster _si) {
927 assert(runs.size() == 1);
929 si = _si;
931 runs[0].rescale(scale);
933 rediff();
936 void push_element() {
937 assert(runs.size() == 1);
939 runs[0].offset.push_element();
941 rediff();
944 unsigned int get_current_index() {
945 assert (runs.size() > 0);
947 return runs[0].offset.get_current_index();
950 void set_current_index(unsigned int i) {
951 assert(runs.size() == 1);
952 runs[0].offset.set_current_index(i);
953 rediff();
956 void calculate_element_region() {
957 assert(runs.size() == 1);
959 if (get_offset().get_current_index() > 0
960 && get_offset().is_nontrivial())
961 align::calculate_element_region(&runs[0].offset, si, ax_count);
964 ~diff_stat_t() {
967 diff_stat_t &operator=(const diff_stat_t &dst) {
969 * Copy run information.
971 runs = dst.runs;
972 old_runs = dst.old_runs;
975 * Copy diff variables
977 si = dst.si;
978 ax_count = dst.ax_count;
979 frame = dst.frame;
980 perturb_multipliers = dst.perturb_multipliers;
982 return *this;
985 diff_stat_t(const diff_stat_t &dst) : runs(), old_runs(),
986 perturb_multipliers() {
987 operator=(dst);
990 ale_accum get_result() {
991 assert(runs.size() == 1);
992 return runs[0].result;
995 ale_accum get_divisor() {
996 assert(runs.size() == 1);
997 return runs[0].divisor;
1000 transformation get_offset() {
1001 assert(runs.size() == 1);
1002 return runs[0].offset;
1005 int operator!=(diff_stat_t &param) {
1006 return (get_error() != param.get_error());
1009 int operator==(diff_stat_t &param) {
1010 return !(operator!=(param));
1013 ale_pos get_error_perturb() {
1014 assert(runs.size() == 1);
1015 return runs[0].get_error_perturb();
1018 ale_accum get_error() const {
1019 assert(runs.size() == 1);
1020 return runs[0].get_error();
1023 public:
1025 * Get the set of transformations produced by a given perturbation
1027 void get_perturb_set(std::vector<transformation> *set,
1028 ale_pos adj_p, ale_pos adj_o, ale_pos adj_b,
1029 ale_pos *current_bd, ale_pos *modified_bd,
1030 std::vector<ale_pos> multipliers = std::vector<ale_pos>()) {
1032 assert(runs.size() == 1);
1034 transformation test_t;
1037 * Translational or euclidean transformation
1040 for (unsigned int i = 0; i < 2; i++)
1041 for (unsigned int s = 0; s < 2; s++) {
1043 if (!multipliers.size())
1044 multipliers.push_back(1);
1046 assert(finite(multipliers[0]));
1048 test_t = get_offset();
1050 // test_t.eu_modify(i, (s ? -adj_p : adj_p) * multipliers[0]);
1051 test_t.translate((i ? point(1, 0) : point(0, 1))
1052 * (s ? -adj_p : adj_p)
1053 * multipliers[0]);
1055 test_t.snap(adj_p / 2);
1057 set->push_back(test_t);
1058 multipliers.erase(multipliers.begin());
1062 if (alignment_class > 0)
1063 for (unsigned int s = 0; s < 2; s++) {
1065 if (!multipliers.size())
1066 multipliers.push_back(1);
1068 assert(multipliers.size());
1069 assert(finite(multipliers[0]));
1071 if (!(adj_o * multipliers[0] < rot_max))
1072 return;
1074 ale_pos adj_s = (s ? 1 : -1) * adj_o * multipliers[0];
1076 test_t = get_offset();
1078 run_index ori = get_run_index(set->size());
1079 point centroid = point::undefined();
1081 if (!old_runs.count(ori))
1082 ori = get_run_index(0);
1084 if (!centroid.finite() && old_runs.count(ori)) {
1085 centroid = old_runs[ori].get_error_centroid();
1087 if (!centroid.finite())
1088 centroid = old_runs[ori].get_centroid();
1090 centroid *= test_t.scale()
1091 / old_runs[ori].offset.scale();
1094 if (!centroid.finite() && !test_t.is_projective()) {
1095 test_t.eu_modify(2, adj_s);
1096 } else if (!centroid.finite()) {
1097 centroid = point(si.input->height() / 2,
1098 si.input->width() / 2);
1100 test_t.rotate(centroid + si.accum->offset(),
1101 adj_s);
1102 } else {
1103 test_t.rotate(centroid + si.accum->offset(),
1104 adj_s);
1107 test_t.snap(adj_p / 2);
1109 set->push_back(test_t);
1110 multipliers.erase(multipliers.begin());
1113 if (alignment_class == 2) {
1116 * Projective transformation
1119 for (unsigned int i = 0; i < 4; i++)
1120 for (unsigned int j = 0; j < 2; j++)
1121 for (unsigned int s = 0; s < 2; s++) {
1123 if (!multipliers.size())
1124 multipliers.push_back(1);
1126 assert(multipliers.size());
1127 assert(finite(multipliers[0]));
1129 ale_pos adj_s = (s ? -1 : 1) * adj_p * multipliers [0];
1131 test_t = get_offset();
1133 if (perturb_type == 0)
1134 test_t.gpt_modify(j, i, adj_s);
1135 else if (perturb_type == 1)
1136 test_t.gr_modify(j, i, adj_s);
1137 else
1138 assert(0);
1140 test_t.snap(adj_p / 2);
1142 set->push_back(test_t);
1143 multipliers.erase(multipliers.begin());
1149 * Barrel distortion
1152 if (bda_mult != 0 && adj_b != 0) {
1154 for (unsigned int d = 0; d < get_offset().bd_count(); d++)
1155 for (unsigned int s = 0; s < 2; s++) {
1157 if (!multipliers.size())
1158 multipliers.push_back(1);
1160 assert (multipliers.size());
1161 assert (finite(multipliers[0]));
1163 ale_pos adj_s = (s ? -1 : 1) * adj_b * multipliers[0];
1165 if (bda_rate > 0 && fabs(modified_bd[d] + adj_s - current_bd[d]) > bda_rate)
1166 continue;
1168 transformation test_t = get_offset();
1170 test_t.bd_modify(d, adj_s);
1172 set->push_back(test_t);
1177 void confirm() {
1178 assert(runs.size() == 2);
1179 runs[0] = runs[1];
1180 runs.pop_back();
1183 void discard() {
1184 assert(runs.size() == 2);
1185 runs.pop_back();
1188 void perturb_test(ale_pos perturb, ale_pos adj_p, ale_pos adj_o, ale_pos adj_b,
1189 ale_pos *current_bd, ale_pos *modified_bd, int stable) {
1191 assert(runs.size() == 1);
1193 std::vector<transformation> t_set;
1195 if (perturb_multipliers.size() == 0) {
1196 get_perturb_set(&t_set, adj_p, adj_o, adj_b,
1197 current_bd, modified_bd);
1199 for (unsigned int i = 0; i < t_set.size(); i++) {
1200 diff_stat_t test = *this;
1202 test.diff(si, perturb, t_set[i], ax_count, frame);
1204 test.confirm();
1206 if (finite(adj_p / test.get_error_perturb()))
1207 perturb_multipliers.push_back(adj_p / test.get_error_perturb());
1208 else
1209 perturb_multipliers.push_back(1);
1213 t_set.clear();
1216 get_perturb_set(&t_set, adj_p, adj_o, adj_b, current_bd, modified_bd,
1217 perturb_multipliers);
1219 int found_unreliable = 1;
1220 std::vector<int> tested(t_set.size(), 0);
1222 for (unsigned int i = 0; i < t_set.size(); i++) {
1223 run_index ori = get_run_index(i);
1226 * Check for stability
1228 if (stable
1229 && old_runs.count(ori)
1230 && old_runs[ori].offset == t_set[i])
1231 tested[i] = 1;
1234 std::vector<ale_pos> perturb_multipliers_original = perturb_multipliers;
1236 while (found_unreliable) {
1238 found_unreliable = 0;
1240 for (unsigned int i = 0; i < t_set.size(); i++) {
1242 if (tested[i])
1243 continue;
1245 diff(si, perturb, t_set[i], ax_count, frame);
1247 if (!(i < perturb_multipliers.size())
1248 || !finite(perturb_multipliers[i])) {
1250 perturb_multipliers.resize(i + 1);
1252 perturb_multipliers[i] =
1253 adj_p / runs[1].get_error_perturb();
1255 if (finite(perturb_multipliers[i]))
1256 found_unreliable = 1;
1258 continue;
1261 run_index ori = get_run_index(i);
1263 if (old_runs.count(ori) == 0)
1264 old_runs.insert(std::pair<run_index, run>(ori, runs[1]));
1265 else
1266 old_runs[ori] = runs[1];
1268 perturb_multipliers[i] = perturb_multipliers_original[i]
1269 * adj_p / runs[1].get_error_perturb();
1271 if (!finite(perturb_multipliers[i]))
1272 perturb_multipliers[i] = 1;
1274 tested[i] = 1;
1276 if (better()
1277 && runs[1].get_error() < runs[0].get_error()
1278 && perturb_multipliers[i]
1279 / perturb_multipliers_original[i] < 2) {
1280 runs[0] = runs[1];
1281 runs.pop_back();
1282 return;
1287 if (runs.size() > 1)
1288 runs.pop_back();
1290 if (!found_unreliable)
1291 return;
1296 * Attempt to make the current element non-trivial, by finding a nearby
1297 * alignment admitting a non-empty element region.
1299 void make_element_nontrivial(ale_pos adj_p, ale_pos adj_o) {
1300 assert(runs.size() == 1);
1302 transformation *t = &runs[0].offset;
1304 if (t->is_nontrivial())
1305 return;
1307 calculate_element_region();
1309 if (t->is_nontrivial())
1310 return;
1312 std::vector<transformation> t_set;
1313 get_perturb_set(&t_set, adj_p, adj_o, 0, NULL, NULL);
1315 for (unsigned int i = 0; i < t_set.size(); i++) {
1317 align::calculate_element_region(&t_set[i], si, ax_count);
1319 if (t_set[i].is_nontrivial()) {
1320 *t = t_set[i];
1321 return;
1330 * Adjust exposure for an aligned frame B against reference A.
1332 * Expects full-LOD images.
1334 * This function is a bit of a mess, as it reflects rather ad-hoc rules
1335 * regarding what seems to work w.r.t. certainty. Using certainty in the
1336 * first pass seems to result in worse alignment, while not using certainty
1337 * in the second pass results in incorrect determination of exposure.
1339 * [Note that this may have been due to a bug in certainty determination
1340 * within this function.]
1342 static void set_exposure_ratio(unsigned int m, struct scale_cluster c,
1343 transformation t, int ax_count, int pass_number) {
1345 if (_exp_register == 2) {
1347 * Use metadata only.
1349 ale_real gain_multiplier = image_rw::exp(m).get_gain_multiplier();
1350 pixel multiplier = pixel(gain_multiplier, gain_multiplier, gain_multiplier);
1352 image_rw::exp(m).set_multiplier(multiplier);
1353 ui::get()->exp_multiplier(multiplier[0],
1354 multiplier[1],
1355 multiplier[2]);
1357 return;
1360 pixel_accum asum, bsum;
1362 point offset = c.accum->offset();
1364 for (unsigned int i = 0; i < c.accum->height(); i++)
1365 for (unsigned int j = 0; j < c.accum->width(); j++) {
1367 if (ref_excluded(i, j, offset, c.ax_parameters, ax_count))
1368 continue;
1371 * Transform
1374 struct point q;
1376 q = (c.input_scale < 1.0 && interpolant == NULL)
1377 ? t.scaled_inverse_transform(
1378 point(i + offset[0], j + offset[1]))
1379 : t.unscaled_inverse_transform(
1380 point(i + offset[0], j + offset[1]));
1383 * Check that the transformed coordinates are within
1384 * the boundaries of array c.input, that they are not
1385 * subject to exclusion, and that the weight value in
1386 * the accumulated array is nonzero.
1389 if (input_excluded(q[0], q[1], c.ax_parameters, ax_count))
1390 continue;
1392 if (q[0] >= 0
1393 && q[0] <= c.input->height() - 1
1394 && q[1] >= 0
1395 && q[1] <= c.input->width() - 1
1396 && c.defined->get_pixel(i, j).minabs_norm() != 0) {
1397 pixel a = c.accum->get_pixel(i, j);
1398 pixel b[2];
1400 c.input->get_bl(q, b);
1402 #if 1
1403 pixel weight = (c.aweight
1404 ? c.aweight->get_pixel(i, j)
1405 : pixel(1, 1, 1))
1406 * ((!certainty_weights && pass_number)
1407 ? c.defined->get_pixel(i, j)
1408 : pixel(1, 1, 1))
1409 * (pass_number
1410 ? b[1]
1411 : pixel(1, 1, 1));
1412 #else
1413 pixel weight = pixel(1, 1, 1);
1414 #endif
1416 asum += a * weight;
1417 bsum += b[0] * weight;
1421 // std::cerr << (asum / bsum) << " ";
1423 pixel_accum new_multiplier;
1425 new_multiplier = asum / bsum * image_rw::exp(m).get_multiplier();
1427 if (finite(new_multiplier[0])
1428 && finite(new_multiplier[1])
1429 && finite(new_multiplier[2])) {
1430 image_rw::exp(m).set_multiplier(new_multiplier);
1431 ui::get()->exp_multiplier(new_multiplier[0],
1432 new_multiplier[1],
1433 new_multiplier[2]);
1438 * Copy all ax parameters.
1440 static exclusion *copy_ax_parameters(int local_ax_count, exclusion *source) {
1442 exclusion *dest = (exclusion *) malloc(local_ax_count * sizeof(exclusion));
1444 assert (dest);
1446 if (!dest)
1447 ui::get()->memory_error("exclusion regions");
1449 for (int idx = 0; idx < local_ax_count; idx++)
1450 dest[idx] = source[idx];
1452 return dest;
1456 * Copy ax parameters according to frame.
1458 static exclusion *filter_ax_parameters(int frame, int *local_ax_count) {
1460 exclusion *dest = (exclusion *) malloc(ax_count * sizeof(exclusion));
1462 assert (dest);
1464 if (!dest)
1465 ui::get()->memory_error("exclusion regions");
1467 *local_ax_count = 0;
1469 for (int idx = 0; idx < ax_count; idx++) {
1470 if (ax_parameters[idx].x[4] > frame
1471 || ax_parameters[idx].x[5] < frame)
1472 continue;
1474 dest[*local_ax_count] = ax_parameters[idx];
1476 (*local_ax_count)++;
1479 return dest;
1482 static void scale_ax_parameters(int local_ax_count, exclusion *ax_parameters,
1483 ale_pos ref_scale, ale_pos input_scale) {
1484 for (int i = 0; i < local_ax_count; i++) {
1485 ale_pos scale = (ax_parameters[i].type == exclusion::RENDER)
1486 ? ref_scale
1487 : input_scale;
1489 for (int n = 0; n < 6; n++) {
1490 ax_parameters[i].x[n] = ax_parameters[i].x[n] * scale;
1496 * Prepare the next level of detail for ordinary images.
1498 static const image *prepare_lod(const image *current) {
1499 if (current == NULL)
1500 return NULL;
1502 return current->scale_by_half("prepare_lod");
1506 * Prepare the next level of detail for weighted images.
1508 static const image *prepare_lod(const image *current, const image *weights) {
1509 if (current == NULL)
1510 return NULL;
1512 return current->scale_by_half(weights, "prepare_lod");
1516 * Prepare the next level of detail for definition maps.
1518 static const image *prepare_lod_def(const image *current) {
1519 assert(current);
1521 return current->defined_scale_by_half("prepare_lod_def");
1525 * Initialize scale cluster data structures.
1528 static void init_nl_cluster(struct scale_cluster *sc) {
1531 static struct scale_cluster *init_clusters(int frame, ale_real scale_factor,
1532 const image *input_frame, unsigned int steps,
1533 int *local_ax_count) {
1536 * Allocate memory for the array.
1539 struct scale_cluster *scale_clusters =
1540 (struct scale_cluster *) malloc(steps * sizeof(struct scale_cluster));
1542 assert (scale_clusters);
1544 if (!scale_clusters)
1545 ui::get()->memory_error("alignment");
1548 * Prepare images and exclusion regions for the highest level
1549 * of detail.
1552 scale_clusters[0].accum = reference_image;
1554 ui::get()->constructing_lod_clusters(0.0);
1555 scale_clusters[0].input_scale = scale_factor;
1556 if (scale_factor < 1.0 && interpolant == NULL)
1557 scale_clusters[0].input = input_frame->scale(scale_factor, "alignment");
1558 else
1559 scale_clusters[0].input = input_frame;
1561 scale_clusters[0].defined = reference_defined;
1562 scale_clusters[0].aweight = alignment_weights_const;
1563 scale_clusters[0].ax_parameters = filter_ax_parameters(frame, local_ax_count);
1565 scale_ax_parameters(*local_ax_count, scale_clusters[0].ax_parameters, scale_factor,
1566 (scale_factor < 1.0 && interpolant == NULL) ? scale_factor : 1);
1568 init_nl_cluster(&(scale_clusters[0]));
1571 * Prepare reduced-detail images and exclusion
1572 * regions.
1575 for (unsigned int step = 1; step < steps; step++) {
1576 ui::get()->constructing_lod_clusters(step);
1577 scale_clusters[step].accum = prepare_lod(scale_clusters[step - 1].accum, scale_clusters[step - 1].aweight);
1578 scale_clusters[step].defined = prepare_lod_def(scale_clusters[step - 1].defined);
1579 scale_clusters[step].aweight = prepare_lod(scale_clusters[step - 1].aweight);
1580 scale_clusters[step].ax_parameters
1581 = copy_ax_parameters(*local_ax_count, scale_clusters[step - 1].ax_parameters);
1583 double sf = scale_clusters[step - 1].input_scale / 2;
1584 scale_clusters[step].input_scale = sf;
1586 if (sf >= 1.0 || interpolant != NULL) {
1587 scale_clusters[step].input = scale_clusters[step - 1].input;
1588 scale_ax_parameters(*local_ax_count, scale_clusters[step].ax_parameters, 0.5, 1);
1589 } else if (sf > 0.5) {
1590 scale_clusters[step].input = scale_clusters[step - 1].input->scale(sf, "alignment");
1591 scale_ax_parameters(*local_ax_count, scale_clusters[step].ax_parameters, 0.5, sf);
1592 } else {
1593 scale_clusters[step].input = scale_clusters[step - 1].input->scale(0.5, "alignment");
1594 scale_ax_parameters(*local_ax_count, scale_clusters[step].ax_parameters, 0.5, 0.5);
1597 init_nl_cluster(&(scale_clusters[step]));
1600 return scale_clusters;
1604 * Destroy the first element in the scale cluster data structure.
1606 static void final_clusters(struct scale_cluster *scale_clusters, ale_real scale_factor,
1607 unsigned int steps) {
1609 if (scale_clusters[0].input_scale < 1.0)
1610 delete scale_clusters[0].input;
1612 free((void *)scale_clusters[0].ax_parameters);
1614 for (unsigned int step = 1; step < steps; step++) {
1615 delete scale_clusters[step].accum;
1616 delete scale_clusters[step].defined;
1617 delete scale_clusters[step].aweight;
1619 if (scale_clusters[step].input_scale < 1.0)
1620 delete scale_clusters[step].input;
1622 free((void *)scale_clusters[step].ax_parameters);
1625 free(scale_clusters);
1629 * Calculate the centroid of a control point for the set of frames
1630 * having index lower than m. Divide by any scaling of the output.
1632 static point unscaled_centroid(unsigned int m, unsigned int p) {
1633 assert(_keep);
1635 point point_sum(0, 0);
1636 ale_accum divisor = 0;
1638 for(unsigned int j = 0; j < m; j++) {
1639 point pp = cp_array[p][j];
1641 if (pp.defined()) {
1642 point_sum += kept_t[j].transform_unscaled(pp)
1643 / kept_t[j].scale();
1644 divisor += 1;
1648 if (divisor == 0)
1649 return point::undefined();
1651 return point_sum / divisor;
1655 * Calculate centroid of this frame, and of all previous frames,
1656 * from points common to both sets.
1658 static void centroids(unsigned int m, point *current, point *previous) {
1660 * Calculate the translation
1662 point other_centroid(0, 0);
1663 point this_centroid(0, 0);
1664 ale_pos divisor = 0;
1666 for (unsigned int i = 0; i < cp_count; i++) {
1667 point other_c = unscaled_centroid(m, i);
1668 point this_c = cp_array[i][m];
1670 if (!other_c.defined() || !this_c.defined())
1671 continue;
1673 other_centroid += other_c;
1674 this_centroid += this_c;
1675 divisor += 1;
1679 if (divisor == 0) {
1680 *current = point::undefined();
1681 *previous = point::undefined();
1682 return;
1685 *current = this_centroid / divisor;
1686 *previous = other_centroid / divisor;
1690 * Calculate the RMS error of control points for frame m, with
1691 * transformation t, against control points for earlier frames.
1693 static ale_accum cp_rms_error(unsigned int m, transformation t) {
1694 assert (_keep);
1696 ale_accum err = 0;
1697 ale_accum divisor = 0;
1699 for (unsigned int i = 0; i < cp_count; i++)
1700 for (unsigned int j = 0; j < m; j++) {
1701 const point *p = cp_array[i];
1702 point p_ref = kept_t[j].transform_unscaled(p[j]);
1703 point p_cur = t.transform_unscaled(p[m]);
1705 if (!p_ref.defined() || !p_cur.defined())
1706 continue;
1708 err += p_ref.lengthtosq(p_cur);
1709 divisor += 1;
1712 return sqrt(err / divisor);
1716 * Implement new delta --follow semantics.
1718 * If we have a transformation T such that
1720 * prev_final == T(prev_init)
1722 * Then we also have
1724 * current_init_follow == T(current_init)
1726 * We can calculate T as follows:
1728 * T == prev_final(prev_init^-1)
1730 * Where ^-1 is the inverse operator.
1732 static transformation follow(element_t *element, transformation offset, int lod) {
1734 transformation new_offset = offset;
1737 * Criteria for using following.
1740 if (!element->old_is_default && !element->is_default &&
1741 default_initial_alignment_type == 1) {
1743 * Ensure that the lod for the old initial and final
1744 * alignments are equal to the lod for the new initial
1745 * alignment.
1748 ui::get()->following();
1750 element->old_final_alignment.rescale (1 / pow(2, lod));
1751 element->old_initial_alignment.rescale(1 / pow(2, lod - element->old_lod));
1753 for (offset.set_current_index(0),
1754 element->old_initial_alignment.set_current_index(0),
1755 element->old_final_alignment.set_current_index(0),
1756 new_offset.set_current_index(0);
1758 offset.get_current_index() < _ma_card;
1760 offset.push_element(),
1761 new_offset.push_element()) {
1763 if (alignment_class == 0) {
1765 * Translational transformations
1768 ale_pos t0 = -element->old_initial_alignment.eu_get(0)
1769 + element->old_final_alignment.eu_get(0);
1770 ale_pos t1 = -element->old_initial_alignment.eu_get(1)
1771 + element->old_final_alignment.eu_get(1);
1773 new_offset.eu_modify(0, t0);
1774 new_offset.eu_modify(1, t1);
1776 } else if (alignment_class == 1) {
1778 * Euclidean transformations
1781 ale_pos t2 = -element->old_initial_alignment.eu_get(2)
1782 + element->old_final_alignment.eu_get(2);
1784 new_offset.eu_modify(2, t2);
1786 point p( offset.scaled_height()/2 + offset.eu_get(0) - element->old_initial_alignment.eu_get(0),
1787 offset.scaled_width()/2 + offset.eu_get(1) - element->old_initial_alignment.eu_get(1) );
1789 p = element->old_final_alignment.transform_scaled(p);
1791 new_offset.eu_modify(0, p[0] - offset.scaled_height()/2 - offset.eu_get(0));
1792 new_offset.eu_modify(1, p[1] - offset.scaled_width()/2 - offset.eu_get(1));
1794 } else if (alignment_class == 2) {
1796 * Projective transformations
1799 point p[4];
1801 p[0] = element->old_final_alignment.transform_scaled(element->old_initial_alignment
1802 . scaled_inverse_transform(offset.get_current_element().transform_scaled(point( 0 , 0 ))));
1803 p[1] = element->old_final_alignment.transform_scaled(element->old_initial_alignment
1804 . scaled_inverse_transform(offset.get_current_element().transform_scaled(point(offset.scaled_height(), 0 ))));
1805 p[2] = element->old_final_alignment.transform_scaled(element->old_initial_alignment
1806 . scaled_inverse_transform(offset.get_current_element().transform_scaled(point(offset.scaled_height(), offset.scaled_width()))));
1807 p[3] = element->old_final_alignment.transform_scaled(element->old_initial_alignment
1808 . scaled_inverse_transform(offset.get_current_element().transform_scaled(point( 0 , offset.scaled_width()))));
1810 new_offset.gpt_set(p);
1814 ui::get()->set_offset(offset);
1817 return new_offset;
1820 static void test_global(diff_stat_t *here, scale_cluster si, transformation t,
1821 int local_ax_count, int m, ale_pos local_gs_mo, ale_pos perturb) {
1823 diff_stat_t test(*here);
1825 test.diff(si, perturb, t, local_ax_count, m);
1827 unsigned int ovl = overlap(si, t, local_ax_count);
1829 if (ovl >= local_gs_mo && test.better()) {
1830 test.confirm();
1831 *here = test;
1832 ui::get()->set_match(here->get_error());
1833 ui::get()->set_offset(here->get_offset());
1834 } else {
1835 test.discard();
1841 * Get the set of global transformations for a given density
1843 static void test_globals(diff_stat_t *here,
1844 scale_cluster si, transformation t, int local_gs, ale_pos adj_p,
1845 int local_ax_count, int m, ale_pos local_gs_mo, ale_pos perturb) {
1847 transformation offset = t;
1849 point min, max;
1851 transformation offset_p = offset;
1853 if (!offset_p.is_projective())
1854 offset_p.eu_to_gpt();
1856 min = max = offset_p.gpt_get(0);
1857 for (int p_index = 1; p_index < 4; p_index++) {
1858 point p = offset_p.gpt_get(p_index);
1859 if (p[0] < min[0])
1860 min[0] = p[0];
1861 if (p[1] < min[1])
1862 min[1] = p[1];
1863 if (p[0] > max[0])
1864 max[0] = p[0];
1865 if (p[1] > max[1])
1866 max[1] = p[1];
1869 point inner_min_t = -min;
1870 point inner_max_t = -max + point(si.accum->height(), si.accum->width());
1871 point outer_min_t = -max + point(adj_p - 1, adj_p - 1);
1872 point outer_max_t = point(si.accum->height(), si.accum->width()) - point(adj_p, adj_p);
1874 if (local_gs == 1 || local_gs == 3 || local_gs == 4 || local_gs == 6) {
1877 * Inner
1880 for (ale_pos i = inner_min_t[0]; i <= inner_max_t[0]; i += adj_p)
1881 for (ale_pos j = inner_min_t[1]; j <= inner_max_t[1]; j += adj_p) {
1882 transformation test_t = offset;
1883 test_t.translate(point(i, j));
1884 test_global(here, si, test_t, local_ax_count, m, local_gs_mo, perturb);
1888 if (local_gs == 2 || local_gs == 3 || local_gs == -1 || local_gs == 6) {
1891 * Outer
1894 for (ale_pos i = outer_min_t[0]; i <= outer_max_t[0]; i += adj_p)
1895 for (ale_pos j = outer_min_t[1]; j < inner_min_t[1]; j += adj_p) {
1896 transformation test_t = offset;
1897 test_t.translate(point(i, j));
1898 test_global(here, si, test_t, local_ax_count, m, local_gs_mo, perturb);
1900 for (ale_pos i = outer_min_t[0]; i <= outer_max_t[0]; i += adj_p)
1901 for (ale_pos j = outer_max_t[1]; j > inner_max_t[1]; j -= adj_p) {
1902 transformation test_t = offset;
1903 test_t.translate(point(i, j));
1904 test_global(here, si, test_t, local_ax_count, m, local_gs_mo, perturb);
1906 for (ale_pos i = outer_min_t[0]; i < inner_min_t[0]; i += adj_p)
1907 for (ale_pos j = outer_min_t[1]; j <= outer_max_t[1]; j += adj_p) {
1908 transformation test_t = offset;
1909 test_t.translate(point(i, j));
1910 test_global(here, si, test_t, local_ax_count, m, local_gs_mo, perturb);
1912 for (ale_pos i = outer_max_t[0]; i > inner_max_t[0]; i -= adj_p)
1913 for (ale_pos j = outer_min_t[1]; j <= outer_max_t[1]; j += adj_p) {
1914 transformation test_t = offset;
1915 test_t.translate(point(i, j));
1916 test_global(here, si, test_t, local_ax_count, m, local_gs_mo, perturb);
1921 static void get_translational_set(std::vector<transformation> *set,
1922 transformation t, ale_pos adj_p) {
1924 ale_pos adj_s;
1926 transformation offset = t;
1927 transformation test_t;
1929 for (int i = 0; i < 2; i++)
1930 for (adj_s = -adj_p; adj_s <= adj_p; adj_s += 2 * adj_p) {
1932 test_t = offset;
1934 test_t.translate(i ? point(adj_s, 0) : point(0, adj_s));
1936 set->push_back(test_t);
1940 static int threshold_ok(ale_accum error) {
1941 if ((1 - error) * 100 >= match_threshold)
1942 return 1;
1944 if (!(match_threshold >= 0))
1945 return 1;
1947 return 0;
1951 * Align frame m against the reference.
1953 * XXX: the transformation class currently combines ordinary
1954 * transformations with scaling. This is somewhat convenient for
1955 * some things, but can also be confusing. This method, _align(), is
1956 * one case where special care must be taken to ensure that the scale
1957 * is always set correctly (by using the 'rescale' method).
1959 static diff_stat_t _align(int m, int local_gs, element_t *element) {
1961 const image *input_frame = element->input_frame;
1964 * Local upper/lower data, possibly dependent on image
1965 * dimensions.
1968 ale_pos local_lower, local_upper, local_gs_mo;
1971 * Select the minimum dimension as the reference.
1974 ale_pos reference_size = input_frame->height();
1975 if (input_frame->width() < reference_size)
1976 reference_size = input_frame->width();
1977 ale_pos reference_area = input_frame->height()
1978 * input_frame->width();
1980 if (perturb_lower_percent)
1981 local_lower = perturb_lower
1982 * reference_size
1983 * 0.01
1984 * scale_factor;
1985 else
1986 local_lower = perturb_lower;
1988 if (perturb_upper_percent)
1989 local_upper = perturb_upper
1990 * reference_size
1991 * 0.01
1992 * scale_factor;
1993 else
1994 local_upper = perturb_upper;
1996 local_upper = pow(2, floor(log(local_upper) / log(2)));
1998 if (gs_mo_percent)
1999 local_gs_mo = _gs_mo
2000 * reference_area
2001 * 0.01
2002 * scale_factor;
2003 else
2004 local_gs_mo = _gs_mo;
2007 * Logarithms aren't exact, so we divide repeatedly to discover
2008 * how many steps will occur, and pass this information to the
2009 * user interface.
2012 int step_count = 0;
2013 double step_variable = local_upper;
2014 while (step_variable >= local_lower) {
2015 step_variable /= 2;
2016 step_count++;
2019 ui::get()->set_steps(step_count);
2021 ale_pos perturb = local_upper;
2022 int lod;
2024 if (_keep) {
2025 kept_t[latest] = latest_t;
2026 kept_ok[latest] = latest_ok;
2030 * Maximum level-of-detail. Use a level of detail at most
2031 * 2^lod_diff finer than the adjustment resolution. lod_diff
2032 * is a synonym for lod_max.
2035 const int lod_diff = lod_max;
2038 * Determine how many levels of detail should be prepared.
2042 * Plain (unsigned int) casting seems to be broken in some cases.
2045 unsigned int steps = (perturb > pow(2, lod_max))
2046 ? (unsigned int) lrint(log(perturb) / log(2)) - lod_max + 1 : 1;
2049 * Prepare multiple levels of detail.
2052 int local_ax_count;
2053 struct scale_cluster *scale_clusters = init_clusters(m,
2054 scale_factor, input_frame, steps,
2055 &local_ax_count);
2058 * Initialize variables used in the main loop.
2061 lod = (steps - 1);
2064 * Initialize the default initial transform
2067 if (default_initial_alignment_type == 0) {
2070 * Follow the transformation of the original frame,
2071 * setting new image dimensions.
2074 // element->default_initial_alignment = orig_t;
2075 element->default_initial_alignment.set_current_element(orig_t.get_element(0));
2076 element->default_initial_alignment.set_dimensions(input_frame);
2078 } else if (default_initial_alignment_type == 1)
2081 * Follow previous transformation, setting new image
2082 * dimensions.
2085 element->default_initial_alignment.set_dimensions(input_frame);
2087 else
2088 assert(0);
2090 element->old_is_default = element->is_default;
2093 * Scale default initial transform for lod
2096 element->default_initial_alignment.rescale(1 / pow(2, lod));
2099 * Set the default transformation.
2102 transformation offset = element->default_initial_alignment;
2105 * Load any file-specified transformations
2108 for (offset.set_current_index(0);
2109 offset.get_current_index() < _ma_card;
2110 offset.push_element()) {
2112 offset = tload_next(tload, alignment_class == 2,
2113 offset,
2114 &element->is_default,
2115 offset.get_current_index() == 0);
2119 offset.set_current_index(0);
2121 ui::get()->set_offset(offset);
2123 if (perturb > 0) {
2126 * Apply following logic
2129 transformation new_offset = follow(element, offset, lod);
2131 new_offset.set_current_index(0);
2133 element->old_initial_alignment = offset;
2134 element->old_lod = lod;
2135 offset = new_offset;
2137 } else {
2138 element->old_initial_alignment = offset;
2139 element->old_lod = lod;
2142 struct scale_cluster si = scale_clusters[lod];
2145 * Projective adjustment value
2148 ale_pos adj_p = (perturb >= pow(2, lod_diff))
2149 ? pow(2, lod_diff) : (double) perturb;
2152 * Orientational adjustment value in degrees.
2154 * Since rotational perturbation is now specified as an
2155 * arclength, we have to convert.
2158 ale_pos adj_o = 2 * perturb
2159 / sqrt(pow(scale_clusters[0].input->height(), 2)
2160 + pow(scale_clusters[0].input->width(), 2))
2161 * 180
2162 / M_PI;
2165 * Barrel distortion adjustment value
2168 ale_pos adj_b = perturb * bda_mult;
2171 * Global search overlap requirements.
2174 local_gs_mo /= pow(pow(2, lod), 2);
2177 * Pre-alignment exposure adjustment
2180 if (_exp_register) {
2181 ui::get()->exposure_1();
2182 transformation o = offset;
2183 for (int k = lod; k > 0; k--)
2184 o.rescale(2);
2185 set_exposure_ratio(m, scale_clusters[0], o, local_ax_count, 0);
2189 * Alignment statistics.
2192 diff_stat_t here;
2195 * Current difference (error) value
2198 ui::get()->prematching();
2199 here.diff(si, perturb, offset, local_ax_count, m);
2200 ui::get()->set_match(here.get_error());
2203 * Current and modified barrel distortion parameters
2206 ale_pos current_bd[BARREL_DEGREE];
2207 ale_pos modified_bd[BARREL_DEGREE];
2208 offset.bd_get(current_bd);
2209 offset.bd_get(modified_bd);
2212 * Translational global search step
2215 if (perturb >= local_lower && local_gs != 0 && local_gs != 5
2216 && (local_gs != 6 || element->is_default)) {
2218 ui::get()->global_alignment(perturb, lod);
2219 ui::get()->gs_mo(local_gs_mo);
2221 test_globals(&here, si, here.get_offset(), local_gs, adj_p,
2222 local_ax_count, m, local_gs_mo, perturb);
2224 ui::get()->set_match(here.get_error());
2225 ui::get()->set_offset(here.get_offset());
2229 * Control point alignment
2232 if (local_gs == 5) {
2234 transformation o = here.get_offset();
2236 for (int k = lod; k > 0; k--)
2237 o.rescale(2);
2240 * Determine centroid data
2243 point current, previous;
2244 centroids(m, &current, &previous);
2246 if (current.defined() && previous.defined()) {
2247 o = orig_t;
2248 o.set_dimensions(input_frame);
2249 o.translate((previous - current) * o.scale());
2250 current = previous;
2254 * Determine rotation for alignment classes other than translation.
2257 ale_accum lowest_error = cp_rms_error(m, o);
2259 ale_pos rot_lower = 2 * local_lower
2260 / sqrt(pow(scale_clusters[0].input->height(), 2)
2261 + pow(scale_clusters[0].input->width(), 2))
2262 * 180
2263 / M_PI;
2265 if (alignment_class > 0)
2266 for (ale_pos rot = 30; rot > rot_lower; rot /= 2)
2267 for (ale_pos srot = -rot; srot < rot * 1.5; srot += rot * 2) {
2268 int is_improved = 1;
2269 while (is_improved) {
2270 is_improved = 0;
2271 transformation test_t = o;
2273 * XXX: is this right?
2275 test_t.rotate(current * o.scale(), srot);
2276 ale_pos test_v = cp_rms_error(m, test_t);
2278 if (test_v < lowest_error) {
2279 lowest_error = test_v;
2280 o = test_t;
2281 srot += 3 * rot;
2282 is_improved = 1;
2288 * Determine projective parameters through a local
2289 * minimum search.
2292 if (alignment_class == 2) {
2293 ale_accum adj_p = lowest_error;
2295 if (adj_p < local_lower)
2296 adj_p = local_lower;
2298 while (adj_p >= local_lower) {
2299 transformation test_t = o;
2300 int is_improved = 1;
2301 ale_accum test_v;
2302 ale_accum adj_s;
2304 while (is_improved) {
2305 is_improved = 0;
2307 for (int i = 0; i < 4; i++)
2308 for (int j = 0; j < 2; j++)
2309 for (adj_s = -adj_p; adj_s <= adj_p; adj_s += 2 * adj_p) {
2311 test_t = o;
2313 if (perturb_type == 0)
2314 test_t.gpt_modify(j, i, adj_s);
2315 else if (perturb_type == 1)
2316 test_t.gr_modify(j, i, adj_s);
2317 else
2318 assert(0);
2320 test_v = cp_rms_error(m, test_t);
2322 if (test_v < lowest_error) {
2323 lowest_error = test_v;
2324 o = test_t;
2325 adj_s += 3 * adj_p;
2326 is_improved = 1;
2330 adj_p /= 2;
2334 if (_exp_register)
2335 set_exposure_ratio(m, scale_clusters[0], o, local_ax_count, 0);
2337 for (int k = lod; k > 0; k--)
2338 o.rescale(0.5);
2340 here.diff(si, perturb, o, local_ax_count, m);
2341 here.confirm();
2342 ui::get()->set_match(here.get_error());
2343 ui::get()->set_offset(here.get_offset());
2347 * Announce perturbation size
2350 ui::get()->aligning(perturb, lod);
2353 * Run initial tests to get perturbation multipliers and error
2354 * centroids.
2357 std::vector<transformation> t_set;
2359 here.get_perturb_set(&t_set, adj_p, adj_o, adj_b, current_bd, modified_bd);
2362 * Perturbation adjustment loop.
2365 int stable_count = 0;
2367 while (perturb >= local_lower) {
2370 * Orientational adjustment value in degrees.
2372 * Since rotational perturbation is now specified as an
2373 * arclength, we have to convert.
2376 ale_pos adj_o = 2 * perturb
2377 / sqrt(pow(scale_clusters[0].input->height(), 2)
2378 + pow(scale_clusters[0].input->width(), 2))
2379 * 180
2380 / M_PI;
2383 * Barrel distortion adjustment value
2386 ale_pos adj_b = perturb * bda_mult;
2388 diff_stat_t old_here = here;
2390 here.perturb_test(perturb, adj_p, adj_o, adj_b, current_bd, modified_bd,
2391 stable_count);
2393 if (here.get_offset() == old_here.get_offset())
2394 stable_count++;
2395 else
2396 stable_count = 0;
2398 if (stable_count == 3) {
2400 stable_count = 0;
2402 here.calculate_element_region();
2404 if (here.get_current_index() + 1 < _ma_card) {
2405 here.push_element();
2406 here.make_element_nontrivial(adj_p, adj_o);
2407 element->is_primary = 0;
2408 } else {
2410 here.set_current_index(0);
2412 element->is_primary = 1;
2414 perturb *= 0.5;
2416 if (lod > 0) {
2419 * Work with images twice as large
2422 lod--;
2423 si = scale_clusters[lod];
2426 * Rescale the transforms.
2429 here.rescale(2, si);
2430 element->default_initial_alignment.rescale(2);
2432 } else {
2433 adj_p = perturb;
2437 * Announce changes
2440 ui::get()->alignment_perturbation_level(perturb, lod);
2445 ui::get()->set_match(here.get_error());
2446 ui::get()->set_offset(here.get_offset());
2449 here.set_current_index(0);
2451 offset = here.get_offset();
2453 if (lod > 0) {
2454 here.rescale(pow(2, lod), scale_clusters[0]);
2455 element->default_initial_alignment.rescale(pow(2, lod));
2459 * Post-alignment exposure adjustment
2462 if (_exp_register == 1) {
2463 ui::get()->exposure_2();
2464 set_exposure_ratio(m, scale_clusters[0], offset, local_ax_count, 1);
2468 * Recalculate error
2471 ui::get()->postmatching();
2472 offset.use_full_support();
2473 here.diff(scale_clusters[0], perturb, offset, local_ax_count, m);
2474 here.confirm();
2475 offset.use_restricted_support();
2476 ui::get()->set_match(here.get_error());
2479 * Free the level-of-detail structures
2482 final_clusters(scale_clusters, scale_factor, steps);
2485 * Ensure that the match meets the threshold.
2488 if (threshold_ok(here.get_error())) {
2490 * Update alignment variables
2492 latest_ok = 1;
2493 element->default_initial_alignment = offset;
2494 element->old_final_alignment = offset;
2495 ui::get()->alignment_match_ok();
2496 } else if (local_gs == 4) {
2499 * Align with outer starting points.
2503 * XXX: This probably isn't exactly the right thing to do,
2504 * since variables like old_initial_value have been overwritten.
2507 diff_stat_t nested_result = _align(m, -1, element);
2509 if (threshold_ok(nested_result.get_error())) {
2510 return nested_result;
2511 } else if (nested_result.get_error() < here.get_error()) {
2512 here = nested_result;
2515 if (is_fail_default)
2516 offset = element->default_initial_alignment;
2518 ui::get()->set_match(here.get_error());
2519 ui::get()->alignment_no_match();
2521 } else if (local_gs == -1) {
2523 latest_ok = 0;
2524 latest_t = offset;
2525 return here;
2527 } else {
2528 if (is_fail_default)
2529 offset = element->default_initial_alignment;
2530 latest_ok = 0;
2531 ui::get()->alignment_no_match();
2535 * Write the tonal registration multiplier as a comment.
2538 pixel trm = image_rw::exp(m).get_multiplier();
2539 tsave_trm(tsave, trm[0], trm[1], trm[2]);
2542 * Save the transformation information
2545 for (offset.set_current_index(0);
2546 offset.get_current_index() < _ma_card;
2547 offset.push_element()) {
2549 tsave_next(tsave, offset, alignment_class == 2,
2550 offset.get_current_index() == 0);
2553 offset.set_current_index(0);
2555 latest_t = offset;
2558 * Update match statistics.
2561 match_sum += (1 - here.get_error()) * 100;
2562 match_count++;
2563 latest = m;
2565 return here;
2568 #ifdef USE_FFTW
2570 * High-pass filter for frequency weights
2572 static void hipass(int rows, int cols, fftw_complex *inout) {
2573 for (int i = 0; i < rows * vert_freq_cut; i++)
2574 for (int j = 0; j < cols; j++)
2575 for (int k = 0; k < 2; k++)
2576 inout[i * cols + j][k] = 0;
2577 for (int i = 0; i < rows; i++)
2578 for (int j = 0; j < cols * horiz_freq_cut; j++)
2579 for (int k = 0; k < 2; k++)
2580 inout[i * cols + j][k] = 0;
2581 for (int i = 0; i < rows; i++)
2582 for (int j = 0; j < cols; j++)
2583 for (int k = 0; k < 2; k++)
2584 if (i / (double) rows + j / (double) cols < 2 * avg_freq_cut)
2585 inout[i * cols + j][k] = 0;
2587 #endif
2591 * Reset alignment weights
2593 static void reset_weights() {
2595 alignment_weights_const = NULL;
2597 if (alignment_weights != NULL)
2598 delete alignment_weights;
2600 alignment_weights = NULL;
2604 * Initialize alignment weights
2606 static void init_weights() {
2607 if (alignment_weights != NULL)
2608 return;
2610 int rows = reference_image->height();
2611 int cols = reference_image->width();
2612 int colors = reference_image->depth();
2614 alignment_weights = new image_ale_real(rows, cols,
2615 colors, "alignment_weights");
2617 assert (alignment_weights);
2619 for (int i = 0; i < rows; i++)
2620 for (int j = 0; j < cols; j++)
2621 alignment_weights->set_pixel(i, j, pixel(1, 1, 1));
2625 * Update alignment weights with weight map
2627 static void map_update() {
2629 if (weight_map == NULL)
2630 return;
2632 init_weights();
2634 point map_offset = reference_image->offset() - weight_map->offset();
2636 int rows = reference_image->height();
2637 int cols = reference_image->width();
2639 for (int i = 0; i < rows; i++)
2640 for (int j = 0; j < cols; j++) {
2641 point map_weight_position = map_offset + point(i, j);
2642 if (map_weight_position[0] >= 0
2643 && map_weight_position[1] >= 0
2644 && map_weight_position[0] <= weight_map->height() - 1
2645 && map_weight_position[1] <= weight_map->width() - 1)
2646 alignment_weights->pix(i, j) *= weight_map->get_bl(map_weight_position);
2651 * Update alignment weights with an internal weight map, reflecting a
2652 * summation of certainty values. Use existing memory structures if
2653 * possible. This function updates alignment_weights_const; hence, it
2654 * should not be called prior to any functions that modify the
2655 * alignment_weights structure.
2657 static void imap_update() {
2658 if (alignment_weights == NULL) {
2659 alignment_weights_const = reference_defined;
2660 } else {
2661 int rows = reference_image->height();
2662 int cols = reference_image->width();
2664 for (int i = 0; i < rows; i++)
2665 for (int j = 0; j < cols; j++)
2666 alignment_weights->pix(i, j) *= reference_defined->get_pixel(i, j);
2668 alignment_weights_const = alignment_weights;
2673 * Update alignment weights with algorithmic weights
2675 static void wmx_update() {
2676 #ifdef USE_UNIX
2678 static exposure *exp_def = new exposure_default();
2679 static exposure *exp_bool = new exposure_boolean();
2681 if (wmx_file == NULL || wmx_exec == NULL || wmx_defs == NULL)
2682 return;
2684 unsigned int rows = reference_image->height();
2685 unsigned int cols = reference_image->width();
2687 image_rw::write_image(wmx_file, reference_image);
2688 image_rw::write_image(wmx_defs, reference_defined, exp_bool);
2690 /* execute ... */
2691 int exit_status = 1;
2692 if (!fork()) {
2693 execlp(wmx_exec, wmx_exec, wmx_file, wmx_defs, NULL);
2694 ui::get()->exec_failure(wmx_exec, wmx_file, wmx_defs);
2697 wait(&exit_status);
2699 if (exit_status)
2700 ui::get()->fork_failure("d2::align");
2702 image *wmx_weights = image_rw::read_image(wmx_file, exp_def);
2704 if (wmx_weights->height() != rows || wmx_weights->width() != cols)
2705 ui::get()->error("algorithmic weighting must not change image size");
2707 if (alignment_weights == NULL)
2708 alignment_weights = wmx_weights;
2709 else
2710 for (unsigned int i = 0; i < rows; i++)
2711 for (unsigned int j = 0; j < cols; j++)
2712 alignment_weights->pix(i, j) *= wmx_weights->pix(i, j);
2713 #endif
2717 * Update alignment weights with frequency weights
2719 static void fw_update() {
2720 #ifdef USE_FFTW
2721 if (horiz_freq_cut == 0
2722 && vert_freq_cut == 0
2723 && avg_freq_cut == 0)
2724 return;
2727 * Required for correct operation of --fwshow
2730 assert (alignment_weights == NULL);
2732 int rows = reference_image->height();
2733 int cols = reference_image->width();
2734 int colors = reference_image->depth();
2736 alignment_weights = new image_ale_real(rows, cols,
2737 colors, "alignment_weights");
2739 fftw_complex *inout = (fftw_complex *) fftw_malloc(sizeof(fftw_complex) * rows * cols);
2741 assert (inout);
2743 fftw_plan pf = fftw_plan_dft_2d(rows, cols,
2744 inout, inout,
2745 FFTW_FORWARD, FFTW_ESTIMATE);
2747 fftw_plan pb = fftw_plan_dft_2d(rows, cols,
2748 inout, inout,
2749 FFTW_BACKWARD, FFTW_ESTIMATE);
2751 for (int k = 0; k < colors; k++) {
2752 for (int i = 0; i < rows * cols; i++) {
2753 inout[i][0] = reference_image->get_pixel(i / cols, i % cols)[k];
2754 inout[i][1] = 0;
2757 fftw_execute(pf);
2758 hipass(rows, cols, inout);
2759 fftw_execute(pb);
2761 for (int i = 0; i < rows * cols; i++) {
2762 #if 0
2763 alignment_weights->pix(i / cols, i % cols)[k] = fabs(inout[i][0] / (rows * cols));
2764 #else
2765 alignment_weights->pix(i / cols, i % cols)[k] =
2766 sqrt(pow(inout[i][0] / (rows * cols), 2)
2767 + pow(inout[i][1] / (rows * cols), 2));
2768 #endif
2772 fftw_destroy_plan(pf);
2773 fftw_destroy_plan(pb);
2774 fftw_free(inout);
2776 if (fw_output != NULL)
2777 image_rw::write_image(fw_output, alignment_weights);
2778 #endif
2782 * Update alignment to frame N.
2784 static void update_to(int n) {
2786 assert (n <= latest + 1);
2787 assert (n >= 0);
2789 static std::vector<element_t> elements;
2791 if (latest < 0) {
2793 elements.resize(1);
2796 * Handle the initial frame
2799 elements[0].input_frame = image_rw::open(n);
2801 const image *i = elements[0].input_frame;
2802 int is_default;
2803 transformation result = alignment_class == 2
2804 ? transformation::gpt_identity(i, scale_factor)
2805 : transformation::eu_identity(i, scale_factor);
2806 result = tload_first(tload, alignment_class == 2, result, &is_default);
2807 tsave_first(tsave, result, alignment_class == 2);
2809 if (_keep > 0) {
2810 kept_t = new transformation[image_rw::count()];
2811 kept_ok = (int *) malloc(image_rw::count()
2812 * sizeof(int));
2813 assert (kept_t);
2814 assert (kept_ok);
2816 if (!kept_t || !kept_ok)
2817 ui::get()->memory_error("alignment");
2819 kept_ok[0] = 1;
2820 kept_t[0] = result;
2823 latest = 0;
2824 latest_ok = 1;
2825 latest_t = result;
2827 elements[0].default_initial_alignment = result;
2828 orig_t = result;
2830 image_rw::close(n);
2833 for (int i = latest + 1; i <= n; i++) {
2834 int j = 0;
2837 * Handle supplemental frames.
2840 assert (reference != NULL);
2842 ui::get()->set_arender_current();
2843 reference->sync(i - 1);
2844 ui::get()->clear_arender_current();
2845 reference_image = reference->get_image();
2846 reference_defined = reference->get_defined();
2848 reset_weights();
2849 fw_update();
2850 wmx_update();
2851 map_update();
2853 if (certainty_weights)
2854 imap_update(); /* Must be called after all other _updates */
2856 assert (reference_image != NULL);
2857 assert (reference_defined != NULL);
2859 elements[j].input_frame = image_rw::open(i);
2860 elements[j].is_primary = 1;
2862 _align(i, _gs, &elements[j]);
2864 image_rw::close(n);
2867 if (elements.size() > _ma_card)
2868 elements.resize(_ma_card);
2871 public:
2874 * Set the control point count
2876 static void set_cp_count(unsigned int n) {
2877 assert (cp_array == NULL);
2879 cp_count = n;
2880 cp_array = (const point **) malloc(n * sizeof(const point *));
2884 * Set control points.
2886 static void set_cp(unsigned int i, const point *p) {
2887 cp_array[i] = p;
2891 * Register exposure
2893 static void exp_register() {
2894 _exp_register = 1;
2898 * Register exposure only based on metadata
2900 static void exp_meta_only() {
2901 _exp_register = 2;
2905 * Don't register exposure
2907 static void exp_noregister() {
2908 _exp_register = 0;
2912 * Set alignment class to translation only. Only adjust the x and y
2913 * position of images. Do not rotate input images or perform
2914 * projective transformations.
2916 static void class_translation() {
2917 alignment_class = 0;
2921 * Set alignment class to Euclidean transformations only. Adjust the x
2922 * and y position of images and the orientation of the image about the
2923 * image center.
2925 static void class_euclidean() {
2926 alignment_class = 1;
2930 * Set alignment class to perform general projective transformations.
2931 * See the file gpt.h for more information about general projective
2932 * transformations.
2934 static void class_projective() {
2935 alignment_class = 2;
2939 * Set the default initial alignment to the identity transformation.
2941 static void initial_default_identity() {
2942 default_initial_alignment_type = 0;
2946 * Set the default initial alignment to the most recently matched
2947 * frame's final transformation.
2949 static void initial_default_follow() {
2950 default_initial_alignment_type = 1;
2954 * Perturb output coordinates.
2956 static void perturb_output() {
2957 perturb_type = 0;
2961 * Perturb source coordinates.
2963 static void perturb_source() {
2964 perturb_type = 1;
2968 * Frames under threshold align optimally
2970 static void fail_optimal() {
2971 is_fail_default = 0;
2975 * Frames under threshold keep their default alignments.
2977 static void fail_default() {
2978 is_fail_default = 1;
2982 * Align images with an error contribution from each color channel.
2984 static void all() {
2985 channel_alignment_type = 0;
2989 * Align images with an error contribution only from the green channel.
2990 * Other color channels do not affect alignment.
2992 static void green() {
2993 channel_alignment_type = 1;
2997 * Align images using a summation of channels. May be useful when
2998 * dealing with images that have high frequency color ripples due to
2999 * color aliasing.
3001 static void sum() {
3002 channel_alignment_type = 2;
3006 * Error metric exponent
3009 static void set_metric_exponent(float me) {
3010 metric_exponent = me;
3014 * Match threshold
3017 static void set_match_threshold(float mt) {
3018 match_threshold = mt;
3022 * Perturbation lower and upper bounds.
3025 static void set_perturb_lower(ale_pos pl, int plp) {
3026 perturb_lower = pl;
3027 perturb_lower_percent = plp;
3030 static void set_perturb_upper(ale_pos pu, int pup) {
3031 perturb_upper = pu;
3032 perturb_upper_percent = pup;
3036 * Maximum rotational perturbation.
3039 static void set_rot_max(int rm) {
3042 * Obtain the largest power of two not larger than rm.
3045 rot_max = pow(2, floor(log(rm) / log(2)));
3049 * Barrel distortion adjustment multiplier
3052 static void set_bda_mult(ale_pos m) {
3053 bda_mult = m;
3057 * Barrel distortion maximum rate of change
3060 static void set_bda_rate(ale_pos m) {
3061 bda_rate = m;
3065 * Level-of-detail
3068 static void set_lod_max(int lm) {
3069 lod_max = lm;
3073 * Set the scale factor
3075 static void set_scale(ale_pos s) {
3076 scale_factor = s;
3080 * Set reference rendering to align against
3082 static void set_reference(render *r) {
3083 reference = r;
3087 * Set the interpolant
3089 static void set_interpolant(filter::scaled_filter *f) {
3090 interpolant = f;
3094 * Set alignment weights image
3096 static void set_weight_map(const image *i) {
3097 weight_map = i;
3101 * Set frequency cuts
3103 static void set_frequency_cut(double h, double v, double a) {
3104 horiz_freq_cut = h;
3105 vert_freq_cut = v;
3106 avg_freq_cut = a;
3110 * Set algorithmic alignment weighting
3112 static void set_wmx(const char *e, const char *f, const char *d) {
3113 wmx_exec = e;
3114 wmx_file = f;
3115 wmx_defs = d;
3119 * Show frequency weights
3121 static void set_fl_show(const char *filename) {
3122 fw_output = filename;
3126 * Set transformation file loader.
3128 static void set_tload(tload_t *tl) {
3129 tload = tl;
3133 * Set transformation file saver.
3135 static void set_tsave(tsave_t *ts) {
3136 tsave = ts;
3140 * Get match statistics for frame N.
3142 static int match(int n) {
3143 update_to(n);
3145 if (n == latest)
3146 return latest_ok;
3147 else if (_keep)
3148 return kept_ok[n];
3149 else {
3150 assert(0);
3151 exit(1);
3156 * Message that old alignment data should be kept.
3158 static void keep() {
3159 assert (latest == -1);
3160 _keep = 1;
3164 * Get alignment for frame N.
3166 static transformation of(int n) {
3167 update_to(n);
3168 if (n == latest)
3169 return latest_t;
3170 else if (_keep)
3171 return kept_t[n];
3172 else {
3173 assert(0);
3174 exit(1);
3179 * Set the certainty-weighted flag.
3181 static void certainty_weighted(int flag) {
3182 certainty_weights = flag;
3186 * Set the global search type.
3188 static void gs(const char *type) {
3189 if (!strcmp(type, "local")) {
3190 _gs = 0;
3191 } else if (!strcmp(type, "inner")) {
3192 _gs = 1;
3193 } else if (!strcmp(type, "outer")) {
3194 _gs = 2;
3195 } else if (!strcmp(type, "all")) {
3196 _gs = 3;
3197 } else if (!strcmp(type, "central")) {
3198 _gs = 4;
3199 } else if (!strcmp(type, "defaults")) {
3200 _gs = 6;
3201 } else if (!strcmp(type, "points")) {
3202 _gs = 5;
3203 keep();
3204 } else {
3205 ui::get()->error("bad global search type");
3210 * Multi-alignment contiguity
3212 static void ma_cont(double value) {
3213 _ma_cont = value;
3217 * Multi-alignment cardinality
3219 static void ma_card(unsigned int value) {
3220 assert (value >= 1);
3221 _ma_card = value;
3225 * Set the minimum overlap for global searching
3227 static void gs_mo(ale_pos value, int _gs_mo_percent) {
3228 _gs_mo = value;
3229 gs_mo_percent = _gs_mo_percent;
3233 * Set alignment exclusion regions
3235 static void set_exclusion(exclusion *_ax_parameters, int _ax_count) {
3236 ax_count = _ax_count;
3237 ax_parameters = _ax_parameters;
3241 * Get match summary statistics.
3243 static ale_accum match_summary() {
3244 return match_sum / match_count;
3248 #endif