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/>.
28 /* XXX: Raw import of code from ALE.
31 static struct scale_cluster
*init_clusters(int frame
, ale_pos scale_factor
,
32 const image
*input_frame
, unsigned int steps
,
33 int *local_ax_count
) {
36 * Allocate memory for the array.
39 struct scale_cluster
*scale_clusters
=
40 (struct scale_cluster
*) malloc(steps
* sizeof(struct scale_cluster
));
42 assert (scale_clusters
);
45 ui::get()->memory_error("alignment");
48 * Prepare images and exclusion regions for the highest level
52 scale_clusters
[0].accum
= reference_image
;
54 ui::get()->constructing_lod_clusters(0.0);
55 scale_clusters
[0].input_scale
= scale_factor
;
56 if (scale_factor
< 1.0 && interpolant
== NULL
)
57 scale_clusters
[0].input
= input_frame
->scale(scale_factor
, "alignment");
59 scale_clusters
[0].input
= input_frame
;
61 scale_clusters
[0].certainty
= reference_defined
;
62 scale_clusters
[0].aweight
= alignment_weights
;
63 scale_clusters
[0].ax_parameters
= filter_ax_parameters(frame
, local_ax_count
);
66 * Allocate and determine input frame certainty.
69 if (scale_clusters
[0].input
->get_bayer() != IMAGE_BAYER_NONE
) {
70 scale_clusters
[0].input_certainty
= new_image_bayer_ale_real(
71 scale_clusters
[0].input
->height(),
72 scale_clusters
[0].input
->width(),
73 scale_clusters
[0].input
->depth(),
74 scale_clusters
[0].input
->get_bayer());
76 scale_clusters
[0].input_certainty
= scale_clusters
[0].input
->clone("certainty");
79 for (unsigned int i
= 0; i
< scale_clusters
[0].input_certainty
->height(); i
++)
80 for (unsigned int j
= 0; j
< scale_clusters
[0].input_certainty
->width(); j
++)
81 for (unsigned int k
= 0; k
< 3; k
++)
82 if (scale_clusters
[0].input
->get_channels(i
, j
) & (1 << k
))
83 ((image
*) scale_clusters
[0].input_certainty
)->set_chan(i
, j
, k
,
84 scale_clusters
[0].input
->
85 exp().confidence(scale_clusters
[0].input
->get_pixel(i
, j
))[k
]);
87 scale_ax_parameters(*local_ax_count
, scale_clusters
[0].ax_parameters
, scale_factor
,
88 (scale_factor
< 1.0 && interpolant
== NULL
) ? scale_factor
: (ale_pos
) 1);
90 init_nl_cluster(&(scale_clusters
[0]));
93 * Prepare reduced-detail images and exclusion
97 for (unsigned int step
= 1; step
< steps
; step
++) {
98 ui::get()->constructing_lod_clusters(step
);
99 scale_clusters
[step
].accum
= prepare_lod(scale_clusters
[step
- 1].accum
);
100 scale_clusters
[step
].certainty
= prepare_lod_def(scale_clusters
[step
- 1].certainty
);
101 scale_clusters
[step
].aweight
= prepare_lod_def(scale_clusters
[step
- 1].aweight
);
102 scale_clusters
[step
].ax_parameters
103 = copy_ax_parameters(*local_ax_count
, scale_clusters
[step
- 1].ax_parameters
);
105 double sf
= scale_clusters
[step
- 1].input_scale
/ 2;
106 scale_clusters
[step
].input_scale
= sf
;
108 if (sf
>= 1.0 || interpolant
!= NULL
) {
109 scale_clusters
[step
].input
= scale_clusters
[step
- 1].input
;
110 scale_clusters
[step
].input_certainty
= scale_clusters
[step
- 1].input_certainty
;
111 scale_ax_parameters(*local_ax_count
, scale_clusters
[step
].ax_parameters
, 0.5, 1);
112 } else if (sf
> 0.5) {
113 scale_clusters
[step
].input
= scale_clusters
[step
- 1].input
->scale(sf
, "alignment");
114 scale_clusters
[step
].input_certainty
= scale_clusters
[step
- 1].input
->scale(sf
, "alignment", 1);
115 scale_ax_parameters(*local_ax_count
, scale_clusters
[step
].ax_parameters
, 0.5, sf
);
117 scale_clusters
[step
].input
= scale_clusters
[step
- 1].input
->scale(0.5, "alignment");
118 scale_clusters
[step
].input_certainty
= scale_clusters
[step
- 1].input_certainty
->scale(0.5, "alignment", 1);
119 scale_ax_parameters(*local_ax_count
, scale_clusters
[step
].ax_parameters
, 0.5, 0.5);
122 init_nl_cluster(&(scale_clusters
[step
]));
125 return scale_clusters
;
129 * Align frame m against the reference.
131 * XXX: the transformation class currently combines ordinary
132 * transformations with scaling. This is somewhat convenient for
133 * some things, but can also be confusing. This method, _align(), is
134 * one case where special care must be taken to ensure that the scale
135 * is always set correctly (by using the 'rescale' method).
137 static diff_stat_multi
_align(int m
, int local_gs
, astate_t
*astate
) {
139 const image
*input_frame
= astate
->get_input_frame();
142 * Local upper/lower data, possibly dependent on image
146 ale_pos local_lower
, local_upper
;
147 ale_accum local_gs_mo
;
150 * Select the minimum dimension as the reference.
153 ale_pos reference_size
= input_frame
->height();
154 if (input_frame
->width() < reference_size
)
155 reference_size
= input_frame
->width();
156 ale_accum reference_area
= input_frame
->height()
157 * input_frame
->width();
159 if (perturb_lower_percent
)
160 local_lower
= (double) perturb_lower
161 * (double) reference_size
163 * (double) scale_factor
;
165 local_lower
= perturb_lower
;
167 if (perturb_upper_percent
)
168 local_upper
= (double) perturb_upper
169 * (double) reference_size
171 * (double) scale_factor
;
173 local_upper
= perturb_upper
;
175 local_upper
= pow(2, floor(log(local_upper
) / log(2)));
178 local_gs_mo
= (double) _gs_mo
179 * (double) reference_area
181 * (double) scale_factor
;
183 local_gs_mo
= _gs_mo
;
186 * Logarithms aren't exact, so we divide repeatedly to discover
187 * how many steps will occur, and pass this information to the
192 double step_variable
= local_upper
;
193 while (step_variable
>= local_lower
) {
198 ale_pos perturb
= local_upper
;
201 kept_t
[latest
] = latest_t
;
202 kept_ok
[latest
] = latest_ok
;
206 * Determine how many levels of detail should be prepared, by
207 * calculating the initial (largest) value for the
208 * level-of-detail variable.
211 int lod
= lrint(log(perturb
) / log(2)) - lod_preferred
;
216 while (lod
> 0 && (reference_image
->width() < pow(2, lod
) * min_dimension
217 || reference_image
->height() < pow(2, lod
) * min_dimension
))
220 unsigned int steps
= (unsigned int) lod
+ 1;
223 * Prepare multiple levels of detail.
227 struct scale_cluster
*scale_clusters
= init_clusters(m
,
228 scale_factor
, input_frame
, steps
,
232 * Initialize the default initial transform
235 astate
->init_default();
238 * Set the default transformation.
241 transformation offset
= astate
->get_default();
244 * Establish boundaries
247 offset
.set_current_bounds(reference_image
);
249 ui::get()->alignment_degree_max(offset
.get_coordinate(offset
.stack_depth() - 1).degree
);
251 if (offset
.stack_depth() == 1) {
252 ui::get()->set_steps(step_count
, 0);
254 ui::get()->set_steps(offset
.get_coordinate(offset
.stack_depth() - 1).degree
+ 1, 1);
258 * Load any file-specified transformations
261 for (unsigned int index
= 0; index
< offset
.stack_depth(); index
++) {
263 unsigned int index_2
;
264 offset
.set_current_index(index
);
266 offset
= tload_next(tload
, alignment_class
== 2,
268 &is_default
, offset
.get_current_index() == 0);
270 index_2
= offset
.get_current_index();
272 if (index_2
> index
) {
273 for (unsigned int index_3
= index
; index_3
< index_2
; index_3
++)
274 astate
->set_is_default(index_3
, 1);
279 astate
->set_is_default(index
, is_default
);
282 offset
.set_current_index(0);
284 astate
->init_frame_alignment_primary(&offset
, lod
, perturb
);
287 * Control point alignment
292 transformation o
= offset
;
295 * Determine centroid data
298 point current
, previous
;
299 centroids(m
, ¤t
, &previous
);
301 if (current
.defined() && previous
.defined()) {
303 o
.set_dimensions(input_frame
);
304 o
.translate((previous
- current
) * o
.scale());
309 * Determine rotation for alignment classes other than translation.
312 ale_pos lowest_error
= cp_rms_error(m
, o
);
314 ale_pos rot_lower
= 2 * (double) local_lower
315 / sqrt(pow(scale_clusters
[0].input
->height(), 2)
316 + pow(scale_clusters
[0].input
->width(), 2))
320 if (alignment_class
> 0)
321 for (double rot
= 30; rot
> rot_lower
; rot
/= 2)
322 for (double srot
= -rot
; srot
< rot
* 1.5; srot
+= rot
* 2) {
324 while (is_improved
) {
326 transformation test_t
= o
;
328 * XXX: is this right?
330 test_t
.rotate(current
* o
.scale(), srot
);
331 ale_pos test_v
= cp_rms_error(m
, test_t
);
333 if (test_v
< lowest_error
) {
334 lowest_error
= test_v
;
343 * Determine projective parameters through a local
347 if (alignment_class
== 2) {
348 ale_pos adj_p
= lowest_error
;
350 if (adj_p
< local_lower
)
353 while (adj_p
>= local_lower
) {
354 transformation test_t
= o
;
359 while (is_improved
) {
362 for (int i
= 0; i
< 4; i
++)
363 for (int j
= 0; j
< 2; j
++)
364 for (adj_s
= -adj_p
; adj_s
<= adj_p
; adj_s
+= 2 * adj_p
) {
368 if (perturb_type
== 0)
369 test_t
.gpt_modify(j
, i
, adj_s
);
370 else if (perturb_type
== 1)
371 test_t
.gr_modify(j
, i
, adj_s
);
375 test_v
= cp_rms_error(m
, test_t
);
377 if (test_v
< lowest_error
) {
378 lowest_error
= test_v
;
391 * Pre-alignment exposure adjustment
395 ui::get()->exposure_1();
396 set_exposure_ratio(m
, scale_clusters
[0], offset
, local_ax_count
, 0);
400 * Scale transform for lod
403 for (int lod_
= 0; lod_
< lod
; lod_
++) {
404 transformation s
= offset
;
405 transformation t
= offset
;
407 t
.rescale(1 / (double) 2);
409 if (!(t
.scaled_height() > 0 && t
.scaled_height() < s
.scaled_height())
410 || !(t
.scaled_width() > 0 && t
.scaled_width() < s
.scaled_width())) {
411 perturb
/= pow(2, lod
- lod_
);
419 ui::get()->set_offset(offset
);
421 struct scale_cluster si
= scale_clusters
[lod
];
424 * Projective adjustment value
427 ale_pos adj_p
= perturb
/ pow(2, lod
);
430 * Orientational adjustment value in degrees.
432 * Since rotational perturbation is now specified as an
433 * arclength, we have to convert.
436 ale_pos adj_o
= (double) 2 * (double) perturb
437 / sqrt(pow((double) scale_clusters
[0].input
->height(), (double) 2)
438 + pow((double) scale_clusters
[0].input
->width(), (double) 2))
443 * Barrel distortion adjustment value
446 ale_pos adj_b
= perturb
* bda_mult
;
449 * Global search overlap requirements.
452 local_gs_mo
= (double) local_gs_mo
/ pow(pow(2, lod
), 2);
455 * Alignment statistics.
458 diff_stat_t
here(offset
.elem_bounds());
461 * Current difference (error) value
464 ui::get()->prematching();
465 here
.diff(si
, offset
.get_current_element(), local_ax_count
, m
);
466 ui::get()->set_match(here
.get_error());
469 * Current and modified barrel distortion parameters
472 ale_pos current_bd
[BARREL_DEGREE
];
473 ale_pos modified_bd
[BARREL_DEGREE
];
474 offset
.bd_get(current_bd
);
475 offset
.bd_get(modified_bd
);
478 * Translational global search step
481 if (perturb
>= local_lower
&& local_gs
!= 0 && local_gs
!= 5
482 && (local_gs
!= 6 || astate
->get_is_default(0))) {
484 ui::get()->global_alignment(perturb
, lod
);
485 ui::get()->gs_mo(local_gs_mo
);
487 test_globals(&here
, si
, offset
, local_gs
, adj_p
,
488 local_ax_count
, m
, local_gs_mo
, perturb
);
490 ui::get()->set_match(here
.get_error());
491 ui::get()->set_offset(here
.get_offset());
495 * Perturbation adjustment loop.
498 offset
.set_current_element(here
.get_offset());
500 for (unsigned int i
= 0; i
< offset
.stack_depth(); i
++) {
502 ui::get()->aligning_element(i
, offset
.stack_depth());
504 offset
.set_current_index(i
);
506 ui::get()->start_multi_alignment_element(offset
);
508 ui::get()->set_offset(offset
);
511 astate
->init_frame_alignment_nonprimary(&offset
, lod
, perturb
, i
);
513 if (_exp_register
== 1) {
514 ui::get()->exposure_1();
515 pixel_accum
asum(0, 0, 0), bsum(0, 0, 0);
516 exposure_ratio_iterate
eri(&asum
, &bsum
, scale_clusters
[0], offset
, local_ax_count
, 0,
517 offset
.elem_bounds().scale_to_bounds(scale_clusters
[0].accum
->height(),
518 scale_clusters
[0].accum
->width()));
521 pixel_accum tr
= asum
/ bsum
;
522 ui::get()->exp_multiplier(tr
[0], tr
[1], tr
[2]);
523 offset
.set_tonal_multiplier(tr
);
528 int e_div
= offset
.get_current_coordinate().degree
;
529 ale_pos e_perturb
= perturb
;
530 ale_pos e_adj_p
= adj_p
;
531 ale_pos e_adj_b
= adj_b
;
533 for (int d
= 0; d
< e_div
; d
++) {
545 d2::trans_multi::elem_bounds_t b
= offset
.elem_bounds();
547 for (int dim_satisfied
= 0; e_lod
> 0 && !dim_satisfied
; ) {
548 int height
= scale_clusters
[e_lod
].accum
->height();
549 int width
= scale_clusters
[e_lod
].accum
->width();
551 d2::trans_multi::elem_bounds_int_t bi
= b
.scale_to_bounds(height
, width
);
553 dim_satisfied
= bi
.satisfies_min_dim(min_dimension
);
555 if (!dim_satisfied
) {
562 * Scale transform for lod
565 for (int lod_
= 0; lod_
< e_lod
; lod_
++) {
566 trans_single s
= offset
.get_element(i
);
567 trans_single t
= offset
.get_element(i
);
569 t
.rescale(1 / (double) 2);
571 if (!(t
.scaled_height() > 0 && t
.scaled_height() < s
.scaled_height())
572 || !(t
.scaled_width() > 0 && t
.scaled_width() < s
.scaled_width())) {
573 e_perturb
/= pow(2, e_lod
- lod_
);
577 offset
.set_element(i
, t
);
581 ui::get()->set_offset(offset
);
585 * Announce perturbation size
588 ui::get()->aligning(e_perturb
, e_lod
);
590 si
= scale_clusters
[e_lod
];
592 here
.set_elem_bounds(offset
.elem_bounds());
594 here
.diff(si
, offset
.get_current_element(), local_ax_count
, m
);
598 here
= check_ancestor_path(offset
, si
, here
, local_ax_count
, m
);
600 here
= _align_element(e_perturb
, local_lower
, scale_clusters
,
601 here
, e_adj_p
, adj_o
, e_adj_b
, current_bd
, modified_bd
,
604 offset
.rescale(here
.get_offset().scale() / offset
.scale());
606 offset
.set_current_element(here
.get_offset());
608 if (i
> 0 && _exp_register
== 1) {
609 if (ma_cert_satisfied(scale_clusters
[0], offset
, i
)) {
610 ui::get()->exposure_2();
611 pixel_accum
asum(0, 0, 0), bsum(0, 0, 0);
612 exposure_ratio_iterate
eri(&asum
, &bsum
, scale_clusters
[0], offset
, local_ax_count
, 1,
613 offset
.elem_bounds().scale_to_bounds(scale_clusters
[0].accum
->height(),
614 scale_clusters
[0].accum
->width()));
617 pixel_accum tr
= asum
/ bsum
;
618 ui::get()->exp_multiplier(tr
[0], tr
[1], tr
[2]);
619 offset
.set_tonal_multiplier(tr
);
621 offset
.set_tonal_multiplier(offset
.get_element(offset
.parent_index(i
)).get_tonal_multiplier(point(0, 0)));
623 } else if (_exp_register
== 1) {
624 ui::get()->exposure_2();
625 set_exposure_ratio(m
, scale_clusters
[0], offset
, local_ax_count
, 1);
628 ui::get()->set_offset(offset
);
630 if (i
+ 1 == offset
.stack_depth())
631 ui::get()->alignment_degree_complete(offset
.get_coordinate(i
).degree
);
632 else if (offset
.get_coordinate(i
).degree
!= offset
.get_coordinate(i
+ 1).degree
)
633 ui::get()->alignment_degree_complete(offset
.get_coordinate(i
+ 1).degree
);
636 offset
.set_current_index(0);
639 offset
.set_multi(reference_image
, input_frame
);
642 * Recalculate error on whole frame.
645 ui::get()->postmatching();
646 diff_stat_generic
<transformation
> multi_here(offset
.elem_bounds());
647 multi_here
.diff(scale_clusters
[0], offset
, local_ax_count
, m
);
648 ui::get()->set_match(multi_here
.get_error());
651 * Free the level-of-detail structures
654 final_clusters(scale_clusters
, scale_factor
, steps
);
657 * Ensure that the match meets the threshold.
660 if (threshold_ok(multi_here
.get_error())) {
662 * Update alignment variables
665 astate
->set_default(offset
);
666 astate
->set_final(offset
);
667 ui::get()->alignment_match_ok();
668 } else if (local_gs
== 4) {
671 * Align with outer starting points.
675 * XXX: This probably isn't exactly the right thing to do,
676 * since variables like old_initial_value have been overwritten.
679 diff_stat_multi nested_result
= _align(m
, -1, astate
);
681 if (threshold_ok(nested_result
.get_error())) {
682 return nested_result
;
683 } else if (nested_result
.get_error() < multi_here
.get_error()) {
684 multi_here
= nested_result
;
688 offset
= astate
->get_default();
690 ui::get()->set_match(multi_here
.get_error());
691 ui::get()->alignment_no_match();
693 } else if (local_gs
== -1) {
701 offset
= astate
->get_default();
703 ui::get()->alignment_no_match();
707 * Write the tonal registration multiplier as a comment.
710 pixel trm
= image_rw::exp(m
).get_multiplier();
711 tsave_trm(tsave
, trm
[0], trm
[1], trm
[2]);
714 * Save the transformation information
717 for (unsigned int index
= 0; index
< offset
.stack_depth(); index
++) {
718 offset
.set_current_index(index
);
720 tsave_next(tsave
, offset
, alignment_class
== 2,
721 offset
.get_current_index() == 0);
724 offset
.set_current_index(0);
727 * Update match statistics.
730 match_sum
+= (1 - multi_here
.get_error()) * (ale_accum
) 100;
740 int ale_align(ale_image a
, ale_image b
, ale_trans start
,
741 ale_align_properties align_properties
, ale_trans result
) {
742 #warning function unfinished.