34 return success ? (*this)(
params) : std::pair<const Array2D<double>&,
bool>(
params,
false);
46 return {
params, success };
52 " in nonlinear optimizer. Input params must have size of: " + this->params.size_2D_string() +
".");
56 bool disp_nloptimizer::initial_guess()
const {
61 if (nlinfo_old.empty()) {
63 NLOG_WARN <<
"WARNING - d_initial_guess : nlinfo_old of region " << region_idx <<
" is empty params initial guess cannot be found";
69 double min_distance = std::numeric_limits<double>::max();
70 for (
difference_type nl_idx = 0; nl_idx < nlinfo_old.nodelist.width(); ++nl_idx) {
72 for (
difference_type np_idx = 0; np_idx < nlinfo_old.noderange(nl_idx); np_idx += 2) {
74 difference_type np_bottom_unscaled = nlinfo_old.nodelist(np_idx + 1,nl_idx);
75 for (
difference_type p1_old_unscaled = np_top_unscaled; p1_old_unscaled <= np_bottom_unscaled; ++p1_old_unscaled) {
77 double v_old = disp.
get_v().
get_array()(p1_old_unscaled, p2_old_unscaled);
78 double u_old = disp.
get_u().
get_array()(p1_old_unscaled, p2_old_unscaled);
85 double distance = std::sqrt(std::pow(
params(0) - (p1_old + v_old), 2.0) +
86 std::pow(
params(1) - (p2_old + u_old), 2.0));
88 if (distance < min_distance) {
94 min_distance = distance;
103 bool disp_nloptimizer::iterative_search()
const {
106 bool success = newton();
114 bool disp_nloptimizer::newton()
const {
118 auto fo_pair = disp_interp.first_order(
params(2),
params(3));
120 if (std::isnan(fo_pair.first(0))) {
122 NLOG_WARN <<
"WARNING - d_newton : Interpolated out of bounds (first order returns NaN); for old (p1, p2) = " <<
params(2) <<
" ," <<
params(3);
127 const auto &fo_v = fo_pair.first;
128 const auto &fo_u = fo_pair.second;
135 hess_buf(0,0) = 2 * std::pow(-1 - fo_v(1), 2) + 2 * std::pow(-fo_u(1), 2);
136 hess_buf(1,0) = 2 * -fo_v(2) * (-1 - fo_v(1)) + 2 * (-1 - fo_u(2)) * -fo_u(1);
138 hess_buf(1,1) = 2 * std::pow(-fo_v(2), 2) + 2 * std::pow(-1 - fo_u(2), 2);
143 if (hess_buf_linsolver) {
145 const auto &delta_params = hess_buf_linsolver.solve(
grad_buf);
147 params(2) -= delta_params(0);
148 params(3) -= delta_params(1);
151 auto fo_pair_updated = disp_interp.first_order(
params(2),
params(3));
152 if (!std::isnan(fo_pair_updated.first(0))) {
154 params(4) = fo_pair_updated.first(0);
155 params(5) = fo_pair_updated.second(0);
156 params(6) = fo_pair_updated.first(1);
157 params(7) = fo_pair_updated.first(2);
158 params(8) = fo_pair_updated.second(1);
159 params(9) = fo_pair_updated.second(2);
166 NLOG_WARN <<
"WARNING - d_newton : hessian failed; for old (p1, p2) = " <<
params(2) <<
" ," <<
params(3);
172 A_ref_ptr(std::make_shared<
Array2D<double>>(A_ref)),
173 A_cur_ptr(std::make_shared<
Array2D<double>>(A_cur)),
174 scalefactor(scalefactor),
175 A_cur_interp(A_cur.get_interpolator(interp_type)),
176 subregion_gen(roi.get_contig_subregion_generator(subregion_type, r)),
177 A_cur_cumsum_p1_ptr(std::make_shared<
Array2D<double>>(A_cur)),
178 A_cur_pow_cumsum_p1_ptr(std::make_shared<
Array2D<double>>(pow(A_cur, 2.0))),
179 A_ref_template(2*r+1,2*r+1),
180 A_dref_dp1_ptr(std::make_shared<
Array2D<double>>(A_ref.height(), A_ref.width())),
181 A_dref_dp2_ptr(std::make_shared<
Array2D<double>>(A_ref.height(), A_ref.width())),
183 ref_template_ssd_inv(),
184 A_dref_dv(2*r+1,2*r+1),
185 A_dref_du(2*r+1,2*r+1),
186 A_dref_dv_dp1(2*r+1,2*r+1),
187 A_dref_dv_dp2(2*r+1,2*r+1),
188 A_dref_du_dp1(2*r+1,2*r+1),
189 A_dref_du_dp2(2*r+1,2*r+1),
190 A_cur_template(2*r+1,2*r+1) {
191 if (A_cur_ptr->height() < 2*r+1 || A_cur_ptr->width() < 2*r+1) {
194 throw std::invalid_argument(
"Input current image to subregion optimizer has size of: " + A_cur_ptr->size_2D_string() +
195 ". Size must be bigger than twice the radius plus 1, which is: " + std::to_string(2*r+1) +
".");
201 (*this->A_cur_cumsum_p1_ptr)(p1,p2) += (*this->A_cur_cumsum_p1_ptr)(p1-1,p2);
202 (*this->A_cur_pow_cumsum_p1_ptr)(p1,p2) += (*this->A_cur_pow_cumsum_p1_ptr)(p1-1,p2);
212 const auto &fo_ref = A_ref_interp.first_order(p1,p2);
214 (*this->A_dref_dp1_ptr)(p1,p2) = fo_ref(1);
215 (*this->A_dref_dp2_ptr)(p1,p2) = fo_ref(2);
220 bool subregion_nloptimizer::initial_guess()
const {
225 const auto &subregion_nlinfo = subregion_gen(std::round(
params(0)), std::round(
params(1)));
228 A_ref_template() = 0;
229 ref_template_avg = 0.0;
230 for (
difference_type nl_idx = 0; nl_idx < subregion_nlinfo.nodelist.width(); ++nl_idx) {
232 for (
difference_type np_idx = 0; np_idx < subregion_nlinfo.noderange(nl_idx); np_idx += 2) {
234 difference_type np_bottom = subregion_nlinfo.nodelist(np_idx + 1,nl_idx);
239 A_ref_template(p1_shifted, p2_shifted) = (*A_ref_ptr)(p1,p2);
240 ref_template_avg += A_ref_template(p1_shifted, p2_shifted);
244 ref_template_avg /= subregion_nlinfo.points;
250 ref_template_ssd_inv = 0.0;
251 for (
difference_type nl_idx = 0; nl_idx < subregion_nlinfo.nodelist.width(); ++nl_idx) {
253 for (
difference_type np_idx = 0; np_idx < subregion_nlinfo.noderange(nl_idx); np_idx += 2) {
255 difference_type np_bottom = subregion_nlinfo.nodelist(np_idx + 1,nl_idx);
260 A_ref_template(p1_shifted, p2_shifted) -= ref_template_avg;
261 ref_template_ssd_inv += std::pow(A_ref_template(p1_shifted, p2_shifted), 2.0);
265 ref_template_ssd_inv = std::sqrt(ref_template_ssd_inv);
267 if (std::abs(ref_template_ssd_inv) >= std::numeric_limits<double>::epsilon()) {
268 ref_template_ssd_inv = 1/ref_template_ssd_inv;
271 auto A_xcorr = xcorr(*A_cur_ptr, A_ref_template);
275 double NCC_max = -1.0;
278 for (
difference_type p2 =
params(1) - subregion_nlinfo.left; p2 < A_cur_ptr->width() - (subregion_nlinfo.right -
params(1)); ++p2) {
279 for (
difference_type p1 =
params(0) - subregion_nlinfo.top; p1 < A_cur_ptr->height() - (subregion_nlinfo.bottom -
params(0)); ++p1) {
281 double A_cur_sum = 0;
282 double A_cur_pow_sum = 0;
283 for (
difference_type nl_idx = 0; nl_idx < subregion_nlinfo.nodelist.width(); ++nl_idx) {
285 for (
difference_type np_idx = 0; np_idx < subregion_nlinfo.noderange(nl_idx); np_idx += 2) {
289 if (np_top_shifted > 0) {
290 A_cur_sum -= (*A_cur_cumsum_p1_ptr)(np_top_shifted-1, p2_shifted);
291 A_cur_pow_sum -= (*A_cur_pow_cumsum_p1_ptr)(np_top_shifted-1, p2_shifted);
294 A_cur_sum += (*A_cur_cumsum_p1_ptr)(np_bottom_shifted, p2_shifted);
295 A_cur_pow_sum += (*A_cur_pow_cumsum_p1_ptr)(np_bottom_shifted, p2_shifted);
299 double A_cur_ssd_inv = std::sqrt(A_cur_pow_sum - std::pow(A_cur_sum,2.0)/subregion_nlinfo.points);
300 if (std::abs(A_cur_ssd_inv) >= std::numeric_limits<double>::epsilon()) {
301 A_cur_ssd_inv = 1/A_cur_ssd_inv;
304 double NCC_buf = A_xcorr(p1,p2) * ref_template_ssd_inv * A_cur_ssd_inv;
305 if (NCC_buf > NCC_max) {
323 NLOG_WARN <<
"WARNING - sr_inital_guess : ref_template_ssd_inv less than eps; for (p1, p2) = " <<
params(0) <<
" ," <<
params(1);
327 bool subregion_nloptimizer::iterative_search()
const {
331 last_iteration_count = 0;
335 const auto &subregion_nlinfo = subregion_gen(std::round(
params(0)), std::round(
params(1)));
338 ref_template_avg = 0.0;
339 for (
difference_type nl_idx = 0; nl_idx < subregion_nlinfo.nodelist.width(); ++nl_idx) {
341 for (
difference_type np_idx = 0; np_idx < subregion_nlinfo.noderange(nl_idx); np_idx += 2) {
343 difference_type np_bottom = subregion_nlinfo.nodelist(np_idx + 1,nl_idx);
345 ref_template_avg += (*A_ref_ptr)(p1,p2);
349 ref_template_avg /= subregion_nlinfo.points;
352 ref_template_ssd_inv = 0.0;
353 for (
difference_type nl_idx = 0; nl_idx < subregion_nlinfo.nodelist.width(); ++nl_idx) {
355 for (
difference_type np_idx = 0; np_idx < subregion_nlinfo.noderange(nl_idx); np_idx += 2) {
357 difference_type np_bottom = subregion_nlinfo.nodelist(np_idx + 1,nl_idx);
359 ref_template_ssd_inv += std::pow((*A_ref_ptr)(p1,p2) - ref_template_avg, 2.0);
363 ref_template_ssd_inv = std::sqrt(ref_template_ssd_inv);
365 if (std::abs(ref_template_ssd_inv) >= std::numeric_limits<double>::epsilon()) {
366 ref_template_ssd_inv = 1/ref_template_ssd_inv;
369 for (
difference_type nl_idx = 0; nl_idx < subregion_nlinfo.nodelist.width(); ++nl_idx) {
371 for (
difference_type np_idx = 0; np_idx < subregion_nlinfo.noderange(nl_idx); np_idx += 2) {
373 difference_type np_bottom = subregion_nlinfo.nodelist(np_idx + 1,nl_idx);
378 double p1_delta = p1 -
params(0);
379 double p2_delta = p2 -
params(1);
381 if (std::isnan((*A_dref_dp1_ptr)(p1,p2))) {
385 NLOG_DEBUG <<
"INFO - sr_iterative_search: near boundaries interpolation yield NaN; for (p1, p2) = " << p1 <<
" ," << p2;
389 A_dref_dv(p1_shifted,p2_shifted) = (*A_dref_dp1_ptr)(p1,p2);
390 A_dref_du(p1_shifted,p2_shifted) = (*A_dref_dp2_ptr)(p1,p2);
391 A_dref_dv_dp1(p1_shifted,p2_shifted) = (*A_dref_dp1_ptr)(p1,p2) * p1_delta;
392 A_dref_dv_dp2(p1_shifted,p2_shifted) = (*A_dref_dp1_ptr)(p1,p2) * p2_delta;
393 A_dref_du_dp1(p1_shifted,p2_shifted) = (*A_dref_dp2_ptr)(p1,p2) * p1_delta;
394 A_dref_du_dp2(p1_shifted,p2_shifted) = (*A_dref_dp2_ptr)(p1,p2) * p2_delta;
401 for (
difference_type nl_idx = 0; nl_idx < subregion_nlinfo.nodelist.width(); ++nl_idx) {
403 for (
difference_type np_idx = 0; np_idx < subregion_nlinfo.noderange(nl_idx); np_idx += 2) {
405 difference_type np_bottom = subregion_nlinfo.nodelist(np_idx + 1,nl_idx);
411 hess_buf(0,0) += A_dref_dv(p1_shifted,p2_shifted) * A_dref_dv(p1_shifted,p2_shifted);
412 hess_buf(1,0) += A_dref_dv(p1_shifted,p2_shifted) * A_dref_du(p1_shifted,p2_shifted);
413 hess_buf(2,0) += A_dref_dv(p1_shifted,p2_shifted) * A_dref_dv_dp1(p1_shifted,p2_shifted);
414 hess_buf(3,0) += A_dref_dv(p1_shifted,p2_shifted) * A_dref_dv_dp2(p1_shifted,p2_shifted);
415 hess_buf(4,0) += A_dref_dv(p1_shifted,p2_shifted) * A_dref_du_dp1(p1_shifted,p2_shifted);
416 hess_buf(5,0) += A_dref_dv(p1_shifted,p2_shifted) * A_dref_du_dp2(p1_shifted,p2_shifted);
418 hess_buf(1,1) += A_dref_du(p1_shifted,p2_shifted) * A_dref_du(p1_shifted,p2_shifted);
419 hess_buf(2,1) += A_dref_du(p1_shifted,p2_shifted) * A_dref_dv_dp1(p1_shifted,p2_shifted);
420 hess_buf(3,1) += A_dref_du(p1_shifted,p2_shifted) * A_dref_dv_dp2(p1_shifted,p2_shifted);
421 hess_buf(4,1) += A_dref_du(p1_shifted,p2_shifted) * A_dref_du_dp1(p1_shifted,p2_shifted);
422 hess_buf(5,1) += A_dref_du(p1_shifted,p2_shifted) * A_dref_du_dp2(p1_shifted,p2_shifted);
424 hess_buf(2,2) += A_dref_dv_dp1(p1_shifted,p2_shifted) * A_dref_dv_dp1(p1_shifted,p2_shifted);
425 hess_buf(3,2) += A_dref_dv_dp1(p1_shifted,p2_shifted) * A_dref_dv_dp2(p1_shifted,p2_shifted);
426 hess_buf(4,2) += A_dref_dv_dp1(p1_shifted,p2_shifted) * A_dref_du_dp1(p1_shifted,p2_shifted);
427 hess_buf(5,2) += A_dref_dv_dp1(p1_shifted,p2_shifted) * A_dref_du_dp2(p1_shifted,p2_shifted);
429 hess_buf(3,3) += A_dref_dv_dp2(p1_shifted,p2_shifted) * A_dref_dv_dp2(p1_shifted,p2_shifted);
430 hess_buf(4,3) += A_dref_dv_dp2(p1_shifted,p2_shifted) * A_dref_du_dp1(p1_shifted,p2_shifted);
431 hess_buf(5,3) += A_dref_dv_dp2(p1_shifted,p2_shifted) * A_dref_du_dp2(p1_shifted,p2_shifted);
433 hess_buf(4,4) += A_dref_du_dp1(p1_shifted,p2_shifted) * A_dref_du_dp1(p1_shifted,p2_shifted);
434 hess_buf(5,4) += A_dref_du_dp1(p1_shifted,p2_shifted) * A_dref_du_dp2(p1_shifted,p2_shifted);
436 hess_buf(5,5) += A_dref_du_dp2(p1_shifted,p2_shifted) * A_dref_du_dp2(p1_shifted,p2_shifted);
443 hess_buf(p1,p2) *= 2 * std::pow(ref_template_ssd_inv, 2.0);
451 if (hess_linsolver) {
453 bool success = newton();
454 last_iteration_count = 1;
457 last_iteration_count = counter + 1;
468 bool subregion_nloptimizer::newton()
const {
476 A_cur_template() = 0;
477 double cur_template_avg = 0.0;
478 for (
difference_type nl_idx = 0; nl_idx < subregion_nlinfo.nodelist.width(); ++nl_idx) {
480 for (
difference_type np_idx = 0; np_idx < subregion_nlinfo.noderange(nl_idx); np_idx += 2) {
482 difference_type np_bottom = subregion_nlinfo.nodelist(np_idx + 1,nl_idx);
487 double p1_delta = p1 -
params(0);
488 double p2_delta = p2 -
params(1);
495 A_cur_template(p1_shifted, p2_shifted) = A_cur_interp(p1_transformed, p2_transformed);
497 if (std::isnan(A_cur_template(p1_shifted, p2_shifted))) {
499 NLOG_DEBUG <<
"INFO - sr_newton: near boundaries interpolation yield NaN; for (p1, p2) = " << p1_shifted <<
" ," << p2_shifted;
503 cur_template_avg += A_cur_template(p1_shifted, p2_shifted);
507 cur_template_avg /= subregion_nlinfo.points;
510 double cur_template_ssd_inv = 0.0;
511 for (
difference_type nl_idx = 0; nl_idx < subregion_nlinfo.nodelist.width(); ++nl_idx) {
513 for (
difference_type np_idx = 0; np_idx < subregion_nlinfo.noderange(nl_idx); np_idx += 2) {
515 difference_type np_bottom = subregion_nlinfo.nodelist(np_idx + 1,nl_idx);
520 cur_template_ssd_inv += std::pow(A_cur_template(p1_shifted,p2_shifted) - cur_template_avg, 2.0);
524 cur_template_ssd_inv = std::sqrt(cur_template_ssd_inv);
526 if (std::abs(cur_template_ssd_inv) >= std::numeric_limits<double>::epsilon()) {
527 cur_template_ssd_inv = 1/cur_template_ssd_inv;
532 for (
difference_type nl_idx = 0; nl_idx < subregion_nlinfo.nodelist.width(); ++nl_idx) {
534 for (
difference_type np_idx = 0; np_idx < subregion_nlinfo.noderange(nl_idx); np_idx += 2) {
536 difference_type np_bottom = subregion_nlinfo.nodelist(np_idx + 1,nl_idx);
541 double normalized_diff = ((*A_ref_ptr)(p1,p2) - ref_template_avg) * ref_template_ssd_inv - (A_cur_template(p1_shifted,p2_shifted) - cur_template_avg) * cur_template_ssd_inv;
543 grad_buf(0) += normalized_diff * A_dref_dv(p1_shifted,p2_shifted);
544 grad_buf(1) += normalized_diff * A_dref_du(p1_shifted,p2_shifted);
545 grad_buf(2) += normalized_diff * A_dref_dv_dp1(p1_shifted,p2_shifted);
546 grad_buf(3) += normalized_diff * A_dref_dv_dp2(p1_shifted,p2_shifted);
547 grad_buf(4) += normalized_diff * A_dref_du_dp1(p1_shifted,p2_shifted);
548 grad_buf(5) += normalized_diff * A_dref_du_dp2(p1_shifted,p2_shifted);
551 params(8) += std::pow(normalized_diff, 2.0);
559 grad_buf(p) *= -2 * ref_template_ssd_inv;
563 const auto &delta_p = hess_linsolver.solve(
grad_buf);
566 params(9) = std::sqrt(dot(delta_p,delta_p));
569 double denominator = (delta_p(5) + delta_p(2) + delta_p(5)*delta_p(2) - delta_p(4)*delta_p(3) + 1);
570 if (std::abs(denominator) >= std::numeric_limits<double>::epsilon()) {
573 double dv_dp1 =
params(4);
574 double dv_dp2 =
params(5);
575 double du_dp1 =
params(6);
576 double du_dp2 =
params(7);
578 params(2) = v - ((dv_dp1+1)*(delta_p(0)*(1+delta_p(5))-delta_p(1)*delta_p(3)) + dv_dp2*(delta_p(1)*(1+delta_p(2))-delta_p(0)*delta_p(4)))/denominator;
579 params(3) = u - ((du_dp2+1)*(delta_p(1)*(1+delta_p(2))-delta_p(0)*delta_p(4)) + du_dp1*(delta_p(0)*(1+delta_p(5))-delta_p(1)*delta_p(3)))/denominator;
580 params(4) = ((delta_p(5)+1)*(dv_dp1+1) - delta_p(4)*dv_dp2)/denominator - 1;
581 params(5) = (dv_dp2*(delta_p(2)+1) - delta_p(3)*(dv_dp1+1))/denominator;
582 params(6) = (du_dp1*(delta_p(5)+1) - delta_p(4)*(du_dp2+1))/denominator;
583 params(7) = ((delta_p(2)+1)*(du_dp2+1) - delta_p(3)*du_dp1)/denominator - 1;
589 NLOG_WARN <<
"WARNING - sr_newton: hessian probably failed; for (p1, p2) = " <<
params(0) <<
" ," <<
params(1);
601 auto boundary_updated = boundary;
602 for (difference_type idx = 0; idx < boundary_updated.height(); ++idx) {
604 auto disp_pair = disp_interp(boundary_updated(idx,0) * roi_scalefactor, boundary_updated(idx,1) * roi_scalefactor);
606 if (std::isnan(disp_pair.first)) {
613 boundary_updated(idx,0) += disp_pair.first / roi_scalefactor;
614 boundary_updated(idx,1) += disp_pair.second / roi_scalefactor;
617 return boundary_updated;
632 if (boundary.
height() == 0) {
637 std::vector<std::pair<double, double>> valid_points;
638 valid_points.reserve(boundary.
height());
640 for (difference_type idx = 0; idx < boundary.
height(); ++idx) {
642 double p1 = boundary(idx,0) * roi_scalefactor;
643 double p2 = boundary(idx,1) * roi_scalefactor;
644 auto disp_pair = disp_interp(p1, p2);
647 if (std::isnan(disp_pair.first) || std::isnan(disp_pair.second)) {
652 double new_p1 = boundary(idx,0) + disp_pair.first / roi_scalefactor;
653 double new_p2 = boundary(idx,1) + disp_pair.second / roi_scalefactor;
657 if (new_p1 < margin || new_p1 >= roi_height - margin ||
658 new_p2 < margin || new_p2 >= roi_width - margin) {
662 valid_points.emplace_back(new_p1, new_p2);
666 if (valid_points.empty()) {
672 for (difference_type i = 0; i < static_cast<difference_type>(valid_points.size()); ++i) {
673 boundary_updated(i,0) = valid_points[i].first;
674 boundary_updated(i,1) = valid_points[i].second;
677 return boundary_updated;
686 throw std::invalid_argument(
"Attempted to update ROI2D which has " + std::to_string(roi.
size_regions()) +
688 " regions. The number of regions must be the same and they must correspond to each other.");
698 difference_type roi_scalefactor;
705 throw std::invalid_argument(
"Attempted to update ROI2D which has size of: " + roi.
size_2D_string() +
707 ". Size of ROI2D or reduced ROI2D must match data size of Disp2D.");
711 std::vector<ROI2D::region_boundary> boundaries_updated(roi.
size_regions());
712 for (difference_type region_idx = 0; region_idx < roi.
size_regions(); ++region_idx) {
732 for (
auto &sub_boundary : boundary.sub) {
739 boundary.add, disp_interp, roi_scalefactor, roi.
height(), roi.
width(), 0);
741 for (
auto &sub_boundary : boundary.sub) {
743 sub_boundary, disp_interp, roi_scalefactor, roi.
height(), roi.
width(), 0));
748 boundaries_updated[region_idx] = std::move(boundary_updated);
763 if (boundary.
height() == 0) {
767 std::vector<std::pair<double, double>> valid_points;
768 valid_points.reserve(boundary.
height());
770 for (difference_type idx = 0; idx < boundary.
height(); ++idx) {
771 const double p1 = boundary(idx, 0) * roi_scalefactor;
772 const double p2 = boundary(idx, 1) * roi_scalefactor;
773 const auto disp_pair = disp_interp(p1, p2);
775 if (std::isnan(disp_pair.first) || std::isnan(disp_pair.second)) {
779 const double new_p1 = boundary(idx, 0) + disp_pair.first / roi_scalefactor;
780 const double new_p2 = boundary(idx, 1) + disp_pair.second / roi_scalefactor;
782 if (new_p1 < radius || new_p1 > size_mask_height - radius ||
783 new_p2 < radius || new_p2 > size_mask_width - radius) {
787 valid_points.emplace_back(new_p1, new_p2);
790 if (valid_points.empty()) {
795 for (difference_type idx = 0; idx < difference_type(valid_points.size()); ++idx) {
796 boundary_updated(idx, 0) = valid_points[idx].first;
797 boundary_updated(idx, 1) = valid_points[idx].second;
800 return boundary_updated;
811 throw std::invalid_argument(
"Attempted to matlab_update_roi with ROI2D regions: " +
812 std::to_string(roi.
size_regions()) +
" and Disp2D regions: " +
816 difference_type roi_scalefactor;
823 throw std::invalid_argument(
"Attempted to matlab_update_roi with ROI2D size of: " + roi.
size_2D_string() +
825 " and scale factor of: " + std::to_string(disp.
get_scalefactor()) +
".");
828 std::vector<ROI2D::region_boundary> boundaries_updated(roi.
size_regions());
829 for (difference_type region_idx = 0; region_idx < roi.
size_regions(); ++region_idx) {
847 boundary_updated.
sub.reserve(boundary.sub.size());
848 for (
const auto& sub_boundary : boundary.sub) {
859 boundaries_updated[region_idx] = std::move(boundary_updated);
869 if (nlinfo.
empty()) {
879 fill(mask_buf, nlinfo,
false);
880 mask_buf_new() =
false;
881 difference_type dist = 1;
882 bool points_added =
true;
883 while (points_added) {
886 points_added =
false;
887 for (difference_type nl_idx = 0; nl_idx < nlinfo.
nodelist.
width(); ++nl_idx) {
888 difference_type p2 = nl_idx + nlinfo.
left_nl;
889 for (difference_type np_idx = 0; np_idx < nlinfo.
noderange(nl_idx); np_idx += 2) {
890 difference_type np_top = nlinfo.
nodelist(np_idx,nl_idx);
891 difference_type np_bottom = nlinfo.
nodelist(np_idx + 1,nl_idx);
892 for (difference_type p1 = np_top; p1 <= np_bottom; ++p1) {
893 if (!mask_buf(p1,p2)) {
896 if ((mask_buf.
in_bounds(p1 - 1,p2) && mask_buf(p1 - 1, p2)) ||
897 (mask_buf.
in_bounds(p1 + 1,p2) && mask_buf(p1 + 1, p2)) ||
898 (mask_buf.
in_bounds(p1,p2 - 1) && mask_buf(p1, p2 - 1)) ||
899 (mask_buf.
in_bounds(p1,p2 + 1) && mask_buf(p1, p2 + 1))) {
904 mask_buf_new(p1,p2) =
true;
913 for (difference_type nl_idx = 0; nl_idx < nlinfo.
nodelist.
width(); ++nl_idx) {
914 difference_type p2 = nl_idx + nlinfo.
left_nl;
915 for (difference_type np_idx = 0; np_idx < nlinfo.
noderange(nl_idx); np_idx += 2) {
916 difference_type np_top = nlinfo.
nodelist(np_idx,nl_idx);
917 difference_type np_bottom = nlinfo.
nodelist(np_idx + 1,nl_idx);
918 for (difference_type p1 = np_top; p1 <= np_bottom; ++p1) {
919 mask_buf(p1,p2) = mask_buf_new(p1,p2);
938 for (difference_type region_idx = 0; region_idx < roi.
size_regions(); ++region_idx) {
955 if (nlinfo.
empty()) {
962 auto max_info =
max(SDA, nlinfo);
967 params_buf(10) = max_info.first;
970 auto seed_params_pair = d_nloptimizer.
global(params_buf);
971 if (seed_params_pair.second) {
973 return seed_params_pair.first;
995 difference_type p1_new = queue_params(0) + p1_new_delta;
996 difference_type p2_new = queue_params(1) + p2_new_delta;
997 if (nlinfo.
in_nlinfo(p1_new / roi_scalefactor, p2_new / roi_scalefactor) &&
998 !A_new_ap(p1_new / roi_scalefactor, p2_new / roi_scalefactor)) {
999 A_new_ap(p1_new / roi_scalefactor, p2_new / roi_scalefactor) =
true;
1002 double denominator = (queue_params(8) * queue_params(7) - (1 + queue_params(6)) * (1 + queue_params(9)));
1003 if (std::abs(denominator) >= std::numeric_limits<double>::epsilon()) {
1005 double p1_old_delta = (queue_params(7) * (p2_new - (queue_params(3) + queue_params(5))) - (1 + queue_params(9)) * (p1_new - (queue_params(2) + queue_params(4)))) / denominator;
1006 double p2_old_delta = (queue_params(8) * (p1_new - (queue_params(2) + queue_params(4))) - (1 + queue_params(6)) * (p2_new - (queue_params(3) + queue_params(5)))) / denominator;
1009 params_buf(0) = p1_new;
1010 params_buf(1) = p2_new;
1011 params_buf(2) = queue_params(2) + p1_old_delta;
1012 params_buf(3) = queue_params(3) + p2_old_delta;
1013 params_buf(10) = SDA(p1_new / roi_scalefactor, p2_new / roi_scalefactor);
1016 auto params_pair = d_nloptimizer(params_buf);
1017 if (params_pair.second) {
1019 queue.push(params_pair.first);
1024 auto params_global_pair = d_nloptimizer.
global(params_buf);
1025 if (params_global_pair.second) {
1027 queue.push(params_global_pair.first);
1045 throw std::invalid_argument(
"Attempted to update Data2D which has " + std::to_string(data.
get_roi().
size_regions()) +
1047 " regions. The number of regions must be the same and they must correspond to each other.");
1056 throw std::invalid_argument(
"Attempted to update Data2D which has a data size of " + data.
size_2D_string() +
" and scale factor of: " + std::to_string(data.
get_scalefactor()) +
1058 ". Data2D must either have the same scalefactor as Disp2D with the same data size, or Data2D must be full sized with a reduced size the same as Disp2D.");
1062 auto roi_new =
update(data.
get_roi(), disp, interp_type, mode);
1065 if (roi_new.get_points() == 0) {
1066 NLOG_WARN <<
"WARNING: ROI update in Data2D::update() produced an empty ROI!";
1080 for (difference_type region_idx = 0; region_idx < roi_new.size_regions(); ++region_idx) {
1094 if (!seed_params.empty()) {
1100 std::priority_queue<Array2D<double>, std::vector<Array2D<double>>, std::function<bool(
const Array2D<double>&,
const Array2D<double>&)>> queue(comp);
1103 queue.push(seed_params);
1104 while (!queue.empty()) {
1106 auto queue_params = std::move(queue.top()); queue.pop();
1132 if (disps.empty()) {
1135 }
else if (disps.size() == 1) {
1137 return disps.front();
1142 difference_type scalefactor = disps.front().get_scalefactor();
1143 const auto &roi = disps.front().get_roi();
1144 for (difference_type disp_idx = 1; disp_idx < difference_type(disps.size()); ++disp_idx) {
1145 if (disps[disp_idx].get_scalefactor() != scalefactor ||
1146 disps[disp_idx].data_height() != disps.front().data_height() ||
1147 disps[disp_idx].data_width() != disps.front().data_width() ||
1148 disps[disp_idx].get_roi().size_regions() != roi.size_regions()) {
1149 throw std::invalid_argument(
"Attempted to add displacements with differing scalefactors, sizes, or number of regions. All scalefactors, sizes, and number of regions must be the same.");
1158 A_cc_combined() = 1.0;
1160 for (difference_type region_idx = 0; region_idx < roi.size_regions(); ++region_idx) {
1162 std::vector<Disp2D::nlinfo_interpolator> disp_nlinfo_interps;
1163 for (difference_type disp_idx = 0; disp_idx < difference_type(disps.size()); ++disp_idx) {
1164 disp_nlinfo_interps.push_back(disps[disp_idx].get_nlinfo_interpolator(region_idx, interp_type));
1168 for (difference_type nl_idx = 0; nl_idx < roi.get_nlinfo(region_idx).nodelist.width(); ++nl_idx) {
1169 difference_type p2_unscaled = nl_idx + roi.get_nlinfo(region_idx).left_nl;
1170 difference_type p2 = p2_unscaled * scalefactor;
1171 for (difference_type np_idx = 0; np_idx < roi.get_nlinfo(region_idx).noderange(nl_idx); np_idx += 2) {
1172 difference_type np_top = roi.get_nlinfo(region_idx).nodelist(np_idx,nl_idx);
1173 difference_type np_bottom = roi.get_nlinfo(region_idx).nodelist(np_idx + 1,nl_idx);
1174 for (difference_type p1_unscaled = np_top; p1_unscaled <= np_bottom; ++p1_unscaled) {
1175 difference_type p1 = p1_unscaled * scalefactor;
1180 double cc_min = 1.0;
1181 for (difference_type disp_idx = 0; disp_idx < difference_type(disps.size()); ++disp_idx) {
1185 bool near_nlinfo =
false;
1186 difference_type window = 1;
1187 difference_type p1_unscaled = std::round((p1 + v_added) / scalefactor);
1188 difference_type p2_unscaled = std::round((p2 + u_added) / scalefactor);
1189 for (difference_type p2_window = p2_unscaled - window; p2_window <= p2_unscaled + window; ++p2_window) {
1190 for (difference_type p1_window = p1_unscaled - window; p1_window <= p1_unscaled + window; ++p1_window) {
1191 if (disps[disp_idx].get_roi().get_nlinfo(region_idx).in_nlinfo(p1_window,p2_window)) {
1194 if (near_nlinfo) {
break; }
1196 if (near_nlinfo) {
break; }
1200 auto disp_pair = disp_nlinfo_interps[disp_idx](p1 + v_added, p2 + u_added);
1201 double cc_val = disp_nlinfo_interps[disp_idx].get_cc(p1 + v_added, p2 + u_added);
1202 cc_min = std::min(cc_min, cc_val);
1204 v_added += disp_pair.first;
1205 u_added += disp_pair.second;
1209 v_added = std::numeric_limits<double>::quiet_NaN();
1210 u_added = std::numeric_limits<double>::quiet_NaN();
1213 if (std::isnan(v_added)) {
1219 if (!std::isnan(v_added)) {
1221 A_v_added(p1_unscaled, p2_unscaled) = v_added;
1222 A_u_added(p1_unscaled, p2_unscaled) = u_added;
1223 A_cc_combined(p1_unscaled, p2_unscaled) = cc_min;
1224 A_vp(p1_unscaled, p2_unscaled) =
true;
1231 return { std::move(A_v_added), std::move(A_u_added), std::move(A_cc_combined), roi.form_union(A_vp), scalefactor };
1247 const std::vector<ROI2D> &rois,
1255namespace legacy_detail {
1257 const std::vector<ROI2D> &rois,
1261 if (disps.empty()) {
1263 }
else if (disps.size() == 1) {
1264 return disps.front();
1268 if (disps.size() != rois.size()) {
1269 throw std::invalid_argument(
"Number of displacements (" + std::to_string(disps.size()) +
1270 ") must match number of ROIs (" + std::to_string(rois.size()) +
").");
1274 difference_type scalefactor = disps.front().get_scalefactor();
1275 const auto &roi_first = rois.front();
1276 for (difference_type idx = 1; idx < difference_type(disps.size()); ++idx) {
1277 if (disps[idx].get_scalefactor() != scalefactor ||
1278 disps[idx].data_height() != disps.front().data_height() ||
1279 disps[idx].data_width() != disps.front().data_width() ||
1280 rois[idx].size_regions() != roi_first.size_regions()) {
1281 throw std::invalid_argument(
"Attempted to add displacements with differing scalefactors, sizes, or number of regions.");
1290 A_cc_combined() = 1.0;
1293 const difference_type border_interp = 20;
1295 for (difference_type region_idx = 0; region_idx < roi_first.size_regions(); ++region_idx) {
1297 std::vector<Disp2D::nlinfo_interpolator> disp_nlinfo_interps;
1298 for (difference_type disp_idx = 0; disp_idx < difference_type(disps.size()); ++disp_idx) {
1299 disp_nlinfo_interps.push_back(disps[disp_idx].get_nlinfo_interpolator(region_idx, interp_type));
1303 for (difference_type nl_idx = 0; nl_idx < roi_first.get_nlinfo(region_idx).nodelist.width(); ++nl_idx) {
1304 difference_type p2_unscaled = nl_idx + roi_first.get_nlinfo(region_idx).left_nl;
1305 difference_type p2 = p2_unscaled * scalefactor;
1306 for (difference_type np_idx = 0; np_idx < roi_first.get_nlinfo(region_idx).noderange(nl_idx); np_idx += 2) {
1307 difference_type np_top = roi_first.get_nlinfo(region_idx).nodelist(np_idx,nl_idx);
1308 difference_type np_bottom = roi_first.get_nlinfo(region_idx).nodelist(np_idx + 1,nl_idx);
1309 for (difference_type p1_unscaled = np_top; p1_unscaled <= np_bottom; ++p1_unscaled) {
1310 difference_type p1 = p1_unscaled * scalefactor;
1313 bool prop_success =
true;
1316 double cc_min = 1.0;
1318 double x_cur =
static_cast<double>(p1);
1319 double y_cur =
static_cast<double>(p2);
1321 for (difference_type disp_idx = 0; disp_idx < difference_type(disps.size()); ++disp_idx) {
1324 difference_type x_cur_unscaled = std::round(x_cur / scalefactor);
1325 difference_type y_cur_unscaled = std::round(y_cur / scalefactor);
1328 bool in_bounds =
false;
1329 const difference_type window = border_interp / scalefactor;
1330 for (difference_type y_win = y_cur_unscaled - window; y_win <= y_cur_unscaled + window && !in_bounds; ++y_win) {
1331 for (difference_type x_win = x_cur_unscaled - window; x_win <= x_cur_unscaled + window && !in_bounds; ++x_win) {
1332 if (rois[disp_idx].get_nlinfo(region_idx).in_nlinfo(x_win, y_win)) {
1340 auto disp_pair = disp_nlinfo_interps[disp_idx](x_cur, y_cur);
1342 if (!std::isnan(disp_pair.first) && !std::isnan(disp_pair.second)) {
1343 double cc_val = disp_nlinfo_interps[disp_idx].get_cc(x_cur, y_cur);
1344 cc_min = std::min(cc_min, cc_val);
1347 v_added += disp_pair.first;
1348 u_added += disp_pair.second;
1351 x_cur += disp_pair.first;
1352 y_cur += disp_pair.second;
1354 prop_success =
false;
1358 prop_success =
false;
1365 A_v_added(p1_unscaled, p2_unscaled) = v_added;
1366 A_u_added(p1_unscaled, p2_unscaled) = u_added;
1367 A_cc_combined(p1_unscaled, p2_unscaled) = cc_min;
1368 A_vp(p1_unscaled, p2_unscaled) =
true;
1376 return { std::move(A_v_added), std::move(A_u_added), std::move(A_cc_combined), roi_first.form_union(A_vp), scalefactor };
1385 if (nlinfo.
empty()) {
1387 return Array2D<double>({ std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN() });
1390 double p1_centroid = 0;
1391 double p2_centroid = 0;
1392 for (difference_type nl_idx = 0; nl_idx < nlinfo.
nodelist.
width(); ++nl_idx) {
1393 difference_type p2 = nl_idx + nlinfo.
left_nl;
1394 for (difference_type np_idx = 0; np_idx < nlinfo.
noderange(nl_idx); np_idx += 2) {
1395 difference_type np_top = nlinfo.
nodelist(np_idx,nl_idx);
1396 difference_type np_bottom = nlinfo.
nodelist(np_idx + 1,nl_idx);
1397 for (difference_type p1 = np_top; p1 <= np_bottom; ++p1) {
1404 p1_centroid /= nlinfo.
points;
1405 p2_centroid /= nlinfo.
points;
1421 for (difference_type nl_idx = 0; nl_idx < nlinfo.
nodelist.
width(); ++nl_idx) {
1422 difference_type p2 = nl_idx + nlinfo.
left_nl;
1423 for (difference_type np_idx = 0; np_idx < nlinfo.
noderange(nl_idx); np_idx += 2) {
1424 difference_type np_top = nlinfo.
nodelist(np_idx,nl_idx);
1425 difference_type np_bottom = nlinfo.
nodelist(np_idx + 1,nl_idx);
1426 for (difference_type p1 = np_top; p1 <= np_bottom; ++p1) {
1429 double point1_p1 = origin(0), point1_p2 = origin(1);
1430 double point2_p1 = origin(0) + direc(0), point2_p2 = origin(1) + direc(1);
1431 double point3_p1 = p1, point3_p2 = p2;
1436 double turn_val = point2_p2*point3_p1 - point2_p1*point3_p2 - point1_p2*(point3_p1-point2_p1) + point1_p1*(point3_p2-point2_p2);
1437 if (std::abs(turn_val) < std::numeric_limits<double>::epsilon()) {
1439 partition_diagram(p1,p2) = partition_offset;
1440 }
else if (turn_val > 0){
1442 partition_diagram(p1,p2) = partition_offset;
1445 partition_diagram(p1,p2) = partition_offset+1;
1454 fill(mask_buf, nlinfo,
true);
1455 mask_buf = (std::move(mask_buf) & (partition_diagram == partition_offset));
1459 fill(mask_buf, nlinfo,
true);
1460 mask_buf = (std::move(mask_buf) & (partition_diagram == partition_offset+1));
1463 return { std::move(nlinfo_part1_pair.first), std::move(nlinfo_part2_pair.first) };
1476 std::queue<difference_type> queue;
1477 for (difference_type nl_idx = 0; nl_idx < sub_nlinfo.
nodelist.
width(); ++nl_idx) {
1478 difference_type p2 = nl_idx + sub_nlinfo.
left_nl;
1479 for (difference_type np_idx = 0; np_idx < sub_nlinfo.
noderange(nl_idx); np_idx += 2) {
1480 difference_type np_top = sub_nlinfo.
nodelist(np_idx,nl_idx);
1481 difference_type np_bottom = sub_nlinfo.
nodelist(np_idx + 1,nl_idx);
1482 for (difference_type p1 = np_top; p1 <= np_bottom; ++p1) {
1483 if (nlinfo.
in_nlinfo(p1-1,p2) && A_ap(p1-1,p2)) { A_ap(p1-1,p2) =
false; queue.push(p1-1); queue.push(p2); }
1484 if (nlinfo.
in_nlinfo(p1+1,p2) && A_ap(p1+1,p2)) { A_ap(p1+1,p2) =
false; queue.push(p1+1); queue.push(p2); }
1485 if (nlinfo.
in_nlinfo(p1,p2+1) && A_ap(p1,p2+1)) { A_ap(p1,p2+1) =
false; queue.push(p1); queue.push(p2+1); }
1486 if (nlinfo.
in_nlinfo(p1,p2-1) && A_ap(p1,p2-1)) { A_ap(p1,p2-1) =
false; queue.push(p1); queue.push(p2-1); }
1491 while (!queue.empty()) {
1492 difference_type p1 = queue.front(); queue.pop();
1493 difference_type p2 = queue.front(); queue.pop();
1499 if (nlinfo.
in_nlinfo(p1-1,p2) && A_ap(p1-1,p2)) { A_ap(p1-1,p2) =
false; queue.push(p1-1); queue.push(p2); }
1500 if (nlinfo.
in_nlinfo(p1+1,p2) && A_ap(p1+1,p2)) { A_ap(p1+1,p2) =
false; queue.push(p1+1); queue.push(p2); }
1501 if (nlinfo.
in_nlinfo(p1,p2+1) && A_ap(p1,p2+1)) { A_ap(p1,p2+1) =
false; queue.push(p1); queue.push(p2+1); }
1502 if (nlinfo.
in_nlinfo(p1,p2-1) && A_ap(p1,p2-1)) { A_ap(p1,p2-1) =
false; queue.push(p1); queue.push(p2-1); }
1517 if (nlinfo.
empty()) {
1529 for (difference_type nl_idx = 0; nl_idx < nlinfo.
nodelist.
width(); ++nl_idx) {
1530 difference_type p2 = nl_idx + nlinfo.
left_nl;
1531 for (difference_type np_idx = 0; np_idx < nlinfo.
noderange(nl_idx); np_idx += 2) {
1532 difference_type np_top = nlinfo.
nodelist(np_idx,nl_idx);
1533 difference_type np_bottom = nlinfo.
nodelist(np_idx + 1,nl_idx);
1534 for (difference_type p1 = np_top; p1 <= np_bottom; ++p1) {
1535 Mp1p1 += std::pow(p1 - centroid(0),2);
1536 Mp1p2 += (p1 - centroid(0)) * (p2 - centroid(1));
1537 Mp2p2 += std::pow(p2 - centroid(1),2);
1548 double eig_val1 = 0.0;
1549 double eig_val2 = 0.0;
1552 if (std::abs(Mp1p2) >= std::numeric_limits<double>::epsilon()) {
1553 double trace = Mp1p1 + Mp2p2;
1554 double det = Mp1p1*Mp2p2 - std::pow(Mp1p2,2);
1556 eig_val1 = trace/2.0 + std::sqrt((std::pow(trace,2.0)/4.0 - det));
1557 eig_val2 = trace/2.0 - std::sqrt((std::pow(trace,2.0)/4.0 - det));
1559 eig_vec1(0,0) = eig_val1-Mp2p2; eig_vec1(1,0) = Mp1p2;
1560 eig_vec2(0,0) = eig_val2-Mp2p2; eig_vec2(1,0) = Mp1p2;
1563 eig_vec1 = normalize(std::move(eig_vec1));
1564 eig_vec2 = normalize(std::move(eig_vec2));
1569 eig_vec1(0,0) = 1; eig_vec1(1,0) = 0;
1570 eig_vec2(0,0) = 0; eig_vec2(1,0) = 1;
1574 const auto &minor_axis = eig_val1 <= eig_val2 ? eig_vec1 : eig_vec2;
1575 const auto &major_axis = eig_val1 > eig_val2 ? eig_vec1 : eig_vec2;
1586 auto nlinfos_minor_split_pair =
nlinfo_line_split(minor_axis, centroid, partition_offset, nlinfo, partition_diagram, mask_buf);
1587 if (nlinfos_minor_split_pair.first.size() == 0 || nlinfos_minor_split_pair.second.size() == 0) {
1592 if (nlinfos_minor_split_pair.first.size() != 1 || nlinfos_minor_split_pair.second.size() != 1) {
1594 auto nlinfos_major_split_pair =
nlinfo_line_split(major_axis, centroid, partition_offset, nlinfo, partition_diagram, mask_buf);
1595 if (nlinfos_major_split_pair.first.size() == 0 || nlinfos_major_split_pair.second.size() == 0) {
1600 if (nlinfos_major_split_pair.first.size() != 1 || nlinfos_major_split_pair.second.size() != 1) {
1605 const auto &minor_largest1 = *std::max_element(nlinfos_minor_split_pair.first.begin(), nlinfos_minor_split_pair.first.end(), nlinfo_compare);
1606 const auto &minor_largest2 = *std::max_element(nlinfos_minor_split_pair.second.begin(), nlinfos_minor_split_pair.second.end(), nlinfo_compare);
1608 const auto &major_largest1 = *std::max_element(nlinfos_major_split_pair.first.begin(), nlinfos_major_split_pair.first.end(), nlinfo_compare);
1609 const auto &major_largest2 = *std::max_element(nlinfos_major_split_pair.second.begin(), nlinfos_major_split_pair.second.end(), nlinfo_compare);
1611 difference_type minor_sum = minor_largest1.points + minor_largest2.points;
1612 difference_type major_sum = major_largest1.points + major_largest2.points;
1616 const auto &nlinfo_larger = minor_sum > major_sum ? (minor_largest2.points >= minor_largest1.points ? minor_largest2 : minor_largest1) :
1617 (major_largest2.points >= major_largest1.points ? major_largest2 : major_largest1);
1618 const auto &nlinfo_smaller = minor_sum > major_sum ? (minor_largest2.points < minor_largest1.points ? minor_largest2 : minor_largest1) :
1619 (major_largest2.points < major_largest1.points ? major_largest2 : major_largest1);
1621 auto partition_num_larger = minor_sum > major_sum ? (minor_largest2.points >= minor_largest1.points ? partition_offset+1 : partition_offset) :
1622 (major_largest2.points >= major_largest1.points ? partition_offset+1 : partition_offset);
1623 auto partition_num_smaller = minor_sum > major_sum ? (minor_largest2.points < minor_largest1.points ? partition_offset+1 : partition_offset) :
1624 (major_largest2.points < major_largest1.points ? partition_offset+1 : partition_offset);
1629 fill(mask_buf, nlinfo,
true);
1630 fill(mask_buf, nlinfo_larger,
false);
1631 fill(mask_buf, nlinfo_smaller,
false);
1634 fill(partition_diagram, nlinfo_larger, partition_num_larger);
1635 fill(partition_diagram, nlinfo_smaller, partition_num_smaller);
1646 fill(mask_buf, nlinfo,
true);
1647 mask_buf = (std::move(mask_buf) & (partition_diagram == partition_offset));
1652 fill(mask_buf, nlinfo,
true);
1653 mask_buf = (std::move(mask_buf) & (partition_diagram == partition_offset+1));
1656 if (nlinfo_part1_pair.first.size() != 1 || nlinfo_part2_pair.first.size() != 1) {
1658 throw std::invalid_argument(
"nlinfo_contig_split() did not actually form 2 contiguous subregions");
1661 return { { std::move(nlinfo_part1_pair.first.front()), std::move(nlinfo_part2_pair.first.front()) },
true };
1672 if (part_num2 == part_num1) {
1674 fill(partition_diagram, nlinfo, part_num1);
1679 auto nlinfo_split_pair =
nlinfo_contig_split(part_num1, nlinfo, partition_diagram, mask_buf);
1681 if (nlinfo_split_pair.second) {
1683 difference_type part_num_middle = (part_num1 + part_num2)/2;
1707 fill(partition_diagram, nlinfo, -1);
1710 return partition_diagram;
1725 for (difference_type region_idx = 0; region_idx < roi.
size_regions(); ++region_idx) {
1731 return partition_diagram;
1744 for (difference_type partition_idx = 0; partition_idx < num_partitions; ++partition_idx) {
1746 mask_buf1() =
false;
1747 fill(mask_buf1, nlinfo,
true);
1748 mask_buf1 = (std::move(mask_buf1) & (partition_diagram == partition_idx));
1752 if (nlinfos_pair.first.size() == 0) {
1755 }
else if (nlinfos_pair.first.size() != 1) {
1757 throw std::invalid_argument(
"While calculating partition seeds, it was determined that one of the partitioned regions was not 4-way contiguous.");
1761 get_nlinfo_SDA(SDA, nlinfos_pair.first.front(), mask_buf1, mask_buf2);
1765 auto max_info =
max(SDA, nlinfos_pair.first.front());
1768 seed_buf(partition_idx,0) = max_info.second.first;
1769 seed_buf(partition_idx,1) = max_info.second.second;
1782 std::vector<Array2D<difference_type>> partition_seeds(roi.
size_regions());
1786 for (difference_type region_idx = 0; region_idx < roi.
size_regions(); ++region_idx) {
1792 return partition_seeds;
1806 for (difference_type seed_idx = 0; seed_idx < seeds_pos.
height(); ++seed_idx) {
1807 params_buf(0) = seeds_pos(seed_idx,0);
1808 params_buf(1) = seeds_pos(seed_idx,1);
1810 auto seed_params_pair = sr_nloptimizer.
global(params_buf);
1811 if (seed_params_pair.second && (seed_params.
empty() || seed_params_pair.first(8) < seed_params(8))) {
1814 seed_params = seed_params_pair.first;
1827 double cutoff_corrcoef,
1828 double cutoff_delta_disp,
1838 difference_type p1 = queue_params(0) + p1_delta;
1839 difference_type p2 = queue_params(1) + p2_delta;
1840 if (nlinfo.
in_nlinfo(p1 / scalefactor, p2 / scalefactor) &&
1841 A_ap(p1 / scalefactor, p2 / scalefactor)) {
1842 A_ap(p1 / scalefactor, p2 / scalefactor) =
false;
1848 params_buf(2) = queue_params(2) + p1_delta * queue_params(4) + p2_delta * queue_params(5);
1849 params_buf(3) = queue_params(3) + p1_delta * queue_params(6) + p2_delta * queue_params(7);
1850 params_buf(4) = queue_params(4);
1851 params_buf(5) = queue_params(5);
1852 params_buf(6) = queue_params(6);
1853 params_buf(7) = queue_params(7);
1856 auto params_pair = sr_nloptimizer(params_buf);
1857 if (params_pair.second) {
1859 if (std::sqrt(std::pow(params_pair.first(2) - params_buf(2),2) + std::pow(params_pair.first(3) - params_buf(3),2)) < cutoff_delta_disp &&
1860 params_pair.first(8) < cutoff_corrcoef) {
1861 queue.push(params_pair.first);
1877 double cutoff_corrcoef,
1878 double cutoff_delta_disp,
1892 difference_type num_redundant_seeds = 4;
1895 for (
auto ®ion_seeds: redundant_seeds) {
1897 region_seeds = std::move(region_seeds) * scalefactor;
1902 for (difference_type region_idx = 0; region_idx < roi_reduced.
size_regions(); ++region_idx) {
1903 if (redundant_seeds[region_idx].empty()) {
1913 if (!seed_params.empty()) {
1914 A_ap(seed_params(0) / scalefactor, seed_params(1) / scalefactor) =
false;
1919 std::priority_queue<Array2D<double>, std::vector<Array2D<double>>, std::function<bool(
const Array2D<double>&,
const Array2D<double>&)>> queue(comp);
1922 queue.push(seed_params);
1923 while (!queue.empty()) {
1925 auto queue_params = std::move(queue.top()); queue.pop();
1928 A_vp(queue_params(0) / scalefactor, queue_params(1) / scalefactor) =
true;
1929 A_v(queue_params(0) / scalefactor, queue_params(1) / scalefactor) = queue_params(2);
1930 A_u(queue_params(0) / scalefactor, queue_params(1) / scalefactor) = queue_params(3);
1931 A_cc(queue_params(0) / scalefactor, queue_params(1) / scalefactor) = queue_params(8);
1934 analyze_point(queue_params, -scalefactor, 0, roi_reduced.
get_nlinfo(region_idx), scalefactor, sr_nloptimizer, cutoff_corrcoef, cutoff_delta_disp, queue, A_ap, params_buf);
1935 analyze_point(queue_params, scalefactor, 0, roi_reduced.
get_nlinfo(region_idx), scalefactor, sr_nloptimizer, cutoff_corrcoef, cutoff_delta_disp, queue, A_ap, params_buf);
1936 analyze_point(queue_params, 0, -scalefactor, roi_reduced.
get_nlinfo(region_idx), scalefactor, sr_nloptimizer, cutoff_corrcoef, cutoff_delta_disp, queue, A_ap, params_buf);
1937 analyze_point(queue_params, 0, scalefactor, roi_reduced.
get_nlinfo(region_idx), scalefactor, sr_nloptimizer, cutoff_corrcoef, cutoff_delta_disp, queue, A_ap, params_buf);
1944 A_ap(roi_reduced.
get_mask()) =
false;
1956 double cutoff_corrcoef,
1961 NLOG_DEBUG <<
"DEBUG: RGDIC called with num_threads=" << num_threads;
1964 throw std::invalid_argument(
"Attempted to perform RGDIC on reference image input of size: " + A_ref.
size_2D_string() +
1965 " with current image input of size: " + A_cur.
size_2D_string() +
". Sizes must be the same.");
1969 throw std::invalid_argument(
"Attempted to perform RGDIC on reference image input of size: " + A_ref.
size_2D_string() +
1970 " with ROI input of size: " + roi.
size_2D_string() +
". Sizes must be the same.");
1973 if (scalefactor < 1) {
1974 throw std::invalid_argument(
"Attempted to perform RGDIC with scalefactor: " + std::to_string(scalefactor) +
1975 ". scalefactor must be 1 or greater.");
1979 throw std::invalid_argument(
"Interpolation used for RGDIC must be cubic or greater.");
1983 throw std::invalid_argument(
"Attempted to perform RGDIC with radius: " + std::to_string(r) +
1984 ". radius must be 5 or greater.");
1987 if (num_threads < 1) {
1988 throw std::invalid_argument(
"Attempted to perform RGDIC with number of threads: " + std::to_string(num_threads) +
1989 ". Number of threads must be 1 or greater.");
1992 if (cutoff_corrcoef < 0 || cutoff_corrcoef > 4.0) {
1993 throw std::invalid_argument(
"Input correlation coefficient cutoff of: " + std::to_string(cutoff_corrcoef) +
1994 " is not between [0,4].");
2001 double cutoff_delta_disp = scalefactor;
2004 auto roi_reduced = roi.
reduce(scalefactor);
2024 Array2D<bool> A_vp(roi_reduced.height(), roi_reduced.width());
2027 std::vector<std::thread> threads(num_threads);
2028 for (difference_type thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
2031 roi_reduced.form_union(partition_diagram == thread_idx),
2043 if (
false && debug) {
2051 for (
const auto &subset_seed : subset_seeds) {
2052 if (!subset_seed.empty()) {
2056 const auto &subregion_nlinfo = subregion_gen(subset_seed(0) * scalefactor, subset_seed(1) * scalefactor);
2057 for (difference_type nl_idx = 0; nl_idx < subregion_nlinfo.nodelist.width(); ++nl_idx) {
2058 difference_type p2 = nl_idx + subregion_nlinfo.left_nl;
2059 for (difference_type np_idx = 0; np_idx < subregion_nlinfo.noderange(nl_idx); np_idx += 2) {
2060 difference_type np_top = subregion_nlinfo.nodelist(np_idx,nl_idx);
2061 difference_type np_bottom = subregion_nlinfo.nodelist(np_idx + 1,nl_idx);
2062 for (difference_type p1 = np_top; p1 <= np_bottom; ++p1) {
2063 difference_type p1_shifted = p1 - (subset_seed(0) * scalefactor) + subregion_gen.get_r();
2064 difference_type p2_shifted = p2 - (subset_seed(1) * scalefactor) + subregion_gen.get_r();
2066 subset(p1_shifted, p2_shifted) = A_ref(p1,p2);
2076 imshow(partition_diagram,2000);
2082 while (any_true(A_ap)) {
2090 for (difference_type thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
2091 threads[thread_idx].join();
2100 auto A_vp_buf = A_vp;
2101 for (difference_type region_idx = 0; region_idx < roi_reduced.size_regions(); ++region_idx) {
2102 for (difference_type nl_idx = 0; nl_idx < roi_reduced.get_nlinfo(region_idx).nodelist.width(); ++nl_idx) {
2103 difference_type p2 = nl_idx + roi_reduced.get_nlinfo(region_idx).left_nl;
2104 for (difference_type np_idx = 0; np_idx < roi_reduced.get_nlinfo(region_idx).noderange(nl_idx); np_idx += 2) {
2105 difference_type np_top = roi_reduced.get_nlinfo(region_idx).nodelist(np_idx,nl_idx);
2106 difference_type np_bottom = roi_reduced.get_nlinfo(region_idx).nodelist(np_idx + 1,nl_idx);
2107 for (difference_type p1 = np_top; p1 <= np_bottom; ++p1) {
2108 if ((roi_reduced.get_nlinfo(region_idx).in_nlinfo(p1-1,p2) && A_vp_buf(p1-1,p2) && std::sqrt(std::pow(A_v(p1-1,p2) - A_v(p1,p2),2) + std::pow(A_u(p1-1,p2) - A_u(p1,p2),2)) > cutoff_delta_disp) ||
2109 (roi_reduced.get_nlinfo(region_idx).in_nlinfo(p1+1,p2) && A_vp_buf(p1+1,p2) && std::sqrt(std::pow(A_v(p1+1,p2) - A_v(p1,p2),2) + std::pow(A_u(p1+1,p2) - A_u(p1,p2),2)) > cutoff_delta_disp) ||
2110 (roi_reduced.get_nlinfo(region_idx).in_nlinfo(p1,p2-1) && A_vp_buf(p1,p2-1) && std::sqrt(std::pow(A_v(p1,p2-1) - A_v(p1,p2),2) + std::pow(A_u(p1,p2-1) - A_u(p1,p2),2)) > cutoff_delta_disp) ||
2111 (roi_reduced.get_nlinfo(region_idx).in_nlinfo(p1,p2+1) && A_vp_buf(p1,p2+1) && std::sqrt(std::pow(A_v(p1,p2+1) - A_v(p1,p2),2) + std::pow(A_u(p1,p2+1) - A_u(p1,p2),2)) > cutoff_delta_disp)) {
2112 A_vp(p1,p2) =
false;
2120 auto roi_valid = roi_reduced.form_union(A_vp);
2122 return Disp2D(std::move(A_v), std::move(A_u), std::move(A_cc), roi_valid, scalefactor);
2132 double cutoff_corrcoef,
2134 const std::vector<SeedParams>& seeds_by_region = {},
2135 bool seeds_are_optimized =
false) {
2140 throw std::invalid_argument(
"Attempted to perform RGDIC on reference image input of size: " + A_ref.
size_2D_string() +
2141 " with current image input of size: " + A_cur.
size_2D_string() +
". Sizes must be the same.");
2145 throw std::invalid_argument(
"Attempted to perform RGDIC on reference image input of size: " + A_ref.
size_2D_string() +
2146 " with ROI input of size: " + roi.
size_2D_string() +
". Sizes must be the same.");
2149 if (scalefactor < 1) {
2150 throw std::invalid_argument(
"Attempted to perform RGDIC with scalefactor: " + std::to_string(scalefactor) +
2151 ". scalefactor must be 1 or greater.");
2155 throw std::invalid_argument(
"Interpolation used for RGDIC must be cubic or greater.");
2159 throw std::invalid_argument(
"Attempted to perform RGDIC with radius: " + std::to_string(r) +
2160 ". radius must be 5 or greater.");
2163 if (cutoff_corrcoef < 0 || cutoff_corrcoef > 4.0) {
2164 throw std::invalid_argument(
"Input correlation coefficient cutoff of: " + std::to_string(cutoff_corrcoef) +
2165 " is not between [0,4].");
2172 double cutoff_delta_disp = scalefactor;
2175 auto roi_reduced = roi.
reduce(scalefactor);
2178 auto sr_nloptimizer = details::subregion_nloptimizer(A_ref, A_cur, roi, scalefactor, interp_type, subregion_type, r);
2181 Array2D<double> A_v(roi_reduced.height(), roi_reduced.width());
2182 Array2D<double> A_u(roi_reduced.height(), roi_reduced.width());
2183 Array2D<double> A_cc(roi_reduced.height(), roi_reduced.width());
2189 Array2D<bool> A_ap(roi_reduced.get_mask());
2190 Array2D<bool> A_vp(roi_reduced.height(), roi_reduced.width());
2196 bool use_provided_seeds = !seeds_by_region.empty();
2199 std::vector<Array2D<difference_type>> redundant_seeds;
2201 if (!use_provided_seeds) {
2210 for (
auto ®ion_seeds: redundant_seeds) {
2212 region_seeds = std::move(region_seeds) * scalefactor;
2217 Array2D<double> params_buf(10, 1);
2218 for (difference_type region_idx = 0; region_idx < roi_reduced.size_regions(); ++region_idx) {
2219 Array2D<double> seed_params;
2221 if (use_provided_seeds) {
2222 NLOG_WARN <<
"WARNING - Using provided seed for region " << region_idx;
2224 if (region_idx <
static_cast<difference_type>(seeds_by_region.size())) {
2225 const SeedParams& provided_seed = seeds_by_region[region_idx];
2226 NLOG_DEBUG <<
"DEBUG::Provided seed: " << provided_seed.y <<
" " << provided_seed.x;
2228 if (seeds_are_optimized) {
2230 NLOG_DEBUG <<
"DEBUG::Already Optimized seed: " << provided_seed.y <<
" " << provided_seed.x;
2231 seed_params = provided_seed.to_array();
2234 Array2D<double> single_seed(2, 1);
2235 single_seed(0) =
static_cast<double>(provided_seed.y);
2236 single_seed(1) =
static_cast<double>(provided_seed.x);
2239 params_buf(0) = single_seed(0);
2240 params_buf(1) = single_seed(1);
2241 params_buf(2) = provided_seed.v;
2242 params_buf(3) = provided_seed.u;
2243 params_buf(4) = provided_seed.dv_dy;
2244 params_buf(5) = provided_seed.dv_dx;
2245 params_buf(6) = provided_seed.du_dy;
2246 params_buf(7) = provided_seed.du_dx;
2247 params_buf(8) = 0.0;
2248 params_buf(9) = 0.0;
2253 auto result = sr_nloptimizer(params_buf);
2254 if (result.second) {
2255 seed_params = result.first;
2257 NLOG_DEBUG <<
"DEBUG::Optimized seed: " << seed_params(0) <<
" " << seed_params(1);
2261 NLOG_WARN <<
"Warning - Seed optimization doesn't converge. Use non optimized seed";
2268 if (redundant_seeds[region_idx].empty()) {
2280 if (!seed_params.empty()) {
2281 A_ap(seed_params(0) / scalefactor, seed_params(1) / scalefactor) =
false;
2285 auto comp = [](
const Array2D<double> &a,
const Array2D<double> &b ) {
return a(8) > b(8); };
2286 std::priority_queue<Array2D<double>, std::vector<Array2D<double>>, std::function<bool(
const Array2D<double>&,
const Array2D<double>&)>> queue(comp);
2289 queue.push(seed_params);
2290 while (!queue.empty()) {
2292 auto queue_params = std::move(queue.top()); queue.pop();
2295 A_vp(queue_params(0) / scalefactor, queue_params(1) / scalefactor) =
true;
2296 A_v(queue_params(0) / scalefactor, queue_params(1) / scalefactor) = queue_params(2);
2297 A_u(queue_params(0) / scalefactor, queue_params(1) / scalefactor) = queue_params(3);
2298 A_cc(queue_params(0) / scalefactor, queue_params(1) / scalefactor) = queue_params(8);
2301 details::analyze_point(queue_params, -scalefactor, 0, roi_reduced.get_nlinfo(region_idx), scalefactor, sr_nloptimizer, cutoff_corrcoef, cutoff_delta_disp, queue, A_ap, params_buf);
2302 details::analyze_point(queue_params, scalefactor, 0, roi_reduced.get_nlinfo(region_idx), scalefactor, sr_nloptimizer, cutoff_corrcoef, cutoff_delta_disp, queue, A_ap, params_buf);
2303 details::analyze_point(queue_params, 0, -scalefactor, roi_reduced.get_nlinfo(region_idx), scalefactor, sr_nloptimizer, cutoff_corrcoef, cutoff_delta_disp, queue, A_ap, params_buf);
2304 details::analyze_point(queue_params, 0, scalefactor, roi_reduced.get_nlinfo(region_idx), scalefactor, sr_nloptimizer, cutoff_corrcoef, cutoff_delta_disp, queue, A_ap, params_buf);
2310 if (
false && debug) {
2317 Array2D<double> subset(2*r+1,2*r+1);
2318 for (
const auto &subset_seed : subset_seeds) {
2319 if (!subset_seed.empty()) {
2323 const auto &subregion_nlinfo = subregion_gen(subset_seed(0) * scalefactor, subset_seed(1) * scalefactor);
2324 for (difference_type nl_idx = 0; nl_idx < subregion_nlinfo.nodelist.width(); ++nl_idx) {
2326 for (difference_type np_idx = 0; np_idx < subregion_nlinfo.noderange(nl_idx); np_idx += 2) {
2328 difference_type np_bottom = subregion_nlinfo.nodelist(np_idx + 1,nl_idx);
2329 for (difference_type p1 = np_top; p1 <= np_bottom; ++p1) {
2330 difference_type p1_shifted = p1 - (subset_seed(0) * scalefactor) + subregion_gen.get_r();
2331 difference_type p2_shifted = p2 - (subset_seed(1) * scalefactor) + subregion_gen.get_r();
2333 subset(p1_shifted, p2_shifted) = A_ref(p1,p2);
2350 auto A_vp_buf = A_vp;
2351 for (difference_type region_idx = 0; region_idx < roi_reduced.size_regions(); ++region_idx) {
2352 for (difference_type nl_idx = 0; nl_idx < roi_reduced.get_nlinfo(region_idx).nodelist.width(); ++nl_idx) {
2353 difference_type p2 = nl_idx + roi_reduced.get_nlinfo(region_idx).left_nl;
2354 for (difference_type np_idx = 0; np_idx < roi_reduced.get_nlinfo(region_idx).noderange(nl_idx); np_idx += 2) {
2355 difference_type np_top = roi_reduced.get_nlinfo(region_idx).nodelist(np_idx,nl_idx);
2356 difference_type np_bottom = roi_reduced.get_nlinfo(region_idx).nodelist(np_idx + 1,nl_idx);
2357 for (difference_type p1 = np_top; p1 <= np_bottom; ++p1) {
2358 if ((roi_reduced.get_nlinfo(region_idx).in_nlinfo(p1-1,p2) && A_vp_buf(p1-1,p2) && std::sqrt(std::pow(A_v(p1-1,p2) - A_v(p1,p2),2) + std::pow(A_u(p1-1,p2) - A_u(p1,p2),2)) > cutoff_delta_disp) ||
2359 (roi_reduced.get_nlinfo(region_idx).in_nlinfo(p1+1,p2) && A_vp_buf(p1+1,p2) && std::sqrt(std::pow(A_v(p1+1,p2) - A_v(p1,p2),2) + std::pow(A_u(p1+1,p2) - A_u(p1,p2),2)) > cutoff_delta_disp) ||
2360 (roi_reduced.get_nlinfo(region_idx).in_nlinfo(p1,p2-1) && A_vp_buf(p1,p2-1) && std::sqrt(std::pow(A_v(p1,p2-1) - A_v(p1,p2),2) + std::pow(A_u(p1,p2-1) - A_u(p1,p2),2)) > cutoff_delta_disp) ||
2361 (roi_reduced.get_nlinfo(region_idx).in_nlinfo(p1,p2+1) && A_vp_buf(p1,p2+1) && std::sqrt(std::pow(A_v(p1,p2+1) - A_v(p1,p2),2) + std::pow(A_u(p1,p2+1) - A_u(p1,p2),2)) > cutoff_delta_disp)) {
2362 A_vp(p1,p2) =
false;
2370 auto roi_valid = roi_reduced.form_union(A_vp);
2371 return Disp2D(std::move(A_v), std::move(A_u), std::move(A_cc), roi_valid, scalefactor);
2385 bool save_disps_steps_override) : imgs(imgs),
2387 scalefactor(scalefactor),
2388 interp_type(interp_type),
2389 subregion_type(subregion_type),
2391 num_threads(num_threads),
2394 switch (config_type) {
2396 this->cutoff_corrcoef = 2.0;
2403 this->cutoff_corrcoef = 2.0;
2410 this->cutoff_corrcoef = 0.7;
2431 NLOG_INFO <<
"Step displacement data will be saved (save_disps_steps=true)";
2441 is.read(
reinterpret_cast<char*
>(&num_images), std::streamsize(
sizeof(
difference_type)));
2442 DIC_input.
imgs.resize(num_images);
2443 for (
auto &img : DIC_input.
imgs) {
2454 is.read(
reinterpret_cast<char*
>(&DIC_input.
interp_type), std::streamsize(
sizeof(
INTERP)));
2460 is.read(
reinterpret_cast<char*
>(&DIC_input.
r), std::streamsize(
sizeof(
difference_type)));
2466 is.read(
reinterpret_cast<char*
>(&DIC_input.
cutoff_corrcoef), std::streamsize(
sizeof(
double)));
2469 is.read(
reinterpret_cast<char*
>(&DIC_input.
update_corrcoef), std::streamsize(
sizeof(
double)));
2472 is.read(
reinterpret_cast<char*
>(&DIC_input.
prctile_corrcoef), std::streamsize(
sizeof(
double)));
2481 is.read(
reinterpret_cast<char*
>(&DIC_input.
save_disps_steps), std::streamsize(
sizeof(
bool)));
2484 is.read(
reinterpret_cast<char*
>(&DIC_input.
debug), std::streamsize(
sizeof(
bool)));
2491 std::ifstream is(filename.c_str(), std::ios::in | std::ios::binary);
2492 if (!is.is_open()) {
2493 throw std::invalid_argument(
"Could not open " + filename +
" for loading DIC_analysis_input.");
2513 os.write(
reinterpret_cast<const char*
>(&num_imgs), std::streamsize(
sizeof(
difference_type)));
2514 for (
const auto &img : DIC_input.
imgs) {
2522 os.write(
reinterpret_cast<const char*
>(&DIC_input.
interp_type), std::streamsize(
sizeof(
INTERP)));
2526 os.write(
reinterpret_cast<const char*
>(&DIC_input.
r), std::streamsize(
sizeof(
difference_type)));
2530 os.write(
reinterpret_cast<const char*
>(&DIC_input.
cutoff_corrcoef), std::streamsize(
sizeof(
double)));
2532 os.write(
reinterpret_cast<const char*
>(&DIC_input.
update_corrcoef), std::streamsize(
sizeof(
double)));
2534 os.write(
reinterpret_cast<const char*
>(&DIC_input.
prctile_corrcoef), std::streamsize(
sizeof(
double)));
2540 os.write(
reinterpret_cast<const char*
>(&DIC_input.
save_disps_steps), std::streamsize(
sizeof(
bool)));
2542 os.write(
reinterpret_cast<const char*
>(&DIC_input.
debug), std::streamsize(
sizeof(
bool)));
2547 std::ofstream os(filename.c_str(), std::ios::out | std::ios::binary | std::ios::trunc);
2548 if (!os.is_open()) {
2549 throw std::invalid_argument(
"Could not open " + filename +
" for saving DIC_analysis_input.");
2553 save(DIC_input, os);
2565 is.read(
reinterpret_cast<char*
>(&num_disps), std::streamsize(
sizeof(
difference_type)));
2566 DIC_output.
disps.resize(num_disps);
2567 for (
auto &disp : DIC_output.
disps) {
2576 is.read(
reinterpret_cast<char*
>(&length), std::streamsize(
sizeof(
difference_type)));
2577 DIC_output.
units = std::string(length,
' ');
2578 is.read(
const_cast<char*
>(DIC_output.
units.c_str()), std::streamsize(length));
2581 is.read(
reinterpret_cast<char*
>(&DIC_output.
units_per_pixel), std::streamsize(
sizeof(
double)));
2588 std::ifstream is(filename.c_str(), std::ios::in | std::ios::binary);
2589 if (!is.is_open()) {
2590 throw std::invalid_argument(
"Could not open " + filename +
" for loading DIC_analysis_output.");
2607 os.write(
reinterpret_cast<const char*
>(&num_disps), std::streamsize(
sizeof(
difference_type)));
2608 for (
const auto &disp : DIC_output.
disps) {
2617 os.write(
reinterpret_cast<const char*
>(&length), std::streamsize(
sizeof(
difference_type)));
2618 os.write(DIC_output.
units.c_str(), std::streamsize(length));
2621 os.write(
reinterpret_cast<const char*
>(&DIC_output.
units_per_pixel), std::streamsize(
sizeof(
double)));
2626 std::ofstream os(filename.c_str(), std::ios::out | std::ios::binary | std::ios::trunc);
2627 if (!os.is_open()) {
2628 throw std::invalid_argument(
"Could not open " + filename +
" for saving DIC_analysis_output.");
2632 save(DIC_output, os);
2646 is.read(
reinterpret_cast<char*
>(&num_disps), std::streamsize(
sizeof(
difference_type)));
2654 is.read(
reinterpret_cast<char*
>(&num_rois), std::streamsize(
sizeof(
difference_type)));
2662 is.read(
reinterpret_cast<char*
>(&num_ref_idx), std::streamsize(
sizeof(
difference_type)));
2665 is.read(
reinterpret_cast<char*
>(&idx), std::streamsize(
sizeof(
difference_type)));
2672 std::ifstream is(filename.c_str(), std::ios::in | std::ios::binary);
2673 if (!is.is_open()) {
2674 throw std::invalid_argument(
"Could not open " + filename +
" for loading DIC_analysis_step_data.");
2688 os.write(
reinterpret_cast<const char*
>(&num_disps), std::streamsize(
sizeof(
difference_type)));
2689 for (
const auto &disp : step_data.
step_disps) {
2695 os.write(
reinterpret_cast<const char*
>(&num_rois), std::streamsize(
sizeof(
difference_type)));
2696 for (
const auto &roi : step_data.
step_rois) {
2702 os.write(
reinterpret_cast<const char*
>(&num_ref_idx), std::streamsize(
sizeof(
difference_type)));
2704 os.write(
reinterpret_cast<const char*
>(&idx), std::streamsize(
sizeof(
difference_type)));
2709 std::ofstream os(filename.c_str(), std::ios::out | std::ios::binary | std::ios::trunc);
2710 if (!os.is_open()) {
2711 throw std::invalid_argument(
"Could not open " + filename +
" for saving DIC_analysis_step_data.");
2714 save(step_data, os);
2721 if (DIC_input.
imgs.size() < 2) {
2723 throw std::invalid_argument(
"Only " + std::to_string(DIC_input.
imgs.size()) +
" images provided to DIC_analysis(). 2 or more are required.");
2727 std::chrono::time_point<std::chrono::system_clock> start_analysis = std::chrono::system_clock::now();
2732 DIC_output.
disps.resize(DIC_input.
imgs.size()-1);
2734 DIC_output.
units =
"pixels";
2740 std::vector<Disp2D> step_disps;
2741 std::vector<ROI2D> step_rois;
2742 std::vector<difference_type> step_ref_idx;
2745 if (use_post_process) {
2746 step_disps.resize(DIC_input.
imgs.size()-1);
2747 step_rois.resize(DIC_input.
imgs.size()-1);
2748 step_ref_idx.resize(DIC_input.
imgs.size()-1);
2749 NLOG_INFO <<
"Using POST_PROCESS (MATLAB-style) accumulation mode.";
2755 for (difference_type
ref_idx = 0, cur_idx = 1; cur_idx < difference_type(DIC_input.
imgs.size()); ++cur_idx) {
2759 NLOG_INFO << std::endl <<
"Processing displacement field " << cur_idx <<
" of " << DIC_input.
imgs.size() - 1 <<
".";
2761 NLOG_INFO <<
"Current image: " << DIC_input.
imgs[cur_idx] <<
".";
2763 std::chrono::time_point<std::chrono::system_clock> start_rgdic = std::chrono::system_clock::now();
2766 DIC_input.
imgs[cur_idx].get_gs(),
2776 std::chrono::time_point<std::chrono::system_clock> end_rgdic = std::chrono::system_clock::now();
2777 std::chrono::duration<double> elapsed_seconds_rgdic = end_rgdic - start_rgdic;
2778 NLOG_INFO <<
"Time: " << elapsed_seconds_rgdic.count() <<
".";
2784 if (use_post_process) {
2786 step_disps[cur_idx-1] = disps;
2787 step_rois[cur_idx-1] = roi_ref;
2788 step_ref_idx[cur_idx-1] =
ref_idx;
2790 DIC_output.
disps[cur_idx-1] = disps;
2796 if (added_disps.get_roi().get_points() == 0) {
2797 NLOG_WARN <<
"WARNING: add() at frame " << cur_idx <<
" produced empty ROI!";
2799 NLOG_WARN <<
" Current disps had " << disps.get_roi().get_points() <<
" points.";
2800 NLOG_WARN <<
" Using current disps directly instead of combined result.";
2801 DIC_output.
disps[cur_idx-1] = disps;
2802 }
else if (added_disps.get_roi().get_points() < disps.get_roi().get_points() / 2) {
2803 NLOG_WARN <<
"WARNING: add() at frame " << cur_idx <<
" lost significant points!";
2804 NLOG_WARN <<
" Current disps: " << disps.get_roi().get_points() <<
", Combined: " << added_disps.get_roi().get_points();
2805 DIC_output.
disps[cur_idx-1] = added_disps;
2807 DIC_output.
disps[cur_idx-1] = added_disps;
2810 DIC_output.
disps[cur_idx-1] = disps;
2817 Array2D<double> cc_values = disps.get_cc().get_array()(disps.get_cc().get_roi().get_mask());
2818 if (!cc_values.
empty()) {
2820 NLOG_INFO <<
"Selected correlation coefficient value: " << selected_corrcoef <<
". Correlation coefficient update value: " << DIC_input.
update_corrcoef <<
".";
2822 NLOG_INFO <<
"DA::Updating reference image..." <<
ref_idx <<
" -> " << cur_idx;
2826 auto prev_roi = roi_ref;
2831 NLOG_WARN <<
"WARNING: ROI update at frame " << cur_idx <<
" produced an empty ROI!";
2837 NLOG_WARN <<
" Reverting to previous ROI (frame " <<
ref_idx <<
") to continue analysis.";
2840 if (DIC_input.
debug) {
2842 cv::Mat prev_img = get_cv_img(prev_roi.get_mask(), 0, 255);
2843 cv::Mat curr_img = get_cv_img(roi_ref.
get_mask(), 0, 255);
2844 cv::absdiff(prev_img, curr_img, diff);
2845 cv::imwrite(
"roi_" + std::to_string(cur_idx) +
"_prev.png", prev_img);
2846 cv::imwrite(
"roi_" + std::to_string(cur_idx) +
"_curr.png", curr_img);
2847 cv::imwrite(
"diff_roi_" + std::to_string(cur_idx) +
".png", diff);
2848 NLOG_DEBUG <<
"DEBUG::Saved difference image for frame " << cur_idx;
2855 if (use_post_process) {
2856 NLOG_INFO << std::endl <<
"Post-processing: Combining step displacements...";
2858 for (difference_type cur_idx = 1; cur_idx < difference_type(DIC_input.
imgs.size()); ++cur_idx) {
2860 std::vector<Disp2D> chain_disps;
2861 std::vector<ROI2D> chain_rois;
2864 difference_type idx = cur_idx - 1;
2866 chain_disps.insert(chain_disps.begin(), step_disps[idx]);
2867 chain_rois.insert(chain_rois.begin(), step_rois[idx]);
2869 if (step_ref_idx[idx] == 0) {
2872 idx = step_ref_idx[idx] - 1;
2875 if (chain_disps.size() == 1) {
2877 DIC_output.
disps[cur_idx-1] = chain_disps[0];
2882 if (combined.get_roi().get_points() == 0) {
2883 NLOG_WARN <<
"WARNING: Post-process add_with_rois for frame " << cur_idx <<
" produced empty ROI!";
2884 NLOG_WARN <<
" Using last step displacement instead.";
2885 DIC_output.
disps[cur_idx-1] = step_disps[cur_idx-1];
2887 DIC_output.
disps[cur_idx-1] = combined;
2888 NLOG_INFO <<
"Frame " << cur_idx <<
": Combined " << chain_disps.size() <<
" steps, "
2889 << combined.get_roi().get_points() <<
" valid points.";
2896 if (DIC_input.
save_disps_steps && use_post_process && !step_disps.empty()) {
2902 std::string step_filename =
"DIC_analysis_step_data.bin";
2903 save(step_data, step_filename);
2904 NLOG_INFO <<
"Step displacement data saved to " << step_filename;
2908 std::chrono::time_point<std::chrono::system_clock> end_analysis = std::chrono::system_clock::now();
2909 std::chrono::duration<double> elapsed_seconds_analysis = end_analysis - start_analysis;
2910 NLOG_INFO << std::endl <<
"Total DIC analysis time: " << elapsed_seconds_analysis.count() <<
".";
2919 if (DIC_input.
imgs.size() < 2) {
2920 throw std::invalid_argument(
"Only " + std::to_string(DIC_input.
imgs.size()) +
" images provided to DIC_analysis(). 2 or more are required.");
2923 std::chrono::time_point<std::chrono::system_clock> start_analysis = std::chrono::system_clock::now();
2926 DIC_output.
disps.resize(DIC_input.
imgs.size()-1);
2928 DIC_output.
units =
"pixels";
2932 std::vector<Disp2D> step_disps;
2933 std::vector<ROI2D> step_rois;
2934 std::vector<difference_type> step_ref_idx;
2937 if (use_post_process) {
2938 step_disps.resize(DIC_input.
imgs.size()-1);
2939 step_rois.resize(DIC_input.
imgs.size()-1);
2940 step_ref_idx.resize(DIC_input.
imgs.size()-1);
2941 NLOG_INFO <<
"Using POST_PROCESS (MATLAB-style) accumulation mode.";
2946 std::vector<SeedParams> current_seeds = seeds_by_region;
2947 bool current_seeds_optimized = seeds_are_optimized;
2948 for (difference_type
ref_idx = 0, cur_idx = 1; cur_idx < difference_type(DIC_input.
imgs.size()); ++cur_idx) {
2949 NLOG_INFO << std::endl <<
"Processing displacement field " << cur_idx <<
" of " << DIC_input.
imgs.size() - 1 <<
".";
2951 NLOG_INFO <<
"Current image: " << DIC_input.
imgs[cur_idx] <<
".";
2953 std::chrono::time_point<std::chrono::system_clock> start_rgdic = std::chrono::system_clock::now();
2955 NLOG_DEBUG <<
"Debug:: " << current_seeds[0].x <<
" " << current_seeds[0].y;
2957 DIC_input.
imgs[cur_idx].get_gs(),
2966 current_seeds_optimized);
2968 std::chrono::time_point<std::chrono::system_clock> end_rgdic = std::chrono::system_clock::now();
2969 std::chrono::duration<double> elapsed_seconds_rgdic = end_rgdic - start_rgdic;
2970 NLOG_INFO <<
"Time: " << elapsed_seconds_rgdic.count() <<
".";
2973 if (use_post_process) {
2974 step_disps[cur_idx-1] = disps;
2975 step_rois[cur_idx-1] = roi_ref;
2976 step_ref_idx[cur_idx-1] =
ref_idx;
2977 DIC_output.
disps[cur_idx-1] = disps;
2981 if (added_disps.get_roi().get_points() == 0) {
2982 NLOG_WARN <<
"WARNING: add() at frame " << cur_idx <<
" produced empty ROI! Using current disps.";
2983 DIC_output.
disps[cur_idx-1] = disps;
2985 DIC_output.
disps[cur_idx-1] = added_disps;
2988 DIC_output.
disps[cur_idx-1] = disps;
2992 Array2D<double> cc_values = disps.get_cc().get_array()(disps.get_cc().get_roi().get_mask());
2993 if (!cc_values.
empty()) {
2995 NLOG_INFO <<
"Selected correlation coefficient value: " << selected_corrcoef <<
". Correlation coefficient update value: " << DIC_input.
update_corrcoef <<
".";
2997 NLOG_INFO <<
"DAS::Updating reference image..." <<
ref_idx <<
" -> " << cur_idx;
3001 auto prev_roi = roi_ref;
3006 if (
false && !current_seeds.empty()) {
3007 const auto& disp_for_seeds = use_post_process ? disps : DIC_output.
disps[cur_idx-1];
3008 const auto& u_arr = disp_for_seeds.get_u().get_array();
3009 const auto& v_arr = disp_for_seeds.get_v().get_array();
3011 std::vector<SeedParams> updated_seeds;
3012 updated_seeds.reserve(current_seeds.size());
3013 for (
const auto& seed : current_seeds) {
3015 difference_type ry = seed.y / sf;
3016 difference_type rx = seed.x / sf;
3017 if (ry >= 0 && ry < u_arr.height() && rx >= 0 && rx < u_arr.width()) {
3018 double du = u_arr(ry, rx) * sf;
3019 double dv = v_arr(ry, rx) * sf;
3021 difference_type new_x = seed.x +
static_cast<difference_type
>(std::round(du / sf) * sf);
3022 difference_type new_y = seed.y +
static_cast<difference_type
>(std::round(dv / sf) * sf);
3024 difference_type nry = new_y / sf;
3025 difference_type nrx = new_x / sf;
3026 const auto& new_mask = roi_ref.
get_mask();
3027 if (nry >= 0 && nry < new_mask.height() && nrx >= 0 && nrx < new_mask.width() && new_mask(nry, nrx)) {
3034 new_seed.
du_dx = 0.0;
3035 new_seed.
du_dy = 0.0;
3036 new_seed.
dv_dx = 0.0;
3037 new_seed.
dv_dy = 0.0;
3039 updated_seeds.push_back(new_seed);
3043 if (!updated_seeds.empty()) {
3044 current_seeds = updated_seeds;
3045 current_seeds_optimized =
false;
3046 NLOG_INFO <<
" Seeds propagated: " << updated_seeds.size() <<
"/" << seeds_by_region.size() <<
" remain in updated ROI.";
3048 NLOG_WARN <<
" WARNING: No seeds remain in updated ROI. Clearing seeds (auto-detect will be used).";
3049 current_seeds.clear();
3055 NLOG_WARN <<
"WARNING: ROI update at frame " << cur_idx <<
" produced an empty ROI!";
3056 NLOG_WARN <<
" Previous ROI had " << prev_roi.get_points() <<
" points.";
3061 current_seeds = seeds_by_region;
3062 current_seeds_optimized = seeds_are_optimized;
3063 NLOG_WARN <<
" Reverting to previous ROI (frame " <<
ref_idx <<
") to continue analysis.";
3066 if (DIC_input.
debug) {
3068 cv::Mat prev_img = get_cv_img(prev_roi.get_mask(), 0, 255);
3069 cv::Mat curr_img = get_cv_img(roi_ref.
get_mask(), 0, 255);
3070 cv::absdiff(prev_img, curr_img, diff);
3071 cv::imwrite(
"roi_" + std::to_string(cur_idx) +
"_prev.png", prev_img);
3072 cv::imwrite(
"roi_" + std::to_string(cur_idx) +
"_curr.png", curr_img);
3073 cv::imwrite(
"diff_roi_" + std::to_string(cur_idx) +
".png", diff);
3074 NLOG_DEBUG <<
"DEBUG::Saved difference image for frame " << cur_idx;
3081 if (use_post_process) {
3082 NLOG_INFO << std::endl <<
"Post-processing: Combining step displacements...";
3083 for (difference_type cur_idx = 1; cur_idx < difference_type(DIC_input.
imgs.size()); ++cur_idx) {
3084 std::vector<Disp2D> chain_disps;
3085 std::vector<ROI2D> chain_rois;
3086 difference_type idx = cur_idx - 1;
3088 chain_disps.insert(chain_disps.begin(), step_disps[idx]);
3089 chain_rois.insert(chain_rois.begin(), step_rois[idx]);
3090 if (step_ref_idx[idx] == 0)
break;
3091 idx = step_ref_idx[idx] - 1;
3093 if (chain_disps.size() == 1) {
3094 DIC_output.
disps[cur_idx-1] = chain_disps[0];
3097 if (combined.get_roi().get_points() == 0) {
3098 NLOG_WARN <<
"WARNING: Post-process add_with_rois for frame " << cur_idx <<
" produced empty ROI!";
3099 DIC_output.
disps[cur_idx-1] = step_disps[cur_idx-1];
3101 DIC_output.
disps[cur_idx-1] = combined;
3102 NLOG_INFO <<
"Frame " << cur_idx <<
": Combined " << chain_disps.size() <<
" steps.";
3109 if (DIC_input.
save_disps_steps && use_post_process && !step_disps.empty()) {
3115 std::string step_filename =
"DIC_analysis_sequential_step_data.bin";
3116 save(step_data, step_filename);
3117 NLOG_INFO <<
"Step displacement data saved to " << step_filename;
3120 std::chrono::time_point<std::chrono::system_clock> end_analysis = std::chrono::system_clock::now();
3121 std::chrono::duration<double> elapsed_seconds_analysis = end_analysis - start_analysis;
3122 NLOG_INFO << std::endl <<
"Total DIC analysis time: " << elapsed_seconds_analysis.count() <<
".";
3131 const auto& DIC_input = parallel_input.
base_input;
3133 if (DIC_input.imgs.size() < 2) {
3134 throw std::invalid_argument(
"Only " + std::to_string(DIC_input.imgs.size()) +
" images provided to DIC_analysis(). 2 or more are required.");
3137 std::chrono::time_point<std::chrono::system_clock> start_analysis = std::chrono::system_clock::now();
3140 DIC_output.
disps.resize(DIC_input.imgs.size()-1);
3142 DIC_output.
units =
"pixels";
3146 std::vector<Disp2D> step_disps;
3147 std::vector<ROI2D> step_rois;
3148 std::vector<difference_type> step_ref_idx;
3151 if (use_post_process) {
3152 step_disps.resize(DIC_input.imgs.size()-1);
3153 step_rois.resize(DIC_input.imgs.size()-1);
3154 step_ref_idx.resize(DIC_input.imgs.size()-1);
3155 NLOG_INFO <<
"Using POST_PROCESS (MATLAB-style) accumulation mode.";
3158 ROI2D roi_ref = DIC_input.roi;
3160 std::vector<SeedParams> current_seeds = parallel_input.
seeds_by_region;
3162 for (difference_type
ref_idx = 0, cur_idx = 1; cur_idx < difference_type(DIC_input.imgs.size()); ++cur_idx) {
3163 NLOG_INFO << std::endl <<
"Processing displacement field " << cur_idx <<
" of " << DIC_input.imgs.size() - 1 <<
".";
3165 NLOG_INFO <<
"Current image: " << DIC_input.imgs[cur_idx] <<
".";
3167 std::chrono::time_point<std::chrono::system_clock> start_rgdic = std::chrono::system_clock::now();
3170 DIC_input.imgs[cur_idx].get_gs(),
3172 DIC_input.scalefactor,
3173 DIC_input.interp_type,
3174 DIC_input.subregion_type,
3176 DIC_input.cutoff_corrcoef,
3179 current_seeds_optimized);
3181 std::chrono::time_point<std::chrono::system_clock> end_rgdic = std::chrono::system_clock::now();
3182 std::chrono::duration<double> elapsed_seconds_rgdic = end_rgdic - start_rgdic;
3183 NLOG_INFO <<
"Time: " << elapsed_seconds_rgdic.count() <<
".";
3186 if (use_post_process) {
3187 step_disps[cur_idx-1] = disps;
3188 step_rois[cur_idx-1] = roi_ref;
3189 step_ref_idx[cur_idx-1] =
ref_idx;
3190 DIC_output.
disps[cur_idx-1] = disps;
3193 auto added_disps =
add(std::vector<Disp2D>({ DIC_output.
disps[
ref_idx-1], disps }), DIC_input.interp_type);
3194 if (added_disps.get_roi().get_points() == 0) {
3195 NLOG_WARN <<
"WARNING: add() at frame " << cur_idx <<
" produced empty ROI! Using current disps.";
3196 DIC_output.
disps[cur_idx-1] = disps;
3198 DIC_output.
disps[cur_idx-1] = added_disps;
3201 DIC_output.
disps[cur_idx-1] = disps;
3205 Array2D<double> cc_values = disps.get_cc().get_array()(disps.get_cc().get_roi().get_mask());
3206 if (!cc_values.
empty()) {
3207 double selected_corrcoef = prctile(cc_values, DIC_input.prctile_corrcoef);
3208 NLOG_INFO <<
"Selected correlation coefficient value: " << selected_corrcoef <<
". Correlation coefficient update value: " << DIC_input.update_corrcoef <<
".";
3209 if (selected_corrcoef > DIC_input.update_corrcoef) {
3210 NLOG_INFO <<
"DASP::Updating reference image..." <<
ref_idx <<
" -> " << cur_idx;
3214 auto prev_roi = roi_ref;
3215 roi_ref =
update(prev_roi, use_post_process ? disps : DIC_output.
disps[cur_idx-1], DIC_input.interp_type, DIC_input.roi_update_mode);
3219 if (!current_seeds.empty()) {
3220 const auto& disp_for_seeds = use_post_process ? disps : DIC_output.
disps[cur_idx-1];
3221 const auto& u_arr = disp_for_seeds.get_u().get_array();
3222 const auto& v_arr = disp_for_seeds.get_v().get_array();
3223 difference_type sf = DIC_input.scalefactor;
3224 std::vector<SeedParams> updated_seeds;
3225 updated_seeds.reserve(current_seeds.size());
3226 for (
const auto& seed : current_seeds) {
3227 difference_type ry = seed.y / sf;
3228 difference_type rx = seed.x / sf;
3229 if (ry >= 0 && ry < u_arr.height() && rx >= 0 && rx < u_arr.width()) {
3230 double du = u_arr(ry, rx) * sf;
3231 double dv = v_arr(ry, rx) * sf;
3232 difference_type new_x = seed.x +
static_cast<difference_type
>(std::round(du / sf) * sf);
3233 difference_type new_y = seed.y +
static_cast<difference_type
>(std::round(dv / sf) * sf);
3234 difference_type nry = new_y / sf;
3235 difference_type nrx = new_x / sf;
3236 const auto& new_mask = roi_ref.
get_mask();
3237 if (nry >= 0 && nry < new_mask.height() && nrx >= 0 && nrx < new_mask.width() && new_mask(nry, nrx)) {
3243 new_seed.
du_dx = 0.0;
3244 new_seed.
du_dy = 0.0;
3245 new_seed.
dv_dx = 0.0;
3246 new_seed.
dv_dy = 0.0;
3248 updated_seeds.push_back(new_seed);
3252 if (!updated_seeds.empty()) {
3253 current_seeds = updated_seeds;
3254 current_seeds_optimized =
false;
3255 NLOG_INFO <<
" Seeds propagated: " << updated_seeds.size() <<
"/" << parallel_input.
seeds_by_region.size() <<
" remain in updated ROI.";
3257 NLOG_WARN <<
" WARNING: No seeds remain in updated ROI. Clearing seeds (auto-detect will be used).";
3258 current_seeds.clear();
3264 NLOG_WARN <<
"WARNING: ROI update at frame " << cur_idx <<
" produced an empty ROI!";
3265 NLOG_WARN <<
" Previous ROI had " << prev_roi.get_points() <<
" points.";
3272 NLOG_WARN <<
" Reverting to previous ROI (frame " <<
ref_idx <<
") to continue analysis.";
3275 if (DIC_input.debug) {
3277 cv::Mat prev_img = get_cv_img(prev_roi.get_mask(), 0, 255);
3278 cv::Mat curr_img = get_cv_img(roi_ref.
get_mask(), 0, 255);
3279 cv::absdiff(prev_img, curr_img, diff);
3280 cv::imwrite(
"roi_" + std::to_string(cur_idx) +
"_prev.png", prev_img);
3281 cv::imwrite(
"roi_" + std::to_string(cur_idx) +
"_curr.png", curr_img);
3282 cv::imwrite(
"diff_roi_" + std::to_string(cur_idx) +
".png", diff);
3283 NLOG_DEBUG <<
"DEBUG::Saved difference image for frame " << cur_idx;
3290 if (use_post_process) {
3291 NLOG_INFO << std::endl <<
"Post-processing: Combining step displacements...";
3292 for (difference_type cur_idx = 1; cur_idx < difference_type(DIC_input.imgs.size()); ++cur_idx) {
3293 std::vector<Disp2D> chain_disps;
3294 std::vector<ROI2D> chain_rois;
3295 difference_type idx = cur_idx - 1;
3297 chain_disps.insert(chain_disps.begin(), step_disps[idx]);
3298 chain_rois.insert(chain_rois.begin(), step_rois[idx]);
3299 if (step_ref_idx[idx] == 0)
break;
3300 idx = step_ref_idx[idx] - 1;
3302 if (chain_disps.size() == 1) {
3303 DIC_output.
disps[cur_idx-1] = chain_disps[0];
3305 auto combined =
add_with_rois(chain_disps, chain_rois, DIC_input.interp_type);
3306 if (combined.get_roi().get_points() == 0) {
3307 NLOG_WARN <<
"WARNING: Post-process add_with_rois for frame " << cur_idx <<
" produced empty ROI!";
3308 DIC_output.
disps[cur_idx-1] = step_disps[cur_idx-1];
3310 DIC_output.
disps[cur_idx-1] = combined;
3311 NLOG_INFO <<
"Frame " << cur_idx <<
": Combined " << chain_disps.size() <<
" steps.";
3318 if (DIC_input.save_disps_steps && use_post_process && !step_disps.empty()) {
3324 std::string step_filename =
"DIC_analysis_sequential_seeds_step_data.bin";
3325 save(step_data, step_filename);
3326 NLOG_INFO <<
"Step displacement data saved to " << step_filename;
3329 std::chrono::time_point<std::chrono::system_clock> end_analysis = std::chrono::system_clock::now();
3330 std::chrono::duration<double> elapsed_seconds_analysis = end_analysis - start_analysis;
3331 NLOG_INFO << std::endl <<
"Total DIC analysis time: " << elapsed_seconds_analysis.count() <<
".";
3358 auto v_updated =
update(disp.
get_v(), disp, interp_type, mode);
3359 auto u_updated =
update(disp.
get_u(), disp, interp_type, mode);
3362 return { std::move(v_updated.get_array()), std::move(u_updated.get_array()), v_updated.get_roi(), disp.
get_scalefactor() };
3374 throw std::invalid_argument(
"Changing from Eulerian perspective back to Lagrangian is currently not supported. Just use the original output data from DIC_analysis().");
3377 if (DIC_output.
units !=
"pixels") {
3380 throw std::invalid_argument(
"Changing from Lagrangian to Eulerian perspective must be done before applying units.");
3390 NLOG_INFO << std::endl <<
"Changing perspective...";
3391 for (difference_type disp_idx = 0; disp_idx < difference_type(DIC_output.
disps.size()); ++disp_idx) {
3392 NLOG_INFO <<
"Displacement field " << disp_idx+1 <<
" of " << DIC_output.
disps.size() <<
".";
3396 return DIC_output_updated;
3408 throw std::invalid_argument(
"Changing from Eulerian perspective back to Lagrangian is currently not supported. Just use the original output data from DIC_analysis().");
3411 if (DIC_output.
units !=
"pixels") {
3414 throw std::invalid_argument(
"Changing from Lagrangian to Eulerian perspective must be done before applying units.");
3424 NLOG_INFO << std::endl <<
"Changing perspective with sign inversion (Matlab-compatible)...";
3425 for (difference_type disp_idx = 0; disp_idx < difference_type(DIC_output.
disps.size()); ++disp_idx) {
3426 NLOG_INFO <<
"Displacement field " << disp_idx+1 <<
" of " << DIC_output.
disps.size() <<
".";
3447 return DIC_output_updated;
3454 if (DIC_output.
units !=
"pixels") {
3456 throw std::invalid_argument(
"Units have already been set for this DIC_analysis_output.");
3462 DIC_output_updated.
units = units;
3466 for (difference_type disp_idx = 0; disp_idx < difference_type(DIC_output.
disps.size()); ++disp_idx) {
3468 auto A_v = DIC_output.
disps[disp_idx].get_v().get_array() * units_per_pixel;
3469 auto A_u = DIC_output.
disps[disp_idx].get_u().get_array() * units_per_pixel;
3470 auto A_cc = DIC_output.
disps[disp_idx].get_cc().get_array();
3472 DIC_output_updated.
disps.push_back(
Disp2D(std::move(A_v),
3475 DIC_output.
disps[disp_idx].get_roi(),
3476 DIC_output.
disps[disp_idx].get_scalefactor()));
3479 return DIC_output_updated;
3486 if (cutoff_corrcoef < -1.0 || cutoff_corrcoef > 1.0) {
3487 throw std::invalid_argument(
"Correlation coefficient cutoff must be in range [-1.0, 1.0]. Got: " + std::to_string(cutoff_corrcoef));
3493 DIC_output_filtered.
units = DIC_output.
units;
3496 NLOG_INFO << std::endl <<
"Filtering by correlation coefficient (threshold: " << cutoff_corrcoef <<
")...";
3499 for (difference_type disp_idx = 0; disp_idx < difference_type(DIC_output.
disps.size()); ++disp_idx) {
3500 const auto& disp = DIC_output.
disps[disp_idx];
3501 const auto& cc_array = disp.get_cc().get_array();
3502 const auto& roi_mask = disp.get_roi().get_mask();
3505 Array2D<bool> mask_filtered(roi_mask.height(), roi_mask.width(),
false);
3506 difference_type points_kept = 0;
3507 difference_type points_total = 0;
3509 for (difference_type i = 0; i < roi_mask.height(); ++i) {
3510 for (difference_type j = 0; j < roi_mask.width(); ++j) {
3511 if (roi_mask(i, j)) {
3514 if (cc_array(i, j) >= cutoff_corrcoef) {
3515 mask_filtered(i, j) =
true;
3522 NLOG_INFO <<
" Displacement " << disp_idx+1 <<
": kept " << points_kept
3523 <<
" / " << points_total <<
" points"
3524 << (points_total > 0
3525 ?
" (" + std::to_string(100.0 * points_kept / points_total) +
"%)"
3530 ROI2D roi_filtered(std::move(mask_filtered));
3535 disp.get_v().get_array(),
3536 disp.get_u().get_array(),
3537 disp.get_cc().get_array(),
3539 disp.get_scalefactor()
3543 return DIC_output_filtered;
3556 if (units_per_pixel <= 0) {
3557 throw std::invalid_argument(
"Attempted to calculate least squares strain with units_per_pixel: " + std::to_string(units_per_pixel) +
3558 ". units_per_pixel must be greater than zero.");
3562 throw std::invalid_argument(
"Attempted to calculate least squares strain with radius: " + std::to_string(r) +
3563 ". radius must be 1 or greater.");
3581 for (difference_type region_idx = 0; region_idx < disp.
get_roi().size_regions(); ++region_idx) {
3582 for (difference_type nl_idx = 0; nl_idx < disp.
get_roi().get_nlinfo(region_idx).nodelist.
width(); ++nl_idx) {
3587 for (difference_type p1 = np_top; p1 <= np_bottom; ++p1) {
3594 const auto &subregion_nlinfo = subregion_gen(p1, p2);
3597 for (difference_type nl_idx_subregion = 0; nl_idx_subregion < subregion_nlinfo.nodelist.width(); ++nl_idx_subregion) {
3598 difference_type p2_subregion = nl_idx_subregion + subregion_nlinfo.left_nl;
3599 for (difference_type np_idx_subregion = 0; np_idx_subregion < subregion_nlinfo.noderange(nl_idx_subregion); np_idx_subregion += 2) {
3600 difference_type np_top_subregion = subregion_nlinfo.nodelist(np_idx_subregion,nl_idx_subregion);
3601 difference_type np_bottom_subregion = subregion_nlinfo.nodelist(np_idx_subregion + 1,nl_idx_subregion);
3602 for (difference_type p1_subregion = np_top_subregion; p1_subregion <= np_bottom_subregion; ++p1_subregion) {
3603 difference_type delta_p1 = p1_subregion - p1;
3604 difference_type delta_p2 = p2_subregion - p2;
3609 mat_LS(0,0) += std::pow(delta_p1,2);
3610 mat_LS(1,0) += delta_p1*delta_p2;
3611 mat_LS(2,0) += delta_p1;
3612 mat_LS(1,1) += std::pow(delta_p2,2);
3613 mat_LS(2,1) += delta_p2;
3616 v_LS(0) += delta_p1 * v;
3617 v_LS(1) += delta_p2 * v;
3620 u_LS(0) += delta_p1 * u;
3621 u_LS(1) += delta_p2 * u;
3628 mat_LS(0,1) = mat_LS(1,0);
3629 mat_LS(0,2) = mat_LS(2,0);
3630 mat_LS(1,2) = mat_LS(2,1);
3636 if (mat_LS_linsolver) {
3639 auto dv_dp = mat_LS_linsolver.solve(v_LS);
3640 auto du_dp = mat_LS_linsolver.solve(u_LS);
3648 switch (perspective_type) {
3651 A_eyy(p1,p2) = 0.5*(2*dv_dp(0) + std::pow(du_dp(0),2) + std::pow(dv_dp(0),2));
3652 A_exy(p1,p2) = 0.5*(du_dp(0) + dv_dp(1) + du_dp(1)*du_dp(0) + dv_dp(1)*dv_dp(0));
3653 A_exx(p1,p2) = 0.5*(2*du_dp(1) + std::pow(du_dp(1),2) + std::pow(dv_dp(1),2));
3657 A_eyy(p1,p2) = 0.5*(2*dv_dp(0) - std::pow(du_dp(0),2) - std::pow(dv_dp(0),2));
3658 A_exy(p1,p2) = 0.5*(du_dp(0) + dv_dp(1) - du_dp(1)*du_dp(0) - dv_dp(1)*dv_dp(0));
3659 A_exx(p1,p2) = 0.5*(2*du_dp(1) - std::pow(du_dp(1),2) - std::pow(dv_dp(1),2));
3687 is.read(
reinterpret_cast<char*
>(&strain_input.
r), std::streamsize(
sizeof(
difference_type)));
3689 return strain_input;
3694 std::ifstream is(filename.c_str(), std::ios::in | std::ios::binary);
3695 if (!is.is_open()) {
3696 throw std::invalid_argument(
"Could not open " + filename +
" for loading strain_analysis_input.");
3705 return strain_input;
3719 os.write(
reinterpret_cast<const char*
>(&strain_input.
r), std::streamsize(
sizeof(
difference_type)));
3724 std::ofstream os(filename.c_str(), std::ios::out | std::ios::binary | std::ios::trunc);
3725 if (!os.is_open()) {
3726 throw std::invalid_argument(
"Could not open " + filename +
" for saving strain_analysis_input.");
3730 save(strain_input, os);
3742 is.read(
reinterpret_cast<char*
>(&num_strains), std::streamsize(
sizeof(
difference_type)));
3743 strain_output.
strains.resize(num_strains);
3744 for (
auto &strain : strain_output.
strains) {
3748 return strain_output;
3753 std::ifstream is(filename.c_str(), std::ios::in | std::ios::binary);
3754 if (!is.is_open()) {
3755 throw std::invalid_argument(
"Could not open " + filename +
" for loading strain_analysis_output.");
3764 return strain_output;
3772 os.write(
reinterpret_cast<const char*
>(&num_strains), std::streamsize(
sizeof(
difference_type)));
3773 for (
const auto &strain : strain_output.
strains) {
3780 std::ofstream os(filename.c_str(), std::ios::out | std::ios::binary | std::ios::trunc);
3781 if (!os.is_open()) {
3782 throw std::invalid_argument(
"Could not open " + filename +
" for saving strain_analysis_output.");
3786 save(strain_output, os);
3799 NLOG_INFO << std::endl <<
"Processing strain fields...";
3800 for (difference_type disp_idx = 0; disp_idx < difference_type(strain_input.
DIC_output.
disps.size()); ++disp_idx) {
3810 return strain_output;
3820 bool enable_colorbar =
true,
3821 bool enable_axes =
true,
3822 bool enable_scalebar =
true,
3823 const std::string &units =
"pixels",
3824 double units_per_pixel = 1.0,
3825 double num_units = -1.0,
3826 double font_size = 1.0,
3828 int colormap = cv::COLORMAP_JET) {
3838 if (alpha < 0 || alpha > 1) {
3839 throw std::invalid_argument(
"alpha input for cv_ncorr_data_over_img() must be between 0 and 1.");
3842 if (units_per_pixel <= 0) {
3843 throw std::invalid_argument(
"units_per_pixel input for cv_ncorr_data_over_img() must be greater than 0.");
3846 if (num_units != -1 && num_units <= 0) {
3847 throw std::invalid_argument(
"num_units input for cv_ncorr_data_over_img() must be greater than 0.");
3850 if (font_size <= 0) {
3851 throw std::invalid_argument(
"font_size input for cv_ncorr_data_over_img() must be greater than 0.");
3854 if (num_tick_marks < 2) {
3855 throw std::invalid_argument(
"num_tick_marks input for cv_ncorr_data_over_img() must be greater than or equal to 2.");
3859 auto A_img = img.
get_gs();
3860 cv::Mat cv_img = get_cv_img(A_img,
min(A_img),
max(A_img));
3864 cv::Mat cv_data = get_cv_img(data.
get_array(), min_data, max_data);
3865 cv::applyColorMap(cv_data, cv_data, colormap);
3868 difference_type border = 20;
3869 difference_type plot_height = data.
data_height() + 2*border;
3870 difference_type plot_width = data.
data_width() + 2*border;
3871 cv::Mat cv_plot(plot_height, plot_width, CV_8UC3, cv::Vec3b(255,255,255));
3872 for (difference_type p2 = border; p2 < data.
data_width() + border; ++p2) {
3873 difference_type p2_data = p2 - border;
3874 for (difference_type p1 = border; p1 < data.
data_height() + border; ++p1) {
3875 difference_type p1_data = p1 - border;
3878 auto data_rgb = cv_data.at<cv::Vec3b>(p1_data,p2_data);
3879 auto img_gs = cv_img.at<uchar>(p1_data,p2_data);
3881 if (data.
get_roi()(p1_data,p2_data)) {
3883 cv_plot.at<cv::Vec3b>(p1,p2) = cv::Vec3b(img_gs*(1-alpha) + alpha*data_rgb.val[0],
3884 img_gs*(1-alpha) + alpha*data_rgb.val[1],
3885 img_gs*(1-alpha) + alpha*data_rgb.val[2]);
3888 cv_plot.at<cv::Vec3b>(p1,p2) = cv::Vec3b(img_gs, img_gs, img_gs);
3893 if (enable_colorbar) {
3898 auto font_face = cv::FONT_HERSHEY_PLAIN;
3899 int font_thickness = 1;
3905 difference_type num_chars = 8;
3906 difference_type text_offset_left = 5;
3907 difference_type colorbar_width = 20;
3909 difference_type colorbar_bg_width = 0;
3910 for (difference_type num_mark = 0; num_mark < num_tick_marks; ++num_mark) {
3911 auto text_width = cv::getTextSize(std::to_string(
double(num_tick_marks-num_mark-1)/(num_tick_marks-1) * (min_data-max_data) + max_data).substr(0,num_chars), font_face, 0.75*font_size, font_thickness, &baseline).width;
3912 if (text_width > colorbar_bg_width) {
3913 colorbar_bg_width = text_width;
3916 colorbar_bg_width += colorbar_width + text_offset_left + border;
3919 cv::Mat cv_colorbar(plot_height, colorbar_bg_width, cv::DataType<uchar>::type);
3920 for (difference_type p2 = 0; p2 < colorbar_width; ++p2) {
3921 for (difference_type p1 = border; p1 < data.
data_height() + border; ++p1) {
3924 cv_colorbar.at<uchar>(p1,p2) =
double((data.
data_height() + border - 1) - p1)/(data.
data_height()-1) * 255;
3927 cv::applyColorMap(cv_colorbar, cv_colorbar, colormap);
3930 cv_colorbar(cv::Range(0,border),cv::Range::all()) = cv::Vec3b(255,255,255);
3931 cv_colorbar(cv::Range(data.
data_height()+border,data.
data_height()+2*border),cv::Range::all()) = cv::Vec3b(255,255,255);
3932 cv_colorbar(cv::Range::all(),cv::Range(colorbar_width,colorbar_bg_width)) = cv::Vec3b(255,255,255);
3935 cv_colorbar(cv::Range(border, border+1),cv::Range(0,colorbar_width)) = cv::Vec3b(0,0,0);
3936 cv_colorbar(cv::Range(data.
data_height()+border-1,data.
data_height()+border),cv::Range(0,colorbar_width)) = cv::Vec3b(0,0,0);
3937 cv_colorbar(cv::Range(border,data.
data_height()+border),cv::Range(0,1)) = cv::Vec3b(0,0,0);
3938 cv_colorbar(cv::Range(border,data.
data_height()+border),cv::Range(colorbar_width-1,colorbar_width)) = cv::Vec3b(0,0,0);
3941 difference_type tick_mark_width = 4;
3942 for (difference_type num_mark = 0; num_mark < num_tick_marks; ++num_mark) {
3943 difference_type p1 = (num_mark*(data.
data_height()-1))/(num_tick_marks-1) + border;
3946 cv_colorbar(cv::Range(p1, p1+1),cv::Range(0,tick_mark_width)) = cv::Vec3b(0,0,0);
3947 cv_colorbar(cv::Range(p1, p1+1),cv::Range(colorbar_width-tick_mark_width,colorbar_width)) = cv::Vec3b(0,0,0);
3950 std::string tick_mark_label_str = std::to_string(num_mark*(min_data-max_data)/(num_tick_marks-1) + max_data).substr(0,num_chars);
3951 auto text_size = cv::getTextSize(tick_mark_label_str, font_face, 0.75*font_size, font_thickness, &baseline);
3952 cv::putText(cv_colorbar,
3953 tick_mark_label_str,
3954 cv::Point(colorbar_width + text_offset_left, num_mark*(data.
data_height()-1)/(num_tick_marks-1) + border + text_size.height/2),
3962 cv::hconcat(cv_plot, cv_colorbar, cv_plot);
3970 cv::Point bg_axes_pts[1][8];
3971 bg_axes_pts[0][0] = cv::Point(border, border);
3972 bg_axes_pts[0][1] = cv::Point(border, border+axes_length);
3973 bg_axes_pts[0][2] = cv::Point(border+0.20*axes_length, border+0.80*axes_length);
3974 bg_axes_pts[0][3] = cv::Point(border+0.10*axes_length, border+0.80*axes_length);
3975 bg_axes_pts[0][4] = cv::Point(border+0.10*axes_length, border+0.10*axes_length);
3976 bg_axes_pts[0][5] = cv::Point(border+0.80*axes_length, border+0.10*axes_length);
3977 bg_axes_pts[0][6] = cv::Point(border+0.80*axes_length, border+0.20*axes_length);
3978 bg_axes_pts[0][7] = cv::Point(border+axes_length, border);
3979 const cv::Point* pts1[1] = { bg_axes_pts[0] };
3980 int npts1[] = { 8 };
3981 cv::fillPoly(cv_plot, pts1, npts1, 1, cv::Scalar(255,255,255));
3984 cv::Point fg_axes_pts[1][8];
3985 fg_axes_pts[0][0] = cv::Point(border, border);
3986 fg_axes_pts[0][1] = cv::Point(border, border+0.95*axes_length);
3987 fg_axes_pts[0][2] = cv::Point(border+0.125*axes_length, border+0.825*axes_length);
3988 fg_axes_pts[0][3] = cv::Point(border+0.070*axes_length, border+0.825*axes_length);
3989 fg_axes_pts[0][4] = cv::Point(border+0.070*axes_length, border+0.070*axes_length);
3990 fg_axes_pts[0][5] = cv::Point(border+0.825*axes_length, border+0.070*axes_length);
3991 fg_axes_pts[0][6] = cv::Point(border+0.825*axes_length, border+0.125*axes_length);
3992 fg_axes_pts[0][7] = cv::Point(border+0.95*axes_length, border);
3993 const cv::Point* pts2[1] = { fg_axes_pts[0] };
3994 int npts2[] = { 8 };
3995 cv::fillPoly(cv_plot, pts2, npts2, 1, cv::Scalar(0,0,0));
3998 difference_type label_border = 5;
3999 difference_type label_offset = 5;
4000 double label_alpha = 0.5;
4003 auto font_face = cv::FONT_HERSHEY_PLAIN;
4004 int font_thickness = 1;
4008 std::string y_str =
"Y";
4009 auto text_size_y = cv::getTextSize(y_str, font_face, font_size, font_thickness, &baseline);
4010 for (difference_type p2 = border+label_offset; p2 < border+label_offset+2.0*label_border+text_size_y.width; ++p2) {
4011 for (difference_type p1 = border+axes_length+label_offset; p1 < border+axes_length+label_offset+2.0*label_border+text_size_y.height; ++p1) {
4012 if (p1 >= 0 && p1 < cv_plot.rows &&
4013 p2 >= 0 && p2 < cv_plot.cols) {
4014 auto cv_plot_rgb = cv_plot.at<cv::Vec3b>(p1,p2);
4016 cv_plot.at<cv::Vec3b>(p1,p2) = cv::Vec3b(label_alpha*cv_plot_rgb.val[0],
4017 label_alpha*cv_plot_rgb.val[1],
4018 label_alpha*cv_plot_rgb.val[2]);
4022 cv::putText(cv_plot,
4024 cv::Point(border+label_offset+label_border, border+axes_length+label_offset+label_border+text_size_y.height),
4027 cv::Scalar::all(255),
4031 std::string x_str =
"X";
4032 auto text_size_x = cv::getTextSize(x_str, font_face, font_size, font_thickness, &baseline);
4033 for (difference_type p2 = border+axes_length+label_offset; p2 < border+axes_length+label_offset+2.0*label_border+text_size_x.width; ++p2) {
4034 for (difference_type p1 = border+label_offset; p1 < border+label_offset+2.0*label_border+text_size_x.height; ++p1) {
4035 if (p1 >= 0 && p1 < cv_plot.rows &&
4036 p2 >= 0 && p2 < cv_plot.cols) {
4037 auto cv_plot_rgb = cv_plot.at<cv::Vec3b>(p1,p2);
4039 cv_plot.at<cv::Vec3b>(p1,p2) = cv::Vec3b(label_alpha*cv_plot_rgb.val[0],
4040 label_alpha*cv_plot_rgb.val[1],
4041 label_alpha*cv_plot_rgb.val[2]);
4045 cv::putText(cv_plot,
4047 cv::Point(border+axes_length+label_offset+label_border, border+label_offset+label_border+text_size_x.height),
4050 cv::Scalar::all(255),
4054 if (enable_scalebar) {
4060 difference_type scalebar_width =
4063 :
static_cast<difference_type
>(
4064 static_cast<double>(num_units) /
4067 difference_type scalebar_height = 5;
4070 if (num_units == -1) {
4076 difference_type decimal_places = 2;
4077 std::stringstream ss_scalebar;
4078 ss_scalebar << std::fixed << std::setprecision(std::numeric_limits<double>::digits10) << num_units;
4079 std::string scalebar_str_unformatted = ss_scalebar.str();
4080 auto idx_period = scalebar_str_unformatted.find(
".");
4081 std::string scalebar_str = idx_period == std::string::npos ? scalebar_str_unformatted : scalebar_str_unformatted.substr(0, idx_period + decimal_places + 1) +
" " + units;
4084 auto font_face = cv::FONT_HERSHEY_PLAIN;
4085 int font_thickness = 1;
4087 auto text_size = cv::getTextSize(scalebar_str, font_face, font_size, font_thickness, &baseline);
4090 difference_type scalebar_bg_border = 10;
4091 difference_type scalebar_bg_width = scalebar_width + 2*scalebar_bg_border;
4092 difference_type scalebar_bg_height = scalebar_height + 3*scalebar_bg_border + text_size.height;
4093 difference_type scalebar_bg_offset = 10;
4094 double scalebar_bg_alpha = 0.5;
4095 for (difference_type p2 = border+scalebar_bg_offset; p2 < border+scalebar_bg_offset+scalebar_bg_width; ++p2) {
4096 for (difference_type p1 = border+data.
data_height()-scalebar_bg_offset-scalebar_bg_height; p1 < border+data.
data_height()-scalebar_bg_offset; ++p1) {
4097 if (p1 >= 0 && p1 < cv_plot.rows &&
4098 p2 >= 0 && p2 < cv_plot.cols) {
4099 auto cv_plot_rgb = cv_plot.at<cv::Vec3b>(p1,p2);
4101 cv_plot.at<cv::Vec3b>(p1,p2) = cv::Vec3b(scalebar_bg_alpha*cv_plot_rgb.val[0],
4102 scalebar_bg_alpha*cv_plot_rgb.val[1],
4103 scalebar_bg_alpha*cv_plot_rgb.val[2]);
4109 for (difference_type p2 = border+scalebar_bg_offset+scalebar_bg_border; p2 < border+scalebar_bg_offset+scalebar_bg_border+scalebar_width; ++p2) {
4110 for (difference_type p1 = border+data.
data_height()-scalebar_bg_offset-scalebar_bg_border-scalebar_height; p1 < border+data.
data_height()-scalebar_bg_offset-scalebar_bg_border; ++p1) {
4111 if (p1 >= 0 && p1 < cv_plot.rows &&
4112 p2 >= 0 && p2 < cv_plot.cols) {
4113 cv_plot.at<cv::Vec3b>(p1,p2) = cv::Vec3b(255,255,255);
4119 cv::putText(cv_plot,
4121 cv::Point(border+scalebar_bg_offset+scalebar_bg_width/2.0-text_size.width/2.0, border+data.
data_height()-scalebar_bg_offset-2*scalebar_bg_border-scalebar_height),
4124 cv::Scalar::all(255),
4134 double min_data = 0, max_data = 0;
4136 if (!data_values.
empty()) {
4137 min_data = prctile(data_values,0.01);
4138 max_data = prctile(data_values,0.99);
4152 cv::imshow(
"Ncorr data", cv_img);
4153 delay == -1 ? cv::waitKey() : cv::waitKey(delay);
4162 bool enable_colorbar,
4164 bool enable_scalebar,
4165 const std::string &units,
4166 double units_per_pixel,
4186 cv::imwrite(filename, cv_data_img);
4190 const std::vector<Image2D> &imgs,
4191 const std::vector<Data2D> &data,
4196 bool enable_colorbar,
4198 bool enable_scalebar,
4199 const std::string &units,
4200 double units_per_pixel,
4212 if (imgs.size() != 1 && imgs.size() != data.size()) {
4214 throw std::invalid_argument(
"Number of images used in save_data_over_img_video() must either be 1, or equal to the number of input data.");
4218 throw std::invalid_argument(
"Number of Data2D used in save_data_over_img_video() must be greater than or equal to 1.");
4222 for (difference_type data_idx = 1; data_idx < difference_type(data.size()); ++data_idx) {
4223 if (data[data_idx].data_height() != data.front().data_height() ||
4224 data[data_idx].data_width() != data.front().data_width()) {
4225 throw std::invalid_argument(
"Attempted to use save_data_over_img_video() with data of differing sizes. All data must be the same size.");
4230 throw std::invalid_argument(
"fps input for save_data_over_img_video() must be greater than 0.");
4233 if (end_delay < 0) {
4234 throw std::invalid_argument(
"end_delay input for save_data_over_img_video() must be greater than or equal to 0.");
4239 NLOG_INFO << std::endl <<
"Saving video: " << filename <<
"...";
4242 cv::VideoWriter output_video;
4245 for (difference_type data_idx = 0; data_idx < difference_type(data.size()); ++data_idx) {
4246 NLOG_INFO <<
"Frame " << data_idx+1 <<
" of " << data.size() <<
".";
4263 if (data_idx == 0) {
4266 output_video.open(filename,
4269 { cv_data_img.cols, cv_data_img.rows },
4272 if (!output_video.isOpened()) {
4273 throw std::invalid_argument(
"Cannot open video file: " + filename +
" for save_data_over_img_video().");
4277 output_video << cv_data_img;
4281 for (difference_type idx = 0; idx < fps*end_delay; ++idx) {
4307 bool enable_colorbar,
4309 bool enable_scalebar,
4320 switch (disp_type) {
4332 Array2D<double> data_values_first = get_disp(DIC_output.
disps.front()).get_array()(DIC_output.
disps.front().get_roi().get_mask());
4333 Array2D<double> data_values_last = get_disp(DIC_output.
disps.back()).get_array()(DIC_output.
disps.back().get_roi().get_mask());
4334 if (std::isnan(min_disp) && !data_values_first.
empty() && !data_values_last.
empty()) {
4335 min_disp = std::min(prctile(data_values_first,0.01), prctile(data_values_last,0.01));
4337 if (std::isnan(max_disp) && !data_values_first.
empty() && !data_values_last.
empty()) {
4338 max_disp = std::max(prctile(data_values_first,0.99), prctile(data_values_last,0.99));
4342 std::vector<Data2D> data;
4343 for (difference_type disp_idx = 0; disp_idx < difference_type(DIC_output.
disps.size()); ++disp_idx) {
4344 data.push_back(get_disp(DIC_output.
disps[disp_idx]));
4348 std::vector<Image2D> imgs;
4351 imgs.push_back(DIC_input.
imgs.front());
4354 for (difference_type img_idx = 1; img_idx < difference_type(DIC_input.
imgs.size()); ++img_idx) {
4355 imgs.push_back(DIC_input.
imgs[img_idx]);
4389 bool enable_colorbar,
4391 bool enable_scalebar,
4402 switch (strain_type) {
4417 Array2D<double> data_values_first = get_strain(strain_output.
strains.front()).get_array()(strain_output.
strains.front().get_roi().get_mask());
4419 if (std::isnan(min_strain) && !data_values_first.
empty() && !data_values_last.
empty()) {
4420 min_strain = std::min(prctile(data_values_first,0.01), prctile(data_values_last,0.01));
4422 if (std::isnan(max_strain) && !data_values_first.
empty() && !data_values_last.
empty()) {
4423 max_strain = std::max(prctile(data_values_first,0.99), prctile(data_values_last,0.99));
4427 std::vector<Data2D> data;
4428 for (difference_type strain_idx = 0; strain_idx < difference_type(strain_output.
strains.size()); ++strain_idx) {
4429 data.push_back(get_strain(strain_output.
strains[strain_idx]));
4433 std::vector<Image2D> imgs;
4439 for (difference_type img_idx = 1; img_idx < difference_type(strain_input.
DIC_input.
imgs.size()); ++img_idx) {
4472 const ROI2D& roi_reduced,
4475 double cutoff_corrcoef,
4481 auto H = roi_reduced.
height();
4482 auto W = roi_reduced.
width();
4488 double cutoff_delta_disp = scalefactor;
4508 seed_params(0) = seedparams.
y;
4509 seed_params(1) = seedparams.
x;
4510 seed_params(2) = seedparams.
v;
4511 seed_params(3) = seedparams.
u;
4512 seed_params(4) = seedparams.
dv_dy;
4513 seed_params(5) = seedparams.
dv_dx;
4514 seed_params(6) = seedparams.
du_dy;
4515 seed_params(7) = seedparams.
du_dx;
4516 seed_params(8) = seedparams.
corrcoef;
4519 if (!seed_params.
empty()) {
4520 A_ap(seed_params(0) / scalefactor, seed_params(1) / scalefactor) =
false;
4525 std::priority_queue<Array2D<double>, std::vector<Array2D<double>>, std::function<bool(
const Array2D<double>&,
const Array2D<double>&)>> queue(comp);
4528 queue.push(seed_params);
4529 while (!queue.empty()) {
4531 auto queue_params = std::move(queue.top()); queue.pop();
4534 A_vp(queue_params(0) / scalefactor, queue_params(1) / scalefactor) =
true;
4535 A_v(queue_params(0) / scalefactor, queue_params(1) / scalefactor) = queue_params(2);
4536 A_u(queue_params(0) / scalefactor, queue_params(1) / scalefactor) = queue_params(3);
4537 A_cc(queue_params(0) / scalefactor, queue_params(1) / scalefactor) = queue_params(8);
4540 details::analyze_point(queue_params, -scalefactor, 0, roi_reduced.
get_nlinfo(region_idx), scalefactor, sr_nloptimizer, cutoff_corrcoef, cutoff_delta_disp, queue, A_ap, params_buf);
4541 details::analyze_point(queue_params, scalefactor, 0, roi_reduced.
get_nlinfo(region_idx), scalefactor, sr_nloptimizer, cutoff_corrcoef, cutoff_delta_disp, queue, A_ap, params_buf);
4542 details::analyze_point(queue_params, 0, -scalefactor, roi_reduced.
get_nlinfo(region_idx), scalefactor, sr_nloptimizer, cutoff_corrcoef, cutoff_delta_disp, queue, A_ap, params_buf);
4543 details::analyze_point(queue_params, 0, scalefactor, roi_reduced.
get_nlinfo(region_idx), scalefactor, sr_nloptimizer, cutoff_corrcoef, cutoff_delta_disp, queue, A_ap, params_buf);
4550 auto A_vp_buf = A_vp;
4558 if ((roi_reduced.
get_nlinfo(region_idx).
in_nlinfo(p1-1,p2) && A_vp_buf(p1-1,p2) && std::sqrt(std::pow(A_v(p1-1,p2) - A_v(p1,p2),2) + std::pow(A_u(p1-1,p2) - A_u(p1,p2),2)) > cutoff_delta_disp) ||
4559 (roi_reduced.
get_nlinfo(region_idx).
in_nlinfo(p1+1,p2) && A_vp_buf(p1+1,p2) && std::sqrt(std::pow(A_v(p1+1,p2) - A_v(p1,p2),2) + std::pow(A_u(p1+1,p2) - A_u(p1,p2),2)) > cutoff_delta_disp) ||
4560 (roi_reduced.
get_nlinfo(region_idx).
in_nlinfo(p1,p2-1) && A_vp_buf(p1,p2-1) && std::sqrt(std::pow(A_v(p1,p2-1) - A_v(p1,p2),2) + std::pow(A_u(p1,p2-1) - A_u(p1,p2),2)) > cutoff_delta_disp) ||
4561 (roi_reduced.
get_nlinfo(region_idx).
in_nlinfo(p1,p2+1) && A_vp_buf(p1,p2+1) && std::sqrt(std::pow(A_v(p1,p2+1) - A_v(p1,p2),2) + std::pow(A_u(p1,p2+1) - A_u(p1,p2),2)) > cutoff_delta_disp)) {
4562 A_vp(p1,p2) =
false;
4570 auto roi_valid = roi_reduced.
form_union(A_vp);
4572 NLOG_DEBUG <<
"RGDIC completed with manual seed";
4574 return Disp2D(std::move(A_v), std::move(A_u), std::move(A_cc), roi_valid, scalefactor);
4586 const std::vector<SeedParams>& seeds_by_region,
4587 double cutoff_corrcoef,
4592 std::vector<SeedComputationData> selected_data;
4595 NLOG_DEBUG <<
"\n=== Compute seed parameters for all frames with ROI updates ===> ";
4596 NLOG_DEBUG <<
"\n starting with " << seeds_by_region.size() <<
" seeds";
4601 ROI2D roi_current = roi;
4602 std::vector<SeedParams> seeds_current = seeds_by_region;
4605 while (idx < A_curs.size()) {
4609 NLOG_DEBUG <<
"\n=== Seeding analyzing frame " << (idx + 1) <<
" ===> ";
4633 selected_data.emplace_back(roi_current, seed_results.
seeds, sr_nloptimizer);
4638 NLOG_DEBUG <<
"Failed! Updating reference ===>";
4641 auto A_prev = A_curs[idx-1];
4644 auto prev_sr_nloptimizer = selected_data.back().sr_nloptimizer;
4645 auto prev_seedparams = selected_data.back().seed_params_by_region;
4646 auto prev_roi = selected_data.back().roi;
4650 prev_sr_nloptimizer,
4651 prev_roi.reduce(scalefactor),
4652 prev_seedparams[region_idx],
4660 A_ref_current = A_prev;
4661 roi_current =
update(prev_roi, disps, interp_type);
4667 NLOG_DEBUG <<
"Reference updated, seeds propagated <====";
4673 NLOG_DEBUG <<
"\nComputed seed parameters for " << selected_data.size() <<
" frames";
4676 return selected_data;
4681 std::vector<SeedParams> propagated_seeds;
4682 propagated_seeds.reserve(seeds.size());
4684 for (
const auto& seed : seeds) {
4689 new_seed.
x = seed.x + std::round(seed.u / (spacing + 1)) * (spacing + 1);
4690 new_seed.
y = seed.y + std::round(seed.v / (spacing + 1)) * (spacing + 1);
4692 propagated_seeds.push_back(new_seed);
4695 return propagated_seeds;
4703 const std::vector<SeedParams>& seed_positions,
4705 int cutoff_iteration,
4706 double cutoff_max_diffnorm,
4707 double cutoff_max_corrcoef,
4712 result.
seeds.reserve(seed_positions.size());
4713 result.
convergence.reserve(seed_positions.size());
4718 for (
const auto& seed_pos : seed_positions) {
4720 if (seed_pos.x < radius || seed_pos.x >= ref_gs.
width() - radius ||
4721 seed_pos.y < radius || seed_pos.y >= ref_gs.
height() - radius) {
4727 if (!roi.
get_mask()(seed_pos.y, seed_pos.x)) {
4735 params_init(0) = seed_pos.y;
4736 params_init(1) = seed_pos.x;
4737 params_init(2) = 0.0;
4738 params_init(3) = 0.0;
4739 params_init(4) = 0.0;
4740 params_init(5) = 0.0;
4741 params_init(6) = 0.0;
4742 params_init(7) = 0.0;
4743 params_init(8) = 0.0;
4744 params_init(9) = 0.0;
4747 auto result_pair = sr_nloptimizer.
global(params_init);
4748 const auto& params_guess = result_pair.first;
4750 if (!result_pair.second) {
4752 NLOG_DEBUG << region_idx <<
": Seed analysis failed at seed " << seed_pos.x <<
", " << seed_pos.y <<
"=> global failed";
4759 auto result_iter = sr_nloptimizer(params_guess);
4760 const auto& params_result = result_iter.first;
4762 if (!result_iter.second) {
4764 NLOG_DEBUG << region_idx <<
": Seed analysis failed at seed " << seed_pos.x <<
", " << seed_pos.y <<
"=> iterative search failed";
4772 seed_result.
x = seed_pos.x;
4773 seed_result.
y = seed_pos.y;
4774 seed_result.
v = params_result(2);
4775 seed_result.
u = params_result(3);
4776 seed_result.
dv_dx = params_result(4);
4777 seed_result.
dv_dy = params_result(5);
4778 seed_result.
du_dx = params_result(6);
4779 seed_result.
du_dy = params_result(7);
4780 seed_result.
corrcoef = params_result(8);
4785 convergence.
diffnorm = params_result(9);
4787 result.
seeds.push_back(seed_result);
4791 if (seed_result.
corrcoef > cutoff_max_corrcoef || convergence.
diffnorm > cutoff_max_diffnorm) {
4793 NLOG_DEBUG << region_idx <<
": Seed analysis failed at seed " << seed_pos.x <<
", " << seed_pos.y <<
"=> corrcoef: " << seed_result.
corrcoef <<
", diffnorm: " << convergence.
diffnorm;
4808struct matlab_seed_segment final {
4814Array2D<double> matlab_make_seed_params_init(
const SeedParams& seed) {
4815 Array2D<double> params_init(10, 1);
4816 params_init(0) = seed.y;
4817 params_init(1) = seed.x;
4818 params_init(2) = 0.0;
4819 params_init(3) = 0.0;
4820 params_init(4) = 0.0;
4821 params_init(5) = 0.0;
4822 params_init(6) = 0.0;
4823 params_init(7) = 0.0;
4824 params_init(8) = 0.0;
4825 params_init(9) = 0.0;
4829bool matlab_seed_matches_region(
const ROI2D& roi_reduced,
4830 const SeedParams& seed,
4831 matlab_difference_type scalefactor,
4832 matlab_difference_type region_idx) {
4833 if (scalefactor <= 0) {
4836 const matlab_difference_type reduced_y = seed.y / scalefactor;
4837 const matlab_difference_type reduced_x = seed.x / scalefactor;
4838 const auto region_idx_pair = roi_reduced.get_region_idx(reduced_y, reduced_x);
4839 return region_idx_pair.first == region_idx;
4842bool matlab_seed_positions_are_unique(
const std::vector<SeedParams>& seeds,
4843 matlab_difference_type scalefactor) {
4844 for (std::size_t i = 0; i < seeds.size(); ++i) {
4845 for (std::size_t j = i + 1; j < seeds.size(); ++j) {
4846 if (seeds[i].x / scalefactor == seeds[j].x / scalefactor &&
4847 seeds[i].y / scalefactor == seeds[j].y / scalefactor) {
4855std::vector<SeedParams> matlab_propagate_seeds(
const std::vector<SeedParams>& seeds,
4856 matlab_difference_type scalefactor) {
4857 std::vector<SeedParams> propagated_seeds;
4858 propagated_seeds.reserve(seeds.size());
4860 for (
const auto& seed : seeds) {
4861 SeedParams propagated = seed;
4862 propagated.x = seed.x +
static_cast<matlab_difference_type
>(std::round(seed.u / scalefactor) * scalefactor);
4863 propagated.y = seed.y +
static_cast<matlab_difference_type
>(std::round(seed.v / scalefactor) * scalefactor);
4866 propagated.du_dx = 0.0;
4867 propagated.du_dy = 0.0;
4868 propagated.dv_dx = 0.0;
4869 propagated.dv_dy = 0.0;
4870 propagated.corrcoef = 0.0;
4871 propagated_seeds.push_back(propagated);
4874 return propagated_seeds;
4877matlab_seed_segment matlab_compute_seed_segment(
const DIC_analysis_parallel_input& input,
4878 matlab_difference_type
ref_idx,
4879 const ROI2D& roi_ref,
4880 const std::vector<SeedParams>& seeds_by_region,
4881 bool seeds_are_optimized) {
4884 const auto& DIC_input = input.base_input;
4885 matlab_seed_segment segment;
4888 if (seeds_by_region.empty()) {
4892 auto roi_reduced = roi_ref.reduce(DIC_input.scalefactor);
4893 if (
difference_type(seeds_by_region.size()) != roi_reduced.size_regions()) {
4894 throw std::invalid_argument(
"matlab_DIC_analysis requires seeds_by_region.size() to match the number of ROI regions.");
4897 std::vector<SeedParams> predicted_seeds = seeds_are_optimized ?
4898 matlab_propagate_seeds(seeds_by_region, DIC_input.scalefactor) :
4901 if (!matlab_seed_positions_are_unique(predicted_seeds, DIC_input.scalefactor)) {
4902 throw std::invalid_argument(
"matlab_DIC_analysis received duplicate seed positions on the reduced grid.");
4905 segment.seeds_by_frame.reserve(DIC_input.imgs.size() -
ref_idx - 1);
4907 const auto A_ref = DIC_input.imgs[
ref_idx].get_gs();
4908 for (difference_type cur_idx =
ref_idx + 1; cur_idx <
difference_type(DIC_input.imgs.size()); ++cur_idx) {
4909 const auto A_cur = DIC_input.imgs[cur_idx].get_gs();
4910 auto sr_nloptimizer = details::subregion_nloptimizer(
4914 DIC_input.scalefactor,
4915 DIC_input.interp_type,
4916 DIC_input.subregion_type,
4920 std::vector<SeedParams> optimized_seeds;
4921 optimized_seeds.reserve(predicted_seeds.size());
4922 bool frame_success = matlab_seed_positions_are_unique(predicted_seeds, DIC_input.scalefactor);
4924 for (difference_type region_idx = 0;
4925 frame_success && region_idx <
difference_type(predicted_seeds.size());
4927 const auto& predicted_seed = predicted_seeds[region_idx];
4928 if (!matlab_seed_matches_region(roi_reduced, predicted_seed, DIC_input.scalefactor, region_idx)) {
4929 if (DIC_input.debug) {
4930 NLOG_DEBUG <<
"matlab_DIC_analysis: seed for region " << region_idx
4931 <<
" is outside its ROI at frame " << cur_idx <<
".";
4933 frame_success =
false;
4937 auto params_init = matlab_make_seed_params_init(predicted_seed);
4938 auto global_result = sr_nloptimizer.global(params_init);
4939 if (!global_result.second) {
4940 if (DIC_input.debug) {
4941 NLOG_DEBUG <<
"matlab_DIC_analysis: global seed optimization failed for region "
4942 << region_idx <<
" at frame " << cur_idx <<
".";
4944 frame_success =
false;
4949 const double diffnorm = global_result.first(9);
4950 const int num_iterations = sr_nloptimizer.get_last_iteration_count();
4954 bool iteration_saturated = (cur_idx >
ref_idx + 1) && (num_iterations >= 100);
4956 if (optimized_seed.corrcoef > input.cutoff_max_corrcoef ||
4957 diffnorm > input.cutoff_max_diffnorm ||
4958 iteration_saturated ||
4959 !matlab_seed_matches_region(roi_reduced, optimized_seed, DIC_input.scalefactor, region_idx)) {
4960 if (DIC_input.debug) {
4961 NLOG_DEBUG <<
"matlab_DIC_analysis: seed quality failed for region " << region_idx
4962 <<
" at frame " << cur_idx
4963 <<
" corrcoef=" << optimized_seed.corrcoef
4964 <<
" diffnorm=" << diffnorm
4965 <<
" iterations=" << num_iterations
4966 <<
" saturated=" << (iteration_saturated ?
"yes" :
"no") <<
".";
4968 frame_success =
false;
4972 optimized_seeds.push_back(optimized_seed);
4975 if (!frame_success || !matlab_seed_positions_are_unique(optimized_seeds, DIC_input.scalefactor)) {
4976 if (DIC_input.debug) {
4977 NLOG_DEBUG <<
"matlab_DIC_analysis: stopping seed schedule at frame " << cur_idx <<
".";
4982 segment.seeds_by_frame.push_back(optimized_seeds);
4990 if (!segment.seeds_by_frame.empty()) {
4991 segment.terminal_seeds = segment.seeds_by_frame.back();
4997std::vector<Disp2D> matlab_run_segment_dic(
const DIC_analysis_parallel_input& input,
4998 const ROI2D& roi_ref,
4999 const matlab_seed_segment& segment,
5000 bool run_in_parallel) {
5003 std::vector<Disp2D> segment_disps(segment.seeds_by_frame.size());
5004 if (segment.seeds_by_frame.empty()) {
5005 return segment_disps;
5008 const auto& DIC_input = input.base_input;
5009 const auto A_ref = DIC_input.imgs[segment.ref_idx].get_gs();
5014 const auto A_cur = DIC_input.imgs[cur_idx].get_gs();
5019 DIC_input.scalefactor,
5020 DIC_input.interp_type,
5021 DIC_input.subregion_type,
5023 DIC_input.cutoff_corrcoef,
5025 segment.seeds_by_frame[frame_idx],
5030 if (!run_in_parallel || num_frames <= 1) {
5031 for (difference_type frame_idx = 0; frame_idx < num_frames; ++frame_idx) {
5032 segment_disps[frame_idx] = compute_frame(frame_idx);
5034 return segment_disps;
5037 const difference_type requested_threads = std::min(num_frames, DIC_input.num_threads);
5038 NLOG_DEBUG <<
"\n[matlab_run_segment_dic] Parallel dispatch:"
5039 <<
"\n num_frames = " << num_frames
5040 <<
"\n DIC num_threads = " << DIC_input.num_threads
5041 <<
"\n OMP_NUM_THREADS = " << (std::getenv(
"OMP_NUM_THREADS") ? std::getenv(
"OMP_NUM_THREADS") :
"not set")
5042 <<
"\n omp_get_max_threads() = " << omp_get_max_threads()
5043 <<
"\n Requesting = " << requested_threads <<
" threads";
5045 std::vector<double> per_thread_seconds(requested_threads, 0.0);
5046 std::vector<long> per_thread_frames (requested_threads, 0);
5047 const double t_wall_begin = omp_get_wtime();
5049 #pragma omp parallel for num_threads(requested_threads) schedule(dynamic)
5050 for (difference_type frame_idx = 0; frame_idx < num_frames; ++frame_idx) {
5051 const int tid = omp_get_thread_num();
5052 const int nthr = omp_get_num_threads();
5053 const double t0 = omp_get_wtime();
5055 segment_disps[frame_idx] = compute_frame(frame_idx);
5057 const double dt = omp_get_wtime() - t0;
5058 per_thread_seconds[tid] += dt;
5059 per_thread_frames [tid] += 1;
5060 #pragma omp critical
5062 NLOG_DEBUG <<
" [segment_dic] frame " << frame_idx
5063 <<
" on thread " << tid <<
" / " << nthr
5064 <<
" took " << dt <<
" s";
5068 const double t_wall = omp_get_wtime() - t_wall_begin;
5069 double total_cpu = 0.0;
5070 NLOG_DEBUG <<
"[matlab_run_segment_dic] per-thread summary:";
5071 for (
int t = 0; t < requested_threads; ++t) {
5073 <<
": " << per_thread_frames[t] <<
" frames, "
5074 << per_thread_seconds[t] <<
" s";
5075 total_cpu += per_thread_seconds[t];
5077 NLOG_DEBUG <<
" wall = " << t_wall <<
" s, sum-of-threads = "
5078 << total_cpu <<
" s, speedup = "
5079 << (t_wall > 0.0 ? total_cpu / t_wall : 0.0)
5080 <<
" (ideal " << requested_threads <<
")";
5082 return segment_disps;
5085DIC_analysis_output matlab_DIC_analysis_impl(
const DIC_analysis_parallel_input& input,
5086 bool run_in_parallel,
5087 const std::string& mode_name) {
5090 const auto& DIC_input = input.base_input;
5091 if (DIC_input.imgs.size() < 2) {
5092 throw std::invalid_argument(
"matlab_DIC_analysis requires at least 2 images.");
5094 if (input.seeds_by_region.empty()) {
5095 throw std::invalid_argument(
"matlab_DIC_analysis requires one manual/preset seed per ROI region.");
5098 DIC_analysis_output DIC_output;
5099 DIC_output.disps.resize(DIC_input.imgs.size() - 1);
5101 DIC_output.units =
"pixels";
5102 DIC_output.units_per_pixel = 1.0;
5107 std::vector<Disp2D> step_disps(DIC_output.disps.size());
5108 std::vector<ROI2D> step_rois(DIC_output.disps.size());
5109 std::vector<difference_type> step_ref_idx(DIC_output.disps.size());
5112 ROI2D roi_ref = DIC_input.roi;
5113 std::vector<SeedParams> current_seeds = input.seeds_by_region;
5114 bool current_seeds_optimized = input.seeds_are_optimized;
5115 int num_segments = 0;
5118 const auto segment = matlab_compute_seed_segment(
5123 current_seeds_optimized
5126 if (segment.seeds_by_frame.empty()) {
5128 throw std::runtime_error(mode_name +
" could not seed any current image.");
5130 throw std::runtime_error(
5131 mode_name +
" could not seed the segment starting at reference frame " +
5132 std::to_string(
ref_idx + 1) +
"."
5136 const auto segment_disps = matlab_run_segment_dic(input, roi_ref, segment, run_in_parallel);
5138 for (difference_type frame_idx = 0; frame_idx <
difference_type(segment_disps.size()); ++frame_idx) {
5142 DIC_output.disps[cur_idx - 1] = segment_disps[frame_idx];
5143 next_roi_ref =
matlab_update_roi(roi_ref, segment_disps[frame_idx], DIC_input.interp_type, DIC_input.r);
5145 step_disps[cur_idx - 1] = segment_disps[frame_idx];
5153 step_rois[cur_idx - 1] = segment_disps[frame_idx].get_roi();
5154 step_ref_idx[cur_idx - 1] =
ref_idx;
5158 if (DIC_input.debug) {
5159 NLOG_DEBUG <<
"matlab_DIC_analysis: processed segment "
5160 << (
ref_idx + 1) <<
" -> " << (segment_end_idx + 1)
5161 <<
" (" << segment.seeds_by_frame.size() <<
" frame(s)).";
5170 roi_ref = next_roi_ref;
5171 current_seeds = segment.terminal_seeds;
5172 current_seeds_optimized =
true;
5186 if (num_segments > 1) {
5187 if (DIC_input.debug) {
5188 NLOG_DEBUG <<
"matlab_DIC_analysis: chaining " << num_segments
5189 <<
" segments back to global reference (frame 1).";
5191 for (difference_type cur_idx = 1; cur_idx <
difference_type(DIC_input.imgs.size()); ++cur_idx) {
5193 std::vector<Disp2D> chain_disps;
5194 std::vector<ROI2D> chain_rois;
5197 chain_disps.insert(chain_disps.begin(), step_disps[idx]);
5198 chain_rois.insert(chain_rois.begin(), step_rois[idx]);
5199 if (step_ref_idx[idx] == 0) {
5202 idx = step_ref_idx[idx] - 1;
5205 if (chain_disps.size() <= 1) {
5210 auto combined =
add_with_rois(chain_disps, chain_rois, DIC_input.interp_type);
5211 if (combined.get_roi().get_points() == 0) {
5212 NLOG_WARN <<
"WARNING: matlab_DIC_analysis multi-ref chaining produced empty ROI at frame "
5213 << (cur_idx + 1) <<
"; keeping segment-local displacement.";
5216 DIC_output.disps[cur_idx - 1] = std::move(combined);
5226 if (DIC_input.save_disps_steps && !step_disps.empty()) {
5227 DIC_analysis_step_data step_data;
5228 step_data.step_disps = step_disps;
5229 step_data.step_rois = step_rois;
5230 step_data.step_ref_idx = step_ref_idx;
5232 const std::string step_filename = run_in_parallel ?
5233 "matlab_DIC_analysis_parallel_step_data.bin" :
5234 "matlab_DIC_analysis_sequential_step_data.bin";
5235 save(step_data, step_filename);
5236 NLOG_INFO <<
"Step displacement data saved to " << step_filename;
5246 const std::vector<SeedParams>& seeds_by_region,
5247 bool seeds_are_optimized) {
5252 return matlab_DIC_analysis_impl(input,
false,
"matlab_DIC_analysis_sequential");
5256 return matlab_DIC_analysis_impl(input,
true,
"matlab_DIC_analysis_parallel");
5261 typedef std::ptrdiff_t difference_type;
5265 if (DIC_input.imgs.size() < 2) {
5266 throw std::invalid_argument(
"DIC_analysis_parallel requires at least 2 images.");
5271 throw std::invalid_argument(
"DIC_analysis_parallel requires seeds_by_region to be provided.");
5274 NLOG_INFO << std::endl <<
"Starting seed-based parallel DIC analysis...";
5279 std::chrono::time_point<std::chrono::system_clock> start_analysis = std::chrono::system_clock::now();
5283 DIC_output.
disps.resize(DIC_input.imgs.size() - 1);
5285 DIC_output.
units =
"pixels";
5289 NLOG_INFO <<
"\nPhase 1: Computing seed parameters for all frames...";
5292 std::vector<Array2D<double>> A_curs;
5293 A_curs.reserve(DIC_input.imgs.size() - 1);
5294 for (
size_t i = 1; i < DIC_input.imgs.size(); ++i) {
5295 A_curs.push_back(DIC_input.imgs[i].get_gs());
5300 DIC_input.imgs[0].get_gs(),
5303 DIC_input.scalefactor,
5304 DIC_input.interp_type,
5305 DIC_input.subregion_type,
5308 DIC_input.cutoff_corrcoef,
5313 if (seed_data.empty()) {
5314 throw std::runtime_error(
"No valid seed parameters could be computed.");
5317 NLOG_INFO <<
"Successfully computed seed parameters for " << seed_data.size() <<
" frames";
5320 NLOG_INFO <<
"\nPhase 2: Computing displacements in parallel...";
5322 size_t safe_batch_size = seed_data.size();
5325 NLOG_INFO <<
"Safe batch size: " << safe_batch_size;
5326 NLOG_INFO <<
"Number of threads: " << DIC_input.num_threads;
5327 NLOG_DEBUG <<
"static_cast<difference_type>(safe_batch_size): " <<
static_cast<difference_type
>(safe_batch_size);
5328 NLOG_DEBUG <<
"Number of threads: " << std::min(
static_cast<difference_type
>(safe_batch_size), DIC_input.num_threads);
5329 #pragma omp parallel for num_threads(std::min(static_cast<difference_type>(safe_batch_size), DIC_input.num_threads)) schedule(dynamic)
5330 for (
size_t i = 0; i < safe_batch_size; ++i) {
5331 difference_type frame_idx =
static_cast<difference_type
>(i) + 1;
5332 int thread_id = omp_get_thread_num();
5333 int total_threads = omp_get_num_threads();
5334 NLOG_DEBUG <<
"Running on thread " << thread_id <<
" of " << total_threads;
5336 #pragma omp critical
5338 NLOG_INFO <<
" Processing frame " << frame_idx <<
" (parallel)";
5342 const auto& frame_data = seed_data[i];
5343 const auto& roi_for_frame = frame_data.roi;
5344 const auto& sr_nloptimizer_for_frame = frame_data.sr_nloptimizer;
5347 if (frame_data.seed_params_by_region.empty()) {
5348 #pragma omp critical
5350 NLOG_WARN <<
" WARNING: No seed params for frame " << frame_idx <<
", skipping.";
5354 const auto& seed_params_for_frame = frame_data.seed_params_by_region[0];
5359 sr_nloptimizer_for_frame,
5360 roi_for_frame.reduce(DIC_input.scalefactor),
5361 seed_params_for_frame,
5362 DIC_input.scalefactor,
5363 DIC_input.cutoff_corrcoef,
5369 #pragma omp critical
5371 DIC_output.
disps[frame_idx - 1] = disps;
5373 }
catch (
const std::exception& e) {
5374 #pragma omp critical
5376 NLOG_ERROR <<
" ERROR: Frame " << frame_idx <<
" failed: " << e.what();
5382 std::chrono::time_point<std::chrono::system_clock> end_analysis = std::chrono::system_clock::now();
5383 std::chrono::duration<double> elapsed_seconds = end_analysis - start_analysis;
5384 NLOG_INFO << std::endl <<
"Total parallel DIC analysis time: " << elapsed_seconds.count() <<
" seconds";
5409namespace exact_detail {
5420 if (H < 2 || W < 2)
return;
5425 const bool a = !inside_region(y, x);
5426 active_old(y, x) = a;
5427 active_new(y, x) = a;
5434 if (!active_old(y, x))
continue;
5437 if (y > 0 && !active_old(y - 1, x)) { sum += plot(y - 1, x); ++n; ++total; }
5438 if (y < H - 1 && !active_old(y + 1, x)) { sum += plot(y + 1, x); ++n; ++total; }
5439 if (x > 0 && !active_old(y, x - 1)) { sum += plot(y, x - 1); ++n; ++total; }
5440 if (x < W - 1 && !active_old(y, x + 1)) { sum += plot(y, x + 1); ++n; ++total; }
5442 plot(y, x) = sum / double(n);
5443 active_new(y, x) =
false;
5447 if (total == 0)
break;
5450 active_old(y, x) = active_new(y, x);
5487 if (out.
empty)
return;
5510 if (lx < 0 || lx >= W)
continue;
5518 if (ly < 0 || ly >= H)
continue;
5522 inside(ly, lx) =
true;
5538 if (out.
empty)
return;
5557 const std::vector<ROI2D>& rois) {
5563 if (disps.empty())
return Disp2D();
5564 if (disps.size() == 1)
return disps.front();
5565 if (disps.size() != rois.size()) {
5566 throw std::invalid_argument(
5567 "exact_add_with_rois: disps.size() (" + std::to_string(disps.size()) +
5568 ") != rois.size() (" + std::to_string(rois.size()) +
").");
5571 const difference_type scalefactor = disps.front().get_scalefactor();
5572 const auto& roi_first = rois.front();
5573 for (std::size_t k = 1; k < disps.size(); ++k) {
5574 if (disps[k].get_scalefactor() != scalefactor ||
5575 disps[k].data_height() != disps.front().data_height() ||
5576 disps[k].data_width() != disps.front().data_width() ||
5577 rois[k].size_regions() != roi_first.size_regions()) {
5578 throw std::invalid_argument(
5579 "exact_add_with_rois: scalefactor / data size / region count mismatch.");
5583 const difference_type border = 20;
5591 for (difference_type region_idx = 0;
5592 region_idx < roi_first.size_regions();
5598 std::vector<exact_region_disp> per_link(disps.size());
5599 for (std::size_t k = 0; k < disps.size(); ++k) {
5600 fill_region_extrap(disps[k],
5601 rois[k].get_nlinfo(region_idx),
5605 for (
auto& link : per_link) bind_interpolators(link);
5606 if (per_link.front().empty)
continue;
5608 const auto& nlinfo0 = roi_first.get_nlinfo(region_idx);
5609 if (nlinfo0.empty())
continue;
5611 for (difference_type nl_idx = 0;
5612 nl_idx < nlinfo0.nodelist.width();
5614 const difference_type p2_red = nl_idx + nlinfo0.left_nl;
5615 for (difference_type np_idx = 0;
5616 np_idx < nlinfo0.noderange(nl_idx);
5618 const difference_type top_n = nlinfo0.nodelist(np_idx, nl_idx);
5619 const difference_type bot_n = nlinfo0.nodelist(np_idx + 1, nl_idx);
5620 for (difference_type p1_red = top_n; p1_red <= bot_n; ++p1_red) {
5623 double y_red = double(p1_red);
5624 double x_red = double(p2_red);
5625 double v_added = 0.0;
5626 double u_added = 0.0;
5627 double cc_min = 1.0;
5630 for (std::size_t k = 0; k < per_link.size(); ++k) {
5631 const auto& e = per_link[k];
5632 if (e.empty) { ok =
false;
break; }
5634 const difference_type y_round =
5635 static_cast<difference_type
>(std::round(y_red));
5636 const difference_type x_round =
5637 static_cast<difference_type
>(std::round(x_red));
5638 if (!e.nlinfo->in_nlinfo(y_round, x_round)) {
5642 const double ly = y_red - double(e.region_top) + double(e.border);
5643 const double lx = x_red - double(e.region_left) + double(e.border);
5644 if (ly < 0.0 || lx < 0.0 ||
5645 ly >
double(e.extrap_height - 1) ||
5646 lx >
double(e.extrap_width - 1)) {
5650 const double dv = e.v_interp(ly, lx);
5651 const double du = e.u_interp(ly, lx);
5652 if (std::isnan(dv) || std::isnan(du)) { ok =
false;
break; }
5654 const double cc = e.cc_interp(ly, lx);
5655 if (!std::isnan(cc) && cc < cc_min) cc_min = cc;
5660 y_red += dv / double(scalefactor);
5661 x_red += du / double(scalefactor);
5665 A_v (p1_red, p2_red) = v_added;
5666 A_u (p1_red, p2_red) = u_added;
5667 A_cc(p1_red, p2_red) = cc_min;
5668 A_vp(p1_red, p2_red) =
true;
5676 return { std::move(A_v), std::move(A_u), std::move(A_cc),
5677 roi_first.form_union(A_vp), scalefactor };
5685 bool run_in_parallel,
5686 const std::string& mode_name) {
5690 if (DIC_input.imgs.size() < 2) {
5691 throw std::invalid_argument(mode_name +
" requires at least 2 images.");
5694 throw std::invalid_argument(mode_name +
" requires one manual/preset seed per ROI region.");
5698 DIC_output.
disps.resize(DIC_input.imgs.size() - 1);
5700 DIC_output.
units =
"pixels";
5703 std::vector<Disp2D> step_disps (DIC_output.
disps.size());
5704 std::vector<ROI2D> step_rois (DIC_output.
disps.size());
5705 std::vector<difference_type> step_ref_idx (DIC_output.
disps.size());
5708 ROI2D roi_ref = DIC_input.roi;
5711 int num_segments = 0;
5713 while (
ref_idx < difference_type(DIC_input.imgs.size()) - 1) {
5714 const auto segment = matlab_compute_seed_segment(
5715 input,
ref_idx, roi_ref, current_seeds, current_seeds_optimized);
5717 if (segment.seeds_by_frame.empty()) {
5719 throw std::runtime_error(mode_name +
" could not seed any current image.");
5721 throw std::runtime_error(
5722 mode_name +
" could not seed the segment starting at reference frame " +
5723 std::to_string(
ref_idx + 1) +
".");
5726 const auto segment_disps = matlab_run_segment_dic(input, roi_ref, segment, run_in_parallel);
5728 for (difference_type frame_idx = 0;
5729 frame_idx < difference_type(segment_disps.size());
5731 const difference_type cur_idx =
ref_idx + frame_idx + 1;
5732 DIC_output.
disps[cur_idx - 1] = segment_disps[frame_idx];
5734 DIC_input.interp_type, DIC_input.r);
5735 step_disps [cur_idx - 1] = segment_disps[frame_idx];
5736 step_rois [cur_idx - 1] = segment_disps[frame_idx].get_roi();
5737 step_ref_idx[cur_idx - 1] =
ref_idx;
5740 const difference_type segment_end_idx =
ref_idx + difference_type(segment.seeds_by_frame.size());
5741 if (DIC_input.debug) {
5742 NLOG_DEBUG << mode_name <<
": processed segment "
5743 << (
ref_idx + 1) <<
" -> " << (segment_end_idx + 1)
5744 <<
" (" << segment.seeds_by_frame.size() <<
" frame(s)).";
5748 if (
ref_idx >= difference_type(DIC_input.imgs.size()) - 1)
break;
5749 roi_ref = next_roi_ref;
5750 current_seeds = segment.terminal_seeds;
5751 current_seeds_optimized =
true;
5754 if (num_segments > 1) {
5755 if (DIC_input.debug) {
5756 NLOG_DEBUG << mode_name <<
": chaining " << num_segments
5757 <<
" segments back to global reference (frame 1) "
5758 <<
"via exact_add_with_rois (MATLAB ncorr_alg_addanalysis).";
5760 for (difference_type cur_idx = 1;
5761 cur_idx < difference_type(DIC_input.imgs.size());
5763 std::vector<Disp2D> chain_disps;
5764 std::vector<ROI2D> chain_rois;
5765 difference_type idx = cur_idx - 1;
5767 chain_disps.insert(chain_disps.begin(), step_disps[idx]);
5768 chain_rois .insert(chain_rois .begin(), step_rois [idx]);
5769 if (step_ref_idx[idx] == 0)
break;
5770 idx = step_ref_idx[idx] - 1;
5772 if (chain_disps.size() <= 1)
continue;
5775 if (combined.get_roi().get_points() == 0) {
5777 <<
" multi-ref chaining produced empty ROI at frame "
5779 <<
"; keeping segment-local displacement.";
5782 DIC_output.
disps[cur_idx - 1] = std::move(combined);
5786 if (DIC_input.save_disps_steps && !step_disps.empty()) {
5792 const std::string filename = run_in_parallel ?
5793 "exact_matlab_DIC_analysis_parallel_step_data.bin" :
5794 "exact_matlab_DIC_analysis_sequential_step_data.bin";
5795 save(step_data, filename);
5796 NLOG_INFO <<
"Step displacement data saved to " << filename;
5803 const std::vector<SeedParams>& seeds_by_region,
5804 bool seeds_are_optimized) {
difference_type width() const
std::string size_2D_string() const
pointer get_pointer() const
std::enable_if< std::is_floating_point< value_type >::value, T_output >::type get_linsolver(LINSOLVER linsolver_type) const
difference_type height() const
std::enable_if< std::is_floating_point< value_type >::value, T_output >::type get_interpolator(INTERP interp_type) const
bool same_size(const T_container &A) const
difference_type size() const
bool in_bounds(difference_type p) const
const Array2D< double > & get_array() const
const ROI2D & get_roi() const
difference_type get_scalefactor() const
difference_type data_width() const
nlinfo_interpolator get_nlinfo_interpolator(difference_type, INTERP) const
difference_type data_height() const
std::string size_2D_string() const
std::string size_2D_string() const
difference_type data_width() const
static Disp2D load(std::ifstream &)
difference_type get_scalefactor() const
const Data2D & get_u() const
difference_type data_height() const
nlinfo_interpolator get_nlinfo_interpolator(difference_type, INTERP) const
const ROI2D & get_roi() const
details::Disp2D_nlinfo_interpolator nlinfo_interpolator
const Data2D & get_v() const
const Data2D & get_cc() const
static Image2D load(std::ifstream &)
Array2D< double > get_gs() const
contig_subregion_generator get_contig_subregion_generator(SUBREGION, difference_type) const
std::ptrdiff_t difference_type
ROI2D reduce(difference_type) const
difference_type size_regions() const
difference_type width() const
const Array2D< bool > & get_mask() const
const region_nlinfo & get_nlinfo(difference_type) const
static ROI2D load(std::ifstream &)
ROI2D form_union(const Array2D< bool > &) const
difference_type height() const
difference_type get_points() const
std::string size_2D_string() const
const region_boundary & get_boundary(difference_type) const
const Data2D & get_exx() const
const Data2D & get_exy() const
const Data2D & get_eyy() const
static Strain2D load(std::ifstream &)
const ROI2D::region_nlinfo & get_subregion_nlinfo() const
difference_type get_r() const
nloptimizer_base::difference_type difference_type
Array2D< double > grad_buf
void chk_input_params_size(const Array2D< double > &) const
virtual bool iterative_search() const =0
std::pair< const Array2D< double > &, bool > global(const Array2D< double > &) const
std::pair< const Array2D< double > &, bool > operator()(const Array2D< double > &) const
difference_type cutoff_iterations
virtual bool initial_guess() const =0
Array2D< double > hess_buf
nloptimizer_base::difference_type difference_type
subregion_nloptimizer() noexcept
int get_last_iteration_count() const
Lightweight, dependency-free logging facility for CppNCorr.
std::pair< std::vector< ROI2D::region_nlinfo >, std::vector< ROI2D::region_nlinfo > > nlinfo_line_split(const Array2D< double > &direc, const Array2D< double > &origin, ROI2D::difference_type partition_offset, const ROI2D::region_nlinfo &nlinfo, Array2D< ROI2D::difference_type > &partition_diagram, Array2D< bool > &mask_buf)
Array2D< ROI2D::difference_type > get_ROI_SDA(const ROI2D &roi)
cv::Mat cv_ncorr_data_over_img(const Image2D &img, const Data2D &data, double alpha, double min_data, double max_data, bool enable_colorbar=true, bool enable_axes=true, bool enable_scalebar=true, const std::string &units="pixels", double units_per_pixel=1.0, double num_units=-1.0, double font_size=1.0, ROI2D::difference_type num_tick_marks=11, int colormap=cv::COLORMAP_JET)
Array2D< double > matlab_update_boundary(const Array2D< double > &boundary, const Disp2D::nlinfo_interpolator &disp_interp, const ROI2D::difference_type roi_scalefactor, const ROI2D::difference_type size_mask_height, const ROI2D::difference_type size_mask_width, const ROI2D::difference_type radius)
Array2D< ROI2D::difference_type > & get_nlinfo_SDA(Array2D< ROI2D::difference_type > &SDA, const ROI2D::region_nlinfo &nlinfo, Array2D< bool > &mask_buf, Array2D< bool > &mask_buf_new)
std::vector< Array2D< ROI2D::difference_type > > get_ROI_partition_diagram_seeds(const Array2D< ROI2D::difference_type > &partition_diagram, const ROI2D &roi, ROI2D::difference_type num_partitions)
bool recursive_nlinfo_partition_diagram(ROI2D::difference_type part_num1, ROI2D::difference_type part_num2, const ROI2D::region_nlinfo &nlinfo, Array2D< ROI2D::difference_type > &partition_diagram, Array2D< bool > &mask_buf)
Array2D< double > get_seed_params(const Data2D &data, const ROI2D::region_nlinfo &nlinfo, const disp_nloptimizer &d_nloptimizer, const Array2D< ROI2D::difference_type > &SDA, Array2D< double > ¶ms_buf)
Array2D< double > get_centroid(const ROI2D::region_nlinfo &nlinfo)
void nlinfo_contig_expansion(const ROI2D::region_nlinfo &sub_nlinfo, const ROI2D::region_nlinfo &nlinfo, ROI2D::difference_type val, Array2D< ROI2D::difference_type > &A, Array2D< bool > &A_ap)
Array2D< double > update_boundary_skip_all(const Array2D< double > &boundary, const Disp2D::nlinfo_interpolator &disp_interp, const ROI2D::difference_type roi_scalefactor)
Array2D< ROI2D::difference_type > & get_nlinfo_partition_diagram(const ROI2D::region_nlinfo &nlinfo, ROI2D::difference_type num_partitions, Array2D< ROI2D::difference_type > &partition_diagram, Array2D< bool > &mask_buf)
std::pair< std::pair< ROI2D::region_nlinfo, ROI2D::region_nlinfo >, bool > nlinfo_contig_split(ROI2D::difference_type partition_offset, const ROI2D::region_nlinfo &nlinfo, Array2D< ROI2D::difference_type > &partition_diagram, Array2D< bool > &mask_buf)
Disp2D update(const Disp2D &disp, INTERP interp_type, ROI_UPDATE_MODE mode=ROI_UPDATE_MODE::SKIP_INVALID)
void worker_RGDIC(subregion_nloptimizer sr_nloptimizer, ROI2D roi_reduced, ROI2D::difference_type scalefactor, double cutoff_corrcoef, double cutoff_delta_disp, Array2D< double > &A_v, Array2D< double > &A_u, Array2D< double > &A_cc, Array2D< bool > &A_ap, Array2D< bool > &A_vp)
Array2D< ROI2D::difference_type > get_ROI_partition_diagram(const ROI2D &roi, ROI2D::difference_type num_partitions)
bool analyze_point(const Array2D< double > &queue_params, ROI2D::difference_type p1_new_delta, ROI2D::difference_type p2_new_delta, const ROI2D::region_nlinfo &nlinfo, ROI2D::difference_type roi_scalefactor, const disp_nloptimizer &d_nloptimizer, const Array2D< ROI2D::difference_type > &SDA, std::priority_queue< Array2D< double >, std::vector< Array2D< double > >, std::function< bool(const Array2D< double > &, const Array2D< double > &)> > &queue, Array2D< bool > &A_new_ap, Array2D< double > ¶ms_buf)
Array2D< ROI2D::difference_type > get_nlinfo_partition_diagram_seeds(const Array2D< ROI2D::difference_type > &partition_diagram, const ROI2D::region_nlinfo &nlinfo, ROI2D::difference_type num_partitions, Array2D< ROI2D::difference_type > &SDA, Array2D< bool > &mask_buf1, Array2D< bool > &mask_buf2)
Array2D< double > update_boundary_skip_invalid(const Array2D< double > &boundary, const Disp2D::nlinfo_interpolator &disp_interp, const ROI2D::difference_type roi_scalefactor, const ROI2D::difference_type roi_height, const ROI2D::difference_type roi_width, const ROI2D::difference_type margin=0)
void expand_filt(Array2D< double > &plot, const Array2D< bool > &inside_region)
void bind_interpolators(exact_region_disp &out)
ROI2D::difference_type difference_type
void fill_region_extrap(const Disp2D &disp, const ROI2D::region_nlinfo &nlinfo, difference_type border, exact_region_disp &out)
Disp2D add_with_rois_legacy(const std::vector< Disp2D > &disps, const std::vector< ROI2D > &rois, INTERP interp_type)
void set_debug(bool on)
Lower the console threshold to Debug when on is true (used to honour the engine's existing debug flag...
ROI2D matlab_update_roi(const ROI2D &, const Disp2D &, INTERP, ROI2D::difference_type radius)
T_container & fill(T_container &A, const ROI2D::region_nlinfo &nlinfo, const T &val)
void imshow(const Data2D &data, Data2D::difference_type delay)
Strain2D LS_strain(const Disp2D &, PERSPECTIVE, double, SUBREGION, ROI2D::difference_type)
DIC_analysis_output DIC_analysis_sequential(const DIC_analysis_input &)
DIC_analysis_output DIC_analysis_parallel(const DIC_analysis_parallel_input &)
Disp2D add(const std::vector< Disp2D > &, INTERP)
std::vector< SeedComputationData > compute_only_seed_points(const Array2D< double > &A_ref, const std::vector< Array2D< double > > &A_curs, const ROI2D &roi, ROI2D::difference_type scalefactor, INTERP interp_type, SUBREGION subregion_type, const std::vector< SeedParams > &seeds_by_region, double cutoff_corrcoef, ROI2D::difference_type region_idx=0, bool debug=false)
Disp2D RGDIC_with_seeds(const Array2D< double > &A_ref, const Array2D< double > &A_cur, const ROI2D &roi, const DIC_analysis_parallel_input &input)
strain_analysis_output strain_analysis(const strain_analysis_input &)
std::pair< typename T_container::value_type, typename T_container::coords > min(T_container &A, const ROI2D::region_nlinfo &nlinfo)
std::pair< typename T_container::value_type, typename T_container::coords > max(T_container &A, const ROI2D::region_nlinfo &nlinfo)
DIC_analysis_output exact_matlab_DIC_analysis_sequential(const DIC_analysis_input &DIC_input, const std::vector< SeedParams > &seeds_by_region={}, bool seeds_are_optimized=false)
Disp2D exact_add_with_rois(const std::vector< Disp2D > &disps, const std::vector< ROI2D > &rois)
void save(const Data2D &data, std::ofstream &os)
void save_strain_video(const std::string &, const strain_analysis_input &, const strain_analysis_output &, STRAIN, double, double, double=std::numeric_limits< double >::quiet_NaN(), double=std::numeric_limits< double >::quiet_NaN(), bool=true, bool=true, bool=true, double=-1.0, double=1.0, ROI2D::difference_type=11, int=cv::COLORMAP_JET, double=2.0, int=cv::VideoWriter::fourcc('M', 'J', 'P', 'G'))
Disp2D RGDIC(const Array2D< double > &, const Array2D< double > &, const ROI2D &, ROI2D::difference_type, INTERP, SUBREGION, ROI2D::difference_type, ROI2D::difference_type, double, bool)
Disp2D compute_displacements(const details::subregion_nloptimizer &sr_nloptimizer, const ROI2D &roi_reduced, const SeedParams &seedparams, ROI2D::difference_type scalefactor, double cutoff_corrcoef, ROI2D::difference_type region_idx, bool debug)
DIC_analysis_output filter_by_correlation(const DIC_analysis_output &, double)
void save_ncorr_data_over_img(const std::string &, const Image2D &, const Data2D &, double, double, double, bool, bool, bool, const std::string &, double, double, double, ROI2D::difference_type, int)
DIC_analysis_output matlab_DIC_analysis_parallel(const DIC_analysis_parallel_input &)
DIC_analysis_output exact_matlab_DIC_analysis_parallel(const DIC_analysis_parallel_input &)
std::vector< SeedParams > propagate_seeds(const std::vector< SeedParams > &seeds, ROI2D::difference_type spacing)
void save_DIC_video(const std::string &, const DIC_analysis_input &, const DIC_analysis_output &, DISP, double, double, double=std::numeric_limits< double >::quiet_NaN(), double=std::numeric_limits< double >::quiet_NaN(), bool=true, bool=true, bool=true, double=-1.0, double=1.0, ROI2D::difference_type=11, int=cv::COLORMAP_JET, double=2.0, int=cv::VideoWriter::fourcc('M', 'J', 'P', 'G'))
DIC_analysis_output change_perspective(const DIC_analysis_output &, INTERP)
void save_ncorr_data_over_img_video(const std::string &, const std::vector< Image2D > &, const std::vector< Data2D > &, double, double, double, double, bool, bool, bool, const std::string &, double, double, double, ROI2D::difference_type, int, double, int)
Disp2D add_with_rois(const std::vector< Disp2D > &disps, const std::vector< ROI2D > &rois, INTERP interp_type)
DIC_analysis_output set_units(const DIC_analysis_output &, const std::string &, double)
void imshow_ncorr_data_over_img(const Image2D &, const Data2D &, ROI2D::difference_type=-1)
ROI2D update(const ROI2D &, const Disp2D &, INTERP, ROI_UPDATE_MODE mode=ROI_UPDATE_MODE::SKIP_ALL)
DIC_analysis_output DIC_analysis(const DIC_analysis_input &)
DIC_analysis_output exact_matlab_DIC_analysis_impl(const DIC_analysis_parallel_input &input, bool run_in_parallel, const std::string &mode_name)
Disp2D RGDIC_without_thread(const Array2D< double > &A_ref, const Array2D< double > &A_cur, const ROI2D &roi, ROI2D::difference_type scalefactor, INTERP interp_type, SUBREGION subregion_type, ROI2D::difference_type r, double cutoff_corrcoef, bool debug, const std::vector< SeedParams > &seeds_by_region={}, bool seeds_are_optimized=false)
SeedAnalysisResult analyze_seeds(const details::subregion_nloptimizer &sr_nloptimizer, const Array2D< double > &ref_gs, const ROI2D &roi, const std::vector< SeedParams > &seed_positions, ROI2D::difference_type radius, int cutoff_iteration, double cutoff_max_diffnorm, double cutoff_max_corrcoef, bool debug=true)
DIC_analysis_output matlab_DIC_analysis_sequential(const DIC_analysis_input &DIC_input, const std::vector< SeedParams > &seeds_by_region={}, bool seeds_are_optimized=false)
DIC_analysis_output change_perspective_with_inversion(const DIC_analysis_output &, INTERP)
matlab_difference_type ref_idx
std::vector< std::vector< SeedParams > > seeds_by_frame
std::vector< SeedParams > terminal_seeds
std::vector< Disp2D > disps
PERSPECTIVE perspective_type
ROI2D::difference_type difference_type
static DIC_analysis_output load(std::ifstream &)
std::vector< ROI2D > step_rois
ROI2D::difference_type difference_type
std::vector< difference_type > step_ref_idx
static DIC_analysis_step_data load(std::ifstream &)
std::vector< Disp2D > step_disps
std::vector< Array2D< double > > sub
Array2D< difference_type > noderange
bool in_nlinfo(difference_type, difference_type) const
Array2D< difference_type > nodelist
static std::pair< std::vector< ROI2D::region_nlinfo >, bool > form_nlinfos(const Array2D< bool > &, ROI2D::difference_type=0)
std::vector< SeedParams > seeds
std::vector< SeedConvergence > convergence
static SeedParams from_array(const Array2D< double > ¶ms)
difference_type region_left
Array2D< double >::interpolator v_interp
Array2D< double >::interpolator cc_interp
Array2D< double >::interpolator u_interp
Array2D< double > plot_v_extrap
difference_type extrap_width
difference_type extrap_height
Array2D< double > plot_cc_extrap
difference_type region_top
Array2D< double > plot_u_extrap
const ROI2D::region_nlinfo * nlinfo
ROI2D::difference_type difference_type
static strain_analysis_output load(std::ifstream &)
std::vector< Strain2D > strains