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)
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
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/>.
26 #warning raw imported code should be revised for Libale.
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.
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.
48 ale_trans old_initial_alignment
;
49 ale_trans old_final_alignment
;
50 ale_trans default_initial_alignment
;
52 std::vector
<int> is_default
;
53 ale_image input_frame
;
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
);
68 ale_image
get_input_frame() const {
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
) {
110 * Implement new delta --follow semantics.
112 * If we have a transformation T such that
114 * prev_final == T(prev_init)
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
) {
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);
140 } else if (alignment_class
== 1) {
142 * Euclidean transformations
145 ale_pos t2
= -a
.eu_get(2) + b
.eu_get(2);
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
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()))));
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:
186 * t[][] calculated final alignments
187 * s[][] alignments as loaded from file
190 * fundamental (primary) 0
191 * non-fundamental (non-primary) m!=0
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
);
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
) {
244 unsigned int parent_index
= offset
->parent_index(index
);
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
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
);
270 offset
->get_coordinate(parent_index
);
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
283 ui::get()->following();
286 * XXX: Although it is different, the below
287 * should be equivalent to the comment
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
);
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
333 default_initial_alignment
.set_dimensions(input_frame
);
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,
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
);
364 ui::get()->memory_error("alignment");
367 * Prepare images and exclusion regions for the highest level
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");
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());
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
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
);
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
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))
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
,
493 if (here
.get_offset() == old_offset
)
498 if (stable_count
== 3) {
505 && lod
> lrint(log(perturb
) / log(2)) - lod_preferred
) {
508 * Work with images twice as large
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
);
525 adj_p
= perturb
/ pow(2, lod
);
532 ui::get()->alignment_perturbation_level(perturb
, lod
);
535 ui::get()->set_match(here
.get_error());
536 ui::get()->set_offset(here
.get_offset());
540 ale_pos rescale_factor
= (double) scale_factor
541 / (double) here
.get_offset().scale();
543 here
.rescale(rescale_factor
, scale_clusters
[0]);
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
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
584 * (double) scale_factor
;
586 local_lower
= perturb_lower
;
588 if (perturb_upper_percent
)
589 local_upper
= (double) perturb_upper
590 * (double) reference_size
592 * (double) scale_factor
;
594 local_upper
= perturb_upper
;
596 local_upper
= pow(2, floor(log(local_upper
) / log(2)));
599 local_gs_mo
= (double) _gs_mo
600 * (double) reference_area
602 * (double) scale_factor
;
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
613 double step_variable
= local_upper
;
614 while (step_variable
>= local_lower
) {
619 ale_pos perturb
= local_upper
;
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
;
637 while (lod
> 0 && (reference_image
->width() < pow(2, lod
) * min_dimension
638 || reference_image
->height() < pow(2, lod
) * min_dimension
))
641 unsigned int steps
= (unsigned int) lod
+ 1;
644 * Prepare multiple levels of detail.
648 struct scale_cluster
*scale_clusters
= init_clusters(m
,
649 scale_factor
, input_frame
, steps
,
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);
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
++) {
684 unsigned int index_2
;
685 offset
.set_current_index(index
);
687 offset
= tload_next(tload
, alignment_class
== 2,
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);
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
713 transformation o
= offset
;
716 * Determine centroid data
719 point current
, previous
;
720 centroids(m
, ¤t
, &previous
);
722 if (current
.defined() && previous
.defined()) {
724 o
.set_dimensions(input_frame
);
725 o
.translate((previous
- current
) * o
.scale());
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))
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) {
745 while (is_improved
) {
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
;
764 * Determine projective parameters through a local
768 if (alignment_class
== 2) {
769 ale_pos adj_p
= lowest_error
;
771 if (adj_p
< local_lower
)
774 while (adj_p
>= local_lower
) {
775 transformation test_t
= o
;
780 while (is_improved
) {
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
) {
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
);
796 test_v
= cp_rms_error(m
, test_t
);
798 if (test_v
< lowest_error
) {
799 lowest_error
= test_v
;
812 * Pre-alignment exposure adjustment
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_
);
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))
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
);
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()));
942 pixel_accum tr
= asum
/ bsum
;
943 ui::get()->exp_multiplier(tr
[0], tr
[1], tr
[2]);
944 offset
.set_tonal_multiplier(tr
);
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
++) {
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
) {
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_
);
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
);
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
,
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()));
1038 pixel_accum tr
= asum
/ bsum
;
1039 ui::get()->exp_multiplier(tr
[0], tr
[1], tr
[2]);
1040 offset
.set_tonal_multiplier(tr
);
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);
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
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) {
1121 if (is_fail_default
)
1122 offset
= astate
->get_default();
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;
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.