pyvale 2025.5.3__cp311-cp311-win_amd64.whl → 2025.7.1__cp311-cp311-win_amd64.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 (95) 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.cp311-win_amd64.pyd +0 -0
  12. pyvale/cython/rastercyth.py +71 -26
  13. pyvale/data/DIC_Challenge_Star_Noise_Def.tiff +0 -0
  14. pyvale/data/DIC_Challenge_Star_Noise_Ref.tiff +0 -0
  15. pyvale/data/plate_hole_def0000.tiff +0 -0
  16. pyvale/data/plate_hole_def0001.tiff +0 -0
  17. pyvale/data/plate_hole_ref0000.tiff +0 -0
  18. pyvale/data/plate_rigid_def0000.tiff +0 -0
  19. pyvale/data/plate_rigid_def0001.tiff +0 -0
  20. pyvale/data/plate_rigid_ref0000.tiff +0 -0
  21. pyvale/dataset.py +96 -6
  22. pyvale/dic/cpp/dicbruteforce.cpp +370 -0
  23. pyvale/dic/cpp/dicfourier.cpp +648 -0
  24. pyvale/dic/cpp/dicinterpolator.cpp +559 -0
  25. pyvale/dic/cpp/dicmain.cpp +215 -0
  26. pyvale/dic/cpp/dicoptimizer.cpp +675 -0
  27. pyvale/dic/cpp/dicrg.cpp +137 -0
  28. pyvale/dic/cpp/dicscanmethod.cpp +677 -0
  29. pyvale/dic/cpp/dicsmooth.cpp +138 -0
  30. pyvale/dic/cpp/dicstrain.cpp +383 -0
  31. pyvale/dic/cpp/dicutil.cpp +563 -0
  32. pyvale/dic2d.py +164 -0
  33. pyvale/dic2dcpp.cp311-win_amd64.pyd +0 -0
  34. pyvale/dicchecks.py +476 -0
  35. pyvale/dicdataimport.py +247 -0
  36. pyvale/dicregionofinterest.py +887 -0
  37. pyvale/dicresults.py +55 -0
  38. pyvale/dicspecklegenerator.py +238 -0
  39. pyvale/dicspecklequality.py +305 -0
  40. pyvale/dicstrain.py +387 -0
  41. pyvale/dicstrainresults.py +37 -0
  42. pyvale/errorintegrator.py +10 -8
  43. pyvale/examples/basics/ex1_1_basicscalars_therm2d.py +124 -113
  44. pyvale/examples/basics/ex1_2_sensormodel_therm2d.py +124 -132
  45. pyvale/examples/basics/ex1_3_customsens_therm3d.py +199 -195
  46. pyvale/examples/basics/ex1_4_basicerrors_therm3d.py +125 -121
  47. pyvale/examples/basics/ex1_5_fielderrs_therm3d.py +145 -141
  48. pyvale/examples/basics/ex1_6_caliberrs_therm2d.py +96 -101
  49. pyvale/examples/basics/ex1_7_spatavg_therm2d.py +109 -105
  50. pyvale/examples/basics/ex2_1_basicvectors_disp2d.py +92 -91
  51. pyvale/examples/basics/ex2_2_vectorsens_disp2d.py +96 -90
  52. pyvale/examples/basics/ex2_3_sensangle_disp2d.py +88 -89
  53. pyvale/examples/basics/ex2_4_chainfielderrs_disp2d.py +172 -171
  54. pyvale/examples/basics/ex2_5_vectorfields3d_disp3d.py +88 -86
  55. pyvale/examples/basics/ex3_1_basictensors_strain2d.py +90 -90
  56. pyvale/examples/basics/ex3_2_tensorsens2d_strain2d.py +93 -91
  57. pyvale/examples/basics/ex3_3_tensorsens3d_strain3d.py +172 -160
  58. pyvale/examples/basics/ex4_1_expsim2d_thermmech2d.py +154 -148
  59. pyvale/examples/basics/ex4_2_expsim3d_thermmech3d.py +249 -231
  60. pyvale/examples/dic/ex1_region_of_interest.py +98 -0
  61. pyvale/examples/dic/ex2_plate_with_hole.py +149 -0
  62. pyvale/examples/dic/ex3_plate_with_hole_strain.py +93 -0
  63. pyvale/examples/dic/ex4_dic_blender.py +95 -0
  64. pyvale/examples/dic/ex5_dic_challenge.py +102 -0
  65. pyvale/examples/imagedef2d/ex_imagedef2d_todisk.py +4 -2
  66. pyvale/examples/renderblender/ex1_1_blenderscene.py +152 -105
  67. pyvale/examples/renderblender/ex1_2_blenderdeformed.py +151 -100
  68. pyvale/examples/renderblender/ex2_1_stereoscene.py +183 -116
  69. pyvale/examples/renderblender/ex2_2_stereodeformed.py +185 -112
  70. pyvale/examples/renderblender/ex3_1_blendercalibration.py +164 -109
  71. pyvale/examples/renderrasterisation/ex_rastenp.py +74 -35
  72. pyvale/examples/renderrasterisation/ex_rastercyth_oneframe.py +6 -13
  73. pyvale/examples/renderrasterisation/ex_rastercyth_static_cypara.py +2 -2
  74. pyvale/examples/renderrasterisation/ex_rastercyth_static_pypara.py +2 -4
  75. pyvale/imagedef2d.py +3 -2
  76. pyvale/imagetools.py +137 -0
  77. pyvale/rastercy.py +34 -4
  78. pyvale/rasternp.py +300 -276
  79. pyvale/rasteropts.py +58 -0
  80. pyvale/renderer.py +47 -0
  81. pyvale/rendermesh.py +52 -62
  82. pyvale/renderscene.py +51 -0
  83. pyvale/sensorarrayfactory.py +2 -2
  84. pyvale/sensortools.py +19 -35
  85. pyvale/simcases/case21.i +1 -1
  86. pyvale/simcases/run_1case.py +8 -0
  87. pyvale/simtools.py +2 -2
  88. pyvale/visualsimplotter.py +180 -0
  89. {pyvale-2025.5.3.dist-info → pyvale-2025.7.1.dist-info}/METADATA +11 -57
  90. {pyvale-2025.5.3.dist-info → pyvale-2025.7.1.dist-info}/RECORD +93 -56
  91. {pyvale-2025.5.3.dist-info → pyvale-2025.7.1.dist-info}/WHEEL +1 -1
  92. pyvale/examples/visualisation/ex1_1_plot_traces.py +0 -102
  93. pyvale/examples/visualisation/ex2_1_animate_sim.py +0 -89
  94. {pyvale-2025.5.3.dist-info → pyvale-2025.7.1.dist-info}/licenses/LICENSE +0 -0
  95. {pyvale-2025.5.3.dist-info → pyvale-2025.7.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,648 @@
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 <iostream>
10
+ #include <string>
11
+ #include <vector>
12
+ #include <cmath>
13
+ #include <algorithm>
14
+ #include <omp.h>
15
+
16
+ // Program Header files
17
+ #include "./defines.hpp"
18
+ #include "./dicutil.hpp"
19
+ #include "./dicfourier.hpp"
20
+
21
+ namespace fourier {
22
+
23
+ std::vector<Shift> shifts;
24
+
25
+ void init(std::vector<util::SubsetData> &window_data,
26
+ std::vector<int> &ss_sizes,
27
+ std::vector<int> &ss_steps,
28
+ const bool *img_roi, const util::Config &conf){
29
+
30
+ // timer for the initialisation
31
+ //util::Timer timer("entire FFT initislisation");
32
+
33
+ // loop over the window sizes
34
+ for (size_t i = 0; i < ss_sizes.size(); i++) {
35
+
36
+ const int window_size = ss_sizes[i];
37
+ const int window_step = ss_steps[i];
38
+
39
+ // generate subset information for each window size.
40
+ // for the last size all subsets need to sit within the ROI
41
+ if (i==ss_sizes.size()-1)
42
+ window_data.push_back(util::gen_ss_list(img_roi, window_step,
43
+ window_size, conf.px_hori,
44
+ conf.px_vert, false));
45
+ else
46
+ window_data.push_back(util::gen_ss_list(img_roi, window_step,
47
+ window_size, conf.px_hori,
48
+ conf.px_vert, true));
49
+
50
+
51
+ // shifts for each subset size
52
+ Shift shift;
53
+ shift.num_neigh = 4;
54
+
55
+ // resize vectors
56
+ shift.x.resize(window_data[i].num);
57
+ shift.y.resize(window_data[i].num);
58
+ shift.cost.resize(window_data[i].num);
59
+ shift.max_val.resize(window_data[i].num);
60
+
61
+ // we need the neighbours in the previous window size for all sizes
62
+ // except the first
63
+ if (i > 0){
64
+ shift.neighlist.resize(shift.num_neigh*window_data[i].num);
65
+ shift.gen_neighlist(window_data[i], window_data[i-1]);
66
+ }
67
+
68
+ // add the shifts for the current window to the vector
69
+ shifts.push_back(shift);
70
+
71
+ }
72
+ }
73
+
74
+ void remove_outliers(std::vector<double>& shift,
75
+ const util::SubsetData &ssdata,
76
+ const double mad_scale) {
77
+
78
+ std::vector<double> updated = shift;
79
+
80
+ int radius = 2;
81
+
82
+ for (int ss = 0; ss < ssdata.num; ss++) {
83
+
84
+ // subset coords
85
+ int ss_x = ssdata.coords[2*ss];
86
+ int ss_y = ssdata.coords[2*ss+1];
87
+
88
+ // subset x and y index in 2d mask
89
+ int idx_x = ss_x / ssdata.step;
90
+ int idx_y = ss_y / ssdata.step;
91
+
92
+ std::vector<double> neigh_vals;
93
+
94
+ int min_x = std::max(0, idx_x-radius);
95
+ int min_y = std::max(0, idx_y-radius);
96
+ int max_y = std::min(ssdata.num_ss_y, idx_y+radius+1);
97
+ int max_x = std::min(ssdata.num_ss_x, idx_x+radius+1);
98
+
99
+ for (int y = min_y; y < max_y; ++y) {
100
+ for (int x = min_x; x < max_x; ++x) {
101
+
102
+ // index of neighbour
103
+ int nss_idx = ssdata.mask[y*ssdata.num_ss_x+x];
104
+
105
+ // check if invalid neigh
106
+ if (nss_idx == -1 || nss_idx == ss) continue;
107
+
108
+ neigh_vals.push_back(shift[nss_idx]);
109
+ }
110
+ }
111
+
112
+ if (neigh_vals.size() < 4) continue;
113
+
114
+ // Median
115
+ std::sort(neigh_vals.begin(), neigh_vals.end());
116
+ size_t sz = neigh_vals.size();
117
+ double median = (sz % 2 == 0) ? 0.5 * (neigh_vals[sz/2 - 1] + neigh_vals[sz/2]) : neigh_vals[sz/2];
118
+
119
+ // MAD
120
+ std::vector<double> abs_devs;
121
+ abs_devs.reserve(sz);
122
+ for (double v : neigh_vals) abs_devs.push_back(std::abs(v - median));
123
+
124
+ std::sort(abs_devs.begin(), abs_devs.end());
125
+ double mad = (sz % 2 == 0) ? 0.5 * (abs_devs[sz/2 - 1] + abs_devs[sz/2]) : abs_devs[sz/2];
126
+
127
+ if (mad < 1e-12) continue;
128
+
129
+ if (std::abs(shift[ss] - median) > mad_scale * mad) {
130
+ updated[ss] = median;
131
+ }
132
+ }
133
+ shift = std::move(updated);
134
+ }
135
+
136
+ void mgwd(const std::vector<util::SubsetData> &ssdata,
137
+ const double *img_ref, const double *img_def,
138
+ const Interpolator &interp_def, const bool fft_mad,
139
+ const double fft_mad_scale){
140
+
141
+ const int px_hori = interp_def.px_hori;
142
+ const int px_vert = interp_def.px_vert;
143
+
144
+ // TODO: Add a proper flag for this
145
+ bool subpx = true;
146
+
147
+ // Loop over window size
148
+ for (size_t i = 0; i < ssdata.size(); i++){
149
+
150
+ //util::Timer timer("FFT windowing for subset size: " + std::to_string(ssdata[i].size));
151
+
152
+ const int ss_size = ssdata[i].size;
153
+
154
+ std::fill(shifts[i].x.begin(), shifts[i].x.end(), 0.0);
155
+ std::fill(shifts[i].y.begin(), shifts[i].y.end(), 0.0);
156
+
157
+ #pragma omp parallel
158
+ {
159
+
160
+
161
+ // class for FFT
162
+ fourier::FFT fft(ss_size);
163
+
164
+ // loop over subsets for each size/step
165
+ #pragma omp for
166
+ for (int ss = 0; ss < ssdata[i].num; ss++){
167
+
168
+ int ss_x = ssdata[i].coords[2*ss];
169
+ int ss_y = ssdata[i].coords[2*ss+1];
170
+
171
+ // get the seed for the new window size
172
+ auto [prev_x, prev_y] = get_prev_shift(i, ss, ss_x, ss_y, shifts, ssdata);
173
+ double ss_x_shft = ss_x+prev_x;
174
+ double ss_y_shft = ss_y+prev_y;
175
+
176
+ // populate fft.ss_ref with reference subset values
177
+ util::extract_ss(fft.ss_ref,ss_x, ss_y, px_hori, px_vert, img_ref);
178
+
179
+ // populate fft.ss_def with interpolator value
180
+ util::extract_ss_subpx(fft.ss_def, ss_x_shft, ss_y_shft, interp_def);
181
+
182
+ // zero normalise the subsets
183
+ zero_norm_subsets(fft.ss_ref.vals, fft.ss_def.vals, ss_size);
184
+
185
+ // get peaks from the cross correlation
186
+ double peak_x = 0, peak_y = 0, max_val = 0.0;
187
+ fft.correlate();
188
+ fft.find_peak(peak_x, peak_y, max_val, subpx, "GAUSSIAN_2D");
189
+
190
+
191
+ // debugging print cross correlation
192
+ //if (ss == 0){
193
+ // for (int y = 0; y < fft.ss_ref.size; y++){
194
+ // for (int x = 0; x < fft.ss_ref.size; x++){
195
+ // std::cout << x << " " << y << " " << fft.ss_ref.vals[y*fft.ss_ref.size + x] << " " << fft.ss_def.vals[y*fft.ss_def.size + x] << " " << fft.cross_corr[y*fft.ss_ref.size+x] << std::endl;
196
+ // }
197
+ // }
198
+ // std::cout << std::endl;
199
+ //}
200
+ //exit(0);
201
+
202
+ // update the shift arrays
203
+ if (i == 0){
204
+ shifts[i].x[ss] = peak_x;
205
+ shifts[i].y[ss] = peak_y;
206
+ }
207
+ else {
208
+ shifts[i].x[ss] = prev_x+peak_x;
209
+ shifts[i].y[ss] = prev_y+peak_y;
210
+ }
211
+
212
+ // this isn't essential. storing peak amplitude and cost value for shifts
213
+ //util::extract_ss_subpx(fft.ss_def, ss_x+shifts[i].x[ss], ss_y+shifts[i].y[ss], interp_def);
214
+ //shifts[i].cost[ss] = debugcost(fft.ss_ref,fft.ss_def);
215
+ shifts[i].max_val[ss] = max_val;
216
+ }
217
+ }
218
+
219
+ // remove outliers in fft
220
+ if (fft_mad){
221
+ remove_outliers(shifts[i].x, ssdata[i], fft_mad);
222
+ remove_outliers(shifts[i].y, ssdata[i], fft_mad);
223
+ }
224
+
225
+
226
+ //smooth_field(shifts[i].x, ssdata[i], 7.0, 5);
227
+ //smooth_field(shifts[i].y, ssdata[i], 7.0, 5);
228
+
229
+ //for (int ss = 0; ss < ssdata[i].num; ss++){
230
+ // std::cout << ssdata[i].coords[2*ss] << " " << ssdata[i].coords[2*ss+1] << " ";
231
+ // std::cout << shifts[i].x[ss] << " " << shifts[i].y[ss] << " ";
232
+ // std::cout << shifts[i].max_val[ss] << " ";
233
+ // std::cout << shifts[i].cost[ss] << std::endl;
234
+ //}
235
+ //std::cout << std::endl;
236
+ }
237
+
238
+
239
+ }
240
+
241
+
242
+ std::pair<double,double> get_prev_shift(const int i, const int ss,
243
+ const double ss_x, const double ss_y,
244
+ const std::vector<Shift>& shifts,
245
+ const std::vector<util::SubsetData>& ssdata) {
246
+ const double epsilon = 1.0e-8;
247
+ double weight_sum_x = 0.0;
248
+ double weight_sum_y = 0.0;
249
+ double weight_tot = 0.0;
250
+ double prev_x = 0;
251
+ double prev_y = 0;
252
+ double sum_x = 0;
253
+ double sum_y = 0;
254
+
255
+ // assign values for all subset sizes EXCEPT first
256
+ if (i > 0){
257
+
258
+ // weighted average of 4 nearest neighbours
259
+ for (size_t j = 0; j < shifts[i].num_neigh; ++j) {
260
+
261
+ int nidx = shifts[i].neighlist[ss*shifts[i].num_neigh+j];
262
+ int neigh_x = ssdata[i-1].coords[2*nidx];
263
+ int neigh_y = ssdata[i-1].coords[2*nidx+1];
264
+
265
+ double dx = ss_x - neigh_x;
266
+ double dy = ss_y - neigh_y;
267
+ double dist_sq = dx * dx + dy * dy;
268
+
269
+ double weight = 1.0 / (dist_sq + epsilon);
270
+
271
+ //std::cout << nidx << " " << neigh_x << " " << neigh_y << " " << dx << " " << dy << " " << dist_sq << " " << weight << std::endl;
272
+ sum_x += shifts[i-1].x[nidx];
273
+ sum_y += shifts[i-1].y[nidx];
274
+ //weight_sum_x += shifts[i-1].x[nidx] * weight;
275
+ //weight_sum_y += shifts[i-1].y[nidx] * weight;
276
+ //weight_tot += weight;
277
+ }
278
+
279
+ //std::cout << std::endl;
280
+ prev_x = sum_x / shifts[i].num_neigh;
281
+ prev_y = sum_y / shifts[i].num_neigh;
282
+ //prev_x = weight_sum_x / weight_tot;
283
+ //prev_y = weight_sum_y / weight_tot;
284
+ }
285
+ return {prev_x, prev_y};
286
+ }
287
+
288
+
289
+ void smooth_field(std::vector<double>& shift,
290
+ const util::SubsetData& ssdata,
291
+ double sigma = 1.0,
292
+ int radius = 2) {
293
+
294
+ std::vector<double> smoothed = shift;
295
+
296
+ const int width = ssdata.num_ss_x;
297
+ const int height = ssdata.num_ss_y;
298
+
299
+ // Precompute Gaussian weights
300
+ std::vector<std::vector<double>> weights(2 * radius + 1, std::vector<double>(2 * radius + 1));
301
+ for (int dy = -radius; dy <= radius; ++dy) {
302
+ for (int dx = -radius; dx <= radius; ++dx) {
303
+ double dist2 = dx * dx + dy * dy;
304
+ weights[dy + radius][dx + radius] = std::exp(-dist2 / (2.0 * sigma * sigma));
305
+ }
306
+ }
307
+
308
+ for (int y = 0; y < height; ++y) {
309
+ for (int x = 0; x < width; ++x) {
310
+
311
+ int center_idx = ssdata.mask[y * width + x];
312
+ if (center_idx == -1) continue;
313
+
314
+ double sum = 0.0;
315
+ double weight_sum = 0.0;
316
+
317
+ for (int dy = -radius; dy <= radius; ++dy) {
318
+ int ny = y + dy;
319
+ if (ny < 0 || ny >= height) continue;
320
+
321
+ for (int dx = -radius; dx <= radius; ++dx) {
322
+ int nx = x + dx;
323
+ if (nx < 0 || nx >= width) continue;
324
+
325
+ int n_idx = ssdata.mask[ny * width + nx];
326
+ if (n_idx == -1) continue;
327
+
328
+ double val = shift[n_idx];
329
+ double weight = weights[dy + radius][dx + radius];
330
+
331
+ sum += val * weight;
332
+ weight_sum += weight;
333
+ }
334
+ }
335
+
336
+ if (weight_sum > 0.0) {
337
+ smoothed[center_idx] = sum / weight_sum;
338
+ }
339
+ }
340
+ }
341
+
342
+ shift = std::move(smoothed);
343
+ }
344
+
345
+
346
+ double debugcost(const util::Subset &ss_ref, const util::Subset &ss_def){
347
+ const int num_px = ss_def.num_px;
348
+ double cost = 0.0;
349
+ double mean_ref = 0.0;
350
+ double mean_def = 0.0;
351
+
352
+ // loop over pixel values in reference image
353
+ for (int i = 0; i < num_px; i++){
354
+ mean_ref += ss_ref.vals[i];
355
+ mean_def += ss_def.vals[i];
356
+ }
357
+
358
+ mean_ref /= num_px;
359
+ mean_def /= num_px;
360
+
361
+ // get cost function denominators
362
+ double sum_squared_ref = 0.0;
363
+ double sum_squared_def = 0.0;
364
+ for (int i = 0; i < num_px; ++i) {
365
+ sum_squared_ref += (ss_ref.vals[i] - mean_ref)*
366
+ (ss_ref.vals[i] - mean_ref);
367
+ sum_squared_def += (ss_def.vals[i] - mean_def)*
368
+ (ss_def.vals[i] - mean_def);
369
+ }
370
+ double inv_sum_squared_ref = 1.0 / std::sqrt(sum_squared_ref);
371
+ double inv_sum_squared_def = 1.0 / std::sqrt(sum_squared_def);
372
+
373
+
374
+ // calcualte cost
375
+ for (int i = 0; i < num_px; i++){
376
+ double def_norm = ss_def.vals[i] * inv_sum_squared_def;
377
+ double ref_norm = ss_ref.vals[i] * inv_sum_squared_ref;
378
+ cost += (ref_norm - def_norm) * (ref_norm - def_norm);
379
+ }
380
+ return cost;
381
+ }
382
+
383
+
384
+
385
+
386
+
387
+ void zero_norm_subsets(std::vector<double>& ref_vals, std::vector<double>& def_vals, const int ss_size) {
388
+ const int total_px = ss_size * ss_size;
389
+
390
+ // Compute means
391
+ double mean_def = 0.0;
392
+ double mean_ref = 0.0;
393
+ for (int i = 0; i < total_px; ++i) {
394
+ mean_def += def_vals[i];
395
+ mean_ref += ref_vals[i];
396
+ }
397
+ mean_def /= total_px;
398
+ mean_ref /= total_px;
399
+
400
+ // Compute standard deviations
401
+ double std_def = 0.0;
402
+ double std_ref = 0.0;
403
+ for (int i = 0; i < total_px; ++i) {
404
+ std_def += std::pow(def_vals[i] - mean_def, 2);
405
+ std_ref += std::pow(ref_vals[i] - mean_ref, 2);
406
+ }
407
+ std_def = std::sqrt(std_def / total_px);
408
+ std_ref = std::sqrt(std_ref / total_px);
409
+
410
+ // Normalize
411
+ for (int i = 0; i < total_px; ++i) {
412
+ def_vals[i] = (def_vals[i] - mean_def) / std_def;
413
+ ref_vals[i] = (ref_vals[i] - mean_ref) / std_ref;
414
+ }
415
+ }
416
+
417
+
418
+
419
+ void zero_norm_subsets_single(std::vector<double>& ref_vals, int ss_size) {
420
+ const int total_px = ss_size * ss_size;
421
+
422
+ // Compute means
423
+ double mean_def = 0.0;
424
+ double mean_ref = 0.0;
425
+ for (int i = 0; i < total_px; ++i) {
426
+ mean_ref += ref_vals[i];
427
+ }
428
+ mean_def /= total_px;
429
+ mean_ref /= total_px;
430
+
431
+ // Compute standard deviations
432
+ double std_def = 0.0;
433
+ double std_ref = 0.0;
434
+ for (int i = 0; i < total_px; ++i) {
435
+ std_ref += std::pow(ref_vals[i] - mean_ref, 2);
436
+ }
437
+ std_def = std::sqrt(std_def / total_px);
438
+ std_ref = std::sqrt(std_ref / total_px);
439
+
440
+ // Normalize
441
+ for (int i = 0; i < total_px; ++i) {
442
+ ref_vals[i] = (ref_vals[i] - mean_ref) / std_ref;
443
+ }
444
+ }
445
+
446
+
447
+
448
+ void sgwd(const util::SubsetData &ssdata, const int window_size, const double *img_ref, const double *img_def, const Interpolator &interp_def){
449
+
450
+ const int px_hori = interp_def.px_hori;
451
+ const int px_vert = interp_def.px_vert;
452
+
453
+ const int ss_size = ssdata.size;
454
+
455
+ // TODO: Add a proper flag for this
456
+ bool subpx = true;
457
+
458
+ // shifts for each subset size
459
+ Shift shift;
460
+ shift.num_neigh = 4;
461
+
462
+ // resize vectors
463
+ shift.x.resize(ssdata.num);
464
+ shift.y.resize(ssdata.num);
465
+ shift.cost.resize(ssdata.num);
466
+ shift.max_val.resize(ssdata.num);
467
+
468
+ shifts.push_back(shift);
469
+
470
+ // Loop over window size
471
+ #pragma omp parallel
472
+ {
473
+
474
+ // class for FFT
475
+ fourier::FFT fft(window_size);
476
+
477
+ // loop over subsets for each size/step
478
+ #pragma omp for
479
+ for (int ss = 0; ss < ssdata.num; ss++){
480
+ std::cout << ss << std::endl;
481
+ int ss_x = ssdata.coords[2*ss];
482
+ int ss_y = ssdata.coords[2*ss+1];
483
+
484
+ // get the seed for the new window size
485
+ int ss_x_shft, ss_y_shft;
486
+ ss_x_shft = std::clamp(ss_x - window_size/2, 0, px_hori - window_size);
487
+ ss_y_shft = std::clamp(ss_y - window_size/2, 0, px_vert - window_size);
488
+
489
+ // Offsets to center the subset within the larger window
490
+ int offset = window_size/2 - ss_size/2;
491
+ int offset_x, offset_y;
492
+ offset_x = std::min(offset, ss_x);
493
+ offset_y = std::min(offset, ss_y);
494
+
495
+ //std::fill(fft.ss_ref.vals.begin(), fft.ss_ref.vals.end(), 0.0);
496
+
497
+ for (int row = 0; row < ss_size; ++row) {
498
+ for (int col = 0; col < ss_size; ++col) {
499
+
500
+ // Source coordinates in img_def
501
+ int px_y = ss_y + row;
502
+ int px_x = ss_x + col;
503
+
504
+ // Target coordinates in ss_ref
505
+ int target_y = offset_y + row;
506
+ int target_x = offset_x + col;
507
+
508
+ int idx_img = px_y * px_hori + px_x;
509
+ int idx_window = target_y * window_size + target_x;
510
+ fft.ss_ref.vals[idx_window] = img_ref[idx_img];
511
+ }
512
+ }
513
+
514
+ // populate fft.ss_def with interpolator values
515
+ util::extract_ss_subpx(fft.ss_def, ss_x_shft, ss_y_shft, interp_def);
516
+
517
+ // zero normalise the subsets
518
+ //zero_norm_subsets(fft.ss_ref.vals, fft.ss_def.vals, ss_size);
519
+
520
+ // get peaks from the cross correlation
521
+ double peak_x = 0, peak_y = 0, max_val = 0.0;
522
+ fft.correlate();
523
+ fft.find_peak(peak_x, peak_y, max_val, subpx, "GAUSSIAN_2D");
524
+ for (int row = 0; row < ss_size; ++row) {
525
+ for (int col = 0; col < ss_size; ++col) {
526
+
527
+ // Source coordinates in img_def
528
+ //int px_y = ss_y + row;
529
+ //int px_x = ss_x + col;
530
+
531
+ // Target coordinates in ss_ref
532
+ int target_y = offset_y + row;
533
+ int target_x = offset_x + col;
534
+
535
+ //int idx_img = px_y * px_hori + px_x;
536
+ int idx_window = target_y * window_size + target_x;
537
+ fft.ss_ref.vals[idx_window] = 0.0;
538
+ }
539
+ }
540
+
541
+ // debugging print cross correlation
542
+ //if (ss == 0){
543
+ // for (int y = 0; y < fft.ss_ref.size; y++){
544
+ // for (int x = 0; x < fft.ss_ref.size; x++){
545
+ // std::cout << x << " " << y << " " << fft.ss_ref.vals[y*fft.ss_ref.size + x] << " " << fft.ss_def.vals[y*fft.ss_def.size + x] << " " << fft.cross_corr[y*fft.ss_ref.size+x] << std::endl;
546
+ // }
547
+ // }
548
+ // std::cout << std::endl;
549
+ //}
550
+
551
+
552
+ // update the shift arrays
553
+ shifts[0].x[ss] = peak_x;
554
+ shifts[0].y[ss] = peak_y;
555
+
556
+ // this isn't essential. storing peak amplitude and cost value for shifts
557
+ //util::extract_ss_subpx(fft.ss_def, ss_x+shifts[i].x[ss], ss_y+shifts[i].y[ss], interp_def);
558
+ //shifts[i].cost[ss] = debugcost(fft.ss_ref,fft.ss_def);
559
+ }
560
+ }
561
+
562
+ // remove outliers in fft
563
+ //remove_outliers(shifts[0].x, ssdata[i], 4.0);
564
+ //remove_outliers(shifts[0].y, ssdata[i], 4.0);
565
+
566
+ for (int ss = 0; ss < ssdata.num; ss++){
567
+ std::cout << ssdata.coords[2*ss] << " " << ssdata.coords[2*ss+1] << " ";
568
+ std::cout << shifts[0].x[ss] << " " << shifts[0].y[ss] << " ";
569
+ std::cout << shifts[0].max_val[ss] << " ";
570
+ std::cout << shifts[0].cost[ss] << std::endl;
571
+ }
572
+ std::cout << std::endl;
573
+ exit(0);
574
+ }
575
+
576
+ void test(double &peak_x, double &peak_y, int ss_x, int ss_y, const int window_size, const double *img_ref, const double *img_def, const Interpolator &interp_def){
577
+
578
+ const int px_hori = interp_def.px_hori;
579
+ const int px_vert = interp_def.px_vert;
580
+ const int ss_size = 31;
581
+ bool subpx = true;
582
+
583
+ // class for FFT
584
+ fourier::FFT fft(window_size);
585
+
586
+ // get the seed for the new window size
587
+ int ss_x_shft, ss_y_shft;
588
+ ss_x_shft = std::clamp(ss_x - window_size/2 + ss_size/2, 0, px_hori - window_size);
589
+ ss_y_shft = std::clamp(ss_y - window_size/2 + ss_size/2, 0, px_vert - window_size);
590
+
591
+ // Offsets to center the subset within the larger window
592
+ int offset = window_size/2 - ss_size/2;
593
+ int offset_x, offset_y;
594
+ offset_x = std::min(offset, ss_x);
595
+ offset_y = std::min(offset, ss_y);
596
+
597
+ for (int row = 0; row < ss_size; ++row) {
598
+ for (int col = 0; col < ss_size; ++col) {
599
+
600
+ // Source coordinates in img_def
601
+ int px_y = ss_y + row;
602
+ int px_x = ss_x + col;
603
+
604
+ // Target coordinates in ss_ref
605
+ int target_y = offset_y + row;
606
+ int target_x = offset_x + col;
607
+
608
+ int idx_img = px_y * px_hori + px_x;
609
+ int idx_window = target_y * window_size + target_x;
610
+ fft.ss_ref.vals[idx_window] = img_ref[idx_img];
611
+ }
612
+ }
613
+
614
+ // populate fft.ss_def with interpolator values
615
+ util::extract_ss_subpx(fft.ss_def, ss_x_shft, ss_y_shft, interp_def);
616
+
617
+ // zero normalise the subsets
618
+ fourier::zero_norm_subsets(fft.ss_ref.vals, fft.ss_def.vals, window_size);
619
+
620
+ // get peaks from the cross correlation
621
+ double max_val = 0.0;
622
+ fft.correlate();
623
+ fft.find_peak(peak_x, peak_y, max_val, subpx, "GAUSSIAN_2D");
624
+
625
+ // debugging print cross correlation
626
+ //if (ss == 0){
627
+ // for (int y = 0; y < fft.ss_ref.size; y++){
628
+ // for (int x = 0; x < fft.ss_ref.size; x++){
629
+ // std::cout << x << " " << y << " " << fft.ss_ref.vals[y*fft.ss_ref.size + x] << " " << fft.ss_def.vals[y*fft.ss_def.size + x] << " " << fft.cross_corr[y*fft.ss_ref.size+x] << std::endl;
630
+ // }
631
+ // }
632
+ // std::cout << std::endl;
633
+ // std::cout << peak_x << " " << peak_y << std::endl;
634
+ // exit(0);
635
+ //}
636
+ peak_x*=-1.0;
637
+ peak_y*=-1.0;
638
+ std::cout << peak_x << " " << peak_y << std::endl;
639
+ }
640
+
641
+
642
+
643
+
644
+
645
+ }
646
+
647
+
648
+