pyvale 2025.5.3__cp311-cp311-musllinux_1_2_aarch64.whl → 2025.7.0__cp311-cp311-musllinux_1_2_aarch64.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.

Files changed (96) hide show
  1. pyvale/__init__.py +12 -0
  2. pyvale/blendercalibrationdata.py +3 -1
  3. pyvale/blenderscene.py +7 -5
  4. pyvale/blendertools.py +27 -5
  5. pyvale/camera.py +1 -0
  6. pyvale/cameradata.py +3 -0
  7. pyvale/camerasensor.py +147 -0
  8. pyvale/camerastereo.py +4 -4
  9. pyvale/cameratools.py +23 -61
  10. pyvale/cython/rastercyth.c +1657 -1352
  11. pyvale/cython/rastercyth.cpython-311-aarch64-linux-musl.so +0 -0
  12. pyvale/cython/rastercyth.py +71 -26
  13. pyvale/data/plate_hole_def0000.tiff +0 -0
  14. pyvale/data/plate_hole_def0001.tiff +0 -0
  15. pyvale/data/plate_hole_ref0000.tiff +0 -0
  16. pyvale/data/plate_rigid_def0000.tiff +0 -0
  17. pyvale/data/plate_rigid_def0001.tiff +0 -0
  18. pyvale/data/plate_rigid_ref0000.tiff +0 -0
  19. pyvale/dataset.py +96 -6
  20. pyvale/dic/cpp/dicbruteforce.cpp +370 -0
  21. pyvale/dic/cpp/dicfourier.cpp +648 -0
  22. pyvale/dic/cpp/dicinterpolator.cpp +559 -0
  23. pyvale/dic/cpp/dicmain.cpp +215 -0
  24. pyvale/dic/cpp/dicoptimizer.cpp +675 -0
  25. pyvale/dic/cpp/dicrg.cpp +137 -0
  26. pyvale/dic/cpp/dicscanmethod.cpp +677 -0
  27. pyvale/dic/cpp/dicsmooth.cpp +138 -0
  28. pyvale/dic/cpp/dicstrain.cpp +383 -0
  29. pyvale/dic/cpp/dicutil.cpp +563 -0
  30. pyvale/dic2d.py +164 -0
  31. pyvale/dic2dcpp.cpython-311-aarch64-linux-musl.so +0 -0
  32. pyvale/dicchecks.py +476 -0
  33. pyvale/dicdataimport.py +247 -0
  34. pyvale/dicregionofinterest.py +887 -0
  35. pyvale/dicresults.py +55 -0
  36. pyvale/dicspecklegenerator.py +238 -0
  37. pyvale/dicspecklequality.py +305 -0
  38. pyvale/dicstrain.py +387 -0
  39. pyvale/dicstrainresults.py +37 -0
  40. pyvale/errorintegrator.py +10 -8
  41. pyvale/examples/basics/ex1_1_basicscalars_therm2d.py +124 -113
  42. pyvale/examples/basics/ex1_2_sensormodel_therm2d.py +124 -132
  43. pyvale/examples/basics/ex1_3_customsens_therm3d.py +199 -195
  44. pyvale/examples/basics/ex1_4_basicerrors_therm3d.py +125 -121
  45. pyvale/examples/basics/ex1_5_fielderrs_therm3d.py +145 -141
  46. pyvale/examples/basics/ex1_6_caliberrs_therm2d.py +96 -101
  47. pyvale/examples/basics/ex1_7_spatavg_therm2d.py +109 -105
  48. pyvale/examples/basics/ex2_1_basicvectors_disp2d.py +92 -91
  49. pyvale/examples/basics/ex2_2_vectorsens_disp2d.py +96 -90
  50. pyvale/examples/basics/ex2_3_sensangle_disp2d.py +88 -89
  51. pyvale/examples/basics/ex2_4_chainfielderrs_disp2d.py +172 -171
  52. pyvale/examples/basics/ex2_5_vectorfields3d_disp3d.py +88 -86
  53. pyvale/examples/basics/ex3_1_basictensors_strain2d.py +90 -90
  54. pyvale/examples/basics/ex3_2_tensorsens2d_strain2d.py +93 -91
  55. pyvale/examples/basics/ex3_3_tensorsens3d_strain3d.py +172 -160
  56. pyvale/examples/basics/ex4_1_expsim2d_thermmech2d.py +154 -148
  57. pyvale/examples/basics/ex4_2_expsim3d_thermmech3d.py +249 -231
  58. pyvale/examples/dic/ex1_region_of_interest.py +98 -0
  59. pyvale/examples/dic/ex2_plate_with_hole.py +149 -0
  60. pyvale/examples/dic/ex3_plate_with_hole_strain.py +93 -0
  61. pyvale/examples/dic/ex4_dic_blender.py +95 -0
  62. pyvale/examples/dic/ex5_dic_challenge.py +102 -0
  63. pyvale/examples/imagedef2d/ex_imagedef2d_todisk.py +4 -2
  64. pyvale/examples/renderblender/ex1_1_blenderscene.py +152 -105
  65. pyvale/examples/renderblender/ex1_2_blenderdeformed.py +151 -100
  66. pyvale/examples/renderblender/ex2_1_stereoscene.py +183 -116
  67. pyvale/examples/renderblender/ex2_2_stereodeformed.py +185 -112
  68. pyvale/examples/renderblender/ex3_1_blendercalibration.py +164 -109
  69. pyvale/examples/renderrasterisation/ex_rastenp.py +74 -35
  70. pyvale/examples/renderrasterisation/ex_rastercyth_oneframe.py +6 -13
  71. pyvale/examples/renderrasterisation/ex_rastercyth_static_cypara.py +2 -2
  72. pyvale/examples/renderrasterisation/ex_rastercyth_static_pypara.py +2 -4
  73. pyvale/imagedef2d.py +3 -2
  74. pyvale/imagetools.py +137 -0
  75. pyvale/rastercy.py +34 -4
  76. pyvale/rasternp.py +300 -276
  77. pyvale/rasteropts.py +58 -0
  78. pyvale/renderer.py +47 -0
  79. pyvale/rendermesh.py +52 -62
  80. pyvale/renderscene.py +51 -0
  81. pyvale/sensorarrayfactory.py +2 -2
  82. pyvale/sensortools.py +19 -35
  83. pyvale/simcases/case21.i +1 -1
  84. pyvale/simcases/run_1case.py +8 -0
  85. pyvale/simtools.py +2 -2
  86. pyvale/visualsimplotter.py +180 -0
  87. {pyvale-2025.5.3.dist-info → pyvale-2025.7.0.dist-info}/METADATA +11 -57
  88. {pyvale-2025.5.3.dist-info → pyvale-2025.7.0.dist-info}/RECORD +94 -56
  89. {pyvale-2025.5.3.dist-info → pyvale-2025.7.0.dist-info}/WHEEL +1 -1
  90. pyvale.libs/libgcc_s-69c45f16.so.1 +0 -0
  91. pyvale.libs/libgomp-b626072d.so.1.0.0 +0 -0
  92. pyvale.libs/libstdc++-1f1a71be.so.6.0.33 +0 -0
  93. pyvale/examples/visualisation/ex1_1_plot_traces.py +0 -102
  94. pyvale/examples/visualisation/ex2_1_animate_sim.py +0 -89
  95. {pyvale-2025.5.3.dist-info → pyvale-2025.7.0.dist-info}/licenses/LICENSE +0 -0
  96. {pyvale-2025.5.3.dist-info → pyvale-2025.7.0.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
+ }