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.
29 #include "transformation.h"
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
54 static transformation
*kept_t
;
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.
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
;
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
;
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
193 int is_default
, old_is_default
;
196 transformation old_initial_alignment
;
197 transformation old_final_alignment
;
198 transformation default_initial_alignment
;
199 const image
*input_frame
;
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
;
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
;
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.
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.
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
;
334 static exclusion
*ax_parameters
;
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
;
351 const image
*input_max
;
352 const image
*input_min
;
355 struct scale_cluster
{
357 const image
*defined
;
358 const image
*aweight
;
359 exclusion
*ax_parameters
;
364 nl_scale_cluster
*nl_scale_clusters
;
368 * Check for exclusion region coverage in the reference
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])
384 * Check for exclusion region coverage in the input
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])
400 * Overlap function. Determines the number of pixels in areas where
401 * the arrays overlap. Uses the reference array's notion of pixel
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
))
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]));
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
))
446 && ti
<= c
.input
->height() - 1
448 && tj
<= c
.input
->width() - 1
449 && c
.defined
->get_pixel(i
, j
)[0] != 0)
457 * Calculate the region associated with the current multi-alignment
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
);
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
))
480 while ((q
= t
->get_query_point((int) (i
+ offset
[0]),
481 (int) (j
+ offset
[1]))).defined()) {
486 if (input_excluded(ti
, tj
, si
.ax_parameters
, ax_count
))
490 && ti
<= si
.input
->height() - 1
492 && tj
<= si
.input
->width() - 1
493 && si
.defined
->get_pixel(i
, j
)[0] != 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
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
525 transformation offset
;
532 ale_accum centroid
[2], centroid_divisor
;
533 ale_accum de_centroid
[2], de_centroid_v
, de_sum
;
540 min
= point::posinf();
541 max
= point::neginf();
545 centroid_divisor
= 0;
555 void init(transformation _offset
, ale_pos _perturb
) {
562 * Required for STL sanity.
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() {
595 init(_run
.offset
, _run
.perturb
);
603 run
&operator=(const run
&_run
) {
608 init(_run
.offset
, _run
.perturb
);
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
);
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
);
645 c
.input
->get_bl(t
, result
);
647 weight
[0] = result
[1];
652 c
.input
->get_bl(u
, result
);
654 weight
[1] = result
[1];
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
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.
726 for (int k
= 0; k
< 3; 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
);
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
);
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));
771 point
get_error_centroid() {
772 point result
= point(de_centroid
[0] / de_sum
, de_centroid
[1] / de_sum
);
777 ale_pos
get_error_perturb() {
778 ale_pos result
= de_centroid_v
/ de_sum
;
786 * When non-empty, runs.front() is best, runs.back() is
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
;
807 int i_min
, i_max
, j_min
, j_max
;
811 int get_current_index() const {
813 return runs
[0].offset
.get_current_index();
816 struct scale_cluster si
;
820 std::vector
<ale_pos
> perturb_multipliers
;
823 void diff(struct scale_cluster c
, ale_pos perturb
,
825 int _ax_count
, int f
) {
827 if (runs
.size() == 2)
830 runs
.push_back(run(t
, perturb
));
833 ax_count
= _ax_count
;
836 ui::get()->d2_align_sample_start();
838 if (interpolant
!= NULL
)
839 interpolant
->set_parameters(t
, c
.input
, c
.accum
->offset());
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
);
852 subdomain_args
*args
= new subdomain_args
[N
];
854 for (int ti
= 0; ti
< N
; ti
++) {
856 args
[ti
].runs
= runs
;
857 args
[ti
].ax_count
= ax_count
;
859 args
[ti
].i_min
= (c
.accum
->height() * ti
) / N
;
860 args
[ti
].i_max
= (c
.accum
->height() * (ti
+ 1)) / N
;
862 args
[ti
].j_max
= c
.accum
->width();
863 args
[ti
].subdomain
= ti
;
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
]);
869 diff_subdomain(&args
[ti
]);
873 for (int ti
= 0; ti
< N
; ti
++) {
875 pthread_join(threads
[ti
], NULL
);
877 runs
.back().add(args
[ti
].runs
.back());
882 ui::get()->d2_align_sample_stop();
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
);
898 for (unsigned int r
= 0; r
< t_array
.size(); r
++)
899 diff(si
, p_array
[r
], t_array
[r
], ax_count
, frame
);
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);
931 runs
[0].rescale(scale
);
936 void push_element() {
937 assert(runs
.size() == 1);
939 runs
[0].offset
.push_element();
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
);
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
);
967 diff_stat_t
&operator=(const diff_stat_t
&dst
) {
969 * Copy run information.
972 old_runs
= dst
.old_runs
;
975 * Copy diff variables
978 ax_count
= dst
.ax_count
;
980 perturb_multipliers
= dst
.perturb_multipliers
;
985 diff_stat_t(const diff_stat_t
&dst
) : runs(), old_runs(),
986 perturb_multipliers() {
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
¶m
) {
1006 return (get_error() != param
.get_error());
1009 int operator==(diff_stat_t
¶m
) {
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();
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
)
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
))
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(),
1103 test_t
.rotate(centroid
+ si
.accum
->offset(),
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
);
1140 test_t
.snap(adj_p
/ 2);
1142 set
->push_back(test_t
);
1143 multipliers
.erase(multipliers
.begin());
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
)
1168 transformation test_t
= get_offset();
1170 test_t
.bd_modify(d
, adj_s
);
1172 set
->push_back(test_t
);
1178 assert(runs
.size() == 2);
1184 assert(runs
.size() == 2);
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
);
1206 if (finite(adj_p
/ test
.get_error_perturb()))
1207 perturb_multipliers
.push_back(adj_p
/ test
.get_error_perturb());
1209 perturb_multipliers
.push_back(1);
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
1229 && old_runs
.count(ori
)
1230 && old_runs
[ori
].offset
== t_set
[i
])
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
++) {
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;
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]));
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;
1277 && runs
[1].get_error() < runs
[0].get_error()
1278 && perturb_multipliers
[i
]
1279 / perturb_multipliers_original
[i
] < 2) {
1287 if (runs
.size() > 1)
1290 if (!found_unreliable
)
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())
1307 calculate_element_region();
1309 if (t
->is_nontrivial())
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()) {
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],
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
))
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
))
1393 && q
[0] <= c
.input
->height() - 1
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
);
1400 c
.input
->get_bl(q
, b
);
1403 pixel weight
= (c
.aweight
1404 ? c
.aweight
->get_pixel(i
, j
)
1406 * ((!certainty_weights
&& pass_number
)
1407 ? c
.defined
->get_pixel(i
, j
)
1413 pixel weight
= pixel(1, 1, 1);
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],
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
));
1447 ui::get()->memory_error("exclusion regions");
1449 for (int idx
= 0; idx
< local_ax_count
; idx
++)
1450 dest
[idx
] = source
[idx
];
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
));
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
)
1474 dest
[*local_ax_count
] = ax_parameters
[idx
];
1476 (*local_ax_count
)++;
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
)
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
)
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
)
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
) {
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
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");
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
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
);
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
) {
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
];
1642 point_sum
+= kept_t
[j
].transform_unscaled(pp
)
1643 / kept_t
[j
].scale();
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())
1673 other_centroid
+= other_c
;
1674 this_centroid
+= this_c
;
1680 *current
= point::undefined();
1681 *previous
= point::undefined();
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
) {
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())
1708 err
+= p_ref
.lengthtosq(p_cur
);
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)
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
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
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
);
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()) {
1832 ui::get()->set_match(here
->get_error());
1833 ui::get()->set_offset(here
->get_offset());
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
;
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
);
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) {
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) {
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
) {
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
) {
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
)
1944 if (!(match_threshold
>= 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
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
1986 local_lower
= perturb_lower
;
1988 if (perturb_upper_percent
)
1989 local_upper
= perturb_upper
1994 local_upper
= perturb_upper
;
1996 local_upper
= pow(2, floor(log(local_upper
) / log(2)));
1999 local_gs_mo
= _gs_mo
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
2013 double step_variable
= local_upper
;
2014 while (step_variable
>= local_lower
) {
2019 ui::get()->set_steps(step_count
);
2021 ale_pos perturb
= local_upper
;
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.
2041 unsigned int steps
= (perturb
> pow(2, lod_max
))
2042 ? (unsigned int) (log(perturb
) / log(2)) - lod_max
+ 1 : 1;
2046 * Prepare multiple levels of detail.
2050 struct scale_cluster
*scale_clusters
= init_clusters(m
,
2051 scale_factor
, input_frame
, steps
,
2055 * Initialize variables used in the main loop.
2061 * Initialize the default initial transform
2064 if (default_initial_alignment_type
== 0) {
2067 * Follow the transformation of the original frame,
2068 * setting new image dimensions.
2071 // element->default_initial_alignment = orig_t;
2072 element
->default_initial_alignment
.set_current_element(orig_t
.get_element(0));
2073 element
->default_initial_alignment
.set_dimensions(input_frame
);
2075 } else if (default_initial_alignment_type
== 1)
2078 * Follow previous transformation, setting new image
2082 element
->default_initial_alignment
.set_dimensions(input_frame
);
2087 element
->old_is_default
= element
->is_default
;
2090 * Scale default initial transform for lod
2093 element
->default_initial_alignment
.rescale(1 / pow(2, lod
));
2096 * Set the default transformation.
2099 transformation offset
= element
->default_initial_alignment
;
2102 * Load any file-specified transformations
2105 for (offset
.set_current_index(0);
2106 offset
.get_current_index() < _ma_card
;
2107 offset
.push_element()) {
2109 offset
= tload_next(tload
, alignment_class
== 2,
2111 &element
->is_default
,
2112 offset
.get_current_index() == 0);
2116 offset
.set_current_index(0);
2118 ui::get()->set_offset(offset
);
2123 * Apply following logic
2126 transformation new_offset
= follow(element
, offset
, lod
);
2128 new_offset
.set_current_index(0);
2130 element
->old_initial_alignment
= offset
;
2131 element
->old_lod
= lod
;
2132 offset
= new_offset
;
2135 element
->old_initial_alignment
= offset
;
2136 element
->old_lod
= lod
;
2139 struct scale_cluster si
= scale_clusters
[lod
];
2142 * Projective adjustment value
2145 ale_pos adj_p
= (perturb
>= pow(2, lod_diff
))
2146 ? pow(2, lod_diff
) : (double) perturb
;
2149 * Orientational adjustment value in degrees.
2151 * Since rotational perturbation is now specified as an
2152 * arclength, we have to convert.
2155 ale_pos adj_o
= 2 * perturb
2156 / sqrt(pow(scale_clusters
[0].input
->height(), 2)
2157 + pow(scale_clusters
[0].input
->width(), 2))
2162 * Barrel distortion adjustment value
2165 ale_pos adj_b
= perturb
* bda_mult
;
2168 * Global search overlap requirements.
2171 local_gs_mo
/= pow(pow(2, lod
), 2);
2174 * Pre-alignment exposure adjustment
2177 if (_exp_register
) {
2178 ui::get()->exposure_1();
2179 transformation o
= offset
;
2180 for (int k
= lod
; k
> 0; k
--)
2182 set_exposure_ratio(m
, scale_clusters
[0], o
, local_ax_count
, 0);
2186 * Alignment statistics.
2192 * Current difference (error) value
2195 ui::get()->prematching();
2196 here
.diff(si
, perturb
, offset
, local_ax_count
, m
);
2197 ui::get()->set_match(here
.get_error());
2200 * Current and modified barrel distortion parameters
2203 ale_pos current_bd
[BARREL_DEGREE
];
2204 ale_pos modified_bd
[BARREL_DEGREE
];
2205 offset
.bd_get(current_bd
);
2206 offset
.bd_get(modified_bd
);
2209 * Translational global search step
2212 if (perturb
>= local_lower
&& local_gs
!= 0 && local_gs
!= 5
2213 && (local_gs
!= 6 || element
->is_default
)) {
2215 ui::get()->global_alignment(perturb
, lod
);
2216 ui::get()->gs_mo(local_gs_mo
);
2218 test_globals(&here
, si
, here
.get_offset(), local_gs
, adj_p
,
2219 local_ax_count
, m
, local_gs_mo
, perturb
);
2221 ui::get()->set_match(here
.get_error());
2222 ui::get()->set_offset(here
.get_offset());
2226 * Control point alignment
2229 if (local_gs
== 5) {
2231 transformation o
= here
.get_offset();
2233 for (int k
= lod
; k
> 0; k
--)
2237 * Determine centroid data
2240 point current
, previous
;
2241 centroids(m
, ¤t
, &previous
);
2243 if (current
.defined() && previous
.defined()) {
2245 o
.set_dimensions(input_frame
);
2246 o
.translate((previous
- current
) * o
.scale());
2251 * Determine rotation for alignment classes other than translation.
2254 ale_accum lowest_error
= cp_rms_error(m
, o
);
2256 ale_pos rot_lower
= 2 * local_lower
2257 / sqrt(pow(scale_clusters
[0].input
->height(), 2)
2258 + pow(scale_clusters
[0].input
->width(), 2))
2262 if (alignment_class
> 0)
2263 for (ale_pos rot
= 30; rot
> rot_lower
; rot
/= 2)
2264 for (ale_pos srot
= -rot
; srot
< rot
* 1.5; srot
+= rot
* 2) {
2265 int is_improved
= 1;
2266 while (is_improved
) {
2268 transformation test_t
= o
;
2270 * XXX: is this right?
2272 test_t
.rotate(current
* o
.scale(), srot
);
2273 ale_pos test_v
= cp_rms_error(m
, test_t
);
2275 if (test_v
< lowest_error
) {
2276 lowest_error
= test_v
;
2285 * Determine projective parameters through a local
2289 if (alignment_class
== 2) {
2290 ale_accum adj_p
= lowest_error
;
2292 if (adj_p
< local_lower
)
2293 adj_p
= local_lower
;
2295 while (adj_p
>= local_lower
) {
2296 transformation test_t
= o
;
2297 int is_improved
= 1;
2301 while (is_improved
) {
2304 for (int i
= 0; i
< 4; i
++)
2305 for (int j
= 0; j
< 2; j
++)
2306 for (adj_s
= -adj_p
; adj_s
<= adj_p
; adj_s
+= 2 * adj_p
) {
2310 if (perturb_type
== 0)
2311 test_t
.gpt_modify(j
, i
, adj_s
);
2312 else if (perturb_type
== 1)
2313 test_t
.gr_modify(j
, i
, adj_s
);
2317 test_v
= cp_rms_error(m
, test_t
);
2319 if (test_v
< lowest_error
) {
2320 lowest_error
= test_v
;
2332 set_exposure_ratio(m
, scale_clusters
[0], o
, local_ax_count
, 0);
2334 for (int k
= lod
; k
> 0; k
--)
2337 here
.diff(si
, perturb
, o
, local_ax_count
, m
);
2339 ui::get()->set_match(here
.get_error());
2340 ui::get()->set_offset(here
.get_offset());
2344 * Announce perturbation size
2347 ui::get()->aligning(perturb
, lod
);
2350 * Run initial tests to get perturbation multipliers and error
2354 std::vector
<transformation
> t_set
;
2356 here
.get_perturb_set(&t_set
, adj_p
, adj_o
, adj_b
, current_bd
, modified_bd
);
2359 * Perturbation adjustment loop.
2362 int stable_count
= 0;
2364 while (perturb
>= local_lower
) {
2367 * Orientational adjustment value in degrees.
2369 * Since rotational perturbation is now specified as an
2370 * arclength, we have to convert.
2373 ale_pos adj_o
= 2 * perturb
2374 / sqrt(pow(scale_clusters
[0].input
->height(), 2)
2375 + pow(scale_clusters
[0].input
->width(), 2))
2380 * Barrel distortion adjustment value
2383 ale_pos adj_b
= perturb
* bda_mult
;
2385 diff_stat_t old_here
= here
;
2387 here
.perturb_test(perturb
, adj_p
, adj_o
, adj_b
, current_bd
, modified_bd
,
2390 if (here
.get_offset() == old_here
.get_offset())
2395 if (stable_count
== 3) {
2399 here
.calculate_element_region();
2401 if (here
.get_current_index() + 1 < _ma_card
) {
2402 here
.push_element();
2403 here
.make_element_nontrivial(adj_p
, adj_o
);
2404 element
->is_primary
= 0;
2407 here
.set_current_index(0);
2409 element
->is_primary
= 1;
2416 * Work with images twice as large
2420 si
= scale_clusters
[lod
];
2423 * Rescale the transforms.
2426 here
.rescale(2, si
);
2427 element
->default_initial_alignment
.rescale(2);
2437 ui::get()->alignment_perturbation_level(perturb
, lod
);
2442 ui::get()->set_match(here
.get_error());
2443 ui::get()->set_offset(here
.get_offset());
2446 here
.set_current_index(0);
2448 offset
= here
.get_offset();
2451 here
.rescale(pow(2, lod
), scale_clusters
[0]);
2452 element
->default_initial_alignment
.rescale(pow(2, lod
));
2456 * Post-alignment exposure adjustment
2459 if (_exp_register
== 1) {
2460 ui::get()->exposure_2();
2461 set_exposure_ratio(m
, scale_clusters
[0], offset
, local_ax_count
, 1);
2468 ui::get()->postmatching();
2469 offset
.use_full_support();
2470 here
.diff(scale_clusters
[0], perturb
, offset
, local_ax_count
, m
);
2472 offset
.use_restricted_support();
2473 ui::get()->set_match(here
.get_error());
2476 * Free the level-of-detail structures
2479 final_clusters(scale_clusters
, scale_factor
, steps
);
2482 * Ensure that the match meets the threshold.
2485 if (threshold_ok(here
.get_error())) {
2487 * Update alignment variables
2490 element
->default_initial_alignment
= offset
;
2491 element
->old_final_alignment
= offset
;
2492 ui::get()->alignment_match_ok();
2493 } else if (local_gs
== 4) {
2496 * Align with outer starting points.
2500 * XXX: This probably isn't exactly the right thing to do,
2501 * since variables like old_initial_value have been overwritten.
2504 diff_stat_t nested_result
= _align(m
, -1, element
);
2506 if (threshold_ok(nested_result
.get_error())) {
2507 return nested_result
;
2508 } else if (nested_result
.get_error() < here
.get_error()) {
2509 here
= nested_result
;
2512 if (is_fail_default
)
2513 offset
= element
->default_initial_alignment
;
2515 ui::get()->set_match(here
.get_error());
2516 ui::get()->alignment_no_match();
2518 } else if (local_gs
== -1) {
2525 if (is_fail_default
)
2526 offset
= element
->default_initial_alignment
;
2528 ui::get()->alignment_no_match();
2532 * Write the tonal registration multiplier as a comment.
2535 pixel trm
= image_rw::exp(m
).get_multiplier();
2536 tsave_trm(tsave
, trm
[0], trm
[1], trm
[2]);
2539 * Save the transformation information
2542 for (offset
.set_current_index(0);
2543 offset
.get_current_index() < _ma_card
;
2544 offset
.push_element()) {
2546 tsave_next(tsave
, offset
, alignment_class
== 2,
2547 offset
.get_current_index() == 0);
2550 offset
.set_current_index(0);
2555 * Update match statistics.
2558 match_sum
+= (1 - here
.get_error()) * 100;
2567 * High-pass filter for frequency weights
2569 static void hipass(int rows
, int cols
, fftw_complex
*inout
) {
2570 for (int i
= 0; i
< rows
* vert_freq_cut
; i
++)
2571 for (int j
= 0; j
< cols
; j
++)
2572 for (int k
= 0; k
< 2; k
++)
2573 inout
[i
* cols
+ j
][k
] = 0;
2574 for (int i
= 0; i
< rows
; i
++)
2575 for (int j
= 0; j
< cols
* horiz_freq_cut
; j
++)
2576 for (int k
= 0; k
< 2; k
++)
2577 inout
[i
* cols
+ j
][k
] = 0;
2578 for (int i
= 0; i
< rows
; i
++)
2579 for (int j
= 0; j
< cols
; j
++)
2580 for (int k
= 0; k
< 2; k
++)
2581 if (i
/ (double) rows
+ j
/ (double) cols
< 2 * avg_freq_cut
)
2582 inout
[i
* cols
+ j
][k
] = 0;
2588 * Reset alignment weights
2590 static void reset_weights() {
2592 alignment_weights_const
= NULL
;
2594 if (alignment_weights
!= NULL
)
2595 delete alignment_weights
;
2597 alignment_weights
= NULL
;
2601 * Initialize alignment weights
2603 static void init_weights() {
2604 if (alignment_weights
!= NULL
)
2607 int rows
= reference_image
->height();
2608 int cols
= reference_image
->width();
2609 int colors
= reference_image
->depth();
2611 alignment_weights
= new image_ale_real(rows
, cols
,
2612 colors
, "alignment_weights");
2614 assert (alignment_weights
);
2616 for (int i
= 0; i
< rows
; i
++)
2617 for (int j
= 0; j
< cols
; j
++)
2618 alignment_weights
->set_pixel(i
, j
, pixel(1, 1, 1));
2622 * Update alignment weights with weight map
2624 static void map_update() {
2626 if (weight_map
== NULL
)
2631 point map_offset
= reference_image
->offset() - weight_map
->offset();
2633 int rows
= reference_image
->height();
2634 int cols
= reference_image
->width();
2636 for (int i
= 0; i
< rows
; i
++)
2637 for (int j
= 0; j
< cols
; j
++) {
2638 point map_weight_position
= map_offset
+ point(i
, j
);
2639 if (map_weight_position
[0] >= 0
2640 && map_weight_position
[1] >= 0
2641 && map_weight_position
[0] <= weight_map
->height() - 1
2642 && map_weight_position
[1] <= weight_map
->width() - 1)
2643 alignment_weights
->pix(i
, j
) *= weight_map
->get_bl(map_weight_position
);
2648 * Update alignment weights with an internal weight map, reflecting a
2649 * summation of certainty values. Use existing memory structures if
2650 * possible. This function updates alignment_weights_const; hence, it
2651 * should not be called prior to any functions that modify the
2652 * alignment_weights structure.
2654 static void imap_update() {
2655 if (alignment_weights
== NULL
) {
2656 alignment_weights_const
= reference_defined
;
2658 int rows
= reference_image
->height();
2659 int cols
= reference_image
->width();
2661 for (int i
= 0; i
< rows
; i
++)
2662 for (int j
= 0; j
< cols
; j
++)
2663 alignment_weights
->pix(i
, j
) *= reference_defined
->get_pixel(i
, j
);
2665 alignment_weights_const
= alignment_weights
;
2670 * Update alignment weights with algorithmic weights
2672 static void wmx_update() {
2675 static exposure
*exp_def
= new exposure_default();
2676 static exposure
*exp_bool
= new exposure_boolean();
2678 if (wmx_file
== NULL
|| wmx_exec
== NULL
|| wmx_defs
== NULL
)
2681 unsigned int rows
= reference_image
->height();
2682 unsigned int cols
= reference_image
->width();
2684 image_rw::write_image(wmx_file
, reference_image
);
2685 image_rw::write_image(wmx_defs
, reference_defined
, exp_bool
);
2688 int exit_status
= 1;
2690 execlp(wmx_exec
, wmx_exec
, wmx_file
, wmx_defs
, NULL
);
2691 ui::get()->exec_failure(wmx_exec
, wmx_file
, wmx_defs
);
2697 ui::get()->fork_failure("d2::align");
2699 image
*wmx_weights
= image_rw::read_image(wmx_file
, exp_def
);
2701 if (wmx_weights
->height() != rows
|| wmx_weights
->width() != cols
)
2702 ui::get()->error("algorithmic weighting must not change image size");
2704 if (alignment_weights
== NULL
)
2705 alignment_weights
= wmx_weights
;
2707 for (unsigned int i
= 0; i
< rows
; i
++)
2708 for (unsigned int j
= 0; j
< cols
; j
++)
2709 alignment_weights
->pix(i
, j
) *= wmx_weights
->pix(i
, j
);
2714 * Update alignment weights with frequency weights
2716 static void fw_update() {
2718 if (horiz_freq_cut
== 0
2719 && vert_freq_cut
== 0
2720 && avg_freq_cut
== 0)
2724 * Required for correct operation of --fwshow
2727 assert (alignment_weights
== NULL
);
2729 int rows
= reference_image
->height();
2730 int cols
= reference_image
->width();
2731 int colors
= reference_image
->depth();
2733 alignment_weights
= new image_ale_real(rows
, cols
,
2734 colors
, "alignment_weights");
2736 fftw_complex
*inout
= (fftw_complex
*) fftw_malloc(sizeof(fftw_complex
) * rows
* cols
);
2740 fftw_plan pf
= fftw_plan_dft_2d(rows
, cols
,
2742 FFTW_FORWARD
, FFTW_ESTIMATE
);
2744 fftw_plan pb
= fftw_plan_dft_2d(rows
, cols
,
2746 FFTW_BACKWARD
, FFTW_ESTIMATE
);
2748 for (int k
= 0; k
< colors
; k
++) {
2749 for (int i
= 0; i
< rows
* cols
; i
++) {
2750 inout
[i
][0] = reference_image
->get_pixel(i
/ cols
, i
% cols
)[k
];
2755 hipass(rows
, cols
, inout
);
2758 for (int i
= 0; i
< rows
* cols
; i
++) {
2760 alignment_weights
->pix(i
/ cols
, i
% cols
)[k
] = fabs(inout
[i
][0] / (rows
* cols
));
2762 alignment_weights
->pix(i
/ cols
, i
% cols
)[k
] =
2763 sqrt(pow(inout
[i
][0] / (rows
* cols
), 2)
2764 + pow(inout
[i
][1] / (rows
* cols
), 2));
2769 fftw_destroy_plan(pf
);
2770 fftw_destroy_plan(pb
);
2773 if (fw_output
!= NULL
)
2774 image_rw::write_image(fw_output
, alignment_weights
);
2779 * Update alignment to frame N.
2781 static void update_to(int n
) {
2783 assert (n
<= latest
+ 1);
2786 static std::vector
<element_t
> elements
;
2793 * Handle the initial frame
2796 elements
[0].input_frame
= image_rw::open(n
);
2798 const image
*i
= elements
[0].input_frame
;
2800 transformation result
= alignment_class
== 2
2801 ? transformation::gpt_identity(i
, scale_factor
)
2802 : transformation::eu_identity(i
, scale_factor
);
2803 result
= tload_first(tload
, alignment_class
== 2, result
, &is_default
);
2804 tsave_first(tsave
, result
, alignment_class
== 2);
2807 kept_t
= new transformation
[image_rw::count()];
2808 kept_ok
= (int *) malloc(image_rw::count()
2813 if (!kept_t
|| !kept_ok
)
2814 ui::get()->memory_error("alignment");
2824 elements
[0].default_initial_alignment
= result
;
2830 for (int i
= latest
+ 1; i
<= n
; i
++) {
2834 * Handle supplemental frames.
2837 assert (reference
!= NULL
);
2839 ui::get()->set_arender_current();
2840 reference
->sync(i
- 1);
2841 ui::get()->clear_arender_current();
2842 reference_image
= reference
->get_image();
2843 reference_defined
= reference
->get_defined();
2850 if (certainty_weights
)
2851 imap_update(); /* Must be called after all other _updates */
2853 assert (reference_image
!= NULL
);
2854 assert (reference_defined
!= NULL
);
2856 elements
[j
].input_frame
= image_rw::open(i
);
2857 elements
[j
].is_primary
= 1;
2859 _align(i
, _gs
, &elements
[j
]);
2864 if (elements
.size() > _ma_card
)
2865 elements
.resize(_ma_card
);
2871 * Set the control point count
2873 static void set_cp_count(unsigned int n
) {
2874 assert (cp_array
== NULL
);
2877 cp_array
= (const point
**) malloc(n
* sizeof(const point
*));
2881 * Set control points.
2883 static void set_cp(unsigned int i
, const point
*p
) {
2890 static void exp_register() {
2895 * Register exposure only based on metadata
2897 static void exp_meta_only() {
2902 * Don't register exposure
2904 static void exp_noregister() {
2909 * Set alignment class to translation only. Only adjust the x and y
2910 * position of images. Do not rotate input images or perform
2911 * projective transformations.
2913 static void class_translation() {
2914 alignment_class
= 0;
2918 * Set alignment class to Euclidean transformations only. Adjust the x
2919 * and y position of images and the orientation of the image about the
2922 static void class_euclidean() {
2923 alignment_class
= 1;
2927 * Set alignment class to perform general projective transformations.
2928 * See the file gpt.h for more information about general projective
2931 static void class_projective() {
2932 alignment_class
= 2;
2936 * Set the default initial alignment to the identity transformation.
2938 static void initial_default_identity() {
2939 default_initial_alignment_type
= 0;
2943 * Set the default initial alignment to the most recently matched
2944 * frame's final transformation.
2946 static void initial_default_follow() {
2947 default_initial_alignment_type
= 1;
2951 * Perturb output coordinates.
2953 static void perturb_output() {
2958 * Perturb source coordinates.
2960 static void perturb_source() {
2965 * Frames under threshold align optimally
2967 static void fail_optimal() {
2968 is_fail_default
= 0;
2972 * Frames under threshold keep their default alignments.
2974 static void fail_default() {
2975 is_fail_default
= 1;
2979 * Align images with an error contribution from each color channel.
2982 channel_alignment_type
= 0;
2986 * Align images with an error contribution only from the green channel.
2987 * Other color channels do not affect alignment.
2989 static void green() {
2990 channel_alignment_type
= 1;
2994 * Align images using a summation of channels. May be useful when
2995 * dealing with images that have high frequency color ripples due to
2999 channel_alignment_type
= 2;
3003 * Error metric exponent
3006 static void set_metric_exponent(float me
) {
3007 metric_exponent
= me
;
3014 static void set_match_threshold(float mt
) {
3015 match_threshold
= mt
;
3019 * Perturbation lower and upper bounds.
3022 static void set_perturb_lower(ale_pos pl
, int plp
) {
3024 perturb_lower_percent
= plp
;
3027 static void set_perturb_upper(ale_pos pu
, int pup
) {
3029 perturb_upper_percent
= pup
;
3033 * Maximum rotational perturbation.
3036 static void set_rot_max(int rm
) {
3039 * Obtain the largest power of two not larger than rm.
3042 rot_max
= pow(2, floor(log(rm
) / log(2)));
3046 * Barrel distortion adjustment multiplier
3049 static void set_bda_mult(ale_pos m
) {
3054 * Barrel distortion maximum rate of change
3057 static void set_bda_rate(ale_pos m
) {
3065 static void set_lod_max(int lm
) {
3070 * Set the scale factor
3072 static void set_scale(ale_pos s
) {
3077 * Set reference rendering to align against
3079 static void set_reference(render
*r
) {
3084 * Set the interpolant
3086 static void set_interpolant(filter::scaled_filter
*f
) {
3091 * Set alignment weights image
3093 static void set_weight_map(const image
*i
) {
3098 * Set frequency cuts
3100 static void set_frequency_cut(double h
, double v
, double a
) {
3107 * Set algorithmic alignment weighting
3109 static void set_wmx(const char *e
, const char *f
, const char *d
) {
3116 * Show frequency weights
3118 static void set_fl_show(const char *filename
) {
3119 fw_output
= filename
;
3123 * Set transformation file loader.
3125 static void set_tload(tload_t
*tl
) {
3130 * Set transformation file saver.
3132 static void set_tsave(tsave_t
*ts
) {
3137 * Get match statistics for frame N.
3139 static int match(int n
) {
3153 * Message that old alignment data should be kept.
3155 static void keep() {
3156 assert (latest
== -1);
3161 * Get alignment for frame N.
3163 static transformation
of(int n
) {
3176 * Set the certainty-weighted flag.
3178 static void certainty_weighted(int flag
) {
3179 certainty_weights
= flag
;
3183 * Set the global search type.
3185 static void gs(const char *type
) {
3186 if (!strcmp(type
, "local")) {
3188 } else if (!strcmp(type
, "inner")) {
3190 } else if (!strcmp(type
, "outer")) {
3192 } else if (!strcmp(type
, "all")) {
3194 } else if (!strcmp(type
, "central")) {
3196 } else if (!strcmp(type
, "defaults")) {
3198 } else if (!strcmp(type
, "points")) {
3202 ui::get()->error("bad global search type");
3207 * Multi-alignment contiguity
3209 static void ma_cont(double value
) {
3214 * Multi-alignment cardinality
3216 static void ma_card(unsigned int value
) {
3217 assert (value
>= 1);
3222 * Set the minimum overlap for global searching
3224 static void gs_mo(ale_pos value
, int _gs_mo_percent
) {
3226 gs_mo_percent
= _gs_mo_percent
;
3230 * Set alignment exclusion regions
3232 static void set_exclusion(exclusion
*_ax_parameters
, int _ax_count
) {
3233 ax_count
= _ax_count
;
3234 ax_parameters
= _ax_parameters
;
3238 * Get match summary statistics.
3240 static ale_accum
match_summary() {
3241 return match_sum
/ match_count
;