2 * Copyright 2002, 2004, 2005, 2006, 2007, 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/>.
29 * Types for scale clusters.
35 * Experimental non-linear scaling approach.
38 struct nl_scale_cluster
{
41 ale_image aweight_max
;
42 ale_image aweight_min
;
45 ale_image input_certainty_max
;
46 ale_image input_certainty_min
;
52 struct scale_cluster
{
57 ale_image input_certainty
;
61 nl_scale_cluster
*nl_scale_clusters
;
66 static struct scale_cluster
*init_clusters(ale_context ac
, int frame
, ale_pos scale_factor
,
67 ale_image input_frame
, ale_trans input_trans
, ale_image reference_image
,
68 ale_image alignment_weights
, unsigned int steps
, ale_filter interpolant
) {
70 int input_bayer
= ale_trans_get_bayer(input_trans
);
71 ale_real input_gamma
= ale_trans_get_gamma(input_trans
);
72 ale_certainty input_certainty
= ale_trans_retain_certainty(input_trans
);
73 ale_real input_black
= ale_trans_get_black(input_trans
);
78 * Allocate memory for the array.
81 struct scale_cluster
*scale_clusters
=
82 (struct scale_cluster
*) malloc(steps
* sizeof(struct scale_cluster
));
88 * Prepare images and exclusion regions for the highest level
92 scale_clusters
[0].accum
= reference_image
;
94 ale_context_signal(ac
, "ui_constructing_lod_clusters", 0.0);
96 scale_clusters
[0].input_scale
= scale_factor
;
97 if (scale_factor
< 1.0 && interpolant
== NULL
)
98 scale_clusters
[0].input
= ale_scale_image(input_frame
, scale_factor
, ale_context_get_color_type(ac
), input_gamma
, input_bayer
);
100 scale_clusters
[0].input
= input_frame
;
102 scale_clusters
[0].aweight
= alignment_weights
;
105 * Allocate and determine input frame certainty.
108 scale_clusters
[0].input_certainty
= ale_clone_image(scale_clusters
[0].input
, ale_context_get_certainty_type(ac
));
110 ale_eval("MAP_PIXEL(%0I, p, CERTAINTY(%1t, p, %2f, GET_AFFINE_PIXEL(%3i, p, %4f)))",
111 scale_clusters
[0].input_certainty
, input_black
, input_certainty
,
112 scale_clusters
[0].input
, (scale_factor
< 1.0 && interpolant
== NULL
) ? 1 : input_gamma
);
115 init_nl_cluster(&(scale_clusters
[0]));
119 * Prepare reduced-detail images and exclusion
123 for (step
= 1; step
< steps
; step
++) {
124 ale_context_signal(ac
, "ui_constructing_lod_clusters", step
);
125 scale_clusters
[step
].accum
= ale_scale_image(scale_clusters
[step
- 1].accum
, 0.5, ale_context_get_color_type(ac
), 1, ALE_BAYER_NONE
);
126 scale_clusters
[step
].aweight
= ale_scale_image_wt(scale_clusters
[step
- 1].aweight
, 0.5, ale_context_get_weight_type(ac
), ALE_BAYER_NONE
);
128 ale_pos sf
= scale_clusters
[step
- 1].input_scale
;
130 scale_clusters
[step
].input_scale
= sf
/ 2;
132 if (sf
>= 2.0 || interpolant
!= NULL
) {
133 scale_clusters
[step
].input
= scale_clusters
[step
- 1].input
;
134 scale_clusters
[step
].input_certainty
= scale_clusters
[step
- 1].input_certainty
;
135 } else if (sf
>= 1.0) {
136 scale_clusters
[step
].input
= ale_scale_image(scale_clusters
[step
- 1].input
, sf
/ 2, ale_context_get_color_type(ac
), input_gamma
, input_bayer
);
137 scale_clusters
[step
].input_certainty
= ale_scale_image_wt(scale_clusters
[step
- 1].input_certainty
, sf
/ 2, ale_context_get_certainty_type(ac
), input_bayer
);
139 scale_clusters
[step
].input
= ale_scale_image(scale_clusters
[step
- 1].input
, 0.5, ale_context_get_color_type(ac
), 1, ALE_BAYER_NONE
);
140 scale_clusters
[step
].input_certainty
= ale_scale_image_wt(scale_clusters
[step
- 1].input_certainty
, 0.5, ale_context_get_certainty_type(ac
), ALE_BAYER_NONE
);
144 init_nl_cluster(&(scale_clusters
[step
]));
148 ale_certainty_release(input_certainty
);
150 return scale_clusters
;
154 * Not-quite-symmetric difference function. Determines the difference in areas
155 * where the arrays overlap. Uses the reference array's notion of pixel positions.
157 * For the purposes of determining the difference, this function divides each
158 * pixel value by the corresponding image's average pixel magnitude, unless we
161 * a) Extending the boundaries of the image, or
163 * b) following the previous frame's transform
165 * If we are doing monte-carlo pixel sampling for alignment, we
166 * typically sample a subset of available pixels; otherwise, we sample
176 ale_accum centroid
[2], centroid_divisor
; \
177 ale_accum de_centroid
[2], de_centroid_v
, de_sum
; \
180 static void run_init(run
*r
, ale_trans offset
) {
185 r
->min
= point_posinf(2);
186 r
->max
= point_neginf(2);
190 r
->centroid_divisor
= 0;
192 r
->de_centroid
[0] = 0;
193 r
->de_centroid
[1] = 0;
195 r
->de_centroid_v
= 0;
202 static void run_add(run
*r
, const run
*s
) {
205 r
->result
+= s
->result
;
206 r
->divisor
+= s
->divisor
;
208 for (d
= 0; d
< 2; d
++) {
209 if (r
->min
.x
[d
] > s
->min
.x
[d
])
210 r
->min
.x
[d
] = s
->min
.x
[d
];
211 if (r
->max
.x
[d
] < s
->max
.x
[d
])
212 r
->max
.x
[d
] = s
->max
.x
[d
];
213 r
->centroid
[d
] += s
->centroid
[d
];
214 r
->de_centroid
[d
] += s
->de_centroid
[d
];
217 r
->centroid_divisor
+= s
->centroid_divisor
;
218 r
->de_centroid_v
+= s
->de_centroid_v
;
219 r
->de_sum
+= s
->de_sum
;
222 static ale_accum
run_get_error(run
*r
, ale_real metric_exponent
) {
223 return pow(r
->result
/ r
->divisor
, 1/(ale_accum
) metric_exponent
);
226 static void run_sample(int f
, scale_cluster c
, int i
, int j
, point t
, point u
,
227 const run
&comparison
) {
229 pixel pa
= c
.accum
->get_pixel(i
, j
);
231 ale_real this_result
[2] = { 0, 0 };
232 ale_real this_divisor
[2] = { 0, 0 };
236 weight
[0] = pixel(1, 1, 1);
237 weight
[1] = pixel(1, 1, 1);
239 pixel tm
= offset
.get_tonal_multiplier(point(i
, j
) + c
.accum
->offset());
241 if (interpolant
!= NULL
) {
242 interpolant
->filtered(i
, j
, &p
[0], &weight
[0], 1, f
);
244 if (weight
[0].min_norm() > ale_real_weight_floor
) {
251 p
[0] = c
.input
->get_bl(t
);
257 p
[1] = c
.input
->get_bl(u
);
266 if (certainty_weights
== 1) {
269 * For speed, use arithmetic interpolation (get_bl(.))
270 * instead of geometric (get_bl(., 1))
273 weight
[0] *= c
.input_certainty
->get_bl(t
);
275 weight
[1] *= c
.input_certainty
->get_bl(u
);
276 weight
[0] *= c
.certainty
->get_pixel(i
, j
);
277 weight
[1] *= c
.certainty
->get_pixel(i
, j
);
280 if (c
.aweight
!= NULL
) {
281 weight
[0] *= c
.aweight
->get_pixel(i
, j
);
282 weight
[1] *= c
.aweight
->get_pixel(i
, j
);
286 * Update sampling area statistics
298 centroid
[0] += (weight
[0][0] + weight
[0][1] + weight
[0][2]) * i
;
299 centroid
[1] += (weight
[0][0] + weight
[0][1] + weight
[0][2]) * j
;
300 centroid_divisor
+= (weight
[0][0] + weight
[0][1] + weight
[0][2]);
303 * Determine alignment type.
306 for (int m
= 0; m
< (u
.defined() ? 2 : 1); m
++)
307 if (channel_alignment_type
== 0) {
309 * Align based on all channels.
313 for (int k
= 0; k
< 3; k
++) {
314 ale_real achan
= pa
[k
];
315 ale_real bchan
= p
[m
][k
];
317 this_result
[m
] += weight
[m
][k
] * pow(fabs(achan
- bchan
), metric_exponent
);
318 this_divisor
[m
] += weight
[m
][k
] * pow(achan
> bchan
? achan
: bchan
, metric_exponent
);
320 } else if (channel_alignment_type
== 1) {
322 * Align based on the green channel.
325 ale_real achan
= pa
[1];
326 ale_real bchan
= p
[m
][1];
328 this_result
[m
] = weight
[m
][1] * pow(fabs(achan
- bchan
), metric_exponent
);
329 this_divisor
[m
] = weight
[m
][1] * pow(achan
> bchan
? achan
: bchan
, metric_exponent
);
330 } else if (channel_alignment_type
== 2) {
332 * Align based on the sum of all channels.
339 for (int k
= 0; k
< 3; k
++) {
342 wsum
+= weight
[m
][k
] / 3;
345 this_result
[m
] = wsum
* pow(fabs(asum
- bsum
), metric_exponent
);
346 this_divisor
[m
] = wsum
* pow(asum
> bsum
? asum
: bsum
, metric_exponent
);
350 // ale_real de = fabs(this_result[0] / this_divisor[0]
351 // - this_result[1] / this_divisor[1]);
352 ale_real de
= fabs(this_result
[0] - this_result
[1]);
354 de_centroid
[0] += de
* (ale_real
) i
;
355 de_centroid
[1] += de
* (ale_real
) j
;
357 de_centroid_v
+= de
* (ale_real
) t
.lengthto(u
);
362 result
+= (this_result
[0]);
363 divisor
+= (this_divisor
[0]);
366 static void run_rescale(ale_pos scale
) {
367 offset
.rescale(scale
);
369 de_centroid
[0] *= scale
;
370 de_centroid
[1] *= scale
;
371 de_centroid_v
*= scale
;
374 static point
run_get_centroid() {
375 point result
= point(centroid
[0] / centroid_divisor
, centroid
[1] / centroid_divisor
);
377 assert (finite(centroid
[0])
378 && finite(centroid
[1])
379 && (result
.defined() || centroid_divisor
== 0));
384 static point
run_get_error_centroid() {
385 point result
= point(de_centroid
[0] / de_sum
, de_centroid
[1] / de_sum
);
390 static ale_pos
run_get_error_perturb() {
391 ale_pos result
= de_centroid_v
/ de_sum
;
396 template<class diff_trans
>
397 class diff_stat_generic
{
399 transformation::elem_bounds_t elem_bounds
;
402 * When non-empty, runs.front() is best, runs.back() is
406 std::vector
<run
> runs
;
409 * old_runs stores the latest available perturbation set for
410 * each multi-alignment element.
413 typedef int run_index
;
414 std::map
<run_index
, run
> old_runs
;
416 static void *diff_subdomain(void *args
);
418 struct subdomain_args
{
419 struct scale_cluster c
;
420 std::vector
<run
> runs
;
423 int i_min
, i_max
, j_min
, j_max
;
427 struct scale_cluster si
;
431 std::vector
<ale_pos
> perturb_multipliers
;
434 void diff(struct scale_cluster c
, const diff_trans
&t
,
435 int _ax_count
, int f
) {
437 if (runs
.size() == 2)
440 runs
.push_back(run(t
));
443 ax_count
= _ax_count
;
446 ui::get()->d2_align_sample_start();
448 if (interpolant
!= NULL
) {
451 * XXX: This has not been tested, and may be completely
455 transformation tt
= transformation::eu_identity();
456 tt
.set_current_element(t
);
457 interpolant
->set_parameters(tt
, c
.input
, c
.accum
->offset());
464 pthread_t
*threads
= (pthread_t
*) malloc(sizeof(pthread_t
) * N
);
465 pthread_attr_t
*thread_attr
= (pthread_attr_t
*) malloc(sizeof(pthread_attr_t
) * N
);
471 subdomain_args
*args
= new subdomain_args
[N
];
473 transformation::elem_bounds_int_t b
= elem_bounds
.scale_to_bounds(c
.accum
->height(), c
.accum
->width());
475 // fprintf(stdout, "[%d %d] [%d %d]\n",
476 // global_i_min, global_i_max, global_j_min, global_j_max);
478 for (int ti
= 0; ti
< N
; ti
++) {
480 args
[ti
].runs
= runs
;
481 args
[ti
].ax_count
= ax_count
;
483 args
[ti
].i_min
= b
.imin
+ ((b
.imax
- b
.imin
) * ti
) / N
;
484 args
[ti
].i_max
= b
.imin
+ ((b
.imax
- b
.imin
) * (ti
+ 1)) / N
;
485 args
[ti
].j_min
= b
.jmin
;
486 args
[ti
].j_max
= b
.jmax
;
487 args
[ti
].subdomain
= ti
;
489 pthread_attr_init(&thread_attr
[ti
]);
490 pthread_attr_setdetachstate(&thread_attr
[ti
], PTHREAD_CREATE_JOINABLE
);
491 pthread_create(&threads
[ti
], &thread_attr
[ti
], diff_subdomain
, &args
[ti
]);
493 diff_subdomain(&args
[ti
]);
497 for (int ti
= 0; ti
< N
; ti
++) {
499 pthread_join(threads
[ti
], NULL
);
501 runs
.back().add(args
[ti
].runs
.back());
511 ui::get()->d2_align_sample_stop();
517 std::vector
<diff_trans
> t_array
;
519 for (unsigned int r
= 0; r
< runs
.size(); r
++) {
520 t_array
.push_back(runs
[r
].offset
);
525 for (unsigned int r
= 0; r
< t_array
.size(); r
++)
526 diff(si
, t_array
[r
], ax_count
, frame
);
532 assert(runs
.size() >= 2);
533 assert(runs
[0].offset
.scale() == runs
[1].offset
.scale());
535 return (runs
[1].get_error() < runs
[0].get_error()
536 || (!finite(runs
[0].get_error()) && finite(runs
[1].get_error())));
539 int better_defined() {
540 assert(runs
.size() >= 2);
541 assert(runs
[0].offset
.scale() == runs
[1].offset
.scale());
543 return (runs
[1].get_error() < runs
[0].get_error());
546 diff_stat_generic(transformation::elem_bounds_t e
)
547 : runs(), old_runs(), perturb_multipliers() {
551 run_index
get_run_index(unsigned int perturb_index
) {
552 return perturb_index
;
555 run
&get_run(unsigned int perturb_index
) {
556 run_index index
= get_run_index(perturb_index
);
558 assert(old_runs
.count(index
));
559 return old_runs
[index
];
562 void rescale(ale_pos scale
, scale_cluster _si
) {
563 assert(runs
.size() == 1);
567 runs
[0].rescale(scale
);
572 ~diff_stat_generic() {
575 diff_stat_generic
&operator=(const diff_stat_generic
&dst
) {
577 * Copy run information.
580 old_runs
= dst
.old_runs
;
583 * Copy diff variables
586 ax_count
= dst
.ax_count
;
588 perturb_multipliers
= dst
.perturb_multipliers
;
589 elem_bounds
= dst
.elem_bounds
;
594 diff_stat_generic(const diff_stat_generic
&dst
) : runs(), old_runs(),
595 perturb_multipliers() {
599 void set_elem_bounds(transformation::elem_bounds_t e
) {
603 ale_accum
get_result() {
604 assert(runs
.size() == 1);
605 return runs
[0].result
;
608 ale_accum
get_divisor() {
609 assert(runs
.size() == 1);
610 return runs
[0].divisor
;
613 diff_trans
get_offset() {
614 assert(runs
.size() == 1);
615 return runs
[0].offset
;
618 int operator!=(diff_stat_generic
¶m
) {
619 return (get_error() != param
.get_error());
622 int operator==(diff_stat_generic
¶m
) {
623 return !(operator!=(param
));
626 ale_pos
get_error_perturb() {
627 assert(runs
.size() == 1);
628 return runs
[0].get_error_perturb();
631 ale_accum
get_error() const {
632 assert(runs
.size() == 1);
633 return runs
[0].get_error();
638 * Get the set of transformations produced by a given perturbation
640 void get_perturb_set(std::vector
<diff_trans
> *set
,
641 ale_pos adj_p
, ale_pos adj_o
, ale_pos adj_b
,
642 ale_pos
*current_bd
, ale_pos
*modified_bd
,
643 std::vector
<ale_pos
> multipliers
= std::vector
<ale_pos
>()) {
645 assert(runs
.size() == 1);
647 diff_trans
test_t(diff_trans::eu_identity());
650 * Translational or euclidean transformation
653 for (unsigned int i
= 0; i
< 2; i
++)
654 for (unsigned int s
= 0; s
< 2; s
++) {
656 if (!multipliers
.size())
657 multipliers
.push_back(1);
659 assert(finite(multipliers
[0]));
661 test_t
= get_offset();
663 // test_t.eu_modify(i, (s ? -adj_p : adj_p) * multipliers[0]);
664 test_t
.translate((i
? point(1, 0) : point(0, 1))
665 * (s
? -adj_p
: adj_p
)
668 test_t
.snap(adj_p
/ 2);
670 set
->push_back(test_t
);
671 multipliers
.erase(multipliers
.begin());
675 if (alignment_class
> 0)
676 for (unsigned int s
= 0; s
< 2; s
++) {
678 if (!multipliers
.size())
679 multipliers
.push_back(1);
681 assert(multipliers
.size());
682 assert(finite(multipliers
[0]));
684 if (!(adj_o
* multipliers
[0] < rot_max
))
687 ale_pos adj_s
= (s
? 1 : -1) * adj_o
* multipliers
[0];
689 test_t
= get_offset();
691 run_index ori
= get_run_index(set
->size());
692 point centroid
= point::undefined();
694 if (!old_runs
.count(ori
))
695 ori
= get_run_index(0);
697 if (!centroid
.finite() && old_runs
.count(ori
)) {
698 centroid
= old_runs
[ori
].get_error_centroid();
700 if (!centroid
.finite())
701 centroid
= old_runs
[ori
].get_centroid();
703 centroid
*= test_t
.scale()
704 / old_runs
[ori
].offset
.scale();
707 if (!centroid
.finite() && !test_t
.is_projective()) {
708 test_t
.eu_modify(2, adj_s
);
709 } else if (!centroid
.finite()) {
710 centroid
= point(si
.input
->height() / 2,
711 si
.input
->width() / 2);
713 test_t
.rotate(centroid
+ si
.accum
->offset(),
716 test_t
.rotate(centroid
+ si
.accum
->offset(),
720 test_t
.snap(adj_p
/ 2);
722 set
->push_back(test_t
);
723 multipliers
.erase(multipliers
.begin());
726 if (alignment_class
== 2) {
729 * Projective transformation
732 for (unsigned int i
= 0; i
< 4; i
++)
733 for (unsigned int j
= 0; j
< 2; j
++)
734 for (unsigned int s
= 0; s
< 2; s
++) {
736 if (!multipliers
.size())
737 multipliers
.push_back(1);
739 assert(multipliers
.size());
740 assert(finite(multipliers
[0]));
742 ale_pos adj_s
= (s
? -1 : 1) * adj_p
* multipliers
[0];
744 test_t
= get_offset();
746 if (perturb_type
== 0)
747 test_t
.gpt_modify_bounded(j
, i
, adj_s
, elem_bounds
.scale_to_bounds(si
.accum
->height(), si
.accum
->width()));
748 else if (perturb_type
== 1)
749 test_t
.gr_modify(j
, i
, adj_s
);
753 test_t
.snap(adj_p
/ 2);
755 set
->push_back(test_t
);
756 multipliers
.erase(multipliers
.begin());
765 if (bda_mult
!= 0 && adj_b
!= 0) {
767 for (unsigned int d
= 0; d
< get_offset().bd_count(); d
++)
768 for (unsigned int s
= 0; s
< 2; s
++) {
770 if (!multipliers
.size())
771 multipliers
.push_back(1);
773 assert (multipliers
.size());
774 assert (finite(multipliers
[0]));
776 ale_pos adj_s
= (s
? -1 : 1) * adj_b
* multipliers
[0];
778 if (bda_rate
> 0 && fabs(modified_bd
[d
] + adj_s
- current_bd
[d
]) > bda_rate
)
781 diff_trans test_t
= get_offset();
783 test_t
.bd_modify(d
, adj_s
);
785 set
->push_back(test_t
);
791 assert(runs
.size() == 2);
797 assert(runs
.size() == 2);
801 void perturb_test(ale_pos perturb
, ale_pos adj_p
, ale_pos adj_o
, ale_pos adj_b
,
802 ale_pos
*current_bd
, ale_pos
*modified_bd
, int stable
) {
804 assert(runs
.size() == 1);
806 std::vector
<diff_trans
> t_set
;
808 if (perturb_multipliers
.size() == 0) {
809 get_perturb_set(&t_set
, adj_p
, adj_o
, adj_b
,
810 current_bd
, modified_bd
);
812 for (unsigned int i
= 0; i
< t_set
.size(); i
++) {
813 diff_stat_generic test
= *this;
815 test
.diff(si
, t_set
[i
], ax_count
, frame
);
819 if (finite(test
.get_error_perturb()))
820 perturb_multipliers
.push_back(adj_p
/ test
.get_error_perturb());
822 perturb_multipliers
.push_back(1);
829 get_perturb_set(&t_set
, adj_p
, adj_o
, adj_b
, current_bd
, modified_bd
,
830 perturb_multipliers
);
832 int found_unreliable
= 1;
833 std::vector
<int> tested(t_set
.size(), 0);
835 for (unsigned int i
= 0; i
< t_set
.size(); i
++) {
836 run_index ori
= get_run_index(i
);
839 * Check for stability
842 && old_runs
.count(ori
)
843 && old_runs
[ori
].offset
== t_set
[i
])
847 std::vector
<ale_pos
> perturb_multipliers_original
= perturb_multipliers
;
849 while (found_unreliable
) {
851 found_unreliable
= 0;
853 for (unsigned int i
= 0; i
< t_set
.size(); i
++) {
858 diff(si
, t_set
[i
], ax_count
, frame
);
860 if (!(i
< perturb_multipliers
.size())
861 || !finite(perturb_multipliers
[i
])) {
863 perturb_multipliers
.resize(i
+ 1);
865 if (finite(perturb_multipliers
[i
])
867 && finite(adj_p
/ runs
[1].get_error_perturb())) {
868 perturb_multipliers
[i
] =
869 adj_p
/ runs
[1].get_error_perturb();
871 found_unreliable
= 1;
873 perturb_multipliers
[i
] = 1;
878 run_index ori
= get_run_index(i
);
880 if (old_runs
.count(ori
) == 0)
881 old_runs
.insert(std::pair
<run_index
, run
>(ori
, runs
[1]));
883 old_runs
[ori
] = runs
[1];
885 if (finite(perturb_multipliers_original
[i
])
886 && finite(runs
[1].get_error_perturb())
888 && finite(perturb_multipliers_original
[i
] * adj_p
/ runs
[1].get_error_perturb()))
889 perturb_multipliers
[i
] = perturb_multipliers_original
[i
]
890 * adj_p
/ runs
[1].get_error_perturb();
892 perturb_multipliers
[i
] = 1;
897 && runs
[1].get_error() < runs
[0].get_error()
898 && perturb_multipliers
[i
]
899 / perturb_multipliers_original
[i
] < 2) {
910 if (!found_unreliable
)
917 typedef diff_stat_generic
<trans_single
> diff_stat_t
;
918 typedef diff_stat_generic
<trans_multi
> diff_stat_multi
;
922 * Adjust exposure for an aligned frame B against reference A.
924 * Expects full-LOD images.
926 * Note: This method does not use any weighting, by certainty or
927 * otherwise, in the first exposure registration pass, as any bias of
928 * weighting according to color may also bias the exposure registration
929 * result; it does use weighting, including weighting by certainty
930 * (even if certainty weighting is not specified), in the second pass,
931 * under the assumption that weighting by certainty improves handling
932 * of out-of-range highlights, and that bias of exposure measurements
933 * according to color may generally be less harmful after spatial
934 * registration has been performed.
936 class exposure_ratio_iterate
: public thread::decompose_domain
{
941 struct scale_cluster c
;
942 const transformation
&t
;
946 void prepare_subdomains(unsigned int N
) {
947 asums
= new pixel_accum
[N
];
948 bsums
= new pixel_accum
[N
];
950 void subdomain_algorithm(unsigned int thread
,
951 int i_min
, int i_max
, int j_min
, int j_max
) {
953 point offset
= c
.accum
->offset();
955 for (mc_iterate
m(i_min
, i_max
, j_min
, j_max
, thread
); !m
.done(); m
++) {
957 unsigned int i
= (unsigned int) m
.get_i();
958 unsigned int j
= (unsigned int) m
.get_j();
960 if (ref_excluded(i
, j
, offset
, c
.ax_parameters
, ax_count
))
969 q
= (c
.input_scale
< 1.0 && interpolant
== NULL
)
970 ? t
.scaled_inverse_transform(
971 point(i
+ offset
[0], j
+ offset
[1]))
972 : t
.unscaled_inverse_transform(
973 point(i
+ offset
[0], j
+ offset
[1]));
976 * Check that the transformed coordinates are within
977 * the boundaries of array c.input, that they are not
978 * subject to exclusion, and that the weight value in
979 * the accumulated array is nonzero.
982 if (input_excluded(q
[0], q
[1], c
.ax_parameters
, ax_count
))
986 && q
[0] <= c
.input
->height() - 1
988 && q
[1] <= c
.input
->width() - 1
989 && ((pixel
) c
.certainty
->get_pixel(i
, j
)).minabs_norm() != 0) {
990 pixel a
= c
.accum
->get_pixel(i
, j
);
993 b
= c
.input
->get_bl(q
);
995 pixel weight
= ((c
.aweight
&& pass_number
)
996 ? (pixel
) c
.aweight
->get_pixel(i
, j
)
999 ? psqrt(c
.certainty
->get_pixel(i
, j
)
1000 * c
.input_certainty
->get_bl(q
, 1))
1003 asums
[thread
] += a
* weight
;
1004 bsums
[thread
] += b
* weight
;
1009 void finish_subdomains(unsigned int N
) {
1010 for (unsigned int n
= 0; n
< N
; n
++) {
1018 exposure_ratio_iterate(pixel_accum
*_asum
,
1020 struct scale_cluster _c
,
1021 const transformation
&_t
,
1023 int _pass_number
) : decompose_domain(0, _c
.accum
->height(),
1024 0, _c
.accum
->width()),
1030 ax_count
= _ax_count
;
1031 pass_number
= _pass_number
;
1034 exposure_ratio_iterate(pixel_accum
*_asum
,
1036 struct scale_cluster _c
,
1037 const transformation
&_t
,
1040 transformation::elem_bounds_int_t b
) : decompose_domain(b
.imin
, b
.imax
,
1047 ax_count
= _ax_count
;
1048 pass_number
= _pass_number
;
1052 static void set_exposure_ratio(unsigned int m
, struct scale_cluster c
,
1053 const transformation
&t
, int ax_count
, int pass_number
) {
1055 if (_exp_register
== 2) {
1057 * Use metadata only.
1059 ale_real gain_multiplier
= image_rw::exp(m
).get_gain_multiplier();
1060 pixel multiplier
= pixel(gain_multiplier
, gain_multiplier
, gain_multiplier
);
1062 image_rw::exp(m
).set_multiplier(multiplier
);
1063 ui::get()->exp_multiplier(multiplier
[0],
1070 pixel_accum
asum(0, 0, 0), bsum(0, 0, 0);
1072 exposure_ratio_iterate
eri(&asum
, &bsum
, c
, t
, ax_count
, pass_number
);
1075 // std::cerr << (asum / bsum) << " ";
1077 pixel_accum new_multiplier
;
1079 new_multiplier
= asum
/ bsum
* image_rw::exp(m
).get_multiplier();
1081 if (finite(new_multiplier
[0])
1082 && finite(new_multiplier
[1])
1083 && finite(new_multiplier
[2])) {
1084 image_rw::exp(m
).set_multiplier(new_multiplier
);
1085 ui::get()->exp_multiplier(new_multiplier
[0],
1091 static diff_stat_t
_align_element(ale_pos perturb
, ale_pos local_lower
,
1092 scale_cluster
*scale_clusters
, diff_stat_t here
,
1093 ale_pos adj_p
, ale_pos adj_o
, ale_pos adj_b
,
1094 ale_pos
*current_bd
, ale_pos
*modified_bd
,
1095 astate_t
*astate
, int lod
, scale_cluster si
) {
1098 * Run initial tests to get perturbation multipliers and error
1102 std::vector
<d2::trans_single
> t_set
;
1104 here
.get_perturb_set(&t_set
, adj_p
, adj_o
, adj_b
, current_bd
, modified_bd
);
1106 int stable_count
= 0;
1108 while (perturb
>= local_lower
) {
1110 ui::get()->alignment_dims(scale_clusters
[lod
].accum
->height(), scale_clusters
[lod
].accum
->width(),
1111 scale_clusters
[lod
].input
->height(), scale_clusters
[lod
].input
->width());
1114 * Orientational adjustment value in degrees.
1116 * Since rotational perturbation is now specified as an
1117 * arclength, we have to convert.
1120 ale_pos adj_o
= 2 * (double) perturb
1121 / sqrt(pow(scale_clusters
[0].input
->height(), 2)
1122 + pow(scale_clusters
[0].input
->width(), 2))
1127 * Barrel distortion adjustment value
1130 ale_pos adj_b
= perturb
* bda_mult
;
1132 trans_single old_offset
= here
.get_offset();
1134 here
.perturb_test(perturb
, adj_p
, adj_o
, adj_b
, current_bd
, modified_bd
,
1137 if (here
.get_offset() == old_offset
)
1142 if (stable_count
== 3) {
1149 && lod
> lrint(log(perturb
) / log(2)) - lod_preferred
) {
1152 * Work with images twice as large
1156 si
= scale_clusters
[lod
];
1159 * Rescale the transforms.
1162 ale_pos rescale_factor
= (double) scale_factor
1163 / (double) pow(2, lod
)
1164 / (double) here
.get_offset().scale();
1166 here
.rescale(rescale_factor
, si
);
1169 adj_p
= perturb
/ pow(2, lod
);
1176 ui::get()->alignment_perturbation_level(perturb
, lod
);
1179 ui::get()->set_match(here
.get_error());
1180 ui::get()->set_offset(here
.get_offset());
1184 ale_pos rescale_factor
= (double) scale_factor
1185 / (double) here
.get_offset().scale();
1187 here
.rescale(rescale_factor
, scale_clusters
[0]);
1194 * Align frame m against the reference.
1196 * XXX: the transformation class currently combines ordinary
1197 * transformations with scaling. This is somewhat convenient for
1198 * some things, but can also be confusing. This method, _align(), is
1199 * one case where special care must be taken to ensure that the scale
1200 * is always set correctly (by using the 'rescale' method).
1202 static diff_stat_multi
_align(int m
, int local_gs
, astate_t
*astate
) {
1204 ale_image input_frame
= astate
->get_input_frame();
1207 * Local upper/lower data, possibly dependent on image
1211 ale_pos local_lower
, local_upper
;
1212 ale_accum local_gs_mo
;
1215 * Select the minimum dimension as the reference.
1218 ale_pos reference_size
= input_frame
->height();
1219 if (input_frame
->width() < reference_size
)
1220 reference_size
= input_frame
->width();
1221 ale_accum reference_area
= input_frame
->height()
1222 * input_frame
->width();
1224 if (perturb_lower_percent
)
1225 local_lower
= (double) perturb_lower
1226 * (double) reference_size
1228 * (double) scale_factor
;
1230 local_lower
= perturb_lower
;
1232 if (perturb_upper_percent
)
1233 local_upper
= (double) perturb_upper
1234 * (double) reference_size
1236 * (double) scale_factor
;
1238 local_upper
= perturb_upper
;
1240 local_upper
= pow(2, floor(log(local_upper
) / log(2)));
1243 local_gs_mo
= (double) _gs_mo
1244 * (double) reference_area
1246 * (double) scale_factor
;
1248 local_gs_mo
= _gs_mo
;
1251 * Logarithms aren't exact, so we divide repeatedly to discover
1252 * how many steps will occur, and pass this information to the
1257 double step_variable
= local_upper
;
1258 while (step_variable
>= local_lower
) {
1263 ale_pos perturb
= local_upper
;
1266 kept_t
[latest
] = latest_t
;
1267 kept_ok
[latest
] = latest_ok
;
1271 * Determine how many levels of detail should be prepared, by
1272 * calculating the initial (largest) value for the
1273 * level-of-detail variable.
1276 int lod
= lrint(log(perturb
) / log(2)) - lod_preferred
;
1281 while (lod
> 0 && (reference_image
->width() < pow(2, lod
) * min_dimension
1282 || reference_image
->height() < pow(2, lod
) * min_dimension
))
1285 unsigned int steps
= (unsigned int) lod
+ 1;
1288 * Prepare multiple levels of detail.
1292 struct scale_cluster
*scale_clusters
= init_clusters(m
,
1293 scale_factor
, input_frame
, steps
,
1296 #error add check for non-NULL return
1299 * Initialize the default initial transform
1302 astate
->init_default();
1305 * Set the default transformation.
1308 transformation offset
= astate
->get_default();
1311 * Establish boundaries
1314 offset
.set_current_bounds(reference_image
);
1316 ui::get()->alignment_degree_max(offset
.get_coordinate(offset
.stack_depth() - 1).degree
);
1318 if (offset
.stack_depth() == 1) {
1319 ui::get()->set_steps(step_count
, 0);
1321 ui::get()->set_steps(offset
.get_coordinate(offset
.stack_depth() - 1).degree
+ 1, 1);
1325 * Load any file-specified transformations
1328 for (unsigned int index
= 0; index
< offset
.stack_depth(); index
++) {
1330 unsigned int index_2
;
1331 offset
.set_current_index(index
);
1333 offset
= tload_next(tload
, alignment_class
== 2,
1335 &is_default
, offset
.get_current_index() == 0);
1337 index_2
= offset
.get_current_index();
1339 if (index_2
> index
) {
1340 for (unsigned int index_3
= index
; index_3
< index_2
; index_3
++)
1341 astate
->set_is_default(index_3
, 1);
1346 astate
->set_is_default(index
, is_default
);
1349 offset
.set_current_index(0);
1351 astate
->init_frame_alignment_primary(&offset
, lod
, perturb
);
1354 * Control point alignment
1357 if (local_gs
== 5) {
1359 transformation o
= offset
;
1362 * Determine centroid data
1365 point current
, previous
;
1366 centroids(m
, ¤t
, &previous
);
1368 if (current
.defined() && previous
.defined()) {
1370 o
.set_dimensions(input_frame
);
1371 o
.translate((previous
- current
) * o
.scale());
1376 * Determine rotation for alignment classes other than translation.
1379 ale_pos lowest_error
= cp_rms_error(m
, o
);
1381 ale_pos rot_lower
= 2 * (double) local_lower
1382 / sqrt(pow(scale_clusters
[0].input
->height(), 2)
1383 + pow(scale_clusters
[0].input
->width(), 2))
1387 if (alignment_class
> 0)
1388 for (double rot
= 30; rot
> rot_lower
; rot
/= 2)
1389 for (double srot
= -rot
; srot
< rot
* 1.5; srot
+= rot
* 2) {
1390 int is_improved
= 1;
1391 while (is_improved
) {
1393 transformation test_t
= o
;
1395 * XXX: is this right?
1397 test_t
.rotate(current
* o
.scale(), srot
);
1398 ale_pos test_v
= cp_rms_error(m
, test_t
);
1400 if (test_v
< lowest_error
) {
1401 lowest_error
= test_v
;
1410 * Determine projective parameters through a local
1414 if (alignment_class
== 2) {
1415 ale_pos adj_p
= lowest_error
;
1417 if (adj_p
< local_lower
)
1418 adj_p
= local_lower
;
1420 while (adj_p
>= local_lower
) {
1421 transformation test_t
= o
;
1422 int is_improved
= 1;
1426 while (is_improved
) {
1429 for (int i
= 0; i
< 4; i
++)
1430 for (int j
= 0; j
< 2; j
++)
1431 for (adj_s
= -adj_p
; adj_s
<= adj_p
; adj_s
+= 2 * adj_p
) {
1435 if (perturb_type
== 0)
1436 test_t
.gpt_modify(j
, i
, adj_s
);
1437 else if (perturb_type
== 1)
1438 test_t
.gr_modify(j
, i
, adj_s
);
1442 test_v
= cp_rms_error(m
, test_t
);
1444 if (test_v
< lowest_error
) {
1445 lowest_error
= test_v
;
1458 * Pre-alignment exposure adjustment
1461 if (_exp_register
) {
1462 ui::get()->exposure_1();
1463 set_exposure_ratio(m
, scale_clusters
[0], offset
, local_ax_count
, 0);
1467 * Scale transform for lod
1470 for (int lod_
= 0; lod_
< lod
; lod_
++) {
1471 transformation s
= offset
;
1472 transformation t
= offset
;
1474 t
.rescale(1 / (double) 2);
1476 if (!(t
.scaled_height() > 0 && t
.scaled_height() < s
.scaled_height())
1477 || !(t
.scaled_width() > 0 && t
.scaled_width() < s
.scaled_width())) {
1478 perturb
/= pow(2, lod
- lod_
);
1486 ui::get()->set_offset(offset
);
1488 struct scale_cluster si
= scale_clusters
[lod
];
1491 * Projective adjustment value
1494 ale_pos adj_p
= perturb
/ pow(2, lod
);
1497 * Orientational adjustment value in degrees.
1499 * Since rotational perturbation is now specified as an
1500 * arclength, we have to convert.
1503 ale_pos adj_o
= (double) 2 * (double) perturb
1504 / sqrt(pow((double) scale_clusters
[0].input
->height(), (double) 2)
1505 + pow((double) scale_clusters
[0].input
->width(), (double) 2))
1510 * Barrel distortion adjustment value
1513 ale_pos adj_b
= perturb
* bda_mult
;
1516 * Global search overlap requirements.
1519 local_gs_mo
= (double) local_gs_mo
/ pow(pow(2, lod
), 2);
1522 * Alignment statistics.
1525 diff_stat_t
here(offset
.elem_bounds());
1528 * Current difference (error) value
1531 ui::get()->prematching();
1532 here
.diff(si
, offset
.get_current_element(), local_ax_count
, m
);
1533 ui::get()->set_match(here
.get_error());
1536 * Current and modified barrel distortion parameters
1539 ale_pos current_bd
[BARREL_DEGREE
];
1540 ale_pos modified_bd
[BARREL_DEGREE
];
1541 offset
.bd_get(current_bd
);
1542 offset
.bd_get(modified_bd
);
1545 * Translational global search step
1548 if (perturb
>= local_lower
&& local_gs
!= 0 && local_gs
!= 5
1549 && (local_gs
!= 6 || astate
->get_is_default(0))) {
1551 ui::get()->global_alignment(perturb
, lod
);
1552 ui::get()->gs_mo(local_gs_mo
);
1554 test_globals(&here
, si
, offset
, local_gs
, adj_p
,
1555 local_ax_count
, m
, local_gs_mo
, perturb
);
1557 ui::get()->set_match(here
.get_error());
1558 ui::get()->set_offset(here
.get_offset());
1562 * Perturbation adjustment loop.
1565 offset
.set_current_element(here
.get_offset());
1567 for (unsigned int i
= 0; i
< offset
.stack_depth(); i
++) {
1569 ui::get()->aligning_element(i
, offset
.stack_depth());
1571 offset
.set_current_index(i
);
1573 ui::get()->start_multi_alignment_element(offset
);
1575 ui::get()->set_offset(offset
);
1578 astate
->init_frame_alignment_nonprimary(&offset
, lod
, perturb
, i
);
1580 if (_exp_register
== 1) {
1581 ui::get()->exposure_1();
1582 pixel_accum
asum(0, 0, 0), bsum(0, 0, 0);
1583 exposure_ratio_iterate
eri(&asum
, &bsum
, scale_clusters
[0], offset
, local_ax_count
, 0,
1584 offset
.elem_bounds().scale_to_bounds(scale_clusters
[0].accum
->height(),
1585 scale_clusters
[0].accum
->width()));
1588 pixel_accum tr
= asum
/ bsum
;
1589 ui::get()->exp_multiplier(tr
[0], tr
[1], tr
[2]);
1590 offset
.set_tonal_multiplier(tr
);
1595 int e_div
= offset
.get_current_coordinate().degree
;
1596 ale_pos e_perturb
= perturb
;
1597 ale_pos e_adj_p
= adj_p
;
1598 ale_pos e_adj_b
= adj_b
;
1600 for (int d
= 0; d
< e_div
; d
++) {
1612 d2::trans_multi::elem_bounds_t b
= offset
.elem_bounds();
1614 for (int dim_satisfied
= 0; e_lod
> 0 && !dim_satisfied
; ) {
1615 int height
= scale_clusters
[e_lod
].accum
->height();
1616 int width
= scale_clusters
[e_lod
].accum
->width();
1618 d2::trans_multi::elem_bounds_int_t bi
= b
.scale_to_bounds(height
, width
);
1620 dim_satisfied
= bi
.satisfies_min_dim(min_dimension
);
1622 if (!dim_satisfied
) {
1629 * Scale transform for lod
1632 for (int lod_
= 0; lod_
< e_lod
; lod_
++) {
1633 trans_single s
= offset
.get_element(i
);
1634 trans_single t
= offset
.get_element(i
);
1636 t
.rescale(1 / (double) 2);
1638 if (!(t
.scaled_height() > 0 && t
.scaled_height() < s
.scaled_height())
1639 || !(t
.scaled_width() > 0 && t
.scaled_width() < s
.scaled_width())) {
1640 e_perturb
/= pow(2, e_lod
- lod_
);
1644 offset
.set_element(i
, t
);
1648 ui::get()->set_offset(offset
);
1652 * Announce perturbation size
1655 ui::get()->aligning(e_perturb
, e_lod
);
1657 si
= scale_clusters
[e_lod
];
1659 here
.set_elem_bounds(offset
.elem_bounds());
1661 here
.diff(si
, offset
.get_current_element(), local_ax_count
, m
);
1665 here
= check_ancestor_path(offset
, si
, here
, local_ax_count
, m
);
1667 here
= _align_element(e_perturb
, local_lower
, scale_clusters
,
1668 here
, e_adj_p
, adj_o
, e_adj_b
, current_bd
, modified_bd
,
1671 offset
.rescale(here
.get_offset().scale() / offset
.scale());
1673 offset
.set_current_element(here
.get_offset());
1675 if (i
> 0 && _exp_register
== 1) {
1676 if (ma_cert_satisfied(scale_clusters
[0], offset
, i
)) {
1677 ui::get()->exposure_2();
1678 pixel_accum
asum(0, 0, 0), bsum(0, 0, 0);
1679 exposure_ratio_iterate
eri(&asum
, &bsum
, scale_clusters
[0], offset
, local_ax_count
, 1,
1680 offset
.elem_bounds().scale_to_bounds(scale_clusters
[0].accum
->height(),
1681 scale_clusters
[0].accum
->width()));
1684 pixel_accum tr
= asum
/ bsum
;
1685 ui::get()->exp_multiplier(tr
[0], tr
[1], tr
[2]);
1686 offset
.set_tonal_multiplier(tr
);
1688 offset
.set_tonal_multiplier(offset
.get_element(offset
.parent_index(i
)).get_tonal_multiplier(point(0, 0)));
1690 } else if (_exp_register
== 1) {
1691 ui::get()->exposure_2();
1692 set_exposure_ratio(m
, scale_clusters
[0], offset
, local_ax_count
, 1);
1695 ui::get()->set_offset(offset
);
1697 if (i
+ 1 == offset
.stack_depth())
1698 ui::get()->alignment_degree_complete(offset
.get_coordinate(i
).degree
);
1699 else if (offset
.get_coordinate(i
).degree
!= offset
.get_coordinate(i
+ 1).degree
)
1700 ui::get()->alignment_degree_complete(offset
.get_coordinate(i
+ 1).degree
);
1703 offset
.set_current_index(0);
1706 offset
.set_multi(reference_image
, input_frame
);
1709 * Recalculate error on whole frame.
1712 ui::get()->postmatching();
1713 diff_stat_generic
<transformation
> multi_here(offset
.elem_bounds());
1714 multi_here
.diff(scale_clusters
[0], offset
, local_ax_count
, m
);
1715 ui::get()->set_match(multi_here
.get_error());
1718 * Free the level-of-detail structures
1721 final_clusters(scale_clusters
, scale_factor
, steps
);
1724 * Ensure that the match meets the threshold.
1727 if (threshold_ok(multi_here
.get_error())) {
1729 * Update alignment variables
1732 astate
->set_default(offset
);
1733 astate
->set_final(offset
);
1734 ui::get()->alignment_match_ok();
1735 } else if (local_gs
== 4) {
1738 * Align with outer starting points.
1742 * XXX: This probably isn't exactly the right thing to do,
1743 * since variables like old_initial_value have been overwritten.
1746 diff_stat_multi nested_result
= _align(m
, -1, astate
);
1748 if (threshold_ok(nested_result
.get_error())) {
1749 return nested_result
;
1750 } else if (nested_result
.get_error() < multi_here
.get_error()) {
1751 multi_here
= nested_result
;
1754 if (is_fail_default
)
1755 offset
= astate
->get_default();
1757 ui::get()->set_match(multi_here
.get_error());
1758 ui::get()->alignment_no_match();
1760 } else if (local_gs
== -1) {
1767 if (is_fail_default
)
1768 offset
= astate
->get_default();
1770 ui::get()->alignment_no_match();
1774 * Write the tonal registration multiplier as a comment.
1777 pixel trm
= image_rw::exp(m
).get_multiplier();
1778 tsave_trm(tsave
, trm
[0], trm
[1], trm
[2]);
1781 * Save the transformation information
1784 for (unsigned int index
= 0; index
< offset
.stack_depth(); index
++) {
1785 offset
.set_current_index(index
);
1787 tsave_next(tsave
, offset
, alignment_class
== 2,
1788 offset
.get_current_index() == 0);
1791 offset
.set_current_index(0);
1794 * Update match statistics.
1797 match_sum
+= (1 - multi_here
.get_error()) * (ale_accum
) 100;
1805 int ale_align(ale_context ac
, ale_image a
, ale_image b
, ale_trans start
,
1806 ale_align_properties align_properties
, ale_trans result
) {
1807 #warning function unfinished.