src/align.c: Raw import align_element method from ALE.
[libale.git] / src / align.c
bloba2f6d79629c70f192c0a82bc5644c3dca4caadc9
1 /*
2 * Copyright 2008, 2009 David Hilvert <dhilvert@gmail.com>
4 * This file is part of libale.
6 * libale is free software: you can redistribute it and/or modify it under the
7 * terms of the GNU Affero General Public License as published by the Free
8 * Software Foundation, either version 3 of the License, or (at your option)
9 * any later version.
11 * libale is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
14 * more details.
16 * You should have received a copy of the GNU Affero General Public License
17 * along with libale. If not, see <http://www.gnu.org/licenses/>.
20 #include "libale.h"
23 * API Implementation.
26 #warning raw imported code should be revised for Libale.
27 #if 0
29 /* XXX: Raw import of code from ALE.
33 * Original ALE header copyright notice gives '2002, 2004, 2007 David Hilvert'
34 * for the following, but git-blame (correctly) gives 2008.
38 * Alignment state
40 * This structure contains alignment state information. The change
41 * between the non-default old initial alignment and old final
42 * alignment is used to adjust the non-default current initial
43 * alignment. If either the old or new initial alignment is a default
44 * alignment, the old --follow semantics are preserved.
47 class astate_t {
48 ale_trans old_initial_alignment;
49 ale_trans old_final_alignment;
50 ale_trans default_initial_alignment;
51 int old_is_default;
52 std::vector<int> is_default;
53 ale_image input_frame;
55 public:
56 astate_t() :
57 is_default(1) {
59 old_initial_alignment = ale_new_trans(accel::context(), NULL);
60 old_final_alignment = ale_new_trans(accel::context(), NULL);
61 default_initial_alignment = ale_new_trans(accel::context(), NULL);
63 input_frame = NULL;
64 is_default[0] = 1;
65 old_is_default = 1;
68 ale_image get_input_frame() const {
69 return input_frame;
72 void set_is_default(unsigned int index, int value) {
75 * Expand the array, if necessary.
77 if (index == is_default.size());
78 is_default.resize(index + 1);
80 assert (index < is_default.size());
81 is_default[index] = value;
84 int get_is_default(unsigned int index) {
85 assert (index < is_default.size());
86 return is_default[index];
89 ale_trans get_default() {
90 return default_initial_alignment;
93 void set_default(ale_trans t) {
94 default_initial_alignment = t;
97 void default_set_original_bounds(ale_image i) {
98 ale_trans_set_original_bounds(default_initial_alignment, i);
101 void set_final(ale_trans t) {
102 old_final_alignment = t;
105 void set_input_frame(ale_image i) {
106 input_frame = i;
110 * Implement new delta --follow semantics.
112 * If we have a transformation T such that
114 * prev_final == T(prev_init)
116 * Then we also have
118 * current_init_follow == T(current_init)
120 * We can calculate T as follows:
122 * T == prev_final(prev_init^-1)
124 * Where ^-1 is the inverse operator.
126 static trans_single follow(trans_single a, trans_single b, trans_single c) {
127 trans_single cc = c;
129 if (alignment_class == 0) {
131 * Translational transformations
134 ale_pos t0 = -a.eu_get(0) + b.eu_get(0);
135 ale_pos t1 = -a.eu_get(1) + b.eu_get(1);
137 cc.eu_modify(0, t0);
138 cc.eu_modify(1, t1);
140 } else if (alignment_class == 1) {
142 * Euclidean transformations
145 ale_pos t2 = -a.eu_get(2) + b.eu_get(2);
147 cc.eu_modify(2, t2);
149 point p( c.scaled_height()/2 + c.eu_get(0) - a.eu_get(0),
150 c.scaled_width()/2 + c.eu_get(1) - a.eu_get(1) );
152 p = b.transform_scaled(p);
154 cc.eu_modify(0, p[0] - c.scaled_height()/2 - c.eu_get(0));
155 cc.eu_modify(1, p[1] - c.scaled_width()/2 - c.eu_get(1));
157 } else if (alignment_class == 2) {
159 * Projective transformations
162 point p[4];
164 p[0] = b.transform_scaled(a
165 . scaled_inverse_transform(c.transform_scaled(point( 0 , 0 ))));
166 p[1] = b.transform_scaled(a
167 . scaled_inverse_transform(c.transform_scaled(point(c.scaled_height(), 0 ))));
168 p[2] = b.transform_scaled(a
169 . scaled_inverse_transform(c.transform_scaled(point(c.scaled_height(), c.scaled_width()))));
170 p[3] = b.transform_scaled(a
171 . scaled_inverse_transform(c.transform_scaled(point( 0 , c.scaled_width()))));
173 cc.gpt_set(p);
176 return cc;
180 * For multi-alignment following, we use the following approach, not
181 * guaranteed to work with large changes in scene or perspective, but
182 * which should be somewhat flexible:
184 * For
186 * t[][] calculated final alignments
187 * s[][] alignments as loaded from file
188 * previous frame n
189 * current frame n+1
190 * fundamental (primary) 0
191 * non-fundamental (non-primary) m!=0
192 * parent element m'
193 * follow(a, b, c) applying the (a, b) delta T=b(a^-1) to c
195 * following in the case of missing file data might be generated by
197 * t[n+1][0] = t[n][0]
198 * t[n+1][m!=0] = follow(t[n][m'], t[n+1][m'], t[n][m])
200 * cases with all noted file data present might be generated by
202 * t[n+1][0] = follow(s[n][0], t[n][0], s[n+1][0])
203 * t[n+1][m!=0] = follow(s[n+1][m'], t[n+1][m'], s[n+1][m])
205 * For non-following behavior, or where assigning the above is
206 * impossible, we assign the following default
208 * t[n+1][0] = Identity
209 * t[n+1][m!=0] = t[n+1][m']
212 void init_frame_alignment_primary(transformation *offset, int lod, ale_pos perturb) {
214 if (perturb > 0 && !old_is_default && !get_is_default(0)
215 && default_initial_alignment_type == 1) {
218 * Apply following logic for the primary element.
221 ui::get()->following();
223 trans_single new_offset = follow(old_initial_alignment.get_element(0),
224 old_final_alignment.get_element(0),
225 offset->get_element(0));
227 old_initial_alignment = *offset;
229 offset->set_element(0, new_offset);
231 ui::get()->set_offset(new_offset);
232 } else {
233 old_initial_alignment = *offset;
236 is_default.resize(old_initial_alignment.stack_depth());
239 void init_frame_alignment_nonprimary(transformation *offset,
240 int lod, ale_pos perturb, unsigned int index) {
242 assert (index > 0);
244 unsigned int parent_index = offset->parent_index(index);
246 if (perturb > 0
247 && !get_is_default(parent_index)
248 && !get_is_default(index)
249 && default_initial_alignment_type == 1) {
252 * Apply file-based following logic for the
253 * given element.
256 ui::get()->following();
258 trans_single new_offset = follow(old_initial_alignment.get_element(parent_index),
259 offset->get_element(parent_index),
260 offset->get_element(index));
262 old_initial_alignment.set_element(index, offset->get_element(index));
263 offset->set_element(index, new_offset);
265 ui::get()->set_offset(new_offset);
267 return;
270 offset->get_coordinate(parent_index);
273 if (perturb > 0
274 && old_final_alignment.exists(offset->get_coordinate(parent_index))
275 && old_final_alignment.exists(offset->get_current_coordinate())
276 && default_initial_alignment_type == 1) {
279 * Apply nonfile-based following logic for
280 * the given element.
283 ui::get()->following();
286 * XXX: Although it is different, the below
287 * should be equivalent to the comment
288 * description.
291 trans_single a = old_final_alignment.get_element(offset->get_coordinate(parent_index));
292 trans_single b = old_final_alignment.get_element(offset->get_current_coordinate());
293 trans_single c = offset->get_element(parent_index);
295 trans_single new_offset = follow(a, b, c);
297 offset->set_element(index, new_offset);
298 ui::get()->set_offset(new_offset);
300 return;
304 * Handle other cases.
307 if (get_is_default(index)) {
308 offset->set_element(index, offset->get_element(parent_index));
309 ui::get()->set_offset(offset->get_element(index));
313 void init_default() {
315 if (default_initial_alignment_type == 0) {
318 * Follow the transformation of the original frame,
319 * setting new image dimensions.
322 // astate->default_initial_alignment = orig_t;
323 default_initial_alignment.set_current_element(orig_t.get_element(0));
324 default_initial_alignment.set_dimensions(input_frame);
326 } else if (default_initial_alignment_type == 1)
329 * Follow previous transformation, setting new image
330 * dimensions.
333 default_initial_alignment.set_dimensions(input_frame);
335 else
336 assert(0);
338 old_is_default = get_is_default(0);
344 * Original ALE header copyright notice gives '2002, 2004, 2007 David Hilvert'
345 * git-blame (which does not recall prior to 2005 at the moment) additionally
346 * gives changes for the following several methods in years 2005, 2006, 2007,
347 * 2008, and 2009.
350 static struct scale_cluster *init_clusters(int frame, ale_pos scale_factor,
351 const image *input_frame, unsigned int steps,
352 int *local_ax_count) {
355 * Allocate memory for the array.
358 struct scale_cluster *scale_clusters =
359 (struct scale_cluster *) malloc(steps * sizeof(struct scale_cluster));
361 assert (scale_clusters);
363 if (!scale_clusters)
364 ui::get()->memory_error("alignment");
367 * Prepare images and exclusion regions for the highest level
368 * of detail.
371 scale_clusters[0].accum = reference_image;
373 ui::get()->constructing_lod_clusters(0.0);
374 scale_clusters[0].input_scale = scale_factor;
375 if (scale_factor < 1.0 && interpolant == NULL)
376 scale_clusters[0].input = input_frame->scale(scale_factor, "alignment");
377 else
378 scale_clusters[0].input = input_frame;
380 scale_clusters[0].certainty = reference_defined;
381 scale_clusters[0].aweight = alignment_weights;
382 scale_clusters[0].ax_parameters = filter_ax_parameters(frame, local_ax_count);
385 * Allocate and determine input frame certainty.
388 if (scale_clusters[0].input->get_bayer() != IMAGE_BAYER_NONE) {
389 scale_clusters[0].input_certainty = new_image_bayer_ale_real(
390 scale_clusters[0].input->height(),
391 scale_clusters[0].input->width(),
392 scale_clusters[0].input->depth(),
393 scale_clusters[0].input->get_bayer());
394 } else {
395 scale_clusters[0].input_certainty = scale_clusters[0].input->clone("certainty");
398 for (unsigned int i = 0; i < scale_clusters[0].input_certainty->height(); i++)
399 for (unsigned int j = 0; j < scale_clusters[0].input_certainty->width(); j++)
400 for (unsigned int k = 0; k < 3; k++)
401 if (scale_clusters[0].input->get_channels(i, j) & (1 << k))
402 ((image *) scale_clusters[0].input_certainty)->set_chan(i, j, k,
403 scale_clusters[0].input->
404 exp().confidence(scale_clusters[0].input->get_pixel(i, j))[k]);
406 scale_ax_parameters(*local_ax_count, scale_clusters[0].ax_parameters, scale_factor,
407 (scale_factor < 1.0 && interpolant == NULL) ? scale_factor : (ale_pos) 1);
409 init_nl_cluster(&(scale_clusters[0]));
412 * Prepare reduced-detail images and exclusion
413 * regions.
416 for (unsigned int step = 1; step < steps; step++) {
417 ui::get()->constructing_lod_clusters(step);
418 scale_clusters[step].accum = prepare_lod(scale_clusters[step - 1].accum);
419 scale_clusters[step].certainty = prepare_lod_def(scale_clusters[step - 1].certainty);
420 scale_clusters[step].aweight = prepare_lod_def(scale_clusters[step - 1].aweight);
421 scale_clusters[step].ax_parameters
422 = copy_ax_parameters(*local_ax_count, scale_clusters[step - 1].ax_parameters);
424 double sf = scale_clusters[step - 1].input_scale / 2;
425 scale_clusters[step].input_scale = sf;
427 if (sf >= 1.0 || interpolant != NULL) {
428 scale_clusters[step].input = scale_clusters[step - 1].input;
429 scale_clusters[step].input_certainty = scale_clusters[step - 1].input_certainty;
430 scale_ax_parameters(*local_ax_count, scale_clusters[step].ax_parameters, 0.5, 1);
431 } else if (sf > 0.5) {
432 scale_clusters[step].input = scale_clusters[step - 1].input->scale(sf, "alignment");
433 scale_clusters[step].input_certainty = scale_clusters[step - 1].input->scale(sf, "alignment", 1);
434 scale_ax_parameters(*local_ax_count, scale_clusters[step].ax_parameters, 0.5, sf);
435 } else {
436 scale_clusters[step].input = scale_clusters[step - 1].input->scale(0.5, "alignment");
437 scale_clusters[step].input_certainty = scale_clusters[step - 1].input_certainty->scale(0.5, "alignment", 1);
438 scale_ax_parameters(*local_ax_count, scale_clusters[step].ax_parameters, 0.5, 0.5);
441 init_nl_cluster(&(scale_clusters[step]));
444 return scale_clusters;
447 static diff_stat_t _align_element(ale_pos perturb, ale_pos local_lower,
448 scale_cluster *scale_clusters, diff_stat_t here,
449 ale_pos adj_p, ale_pos adj_o, ale_pos adj_b,
450 ale_pos *current_bd, ale_pos *modified_bd,
451 astate_t *astate, int lod, scale_cluster si) {
454 * Run initial tests to get perturbation multipliers and error
455 * centroids.
458 std::vector<d2::trans_single> t_set;
460 here.get_perturb_set(&t_set, adj_p, adj_o, adj_b, current_bd, modified_bd);
462 int stable_count = 0;
464 while (perturb >= local_lower) {
466 ui::get()->alignment_dims(scale_clusters[lod].accum->height(), scale_clusters[lod].accum->width(),
467 scale_clusters[lod].input->height(), scale_clusters[lod].input->width());
470 * Orientational adjustment value in degrees.
472 * Since rotational perturbation is now specified as an
473 * arclength, we have to convert.
476 ale_pos adj_o = 2 * (double) perturb
477 / sqrt(pow(scale_clusters[0].input->height(), 2)
478 + pow(scale_clusters[0].input->width(), 2))
479 * 180
480 / M_PI;
483 * Barrel distortion adjustment value
486 ale_pos adj_b = perturb * bda_mult;
488 trans_single old_offset = here.get_offset();
490 here.perturb_test(perturb, adj_p, adj_o, adj_b, current_bd, modified_bd,
491 stable_count);
493 if (here.get_offset() == old_offset)
494 stable_count++;
495 else
496 stable_count = 0;
498 if (stable_count == 3) {
500 stable_count = 0;
502 perturb *= 0.5;
504 if (lod > 0
505 && lod > lrint(log(perturb) / log(2)) - lod_preferred) {
508 * Work with images twice as large
511 lod--;
512 si = scale_clusters[lod];
515 * Rescale the transforms.
518 ale_pos rescale_factor = (double) scale_factor
519 / (double) pow(2, lod)
520 / (double) here.get_offset().scale();
522 here.rescale(rescale_factor, si);
524 } else {
525 adj_p = perturb / pow(2, lod);
529 * Announce changes
532 ui::get()->alignment_perturbation_level(perturb, lod);
535 ui::get()->set_match(here.get_error());
536 ui::get()->set_offset(here.get_offset());
539 if (lod > 0) {
540 ale_pos rescale_factor = (double) scale_factor
541 / (double) here.get_offset().scale();
543 here.rescale(rescale_factor, scale_clusters[0]);
546 return here;
550 * Align frame m against the reference.
552 * XXX: the transformation class currently combines ordinary
553 * transformations with scaling. This is somewhat convenient for
554 * some things, but can also be confusing. This method, _align(), is
555 * one case where special care must be taken to ensure that the scale
556 * is always set correctly (by using the 'rescale' method).
558 static diff_stat_multi _align(int m, int local_gs, astate_t *astate) {
560 const image *input_frame = astate->get_input_frame();
563 * Local upper/lower data, possibly dependent on image
564 * dimensions.
567 ale_pos local_lower, local_upper;
568 ale_accum local_gs_mo;
571 * Select the minimum dimension as the reference.
574 ale_pos reference_size = input_frame->height();
575 if (input_frame->width() < reference_size)
576 reference_size = input_frame->width();
577 ale_accum reference_area = input_frame->height()
578 * input_frame->width();
580 if (perturb_lower_percent)
581 local_lower = (double) perturb_lower
582 * (double) reference_size
583 * (double) 0.01
584 * (double) scale_factor;
585 else
586 local_lower = perturb_lower;
588 if (perturb_upper_percent)
589 local_upper = (double) perturb_upper
590 * (double) reference_size
591 * (double) 0.01
592 * (double) scale_factor;
593 else
594 local_upper = perturb_upper;
596 local_upper = pow(2, floor(log(local_upper) / log(2)));
598 if (gs_mo_percent)
599 local_gs_mo = (double) _gs_mo
600 * (double) reference_area
601 * (double) 0.01
602 * (double) scale_factor;
603 else
604 local_gs_mo = _gs_mo;
607 * Logarithms aren't exact, so we divide repeatedly to discover
608 * how many steps will occur, and pass this information to the
609 * user interface.
612 int step_count = 0;
613 double step_variable = local_upper;
614 while (step_variable >= local_lower) {
615 step_variable /= 2;
616 step_count++;
619 ale_pos perturb = local_upper;
621 if (_keep) {
622 kept_t[latest] = latest_t;
623 kept_ok[latest] = latest_ok;
627 * Determine how many levels of detail should be prepared, by
628 * calculating the initial (largest) value for the
629 * level-of-detail variable.
632 int lod = lrint(log(perturb) / log(2)) - lod_preferred;
634 if (lod < 0)
635 lod = 0;
637 while (lod > 0 && (reference_image->width() < pow(2, lod) * min_dimension
638 || reference_image->height() < pow(2, lod) * min_dimension))
639 lod--;
641 unsigned int steps = (unsigned int) lod + 1;
644 * Prepare multiple levels of detail.
647 int local_ax_count;
648 struct scale_cluster *scale_clusters = init_clusters(m,
649 scale_factor, input_frame, steps,
650 &local_ax_count);
653 * Initialize the default initial transform
656 astate->init_default();
659 * Set the default transformation.
662 transformation offset = astate->get_default();
665 * Establish boundaries
668 offset.set_current_bounds(reference_image);
670 ui::get()->alignment_degree_max(offset.get_coordinate(offset.stack_depth() - 1).degree);
672 if (offset.stack_depth() == 1) {
673 ui::get()->set_steps(step_count, 0);
674 } else {
675 ui::get()->set_steps(offset.get_coordinate(offset.stack_depth() - 1).degree + 1, 1);
679 * Load any file-specified transformations
682 for (unsigned int index = 0; index < offset.stack_depth(); index++) {
683 int is_default = 1;
684 unsigned int index_2;
685 offset.set_current_index(index);
687 offset = tload_next(tload, alignment_class == 2,
688 offset,
689 &is_default, offset.get_current_index() == 0);
691 index_2 = offset.get_current_index();
693 if (index_2 > index) {
694 for (unsigned int index_3 = index; index_3 < index_2; index_3++)
695 astate->set_is_default(index_3, 1);
697 index = index_2;
700 astate->set_is_default(index, is_default);
703 offset.set_current_index(0);
705 astate->init_frame_alignment_primary(&offset, lod, perturb);
708 * Control point alignment
711 if (local_gs == 5) {
713 transformation o = offset;
716 * Determine centroid data
719 point current, previous;
720 centroids(m, &current, &previous);
722 if (current.defined() && previous.defined()) {
723 o = orig_t;
724 o.set_dimensions(input_frame);
725 o.translate((previous - current) * o.scale());
726 current = previous;
730 * Determine rotation for alignment classes other than translation.
733 ale_pos lowest_error = cp_rms_error(m, o);
735 ale_pos rot_lower = 2 * (double) local_lower
736 / sqrt(pow(scale_clusters[0].input->height(), 2)
737 + pow(scale_clusters[0].input->width(), 2))
738 * 180
739 / M_PI;
741 if (alignment_class > 0)
742 for (double rot = 30; rot > rot_lower; rot /= 2)
743 for (double srot = -rot; srot < rot * 1.5; srot += rot * 2) {
744 int is_improved = 1;
745 while (is_improved) {
746 is_improved = 0;
747 transformation test_t = o;
749 * XXX: is this right?
751 test_t.rotate(current * o.scale(), srot);
752 ale_pos test_v = cp_rms_error(m, test_t);
754 if (test_v < lowest_error) {
755 lowest_error = test_v;
756 o = test_t;
757 srot += 3 * rot;
758 is_improved = 1;
764 * Determine projective parameters through a local
765 * minimum search.
768 if (alignment_class == 2) {
769 ale_pos adj_p = lowest_error;
771 if (adj_p < local_lower)
772 adj_p = local_lower;
774 while (adj_p >= local_lower) {
775 transformation test_t = o;
776 int is_improved = 1;
777 ale_pos test_v;
778 ale_pos adj_s;
780 while (is_improved) {
781 is_improved = 0;
783 for (int i = 0; i < 4; i++)
784 for (int j = 0; j < 2; j++)
785 for (adj_s = -adj_p; adj_s <= adj_p; adj_s += 2 * adj_p) {
787 test_t = o;
789 if (perturb_type == 0)
790 test_t.gpt_modify(j, i, adj_s);
791 else if (perturb_type == 1)
792 test_t.gr_modify(j, i, adj_s);
793 else
794 assert(0);
796 test_v = cp_rms_error(m, test_t);
798 if (test_v < lowest_error) {
799 lowest_error = test_v;
800 o = test_t;
801 adj_s += 3 * adj_p;
802 is_improved = 1;
806 adj_p /= 2;
812 * Pre-alignment exposure adjustment
815 if (_exp_register) {
816 ui::get()->exposure_1();
817 set_exposure_ratio(m, scale_clusters[0], offset, local_ax_count, 0);
821 * Scale transform for lod
824 for (int lod_ = 0; lod_ < lod; lod_++) {
825 transformation s = offset;
826 transformation t = offset;
828 t.rescale(1 / (double) 2);
830 if (!(t.scaled_height() > 0 && t.scaled_height() < s.scaled_height())
831 || !(t.scaled_width() > 0 && t.scaled_width() < s.scaled_width())) {
832 perturb /= pow(2, lod - lod_);
833 lod = lod_;
834 break;
835 } else {
836 offset = t;
840 ui::get()->set_offset(offset);
842 struct scale_cluster si = scale_clusters[lod];
845 * Projective adjustment value
848 ale_pos adj_p = perturb / pow(2, lod);
851 * Orientational adjustment value in degrees.
853 * Since rotational perturbation is now specified as an
854 * arclength, we have to convert.
857 ale_pos adj_o = (double) 2 * (double) perturb
858 / sqrt(pow((double) scale_clusters[0].input->height(), (double) 2)
859 + pow((double) scale_clusters[0].input->width(), (double) 2))
860 * (double) 180
861 / M_PI;
864 * Barrel distortion adjustment value
867 ale_pos adj_b = perturb * bda_mult;
870 * Global search overlap requirements.
873 local_gs_mo = (double) local_gs_mo / pow(pow(2, lod), 2);
876 * Alignment statistics.
879 diff_stat_t here(offset.elem_bounds());
882 * Current difference (error) value
885 ui::get()->prematching();
886 here.diff(si, offset.get_current_element(), local_ax_count, m);
887 ui::get()->set_match(here.get_error());
890 * Current and modified barrel distortion parameters
893 ale_pos current_bd[BARREL_DEGREE];
894 ale_pos modified_bd[BARREL_DEGREE];
895 offset.bd_get(current_bd);
896 offset.bd_get(modified_bd);
899 * Translational global search step
902 if (perturb >= local_lower && local_gs != 0 && local_gs != 5
903 && (local_gs != 6 || astate->get_is_default(0))) {
905 ui::get()->global_alignment(perturb, lod);
906 ui::get()->gs_mo(local_gs_mo);
908 test_globals(&here, si, offset, local_gs, adj_p,
909 local_ax_count, m, local_gs_mo, perturb);
911 ui::get()->set_match(here.get_error());
912 ui::get()->set_offset(here.get_offset());
916 * Perturbation adjustment loop.
919 offset.set_current_element(here.get_offset());
921 for (unsigned int i = 0; i < offset.stack_depth(); i++) {
923 ui::get()->aligning_element(i, offset.stack_depth());
925 offset.set_current_index(i);
927 ui::get()->start_multi_alignment_element(offset);
929 ui::get()->set_offset(offset);
931 if (i > 0) {
932 astate->init_frame_alignment_nonprimary(&offset, lod, perturb, i);
934 if (_exp_register == 1) {
935 ui::get()->exposure_1();
936 pixel_accum asum(0, 0, 0), bsum(0, 0, 0);
937 exposure_ratio_iterate eri(&asum, &bsum, scale_clusters[0], offset, local_ax_count, 0,
938 offset.elem_bounds().scale_to_bounds(scale_clusters[0].accum->height(),
939 scale_clusters[0].accum->width()));
941 eri.run();
942 pixel_accum tr = asum / bsum;
943 ui::get()->exp_multiplier(tr[0], tr[1], tr[2]);
944 offset.set_tonal_multiplier(tr);
948 int e_lod = lod;
949 int e_div = offset.get_current_coordinate().degree;
950 ale_pos e_perturb = perturb;
951 ale_pos e_adj_p = adj_p;
952 ale_pos e_adj_b = adj_b;
954 for (int d = 0; d < e_div; d++) {
955 e_adj_b = 0;
956 e_perturb *= 0.5;
957 if (e_lod > 0) {
958 e_lod--;
959 } else {
960 e_adj_p *= 0.5;
964 if (i > 0) {
966 d2::trans_multi::elem_bounds_t b = offset.elem_bounds();
968 for (int dim_satisfied = 0; e_lod > 0 && !dim_satisfied; ) {
969 int height = scale_clusters[e_lod].accum->height();
970 int width = scale_clusters[e_lod].accum->width();
972 d2::trans_multi::elem_bounds_int_t bi = b.scale_to_bounds(height, width);
974 dim_satisfied = bi.satisfies_min_dim(min_dimension);
976 if (!dim_satisfied) {
977 e_lod--;
978 e_adj_p *= 2;
983 * Scale transform for lod
986 for (int lod_ = 0; lod_ < e_lod; lod_++) {
987 trans_single s = offset.get_element(i);
988 trans_single t = offset.get_element(i);
990 t.rescale(1 / (double) 2);
992 if (!(t.scaled_height() > 0 && t.scaled_height() < s.scaled_height())
993 || !(t.scaled_width() > 0 && t.scaled_width() < s.scaled_width())) {
994 e_perturb /= pow(2, e_lod - lod_);
995 e_lod = lod_;
996 break;
997 } else {
998 offset.set_element(i, t);
1002 ui::get()->set_offset(offset);
1006 * Announce perturbation size
1009 ui::get()->aligning(e_perturb, e_lod);
1011 si = scale_clusters[e_lod];
1013 here.set_elem_bounds(offset.elem_bounds());
1015 here.diff(si, offset.get_current_element(), local_ax_count, m);
1017 here.confirm();
1019 here = check_ancestor_path(offset, si, here, local_ax_count, m);
1021 here = _align_element(e_perturb, local_lower, scale_clusters,
1022 here, e_adj_p, adj_o, e_adj_b, current_bd, modified_bd,
1023 astate, e_lod, si);
1025 offset.rescale(here.get_offset().scale() / offset.scale());
1027 offset.set_current_element(here.get_offset());
1029 if (i > 0 && _exp_register == 1) {
1030 if (ma_cert_satisfied(scale_clusters[0], offset, i)) {
1031 ui::get()->exposure_2();
1032 pixel_accum asum(0, 0, 0), bsum(0, 0, 0);
1033 exposure_ratio_iterate eri(&asum, &bsum, scale_clusters[0], offset, local_ax_count, 1,
1034 offset.elem_bounds().scale_to_bounds(scale_clusters[0].accum->height(),
1035 scale_clusters[0].accum->width()));
1037 eri.run();
1038 pixel_accum tr = asum / bsum;
1039 ui::get()->exp_multiplier(tr[0], tr[1], tr[2]);
1040 offset.set_tonal_multiplier(tr);
1041 } else {
1042 offset.set_tonal_multiplier(offset.get_element(offset.parent_index(i)).get_tonal_multiplier(point(0, 0)));
1044 } else if (_exp_register == 1) {
1045 ui::get()->exposure_2();
1046 set_exposure_ratio(m, scale_clusters[0], offset, local_ax_count, 1);
1049 ui::get()->set_offset(offset);
1051 if (i + 1 == offset.stack_depth())
1052 ui::get()->alignment_degree_complete(offset.get_coordinate(i).degree);
1053 else if (offset.get_coordinate(i).degree != offset.get_coordinate(i + 1).degree)
1054 ui::get()->alignment_degree_complete(offset.get_coordinate(i + 1).degree);
1057 offset.set_current_index(0);
1059 ui::get()->multi();
1060 offset.set_multi(reference_image, input_frame);
1063 * Recalculate error on whole frame.
1066 ui::get()->postmatching();
1067 diff_stat_generic<transformation> multi_here(offset.elem_bounds());
1068 multi_here.diff(scale_clusters[0], offset, local_ax_count, m);
1069 ui::get()->set_match(multi_here.get_error());
1072 * Free the level-of-detail structures
1075 final_clusters(scale_clusters, scale_factor, steps);
1078 * Ensure that the match meets the threshold.
1081 if (threshold_ok(multi_here.get_error())) {
1083 * Update alignment variables
1085 latest_ok = 1;
1086 astate->set_default(offset);
1087 astate->set_final(offset);
1088 ui::get()->alignment_match_ok();
1089 } else if (local_gs == 4) {
1092 * Align with outer starting points.
1096 * XXX: This probably isn't exactly the right thing to do,
1097 * since variables like old_initial_value have been overwritten.
1100 diff_stat_multi nested_result = _align(m, -1, astate);
1102 if (threshold_ok(nested_result.get_error())) {
1103 return nested_result;
1104 } else if (nested_result.get_error() < multi_here.get_error()) {
1105 multi_here = nested_result;
1108 if (is_fail_default)
1109 offset = astate->get_default();
1111 ui::get()->set_match(multi_here.get_error());
1112 ui::get()->alignment_no_match();
1114 } else if (local_gs == -1) {
1116 latest_ok = 0;
1117 latest_t = offset;
1118 return multi_here;
1120 } else {
1121 if (is_fail_default)
1122 offset = astate->get_default();
1123 latest_ok = 0;
1124 ui::get()->alignment_no_match();
1128 * Write the tonal registration multiplier as a comment.
1131 pixel trm = image_rw::exp(m).get_multiplier();
1132 tsave_trm(tsave, trm[0], trm[1], trm[2]);
1135 * Save the transformation information
1138 for (unsigned int index = 0; index < offset.stack_depth(); index++) {
1139 offset.set_current_index(index);
1141 tsave_next(tsave, offset, alignment_class == 2,
1142 offset.get_current_index() == 0);
1145 offset.set_current_index(0);
1148 * Update match statistics.
1151 match_sum += (1 - multi_here.get_error()) * (ale_accum) 100;
1152 match_count++;
1153 latest = m;
1154 latest_t = offset;
1156 return multi_here;
1159 #endif
1161 int ale_align(ale_image a, ale_image b, ale_trans start,
1162 ale_align_properties align_properties, ale_trans result) {
1163 #warning function unfinished.