src/align: Preprocessor-exclude raw-imported code.
[libale.git] / src / align.c
blobf8a68c5d030c0bfc439ea07837162adee694b969
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 #if 0
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);
44 if (!scale_clusters)
45 ui::get()->memory_error("alignment");
48 * Prepare images and exclusion regions for the highest level
49 * of detail.
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");
58 else
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());
75 } else {
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
94 * regions.
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);
116 } else {
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
143 * dimensions.
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
162 * (double) 0.01
163 * (double) scale_factor;
164 else
165 local_lower = perturb_lower;
167 if (perturb_upper_percent)
168 local_upper = (double) perturb_upper
169 * (double) reference_size
170 * (double) 0.01
171 * (double) scale_factor;
172 else
173 local_upper = perturb_upper;
175 local_upper = pow(2, floor(log(local_upper) / log(2)));
177 if (gs_mo_percent)
178 local_gs_mo = (double) _gs_mo
179 * (double) reference_area
180 * (double) 0.01
181 * (double) scale_factor;
182 else
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
188 * user interface.
191 int step_count = 0;
192 double step_variable = local_upper;
193 while (step_variable >= local_lower) {
194 step_variable /= 2;
195 step_count++;
198 ale_pos perturb = local_upper;
200 if (_keep) {
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;
213 if (lod < 0)
214 lod = 0;
216 while (lod > 0 && (reference_image->width() < pow(2, lod) * min_dimension
217 || reference_image->height() < pow(2, lod) * min_dimension))
218 lod--;
220 unsigned int steps = (unsigned int) lod + 1;
223 * Prepare multiple levels of detail.
226 int local_ax_count;
227 struct scale_cluster *scale_clusters = init_clusters(m,
228 scale_factor, input_frame, steps,
229 &local_ax_count);
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);
253 } else {
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++) {
262 int is_default = 1;
263 unsigned int index_2;
264 offset.set_current_index(index);
266 offset = tload_next(tload, alignment_class == 2,
267 offset,
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);
276 index = index_2;
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
290 if (local_gs == 5) {
292 transformation o = offset;
295 * Determine centroid data
298 point current, previous;
299 centroids(m, &current, &previous);
301 if (current.defined() && previous.defined()) {
302 o = orig_t;
303 o.set_dimensions(input_frame);
304 o.translate((previous - current) * o.scale());
305 current = previous;
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))
317 * 180
318 / M_PI;
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) {
323 int is_improved = 1;
324 while (is_improved) {
325 is_improved = 0;
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;
335 o = test_t;
336 srot += 3 * rot;
337 is_improved = 1;
343 * Determine projective parameters through a local
344 * minimum search.
347 if (alignment_class == 2) {
348 ale_pos adj_p = lowest_error;
350 if (adj_p < local_lower)
351 adj_p = local_lower;
353 while (adj_p >= local_lower) {
354 transformation test_t = o;
355 int is_improved = 1;
356 ale_pos test_v;
357 ale_pos adj_s;
359 while (is_improved) {
360 is_improved = 0;
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) {
366 test_t = o;
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);
372 else
373 assert(0);
375 test_v = cp_rms_error(m, test_t);
377 if (test_v < lowest_error) {
378 lowest_error = test_v;
379 o = test_t;
380 adj_s += 3 * adj_p;
381 is_improved = 1;
385 adj_p /= 2;
391 * Pre-alignment exposure adjustment
394 if (_exp_register) {
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_);
412 lod = lod_;
413 break;
414 } else {
415 offset = t;
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))
439 * (double) 180
440 / M_PI;
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);
510 if (i > 0) {
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()));
520 eri.run();
521 pixel_accum tr = asum / bsum;
522 ui::get()->exp_multiplier(tr[0], tr[1], tr[2]);
523 offset.set_tonal_multiplier(tr);
527 int e_lod = lod;
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++) {
534 e_adj_b = 0;
535 e_perturb *= 0.5;
536 if (e_lod > 0) {
537 e_lod--;
538 } else {
539 e_adj_p *= 0.5;
543 if (i > 0) {
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) {
556 e_lod--;
557 e_adj_p *= 2;
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_);
574 e_lod = lod_;
575 break;
576 } else {
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);
596 here.confirm();
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,
602 astate, e_lod, si);
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()));
616 eri.run();
617 pixel_accum tr = asum / bsum;
618 ui::get()->exp_multiplier(tr[0], tr[1], tr[2]);
619 offset.set_tonal_multiplier(tr);
620 } else {
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);
638 ui::get()->multi();
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
664 latest_ok = 1;
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;
687 if (is_fail_default)
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) {
695 latest_ok = 0;
696 latest_t = offset;
697 return multi_here;
699 } else {
700 if (is_fail_default)
701 offset = astate->get_default();
702 latest_ok = 0;
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;
731 match_count++;
732 latest = m;
733 latest_t = offset;
735 return multi_here;
738 #endif
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.