pyvale 2025.5.3__cp311-cp311-win32.whl → 2025.7.1__cp311-cp311-win32.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pyvale might be problematic. Click here for more details.
- pyvale/__init__.py +12 -0
- pyvale/blendercalibrationdata.py +3 -1
- pyvale/blenderscene.py +7 -5
- pyvale/blendertools.py +27 -5
- pyvale/camera.py +1 -0
- pyvale/cameradata.py +3 -0
- pyvale/camerasensor.py +147 -0
- pyvale/camerastereo.py +4 -4
- pyvale/cameratools.py +23 -61
- pyvale/cython/rastercyth.c +1657 -1352
- pyvale/cython/rastercyth.cp311-win32.pyd +0 -0
- pyvale/cython/rastercyth.py +71 -26
- pyvale/data/DIC_Challenge_Star_Noise_Def.tiff +0 -0
- pyvale/data/DIC_Challenge_Star_Noise_Ref.tiff +0 -0
- pyvale/data/plate_hole_def0000.tiff +0 -0
- pyvale/data/plate_hole_def0001.tiff +0 -0
- pyvale/data/plate_hole_ref0000.tiff +0 -0
- pyvale/data/plate_rigid_def0000.tiff +0 -0
- pyvale/data/plate_rigid_def0001.tiff +0 -0
- pyvale/data/plate_rigid_ref0000.tiff +0 -0
- pyvale/dataset.py +96 -6
- pyvale/dic/cpp/dicbruteforce.cpp +370 -0
- pyvale/dic/cpp/dicfourier.cpp +648 -0
- pyvale/dic/cpp/dicinterpolator.cpp +559 -0
- pyvale/dic/cpp/dicmain.cpp +215 -0
- pyvale/dic/cpp/dicoptimizer.cpp +675 -0
- pyvale/dic/cpp/dicrg.cpp +137 -0
- pyvale/dic/cpp/dicscanmethod.cpp +677 -0
- pyvale/dic/cpp/dicsmooth.cpp +138 -0
- pyvale/dic/cpp/dicstrain.cpp +383 -0
- pyvale/dic/cpp/dicutil.cpp +563 -0
- pyvale/dic2d.py +164 -0
- pyvale/dic2dcpp.cp311-win32.pyd +0 -0
- pyvale/dicchecks.py +476 -0
- pyvale/dicdataimport.py +247 -0
- pyvale/dicregionofinterest.py +887 -0
- pyvale/dicresults.py +55 -0
- pyvale/dicspecklegenerator.py +238 -0
- pyvale/dicspecklequality.py +305 -0
- pyvale/dicstrain.py +387 -0
- pyvale/dicstrainresults.py +37 -0
- pyvale/errorintegrator.py +10 -8
- pyvale/examples/basics/ex1_1_basicscalars_therm2d.py +124 -113
- pyvale/examples/basics/ex1_2_sensormodel_therm2d.py +124 -132
- pyvale/examples/basics/ex1_3_customsens_therm3d.py +199 -195
- pyvale/examples/basics/ex1_4_basicerrors_therm3d.py +125 -121
- pyvale/examples/basics/ex1_5_fielderrs_therm3d.py +145 -141
- pyvale/examples/basics/ex1_6_caliberrs_therm2d.py +96 -101
- pyvale/examples/basics/ex1_7_spatavg_therm2d.py +109 -105
- pyvale/examples/basics/ex2_1_basicvectors_disp2d.py +92 -91
- pyvale/examples/basics/ex2_2_vectorsens_disp2d.py +96 -90
- pyvale/examples/basics/ex2_3_sensangle_disp2d.py +88 -89
- pyvale/examples/basics/ex2_4_chainfielderrs_disp2d.py +172 -171
- pyvale/examples/basics/ex2_5_vectorfields3d_disp3d.py +88 -86
- pyvale/examples/basics/ex3_1_basictensors_strain2d.py +90 -90
- pyvale/examples/basics/ex3_2_tensorsens2d_strain2d.py +93 -91
- pyvale/examples/basics/ex3_3_tensorsens3d_strain3d.py +172 -160
- pyvale/examples/basics/ex4_1_expsim2d_thermmech2d.py +154 -148
- pyvale/examples/basics/ex4_2_expsim3d_thermmech3d.py +249 -231
- pyvale/examples/dic/ex1_region_of_interest.py +98 -0
- pyvale/examples/dic/ex2_plate_with_hole.py +149 -0
- pyvale/examples/dic/ex3_plate_with_hole_strain.py +93 -0
- pyvale/examples/dic/ex4_dic_blender.py +95 -0
- pyvale/examples/dic/ex5_dic_challenge.py +102 -0
- pyvale/examples/imagedef2d/ex_imagedef2d_todisk.py +4 -2
- pyvale/examples/renderblender/ex1_1_blenderscene.py +152 -105
- pyvale/examples/renderblender/ex1_2_blenderdeformed.py +151 -100
- pyvale/examples/renderblender/ex2_1_stereoscene.py +183 -116
- pyvale/examples/renderblender/ex2_2_stereodeformed.py +185 -112
- pyvale/examples/renderblender/ex3_1_blendercalibration.py +164 -109
- pyvale/examples/renderrasterisation/ex_rastenp.py +74 -35
- pyvale/examples/renderrasterisation/ex_rastercyth_oneframe.py +6 -13
- pyvale/examples/renderrasterisation/ex_rastercyth_static_cypara.py +2 -2
- pyvale/examples/renderrasterisation/ex_rastercyth_static_pypara.py +2 -4
- pyvale/imagedef2d.py +3 -2
- pyvale/imagetools.py +137 -0
- pyvale/rastercy.py +34 -4
- pyvale/rasternp.py +300 -276
- pyvale/rasteropts.py +58 -0
- pyvale/renderer.py +47 -0
- pyvale/rendermesh.py +52 -62
- pyvale/renderscene.py +51 -0
- pyvale/sensorarrayfactory.py +2 -2
- pyvale/sensortools.py +19 -35
- pyvale/simcases/case21.i +1 -1
- pyvale/simcases/run_1case.py +8 -0
- pyvale/simtools.py +2 -2
- pyvale/visualsimplotter.py +180 -0
- {pyvale-2025.5.3.dist-info → pyvale-2025.7.1.dist-info}/METADATA +11 -57
- {pyvale-2025.5.3.dist-info → pyvale-2025.7.1.dist-info}/RECORD +93 -56
- {pyvale-2025.5.3.dist-info → pyvale-2025.7.1.dist-info}/WHEEL +1 -1
- pyvale/examples/visualisation/ex1_1_plot_traces.py +0 -102
- pyvale/examples/visualisation/ex2_1_animate_sim.py +0 -89
- {pyvale-2025.5.3.dist-info → pyvale-2025.7.1.dist-info}/licenses/LICENSE +0 -0
- {pyvale-2025.5.3.dist-info → pyvale-2025.7.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,675 @@
|
|
|
1
|
+
// ================================================================================
|
|
2
|
+
// pyvale: the python validation engine
|
|
3
|
+
// License: MIT
|
|
4
|
+
// Copyright (C) 2025 The Computer Aided Validation Team
|
|
5
|
+
// ================================================================================
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
// STD library Header files
|
|
9
|
+
#include <algorithm>
|
|
10
|
+
#include <iostream>
|
|
11
|
+
#include <numeric>
|
|
12
|
+
#include <vector>
|
|
13
|
+
#include <cmath>
|
|
14
|
+
#include <array>
|
|
15
|
+
#include <omp.h>
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
// Program Header files
|
|
20
|
+
#include "./dicinterpolator.hpp"
|
|
21
|
+
#include "./dicoptimizer.hpp"
|
|
22
|
+
#include "./dicutil.hpp"
|
|
23
|
+
#include "./defines.hpp"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
namespace optimizer {
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
// function pointer for correlation criteria
|
|
32
|
+
void (*optimize_cost)(const util::Subset &ss_ref, util::Subset &ss_def, const Interpolator &Interp, optimizer::Parameters &opt, const int global_x, const int global_y);
|
|
33
|
+
void (*shape_function)(double &, double &, double , double , std::vector<double> &);
|
|
34
|
+
void (*dshape_dp)(std::vector<double>&, double, double, double, double);
|
|
35
|
+
void (*params_to_displacement)(util::Results &results, double ss_x, double ss_y, std::vector<double> &p);
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
void init(std::string &corr_crit, std::string &shape_func){
|
|
43
|
+
setCostFunction(corr_crit);
|
|
44
|
+
setShapeFunction(shape_func);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
util::Results solve(const double ss_x, const double ss_y, util::Subset &ss_ref, util::Subset &ss_def, const Interpolator &interp_def, optimizer::Parameters &opt, const std::string &corr_crit){
|
|
50
|
+
|
|
51
|
+
int iter = 0;
|
|
52
|
+
double ftol = 0;
|
|
53
|
+
double xtol = 0;
|
|
54
|
+
opt.lambda = 0.001;
|
|
55
|
+
bool converged = false;
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
// trying relative instead of global coordinates for the optimization
|
|
59
|
+
int global_x = ss_ref.x[0];
|
|
60
|
+
int global_y = ss_ref.y[0];
|
|
61
|
+
for (int px = 0; px < ss_ref.num_px; px++){
|
|
62
|
+
ss_ref.x[px] -= global_x;
|
|
63
|
+
ss_ref.y[px] -= global_y;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
while (iter < opt.max_iter) {
|
|
67
|
+
|
|
68
|
+
// perform the optimization
|
|
69
|
+
optimize_cost(ss_ref, ss_def, interp_def, opt, global_x, global_y);
|
|
70
|
+
update_lambda(opt.costp, opt.costpdp, opt.p, opt.pdp, opt.lambda, opt.num_params);
|
|
71
|
+
|
|
72
|
+
// relative change of all parameters
|
|
73
|
+
xtol = std::sqrt(std::inner_product(opt.dp.begin(), opt.dp.end(), opt.dp.begin(), 0.0)) /
|
|
74
|
+
std::sqrt(std::inner_product( opt.p.begin(), opt.p.end(), opt.p.begin(), 0.0));
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
// variation on correlation coefficient
|
|
79
|
+
ftol = std::abs(opt.costpdp - opt.costp);
|
|
80
|
+
|
|
81
|
+
// convergence criteria
|
|
82
|
+
// - rel change in parameters is less than user precision
|
|
83
|
+
// - change in corr coeff is less than precision
|
|
84
|
+
// - cost is less than threshold
|
|
85
|
+
if (corr_crit != "SSD") {
|
|
86
|
+
if ((xtol < opt.precision) && (ftol < opt.precision) && (opt.costp < 1.0-opt.opt_threshold)) {
|
|
87
|
+
//debugPrint(ss_x, ss_y, iter, opt.costp, ftol, xtol, opt.p);
|
|
88
|
+
converged=true;
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
if ((xtol < opt.precision) && (ftol < opt.precision)) {
|
|
94
|
+
//debugPrint(ss_x, ss_y, iter, opt.costp, ftol, xtol, opt.p);
|
|
95
|
+
converged=true;
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
iter++;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
util::Results res(opt.num_params);
|
|
103
|
+
params_to_displacement(res, ss_x-global_x, ss_y-global_y, opt.p);
|
|
104
|
+
res.iter = iter;
|
|
105
|
+
res.ftol = ftol;
|
|
106
|
+
res.xtol = xtol;
|
|
107
|
+
res.p = opt.p;
|
|
108
|
+
res.cost = opt.costp;
|
|
109
|
+
res.converged = converged;
|
|
110
|
+
|
|
111
|
+
// debugging
|
|
112
|
+
//if (iter == opt.max_iter) {
|
|
113
|
+
//debugPrint(ss_x, ss_y, iter, opt.costp, ftol, xtol, opt.p);
|
|
114
|
+
//}
|
|
115
|
+
|
|
116
|
+
return res;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
void ssd(const util::Subset &ss_ref,
|
|
123
|
+
util::Subset &ss_def,
|
|
124
|
+
const Interpolator &interp_def,
|
|
125
|
+
optimizer::Parameters &opt,
|
|
126
|
+
const int global_x,
|
|
127
|
+
const int global_y){
|
|
128
|
+
|
|
129
|
+
const int num_px = ss_def.num_px;
|
|
130
|
+
const int num_params = opt.num_params;
|
|
131
|
+
double dfdx;
|
|
132
|
+
double dfdy;
|
|
133
|
+
|
|
134
|
+
// interpolation data struct
|
|
135
|
+
InterpVals interp_vals;
|
|
136
|
+
|
|
137
|
+
// reset derivative and hessian values
|
|
138
|
+
std::fill(opt.g.begin(), opt.g.end(), 0.0);
|
|
139
|
+
std::fill(opt.H.begin(), opt.H.end(), 0.0);
|
|
140
|
+
|
|
141
|
+
// loop over the subset values
|
|
142
|
+
for (int i = 0; i < num_px; i++){
|
|
143
|
+
|
|
144
|
+
// apply shape function parameters to deformed subset
|
|
145
|
+
shape_function(ss_def.x[i], ss_def.y[i], ss_ref.x[i], ss_ref.y[i], opt.p);
|
|
146
|
+
|
|
147
|
+
// x and y coordinates of reference subset
|
|
148
|
+
double def_x = ss_def.x[i];
|
|
149
|
+
double def_y = ss_def.y[i];
|
|
150
|
+
|
|
151
|
+
// get the subset value and derivitives
|
|
152
|
+
interp_vals = interp_def.eval_bicubic_and_derivs(global_x, global_y, def_x+global_x, def_y+global_y);
|
|
153
|
+
ss_def.vals[i] = interp_vals.f;
|
|
154
|
+
double def = ss_def.vals[i];
|
|
155
|
+
|
|
156
|
+
dfdx = interp_vals.dfdx;
|
|
157
|
+
dfdy = interp_vals.dfdy;
|
|
158
|
+
|
|
159
|
+
// derivative of shape function with repsect to parameters
|
|
160
|
+
dshape_dp(opt.dfdp, def_x, def_y, dfdx, dfdy);
|
|
161
|
+
|
|
162
|
+
// Upper triangle of Hessian Matrix
|
|
163
|
+
for (int row = 0; row < num_params; row++) {
|
|
164
|
+
double dfdp_row = opt.dfdp[row];
|
|
165
|
+
for (int col = row; col < num_params; col++) {
|
|
166
|
+
opt.H[row * num_params + col] += dfdp_row * opt.dfdp[col];
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
double dshape_df = - (ss_ref.vals[i] - def);
|
|
171
|
+
|
|
172
|
+
if (num_params == 2) {
|
|
173
|
+
opt.g[0] += dshape_df*dfdx;
|
|
174
|
+
opt.g[1] += dshape_df*dfdy;
|
|
175
|
+
}
|
|
176
|
+
else if (num_params == 6) {
|
|
177
|
+
opt.g[0] += dshape_df*dfdx;
|
|
178
|
+
opt.g[1] += dshape_df*dfdy;
|
|
179
|
+
opt.g[2] += dshape_df*dfdx*def_x;
|
|
180
|
+
opt.g[3] += dshape_df*dfdx*def_y;
|
|
181
|
+
opt.g[4] += dshape_df*dfdy*def_x;
|
|
182
|
+
opt.g[5] += dshape_df*dfdy*def_y;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
populate_hessian_lower_tri(opt.H, opt.lambda, opt.num_params);
|
|
188
|
+
invertMatrix(opt.H, opt.invH, opt.augmented, opt.num_params);
|
|
189
|
+
update_shapefunc_parameters(opt.pdp, opt.p, opt.dp, opt.invH, opt.g, opt.num_params);
|
|
190
|
+
|
|
191
|
+
// calculate cost function for current and updated parameter values
|
|
192
|
+
opt.costp = 0.0;
|
|
193
|
+
for (int i = 0; i < num_px; i++){
|
|
194
|
+
opt.costp += (ss_ref.vals[i] - ss_def.vals[i]) * (ss_ref.vals[i] - ss_def.vals[i]);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
// calculate cost function for updated parameter values
|
|
199
|
+
opt.costpdp = 0.0;
|
|
200
|
+
for (int i = 0; i < num_px; ++i) {
|
|
201
|
+
shape_function(ss_def.x[i], ss_def.y[i], ss_ref.x[i], ss_ref.y[i], opt.pdp);
|
|
202
|
+
ss_def.vals[i] = interp_def.eval_bicubic(global_x, global_y, ss_def.x[i]+global_x, ss_def.y[i]+global_y);
|
|
203
|
+
opt.costpdp += (ss_ref.vals[i] - ss_def.vals[i]) * (ss_ref.vals[i] - ss_def.vals[i]);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
void nssd(const util::Subset &ss_ref,
|
|
209
|
+
util::Subset &ss_def,
|
|
210
|
+
const Interpolator &interp_def,
|
|
211
|
+
optimizer::Parameters &opt,
|
|
212
|
+
const int global_x,
|
|
213
|
+
const int global_y){
|
|
214
|
+
|
|
215
|
+
// reset derivative and hessian values
|
|
216
|
+
std::fill(opt.g.begin(), opt.g.end(), 0.0);
|
|
217
|
+
std::fill(opt.H.begin(), opt.H.end(), 0.0);
|
|
218
|
+
|
|
219
|
+
const int num_px = ss_def.num_px;
|
|
220
|
+
const int num_params = opt.num_params;
|
|
221
|
+
|
|
222
|
+
std::vector<double> dfdx(num_px);
|
|
223
|
+
std::vector<double> dfdy(num_px);
|
|
224
|
+
|
|
225
|
+
double sum_squared_def = 0.0;
|
|
226
|
+
double sum_squared_ref = 0.0;
|
|
227
|
+
double inv_sum_squared_def;
|
|
228
|
+
double inv_sum_squared_ref;
|
|
229
|
+
|
|
230
|
+
// interpolation data struct
|
|
231
|
+
InterpVals interp_vals;
|
|
232
|
+
|
|
233
|
+
// reset cost function
|
|
234
|
+
opt.costp = 0.0;
|
|
235
|
+
opt.costpdp = 0.0;
|
|
236
|
+
|
|
237
|
+
// get the normalisation values for both reference and deformed subsets
|
|
238
|
+
for (int i = 0; i < num_px; ++i) {
|
|
239
|
+
|
|
240
|
+
// apply shape function parameters to deformed subset
|
|
241
|
+
shape_function(ss_def.x[i], ss_def.y[i], ss_ref.x[i], ss_ref.y[i], opt.p);
|
|
242
|
+
|
|
243
|
+
interp_vals = interp_def.eval_bicubic_and_derivs(global_x, global_y, ss_def.x[i]+global_x, ss_def.y[i]+global_y);
|
|
244
|
+
ss_def.vals[i] = interp_vals.f;
|
|
245
|
+
dfdx[i] = interp_vals.dfdx;
|
|
246
|
+
dfdy[i] = interp_vals.dfdy;
|
|
247
|
+
sum_squared_def += ss_def.vals[i] * ss_def.vals[i];
|
|
248
|
+
sum_squared_ref += ss_ref.vals[i] * ss_ref.vals[i];
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
inv_sum_squared_def = 1.0 / sqrt(sum_squared_def);
|
|
252
|
+
inv_sum_squared_ref = 1.0 / sqrt(sum_squared_ref);
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
// loop over the subset values
|
|
256
|
+
for (int i = 0; i < num_px; i++){
|
|
257
|
+
|
|
258
|
+
// derivative of shape function with repsect to parameters
|
|
259
|
+
dshape_dp(opt.dfdp, ss_def.x[i], ss_def.y[i], dfdx[i], dfdy[i]);
|
|
260
|
+
|
|
261
|
+
double dshape_df = - inv_sum_squared_def * (ss_ref.vals[i] * inv_sum_squared_ref - ss_def.vals[i] * inv_sum_squared_def);
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
if (num_params == 2) {
|
|
265
|
+
opt.g[0] += dshape_df * dfdx[i];
|
|
266
|
+
opt.g[1] += dshape_df * dfdy[i];
|
|
267
|
+
}
|
|
268
|
+
else if (num_params == 6) {
|
|
269
|
+
opt.g[0] += dshape_df * dfdx[i];
|
|
270
|
+
opt.g[1] += dshape_df * dfdy[i];
|
|
271
|
+
opt.g[2] += dshape_df * dfdx[i] * ss_def.x[i];
|
|
272
|
+
opt.g[3] += dshape_df * dfdx[i] * ss_def.y[i];
|
|
273
|
+
opt.g[4] += dshape_df * dfdy[i] * ss_def.x[i];
|
|
274
|
+
opt.g[5] += dshape_df * dfdy[i] * ss_def.y[i];
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Upper triangle of Hessian Matrix
|
|
278
|
+
for (int row = 0; row < num_params; row++) {
|
|
279
|
+
double dfdp_row = opt.dfdp[row];
|
|
280
|
+
for (int col = row; col < num_params; col++) {
|
|
281
|
+
opt.H[row * num_params + col] += inv_sum_squared_def * inv_sum_squared_def * dfdp_row * opt.dfdp[col];
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
populate_hessian_lower_tri(opt.H, opt.lambda, opt.num_params);
|
|
287
|
+
invertMatrix(opt.H, opt.invH, opt.augmented, opt.num_params);
|
|
288
|
+
update_shapefunc_parameters(opt.pdp, opt.p, opt.dp, opt.invH, opt.g, opt.num_params);
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
// calculate cost function for current parameter values
|
|
292
|
+
for (int i = 0; i < num_px; i++){
|
|
293
|
+
double def_norm = ss_def.vals[i] * inv_sum_squared_def;
|
|
294
|
+
double ref_norm = ss_ref.vals[i] * inv_sum_squared_ref;
|
|
295
|
+
opt.costp += (ref_norm - def_norm) * (ref_norm - def_norm);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
// calculate cost function for updated parameter values
|
|
300
|
+
sum_squared_def = 0.0;
|
|
301
|
+
for (int i = 0; i < num_px; ++i) {
|
|
302
|
+
shape_function(ss_def.x[i], ss_def.y[i], ss_ref.x[i], ss_ref.y[i], opt.pdp);
|
|
303
|
+
ss_def.vals[i] = interp_def.eval_bicubic(global_x, global_y, ss_def.x[i]+global_x, ss_def.y[i]+global_y);
|
|
304
|
+
sum_squared_def += ss_def.vals[i] * ss_def.vals[i];
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
inv_sum_squared_def = 1.0 / sqrt(sum_squared_def);
|
|
308
|
+
|
|
309
|
+
for (int i = 0; i < num_px; ++i) {
|
|
310
|
+
double def_norm = ss_def.vals[i] * inv_sum_squared_def;
|
|
311
|
+
double ref_norm = ss_ref.vals[i] * inv_sum_squared_ref;
|
|
312
|
+
opt.costpdp += (ref_norm - def_norm) * (ref_norm - def_norm);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
void znssd(const util::Subset &ss_ref,
|
|
319
|
+
util::Subset &ss_def,
|
|
320
|
+
const Interpolator &interp_def,
|
|
321
|
+
optimizer::Parameters &opt,
|
|
322
|
+
const int global_x,
|
|
323
|
+
const int global_y){
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
// reset derivative and hessian values
|
|
327
|
+
std::fill(opt.g.begin(), opt.g.end(), 0.0);
|
|
328
|
+
std::fill(opt.H.begin(), opt.H.end(), 0.0);
|
|
329
|
+
|
|
330
|
+
const int num_px = ss_def.num_px;
|
|
331
|
+
const int num_params = opt.num_params;
|
|
332
|
+
|
|
333
|
+
std::vector<double> dfdx(num_px);
|
|
334
|
+
std::vector<double> dfdy(num_px);
|
|
335
|
+
|
|
336
|
+
double mean_ref = 0.0;
|
|
337
|
+
double mean_def = 0.0;
|
|
338
|
+
|
|
339
|
+
// interpolation data struct
|
|
340
|
+
InterpVals interp_vals;
|
|
341
|
+
|
|
342
|
+
// reset cost function
|
|
343
|
+
opt.costp = 0.0;
|
|
344
|
+
opt.costpdp = 0.0;
|
|
345
|
+
|
|
346
|
+
// get the normalisation values for both reference and deformed subsets
|
|
347
|
+
for (int i = 0; i < num_px; ++i) {
|
|
348
|
+
|
|
349
|
+
// apply shape function parameters to deformed subset
|
|
350
|
+
shape_function(ss_def.x[i], ss_def.y[i], ss_ref.x[i], ss_ref.y[i], opt.p);
|
|
351
|
+
|
|
352
|
+
interp_vals = interp_def.eval_bicubic_and_derivs(global_x, global_y, ss_def.x[i]+global_x, ss_def.y[i]+global_y);
|
|
353
|
+
ss_def.vals[i] = interp_vals.f;
|
|
354
|
+
dfdx[i] = interp_vals.dfdx;
|
|
355
|
+
dfdy[i] = interp_vals.dfdy;
|
|
356
|
+
|
|
357
|
+
mean_ref += ss_ref.vals[i];
|
|
358
|
+
mean_def += ss_def.vals[i];
|
|
359
|
+
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
mean_def /= num_px;
|
|
363
|
+
mean_ref /= num_px;
|
|
364
|
+
|
|
365
|
+
double sum_squared_ref = 0.0;
|
|
366
|
+
double sum_squared_def = 0.0;
|
|
367
|
+
for (int i = 0; i < num_px; ++i) {
|
|
368
|
+
sum_squared_def += (ss_def.vals[i] - mean_def) * (ss_def.vals[i] - mean_def);
|
|
369
|
+
sum_squared_ref += (ss_ref.vals[i] - mean_ref) * (ss_ref.vals[i] - mean_ref);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
double inv_sum_squared_def = 1.0 / sqrt(sum_squared_def);
|
|
373
|
+
double inv_sum_squared_ref = 1.0 / sqrt(sum_squared_ref);
|
|
374
|
+
|
|
375
|
+
// loop over the subset values
|
|
376
|
+
for (int i = 0; i < num_px; i++){
|
|
377
|
+
|
|
378
|
+
// derivative of shape function with repsect to parameters
|
|
379
|
+
dshape_dp(opt.dfdp, ss_def.x[i], ss_def.y[i], dfdx[i], dfdy[i]);
|
|
380
|
+
|
|
381
|
+
double dshape_df = - inv_sum_squared_def * ((ss_ref.vals[i] - mean_ref) * inv_sum_squared_ref - (ss_def.vals[i] - mean_def) * inv_sum_squared_def);
|
|
382
|
+
|
|
383
|
+
if (num_params == 2) {
|
|
384
|
+
opt.g[0] += dshape_df * dfdx[i];
|
|
385
|
+
opt.g[1] += dshape_df * dfdy[i];
|
|
386
|
+
}
|
|
387
|
+
else if (num_params == 6) {
|
|
388
|
+
opt.g[0] += dshape_df * dfdx[i];
|
|
389
|
+
opt.g[1] += dshape_df * dfdy[i];
|
|
390
|
+
opt.g[2] += dshape_df * dfdx[i] * ss_def.x[i];
|
|
391
|
+
opt.g[3] += dshape_df * dfdx[i] * ss_def.y[i];
|
|
392
|
+
opt.g[4] += dshape_df * dfdy[i] * ss_def.x[i];
|
|
393
|
+
opt.g[5] += dshape_df * dfdy[i] * ss_def.y[i];
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Upper triangle of Hessian Matrix
|
|
397
|
+
for (int row = 0; row < num_params; row++) {
|
|
398
|
+
double dfdp_row = opt.dfdp[row];
|
|
399
|
+
for (int col = row; col < num_params; col++) {
|
|
400
|
+
opt.H[row * num_params + col] += inv_sum_squared_def * inv_sum_squared_def * dfdp_row * opt.dfdp[col];
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
populate_hessian_lower_tri(opt.H, opt.lambda, opt.num_params);
|
|
407
|
+
invertMatrix(opt.H, opt.invH, opt.augmented, opt.num_params);
|
|
408
|
+
update_shapefunc_parameters(opt.pdp, opt.p, opt.dp, opt.invH, opt.g, opt.num_params);
|
|
409
|
+
|
|
410
|
+
// debugging
|
|
411
|
+
//#pragma omp critical
|
|
412
|
+
//{
|
|
413
|
+
// std::cout << "ss_def_x " << ss_def.x[0] << " " << ss_def.x[1] << " " << ss_def.x[2] << std::endl;
|
|
414
|
+
// std::cout << "ss_ref_x " << ss_ref.x[0] << " " << ss_ref.x[1] << " " << ss_ref.x[2] << std::endl;
|
|
415
|
+
// std::cout << "ss_def " << ss_def.vals[0] << " " << ss_def.vals[1] << " " << ss_def.vals[2] << std::endl;
|
|
416
|
+
// std::cout << "ss_ref " << ss_ref.vals[0] << " " << ss_ref.vals[1] << " " << ss_ref.vals[2] << std::endl;
|
|
417
|
+
// std::cout << "means " << mean_def << " " << mean_ref << std::endl;
|
|
418
|
+
// std::cout << "invs " << inv_sum_squared_def << " " << inv_sum_squared_ref << std::endl;
|
|
419
|
+
// std::cout << "dfdx " << dfdx[0] << " " << dfdy[0] << std::endl;
|
|
420
|
+
// std::cout << "dfdp " << opt.dfdp[0] << " " << opt.dfdp[1] << " " << opt.dfdp[2] << " " << opt.dfdp[3] << " " <<opt.dfdp[4] << " " << opt.dfdp[5] << std::endl;
|
|
421
|
+
// std::cout << "g " << opt.g[0] << " " << opt.g[1] << " " << opt.g[2] << " " << opt.g[3] << " " << opt.g[4] << " " << opt.g[5] << std::endl;
|
|
422
|
+
// std::cout << "H " << opt.H[0] << " " << opt.H[1] << " " << opt.H[2] << " " << opt.H[3] << " " << opt.H[4] << " " << opt.H[5] << std::endl;
|
|
423
|
+
// std::cout << "Hi " << opt.invH[0] << " " << opt.invH[1] << " " << opt.invH[2] << " " << opt.invH[3] << " " << opt.invH[4] << " " << opt.invH[5] << std::endl;
|
|
424
|
+
// std::cout << "p " << opt.p[0] << " " << opt.p[1] << " " << opt.p[2] << " " << opt.p[3] << " " << opt.p[4] << " " << opt.p[5] << std::endl;
|
|
425
|
+
// std::cout << "pdp " << opt.pdp[0] << " " << opt.pdp[1] << " " << opt.pdp[2] << " " << opt.pdp[3] << " " << opt.pdp[4] << " " << opt.pdp[5] << std::endl;
|
|
426
|
+
// std::cout << opt.g.size() << std::endl;
|
|
427
|
+
// std::cout << "invs " << inv_sum_squared_def << " " << inv_sum_squared_ref << std::endl;
|
|
428
|
+
// std::cout << "dfdx " << dfdx[0] << " " << dfdy[0] << std::endl;
|
|
429
|
+
// std::cout << "dfdp " << opt.dfdp[0] << " " << opt.dfdp[1] << std::endl;
|
|
430
|
+
// std::cout << "g " << opt.g[0] << " " << opt.g[1] << std::endl;
|
|
431
|
+
// std::cout << "H " << opt.H[0] << " " << opt.H[1] << " " << opt.H[2] << " " << opt.H[3] << std::endl;
|
|
432
|
+
// std::cout << "Hi " << opt.invH[0] << " " << opt.invH[1] << " " << opt.invH[2] << " " << opt.invH[3] << std::endl;
|
|
433
|
+
// std::cout << "p " << opt.p[0] << " " << opt.p[1] << std::endl;
|
|
434
|
+
// std::cout << "pdp " << opt.pdp[0] << " " << opt.pdp[1] << std::endl;
|
|
435
|
+
// exit(0);
|
|
436
|
+
//}
|
|
437
|
+
|
|
438
|
+
// calculate cost function for current parameter values
|
|
439
|
+
for (int i = 0; i < num_px; i++){
|
|
440
|
+
double def_norm = (ss_def.vals[i] - mean_def) * inv_sum_squared_def;
|
|
441
|
+
double ref_norm = (ss_ref.vals[i] - mean_ref) * inv_sum_squared_ref;
|
|
442
|
+
opt.costp += (ref_norm - def_norm) * (ref_norm - def_norm);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
// calculate cost function for updated parameter values
|
|
447
|
+
mean_def = 0.0;
|
|
448
|
+
for (int i = 0; i < num_px; ++i) {
|
|
449
|
+
shape_function(ss_def.x[i], ss_def.y[i], ss_ref.x[i], ss_ref.y[i], opt.pdp);
|
|
450
|
+
ss_def.vals[i] = interp_def.eval_bicubic(global_x, global_y, ss_def.x[i]+global_x, ss_def.y[i]+global_y);
|
|
451
|
+
mean_def += ss_def.vals[i];
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
mean_def /= num_px;
|
|
455
|
+
|
|
456
|
+
sum_squared_def = 0.0;
|
|
457
|
+
for (int i = 0; i < num_px; ++i) {
|
|
458
|
+
sum_squared_def += (ss_def.vals[i] - mean_def) * (ss_def.vals[i] - mean_def);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
inv_sum_squared_def = 1.0 / sqrt(sum_squared_def);
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
for (int i = 0; i < num_px; ++i) {
|
|
465
|
+
double def_norm = (ss_def.vals[i] - mean_def) * inv_sum_squared_def;
|
|
466
|
+
double ref_norm = (ss_ref.vals[i] - mean_ref) * inv_sum_squared_ref;
|
|
467
|
+
opt.costpdp += (ref_norm - def_norm) * (ref_norm - def_norm);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// Inv matrix using Gauss Elim.
|
|
473
|
+
bool invertMatrix(const std::vector<double>& matrix, std::vector<double>& inverse, std::vector<double>& augmented, int num_params) {
|
|
474
|
+
|
|
475
|
+
const int n = num_params;
|
|
476
|
+
|
|
477
|
+
// Initialize augmented matrix with input matrix and identity matrix
|
|
478
|
+
for (int i = 0; i < n; ++i) {
|
|
479
|
+
for (int j = 0; j < n; ++j) {
|
|
480
|
+
augmented[i * (2 * n) + j] = matrix[i * n + j];
|
|
481
|
+
augmented[i * (2 * n) + (j + n)] = (i == j) ? 1.0 : 0.0;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// Gauss-Jordan Elimination
|
|
486
|
+
for (int i = 0; i < n; ++i) {
|
|
487
|
+
// Search for max element in column
|
|
488
|
+
double maxEl = std::abs(augmented[i * (2 * n) + i]);
|
|
489
|
+
int maxRow = i;
|
|
490
|
+
for (int k = i + 1; k < n; ++k) {
|
|
491
|
+
if (std::abs(augmented[k * (2 * n) + i]) > maxEl) {
|
|
492
|
+
maxEl = std::abs(augmented[k * (2 * n) + i]);
|
|
493
|
+
maxRow = k;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// Swap maximum row with current row
|
|
498
|
+
if (i != maxRow) {
|
|
499
|
+
for (int j = 0; j < 2 * n; ++j) {
|
|
500
|
+
std::swap(augmented[i * (2 * n) + j], augmented[maxRow * (2 * n) + j]);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// Make the pivot element 1
|
|
505
|
+
double pivot = augmented[i * (2 * n) + i];
|
|
506
|
+
if (pivot == 0) {
|
|
507
|
+
return false; // Singular matrix, can't invert
|
|
508
|
+
}
|
|
509
|
+
for (int j = 0; j < 2 * n; ++j) {
|
|
510
|
+
augmented[i * (2 * n) + j] /= pivot;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// Make the elements below the pivot 0
|
|
514
|
+
for (int k = i + 1; k < n; ++k) {
|
|
515
|
+
double factor = augmented[k * (2 * n) + i];
|
|
516
|
+
for (int j = 0; j < 2 * n; ++j) {
|
|
517
|
+
augmented[k * (2 * n) + j] -= augmented[i * (2 * n) + j] * factor;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// Perform back substitution to eliminate entries above the pivot
|
|
523
|
+
for (int i = n - 1; i >= 0; --i) {
|
|
524
|
+
for (int k = i - 1; k >= 0; --k) {
|
|
525
|
+
double factor = augmented[k * (2 * n) + i];
|
|
526
|
+
for (int j = 0; j < 2 * n; ++j) {
|
|
527
|
+
augmented[k * (2 * n) + j] -= augmented[i * (2 * n) + j] * factor;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// Extract the inverse matrix from the augmented matrix
|
|
533
|
+
for (int i = 0; i < n; ++i) {
|
|
534
|
+
for (int j = 0; j < n; ++j) {
|
|
535
|
+
inverse[i * n + j] = augmented[i * (2 * n) + (j + n)];
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
return true;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
void populate_hessian_lower_tri(std::vector<double> &H, double lambda, int num_params){
|
|
543
|
+
for (int row = 0; row < num_params; row++) {
|
|
544
|
+
for (int col = row + 1; col < num_params; col++) {
|
|
545
|
+
H[col * num_params + row] = H[row * num_params + col];
|
|
546
|
+
}
|
|
547
|
+
H[row * num_params + row] += lambda * H[row * num_params + row]; // diagonal
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
void update_lambda(double costp, double costpdp, std::vector<double> &p, std::vector<double> &pdp, double &lambda, int num_params){
|
|
552
|
+
|
|
553
|
+
if (costp < costpdp){
|
|
554
|
+
lambda *= 10.0;
|
|
555
|
+
}
|
|
556
|
+
else{
|
|
557
|
+
lambda *= 0.1;
|
|
558
|
+
for (int i = 0; i < num_params; i++){
|
|
559
|
+
p[i] = pdp[i];
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
void update_shapefunc_parameters(std::vector<double> &pdp, std::vector<double> &p, std::vector<double> &dp, std::vector<double> &invH, std::vector<double> &g, int num_params){
|
|
565
|
+
|
|
566
|
+
// multiply inverse with gradient
|
|
567
|
+
for (int i = 0; i < num_params; ++i) {
|
|
568
|
+
dp[i] = 0.0;
|
|
569
|
+
for (int j = 0; j < num_params; ++j) {
|
|
570
|
+
dp[i] += 1.0 * invH[i*num_params + j] * g[j];
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// add p to delta p
|
|
575
|
+
for (int i = 0; i < num_params; ++i) {
|
|
576
|
+
pdp[i] = p[i] - dp[i];
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
|
|
581
|
+
|
|
582
|
+
inline void affine(double &x_new, double &y_new, double x, double y, std::vector<double> &p){
|
|
583
|
+
x_new = p[0] + (1.0+p[2]) * x + p[3] * y;
|
|
584
|
+
y_new = p[1] + (1.0+p[5]) * y + p[4] * x;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
inline void rigid(double &x_new, double &y_new, double x, double y, std::vector<double> &p){
|
|
588
|
+
x_new = p[0] + x;
|
|
589
|
+
y_new = p[1] + y;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
inline void quad(double &x_new, double &y_new, double x, double y, std::vector<double> &p){
|
|
593
|
+
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
inline void daffine_dp(std::vector<double> &dfdp, double x, double y, double dfdx, double dfdy){
|
|
597
|
+
|
|
598
|
+
dfdp[0] = dfdx;
|
|
599
|
+
dfdp[1] = dfdy;
|
|
600
|
+
dfdp[2] = dfdx * x;
|
|
601
|
+
dfdp[3] = dfdx * y;
|
|
602
|
+
dfdp[4] = dfdy * x;
|
|
603
|
+
dfdp[5] = dfdy * y;
|
|
604
|
+
|
|
605
|
+
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
inline void drigid_dp(std::vector<double> &dfdp, double x, double y, double dfdx, double dfdy){
|
|
609
|
+
|
|
610
|
+
dfdp[0] = dfdx;
|
|
611
|
+
dfdp[1] = dfdy;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
inline void dquad_dp(double &x_new, double &y_new, double x, double y, std::vector<double> &p){
|
|
615
|
+
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
inline void affine_parameters_to_displacement(util::Results &res, double ss_x, double ss_y, std::vector<double> &p){
|
|
619
|
+
double x_new = p[0] + (1.0+p[2]) * ss_x + p[3] * ss_y;
|
|
620
|
+
double y_new = p[1] + (1.0+p[5]) * ss_y + p[4] * ss_x;
|
|
621
|
+
res.u = x_new - ss_x;
|
|
622
|
+
res.v = y_new - ss_y;
|
|
623
|
+
res.mag = std::sqrt(res.u * res.u + res.v * res.v);
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
inline void rigid_parameters_to_displacement(util::Results &res, double ss_x, double ss_y, std::vector<double> &p){
|
|
627
|
+
res.u = -p[0];
|
|
628
|
+
res.v = -p[1];
|
|
629
|
+
res.mag = std::sqrt(res.u*res.u + res.v*res.v);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
|
|
633
|
+
|
|
634
|
+
void setCostFunction(const std::string& corr_crit) {
|
|
635
|
+
if (corr_crit == "SSD") optimize_cost = ssd;
|
|
636
|
+
else if (corr_crit == "NSSD") optimize_cost = nssd;
|
|
637
|
+
else if (corr_crit == "ZNSSD") optimize_cost = znssd;
|
|
638
|
+
else {
|
|
639
|
+
std::cerr << "Unexpected Correlation Criteria: '" << corr_crit << "'" << std::endl;
|
|
640
|
+
std::cerr << "Allowed Values: 'SSD', 'NSSD', 'ZNSSD'." << std::endl;
|
|
641
|
+
exit(EXIT_FAILURE);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
void setShapeFunction(const std::string& shape_func) {
|
|
646
|
+
if (shape_func == "RIGID") {
|
|
647
|
+
shape_function = rigid;
|
|
648
|
+
dshape_dp = drigid_dp;
|
|
649
|
+
params_to_displacement = rigid_parameters_to_displacement;
|
|
650
|
+
} else if (shape_func == "AFFINE") {
|
|
651
|
+
shape_function = affine;
|
|
652
|
+
dshape_dp = daffine_dp;
|
|
653
|
+
params_to_displacement = affine_parameters_to_displacement;
|
|
654
|
+
} else {
|
|
655
|
+
std::cerr << "Unexpected Shape Function: '" << shape_func << "'" << std::endl;
|
|
656
|
+
std::cerr << "Allowed Values: 'RIGID', 'AFFINE'." << std::endl;
|
|
657
|
+
exit(EXIT_FAILURE);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
void debugPrint(int ss_x, int ss_y, int iter, double costp, double ftol, double xtol, const std::vector<double>& p) {
|
|
662
|
+
#pragma omp critical
|
|
663
|
+
{
|
|
664
|
+
std::cout << omp_get_thread_num() << " ";
|
|
665
|
+
std::cout << ss_x << " " << ss_y << " ";
|
|
666
|
+
std::cout << iter << " " << costp << " " << ftol << " " << xtol << " ";
|
|
667
|
+
for (size_t i = 0; i < p.size(); ++i) {
|
|
668
|
+
std::cout << p[i] << " ";
|
|
669
|
+
}
|
|
670
|
+
std::cout << std::endl;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
|
|
675
|
+
}
|