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.

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-win32.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-win32.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,677 @@
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 <queue>
10
+ #include <atomic>
11
+ #include <thread>
12
+ #include <cstring>
13
+ #include <omp.h>
14
+ #include <csignal>
15
+
16
+ // Program Header files
17
+ #include "./dicbruteforce.hpp"
18
+ #include "./dicinterpolator.hpp"
19
+ #include "./dicoptimizer.hpp"
20
+ #include "./defines.hpp"
21
+ #include "./dicutil.hpp"
22
+ #include "./dicrg.hpp"
23
+ #include "./indicators.hpp"
24
+ #include "./cursor_control.hpp"
25
+ #include "./dicfourier.hpp"
26
+
27
+ namespace scanmethod {
28
+
29
+
30
+ // for graceful exit
31
+ std::atomic<bool> stop_request(false);
32
+
33
+ void signalHandler(int signal) {
34
+ if (signal == SIGINT) {
35
+ stop_request = true;
36
+ }
37
+ }
38
+
39
+ void image(const double *img_ref,
40
+ const Interpolator &interp_def,
41
+ const util::SubsetData &ssdata,
42
+ const util::Config &conf,
43
+ const int img_num){
44
+
45
+ const int num_ss = ssdata.num;
46
+ const int ss_size = ssdata.size;
47
+
48
+ // progress bar
49
+ indicators::ProgressBar bar;
50
+ util::create_progress_bar(bar, conf.filenames, img_num, 100);
51
+ std::atomic<int> current_progress = 0;
52
+ std::atomic<int> prev_pct = 0;
53
+
54
+ // loop over subsets within the ROI
55
+ #pragma omp parallel shared(stop_request)
56
+ {
57
+
58
+ // initialise subsets
59
+ util::Subset ss_def(ss_size);
60
+ util::Subset ss_ref(ss_size);
61
+
62
+ // optimization parameters
63
+ optimizer::Parameters opt(conf.num_params, conf.max_iter,
64
+ conf.precision, conf.opt_threshold,
65
+ conf.px_vert, conf.px_hori);
66
+
67
+ // if using SSD then not going to use opt_threshold. It can take
68
+ // any value. Convergence will be checked against precision only
69
+ if (conf.corr_crit=="SSD")
70
+ opt.opt_threshold = std::numeric_limits<double>::max();
71
+
72
+
73
+ #pragma omp for
74
+ for (int ss = 0; ss < num_ss; ss++){
75
+
76
+ // exit the main DIC loop when ctrl+C is hit
77
+ if (stop_request){
78
+ continue;
79
+ }
80
+
81
+ // subset coordinate list takes central locations.
82
+ // Converting to top left corner for optimization routine
83
+ int ss_x = ssdata.coords[ss*2];
84
+ int ss_y = ssdata.coords[ss*2+1];
85
+
86
+ // get the reference subset
87
+ util::extract_ss(ss_ref, ss_x, ss_y, conf.px_hori, conf.px_vert, img_ref);
88
+
89
+ for (int i = 0; i < opt.num_params; i++){
90
+ opt.p[i] = 0.0;
91
+ }
92
+
93
+ // perform optimization on subset from deformed image
94
+ double centre_x = ss_x + static_cast<double>(ssdata.size)/2.0 - 0.5;
95
+ double centre_y = ss_y + static_cast<double>(ssdata.size)/2.0 - 0.5;
96
+ util::Results res = optimizer::solve(centre_x, centre_y, ss_ref, ss_def, interp_def, opt, conf.corr_crit);
97
+
98
+ if (conf.corr_crit!="SSD")
99
+ res.cost = 1-res.cost;
100
+
101
+ // append the results for the current subset to result vectors
102
+ util::append_results(img_num, ss, res, num_ss);
103
+
104
+ // update progress bar
105
+ int progress = current_progress.fetch_add(1);
106
+ util::update_progress_bar(bar, progress, num_ss, prev_pct);
107
+
108
+ }
109
+ }
110
+ bar.mark_as_completed();
111
+ indicators::show_console_cursor(true);
112
+
113
+ }
114
+
115
+
116
+
117
+ void image_with_bf(const double *img_ref,
118
+ const double *img_def,
119
+ const Interpolator &interp_def,
120
+ const util::SubsetData &ssdata,
121
+ const util::Config &conf,
122
+ const int img_num){
123
+
124
+ const int num_ss = ssdata.num;
125
+ const int ss_size = ssdata.size;
126
+
127
+ // progress bar
128
+ indicators::ProgressBar bar;
129
+ util::create_progress_bar(bar, conf.filenames, img_num, num_ss);
130
+ std::atomic<int> current_progress = 0;
131
+ std::atomic<int> prev_pct = 0;
132
+
133
+ // initialise subsets
134
+ util::Subset ss_def(ss_size);
135
+ util::Subset ss_ref(ss_size);
136
+
137
+ // optimization parameters
138
+ optimizer::Parameters opt(conf.num_params, conf.max_iter,
139
+ conf.precision, conf.opt_threshold,
140
+ conf.px_vert, conf.px_hori);
141
+
142
+ // if using SSD then not going to use opt_threshold. It can take
143
+ // any value. Convergence will be checked against precision only
144
+ if (conf.corr_crit=="SSD")
145
+ opt.opt_threshold = std::numeric_limits<double>::max();
146
+
147
+
148
+ // brute force scan parameters
149
+ brute::Parameters brute(conf.bf_threshold, conf.max_disp);
150
+
151
+ // perform optimization on subset from deformed image
152
+ util::Results res(conf.num_params);
153
+
154
+ // counter for each thread
155
+ int ss_thread_num = 0;
156
+
157
+ // temp p values for copy from brute force to optimization.
158
+ double ptemp[6] = {0,0,0,0,0,0};
159
+
160
+ // loop over subsets within the ROI
161
+ #pragma omp parallel for firstprivate(ss_ref, ss_def, ss_thread_num, opt, brute, res, ptemp)
162
+ for (int ss = 0; ss < num_ss; ss++){
163
+
164
+ // exit the main DIC loop when ctrl+C is hit
165
+ if (stop_request){
166
+ continue;
167
+ }
168
+
169
+
170
+ // subset coordinate list contains central locations.
171
+ // Converting to top left corner for optimization routine
172
+ int ss_x = ssdata.coords[ss*2];
173
+ int ss_y = ssdata.coords[ss*2+1];
174
+
175
+ // get the reference subset values from the reference img
176
+ util::extract_ss(ss_ref, ss_x, ss_y, conf.px_hori, conf.px_vert, img_ref);
177
+
178
+
179
+ // if first subset in the loop or prev subset was a poor match
180
+ // start search with a brute force scan using the last set of
181
+ // brute force params that gave a good match.
182
+ if ((ss_thread_num == 0) || (res.cost > opt.opt_threshold)){
183
+
184
+ brute::expanding_wavefront(ss_x, ss_y, img_ref,
185
+ conf.px_hori,
186
+ conf.px_vert,
187
+ ss_ref, ss_def, brute);
188
+
189
+ ptemp[0] = brute.p_rigid[0];
190
+ ptemp[1] = brute.p_rigid[1];
191
+
192
+ for (int i = 0; i < opt.num_params; i++){
193
+ opt.p[i] = ptemp[i];
194
+ }
195
+ }
196
+
197
+ double centre_x = ss_x + static_cast<double>(ssdata.size)/2.0 - 0.5;
198
+ double centre_y = ss_y + static_cast<double>(ssdata.size)/2.0 - 0.5;
199
+ util::Results res = optimizer::solve(centre_x, centre_y, ss_ref, ss_def, interp_def, opt, conf.corr_crit);
200
+
201
+
202
+ // if its not SSD, then we need to flip the cost values so that 1.0
203
+ // is a perfect match rather than 0.0
204
+ if (conf.corr_crit!="SSD")
205
+ res.cost = 1-res.cost;
206
+
207
+ // append the results for the current subset to result vectors
208
+ util::append_results(img_num, ss, res, num_ss);
209
+
210
+ ss_thread_num++;
211
+
212
+ // update progress bar
213
+ int progress = current_progress.fetch_add(1);
214
+ util::update_progress_bar(bar, progress, num_ss, prev_pct);
215
+
216
+ }
217
+ bar.mark_as_completed();
218
+ indicators::show_console_cursor(true);
219
+ }
220
+
221
+
222
+
223
+
224
+
225
+
226
+ void reliability_guided(const double *img_ref,
227
+ const double *img_def,
228
+ const Interpolator &interp_def,
229
+ const std::vector<util::SubsetData> &ssdata,
230
+ const util::Config &conf,
231
+ const int img_num,
232
+ const bool save_at_end){
233
+
234
+ // assign some consts for readability
235
+ const int px_hori = conf.px_hori;
236
+ const int px_vert = conf.px_vert;
237
+ const int seed_x = conf.rg_seed.first;
238
+ const int seed_y = conf.rg_seed.second;
239
+ const int nsizes = ssdata.size();
240
+ const int last_size = nsizes-1;
241
+ const int num_ss = ssdata[last_size].num;
242
+ const int ss_size = ssdata[last_size].size;
243
+ const int ss_step = ssdata[last_size].step;
244
+
245
+
246
+ fourier::mgwd(ssdata, img_ref, img_def, interp_def,
247
+ conf.fft_mad, conf.fft_mad_scale);
248
+
249
+ // progress bar
250
+ indicators::ProgressBar bar;
251
+ util::create_progress_bar(bar, conf.filenames, img_num, num_ss);
252
+ std::atomic<int> current_progress(0);
253
+ std::atomic<int> prev_pct(0);
254
+
255
+ // quick check for the initial seed point
256
+ if (!rg::is_valid_point(seed_x, seed_y, ssdata[last_size])) {
257
+ return;
258
+ }
259
+
260
+ // Initialize binary mask for computed points (initialized to 0)
261
+ std::vector<std::atomic<int>> computed_mask(ssdata[last_size].mask.size());
262
+ for (auto& val : computed_mask) val.store(0);
263
+
264
+ // queue for each thread
265
+ std::vector<std::priority_queue<rg::Point>> local_q(omp_get_max_threads());
266
+
267
+ // Mutex vector to protect each queue
268
+ std::vector<std::mutex> queue_mutexes(omp_get_max_threads());
269
+
270
+ # pragma omp parallel
271
+ {
272
+
273
+ int tid = omp_get_thread_num();
274
+ std::priority_queue<rg::Point>& thread_q = local_q[tid];
275
+
276
+ // Initialize ref and def subsets
277
+ util::Subset ss_def(ss_size);
278
+ util::Subset ss_ref(ss_size);
279
+
280
+ // Optimization parameters
281
+ optimizer::Parameters opt(conf.num_params, conf.max_iter,
282
+ conf.precision, conf.opt_threshold,
283
+ px_vert, px_hori);
284
+
285
+ // if using SSD then not going to use opt_threshold. It can take
286
+ // any value. Convergence will be checked against precision only
287
+ if (conf.corr_crit=="SSD")
288
+ opt.opt_threshold = std::numeric_limits<double>::max();
289
+
290
+ brute::Parameters brute(conf.bf_threshold, conf.max_disp);
291
+
292
+ std::vector<std::unique_ptr<fourier::FFT>> fft_windows;
293
+
294
+ for (size_t t = 0; t < ssdata.size(); ++t) {
295
+ fft_windows.push_back(std::make_unique<fourier::FFT>(ssdata[t].size));
296
+ }
297
+
298
+ // TODO: for the seed location I'm going to overwride the max
299
+ // number of iterations to make sure we get a good convergence.
300
+ // this is hardcoded for now. Could do with updating so that
301
+ // the seed location is checked ahead of the main correlation run.
302
+ opt.max_iter = 200;
303
+
304
+ // ---------------------------------------------------------------------------------------------------------------------------
305
+ // PROCESS THE SEED SUBSET
306
+ // ---------------------------------------------------------------------------------------------------------------------------
307
+ if (tid == 0) {
308
+
309
+ // seed coordinates
310
+ int x = seed_x / ss_step;
311
+ int y = seed_y / ss_step;
312
+ int idx = ssdata[last_size].mask[y * ssdata[last_size].num_ss_x + x];
313
+
314
+
315
+ // if the first image. Take the optimization parameters from rigid fourier
316
+ std::fill(opt.p.begin(), opt.p.end(), 0.0);
317
+ opt.p[0] = fourier::shifts[last_size].x[idx];
318
+ opt.p[1] = fourier::shifts[last_size].y[idx];
319
+
320
+ // Extract reference subset and solve for starting seed point
321
+ util::extract_ss(ss_ref, seed_x, seed_y, px_hori, px_vert, img_ref);
322
+
323
+
324
+ double centre_x = seed_x + static_cast<double>(ss_size)/2.0 - 0.5;
325
+ double centre_y = seed_y + static_cast<double>(ss_size)/2.0 - 0.5;
326
+
327
+ util::Results seed_res = optimizer::solve(centre_x, centre_y, ss_ref, ss_def, interp_def, opt, conf.corr_crit);
328
+
329
+ // if its not SSD, then we need to flip the cost values so that 1.0
330
+ // is a perfect match rather than 0.0
331
+ if (conf.corr_crit!="SSD")
332
+ seed_res.cost = 1.0-seed_res.cost;
333
+
334
+ // append the results for the current subset to result vectors
335
+ util::append_results(img_num, idx, seed_res, num_ss);
336
+
337
+ computed_mask[idx].store(1);
338
+
339
+ // int progress = current_progress.fetch_add(1);
340
+ // util::update_progress_bar(bar, progress, num_ss, prev_pct);
341
+
342
+ // loop over the neighbours for the initial seed point
343
+ for (size_t n = 0; n < ssdata[last_size].neigh[idx].size(); n++) {
344
+
345
+ // subset index of neighbour to the current point
346
+ int nidx = ssdata[last_size].neigh[idx][n];
347
+
348
+ int nx = ssdata[last_size].coords[nidx*2];
349
+ int ny = ssdata[last_size].coords[nidx*2+1];
350
+
351
+ util::extract_ss(ss_ref, nx, ny, px_hori, px_vert, img_ref);
352
+
353
+ // get parameter values from fft output or from previous image
354
+ std::fill(opt.p.begin(), opt.p.end(), 0.0);
355
+ opt.p[0] = fourier::shifts[last_size].x[nidx];
356
+ opt.p[1] = fourier::shifts[last_size].y[nidx];
357
+
358
+ // perform optimization for seed point neighbours
359
+ double centre_x = nx + static_cast<double>(ss_size)/2.0 - 0.5;
360
+ double centre_y = ny + static_cast<double>(ss_size)/2.0 - 0.5;
361
+ util::Results nres = optimizer::solve(centre_x, centre_y, ss_ref, ss_def, interp_def, opt, conf.corr_crit);
362
+
363
+ // if its not SSD, then we need to flip the cost values so that 1.0
364
+ // is a perfect match rather than 0.0
365
+ if (conf.corr_crit!="SSD")
366
+ nres.cost = 1.0-nres.cost;
367
+
368
+ // append the results for the current subset to result vectors
369
+ util::append_results(img_num, nidx, nres, num_ss);
370
+
371
+ // update mask
372
+ computed_mask[idx].store(1);
373
+
374
+ // add this point to queue
375
+ // Protect push with mutex
376
+ {
377
+ std::lock_guard<std::mutex> lock(queue_mutexes[0]);
378
+ local_q[0].push(rg::Point(nidx,nres.cost));
379
+ }
380
+
381
+ // update progress bar
382
+ // int progress = current_progress.fetch_add(1);
383
+ // util::update_progress_bar(bar, progress, num_ss, prev_pct);
384
+ }
385
+ }
386
+
387
+
388
+ // ---------------------------------------------------------------------------------------------------------------------------
389
+ // PROCESS ALL OTHER SUBSETS
390
+ // ---------------------------------------------------------------------------------------------------------------------------
391
+ #pragma omp barrier
392
+
393
+ opt.max_iter = conf.max_iter;
394
+
395
+ std::vector<rg::Point> temp_neigh;
396
+ temp_neigh.reserve(4);
397
+
398
+ const int max_idle_iters = 100;
399
+ rg::Point current(0, 0);
400
+
401
+ while (!stop_request) {
402
+ bool got_point = false;
403
+ int idle_iters = 0;
404
+
405
+ // Try own queue safely
406
+ {
407
+ std::lock_guard<std::mutex> lock(queue_mutexes[tid]);
408
+ if (!thread_q.empty()) {
409
+ current = thread_q.top();
410
+ thread_q.pop();
411
+ got_point = true;
412
+ }
413
+ }
414
+
415
+ // Steal if nothing in own queue
416
+ if (!got_point) {
417
+ while (!got_point && idle_iters < max_idle_iters) {
418
+ #pragma omp critical
419
+ {
420
+ for (size_t i = 0; i < local_q.size(); ++i) {
421
+ std::lock_guard<std::mutex> lock(queue_mutexes[i]);
422
+ if (!local_q[i].empty()) {
423
+ current = local_q[i].top();
424
+ local_q[i].pop();
425
+ got_point = true;
426
+ break;
427
+ }
428
+ }
429
+ }
430
+ if (!got_point) {
431
+ ++idle_iters;
432
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
433
+ }
434
+ }
435
+ }
436
+
437
+ if (!got_point) {
438
+ break;
439
+ }
440
+
441
+ temp_neigh.clear();
442
+
443
+
444
+ // index of current point in results arrays
445
+ int idx_results = save_at_end ? img_num * num_ss + current.idx : current.idx;
446
+ int idx_results_p = idx_results * opt.num_params;
447
+
448
+ // loop over neighbouring points
449
+ for (size_t n = 0; n < ssdata[last_size].neigh[current.idx].size(); n++) {
450
+
451
+ // subset index of neighbour to the current point
452
+ int nidx = ssdata[last_size].neigh[current.idx][n];
453
+
454
+ int expected = 0;
455
+ expected = computed_mask[nidx].exchange(1);
456
+ if (expected == 0) {
457
+
458
+ // coords of neigh
459
+ int nx = ssdata[last_size].coords[nidx*2];
460
+ int ny = ssdata[last_size].coords[nidx*2+1];
461
+
462
+ // extract subset
463
+ util::extract_ss(ss_ref, nx, ny, px_hori, px_vert, img_ref);
464
+
465
+ // if the neighbouring subset had not met correlation threshold then try values from fft windowing
466
+ if (util::cost_arr[idx_results] < opt.opt_threshold){
467
+ std::fill(opt.p.begin(), opt.p.end(), 0.0);
468
+ opt.p[0] = fourier::shifts[last_size].x[nidx];
469
+ opt.p[1] = fourier::shifts[last_size].y[nidx];
470
+ }
471
+ else {
472
+ for (int i = 0; i < opt.num_params; i++){
473
+ opt.p[i] = util::p_arr[idx_results_p+i];
474
+ }
475
+ }
476
+
477
+ // optimize
478
+ double centre_x = nx + static_cast<double>(ss_size)/2.0 - 0.5;
479
+ double centre_y = ny + static_cast<double>(ss_size)/2.0 - 0.5;
480
+ util::Results nres = optimizer::solve(centre_x, centre_y, ss_ref, ss_def, interp_def, opt, conf.corr_crit);
481
+
482
+
483
+ // if its not SSD, then we need to flip the cost values so that 1.0
484
+ // is a perfect match rather than 0.0
485
+ if (conf.corr_crit!="SSD")
486
+ nres.cost = 1.0-nres.cost;
487
+
488
+ // append results
489
+ #pragma omp critical
490
+ util::append_results(img_num, nidx, nres, num_ss);
491
+
492
+ // add results to temp neighbour results
493
+ temp_neigh.emplace_back(nidx, nres.cost);
494
+
495
+ // update progress bar
496
+ int progress = current_progress.fetch_add(1);
497
+ util::update_progress_bar(bar, progress, num_ss, prev_pct);
498
+
499
+ }
500
+ }
501
+
502
+ for (const auto& neigh : temp_neigh) {
503
+ std::lock_guard<std::mutex> lock(queue_mutexes[tid]);
504
+ thread_q.push(neigh);
505
+ }
506
+ }
507
+ }
508
+ bar.mark_as_completed();
509
+ indicators::show_console_cursor(true);
510
+
511
+ }
512
+
513
+
514
+ void multi_window_fourier(const double *img_ref,
515
+ const double *img_def,
516
+ const Interpolator &interp_def,
517
+ const std::vector<util::SubsetData> &ssdata,
518
+ const util::Config &conf,
519
+ const int img_num){
520
+
521
+ // for the first image perform the FFT windowing. later images will be
522
+ // seeded with previous images
523
+ fourier::mgwd(ssdata, img_ref, img_def, interp_def,
524
+ conf.fft_mad, conf.fft_mad_scale);
525
+
526
+ const int nsizes = ssdata.size();
527
+ const int last_size = nsizes-1;
528
+
529
+ // get number of subsets and the size for the smalllest window size
530
+ const int num_ss = ssdata[last_size].num;
531
+ const int ss_size = ssdata[last_size].size;
532
+
533
+ // progress bar
534
+ indicators::ProgressBar bar;
535
+ util::create_progress_bar(bar, conf.filenames, img_num, num_ss);
536
+ std::atomic<int> current_progress = 0;
537
+ std::atomic<int> prev_pct = 0;
538
+
539
+ // loop over subsets within the ROI
540
+ #pragma omp parallel shared(stop_request)
541
+ {
542
+
543
+ // initialise subsets
544
+ util::Subset ss_def(ss_size);
545
+ util::Subset ss_ref(ss_size);
546
+
547
+ // optimization parameters
548
+ optimizer::Parameters opt(conf.num_params, conf.max_iter,
549
+ conf.precision, conf.opt_threshold,
550
+ conf.px_vert, conf.px_hori);
551
+
552
+
553
+ #pragma omp for
554
+ for (int ss = 0; ss < num_ss; ss++){
555
+
556
+ // exit the main DIC loop when ctrl+C is hit
557
+ if (stop_request){
558
+ continue;
559
+ }
560
+
561
+ // subset coordinate list takes central locations.
562
+ // Converting to top left corner for optimization routine
563
+ int ss_x = ssdata[last_size].coords[ss*2];
564
+ int ss_y = ssdata[last_size].coords[ss*2+1];
565
+
566
+ // get the reference subset
567
+ util::extract_ss(ss_ref, ss_x, ss_y, conf.px_hori, conf.px_vert, img_ref);
568
+
569
+ std::fill(opt.p.begin(), opt.p.end(), 0.0);
570
+ opt.p[0] = fourier::shifts[last_size].x[ss];
571
+ opt.p[1] = fourier::shifts[last_size].y[ss];
572
+
573
+ // perform optimization on subset from deformed image
574
+ double centre_x = ss_x + static_cast<double>(ss_size)/2.0 - 0.5;
575
+ double centre_y = ss_y + static_cast<double>(ss_size)/2.0 - 0.5;
576
+ util::Results res = optimizer::solve(centre_x, centre_y, ss_ref, ss_def, interp_def, opt, conf.corr_crit);
577
+
578
+
579
+ // if its not SSD, then we need to flip the cost values so that 1.0
580
+ // is a perfect match rather than 0.0
581
+ if (conf.corr_crit!="SSD")
582
+ res.cost = 1.0-res.cost;
583
+
584
+ // append optimization results to results vectors
585
+ #pragma omp critical
586
+ util::append_results(img_num, ss, res, num_ss);
587
+
588
+ // update progress bar
589
+ int progress = current_progress.fetch_add(1);
590
+ util::update_progress_bar(bar, progress, num_ss, prev_pct);
591
+
592
+ }
593
+ }
594
+ bar.mark_as_completed();
595
+ indicators::show_console_cursor(true);
596
+ }
597
+
598
+
599
+ void single_window_fourier(const double *img_ref,
600
+ const double *img_def,
601
+ const Interpolator &interp_def,
602
+ const util::SubsetData &ssdata,
603
+ const util::Config &conf,
604
+ const int img_num){
605
+
606
+ // for the first image perform the FFT windowing. later images will be
607
+ // seeded with previous images
608
+ fourier::sgwd(ssdata, 256, img_ref, img_def, interp_def);
609
+
610
+ // get number of subsets and the size for the smalllest window size
611
+ const int num_ss = ssdata.num;
612
+ const int ss_size = ssdata.size;
613
+
614
+ // progress bar
615
+ indicators::ProgressBar bar;
616
+ util::create_progress_bar(bar, conf.filenames, img_num, num_ss);
617
+ std::atomic<int> current_progress = 0;
618
+ std::atomic<int> prev_pct = 0;
619
+
620
+ // loop over subsets within the ROI
621
+ #pragma omp parallel shared(stop_request)
622
+ {
623
+
624
+ // initialise subsets
625
+ util::Subset ss_def(ss_size);
626
+ util::Subset ss_ref(ss_size);
627
+
628
+ // optimization parameters
629
+ optimizer::Parameters opt(conf.num_params, conf.max_iter,
630
+ conf.precision, conf.opt_threshold,
631
+ conf.px_vert, conf.px_hori);
632
+
633
+
634
+ #pragma omp for
635
+ for (int ss = 0; ss < num_ss; ss++){
636
+
637
+ // exit the main DIC loop when ctrl+C is hit
638
+ if (stop_request){
639
+ continue;
640
+ }
641
+
642
+ // subset coordinate list takes central locations.
643
+ // Converting to top left corner for optimization routine
644
+ int ss_x = ssdata.coords[ss*2];
645
+ int ss_y = ssdata.coords[ss*2+1];
646
+
647
+ // get the reference subset
648
+ util::extract_ss(ss_ref, ss_x, ss_y, conf.px_hori, conf.px_vert, img_ref);
649
+
650
+ std::fill(opt.p.begin(), opt.p.end(), 0.0);
651
+ opt.p[0] = fourier::shifts[0].x[ss];
652
+ opt.p[1] = fourier::shifts[0].y[ss];
653
+
654
+ // perform optimization on subset from deformed image
655
+ double centre_x = ss_x + static_cast<double>(ss_size)/2.0 - 0.5;
656
+ double centre_y = ss_y + static_cast<double>(ss_size)/2.0 - 0.5;
657
+ util::Results res = optimizer::solve(centre_x, centre_y, ss_ref, ss_def, interp_def, opt, conf.corr_crit);
658
+
659
+ // if its not SSD, then we need to flip the cost values so that 1.0
660
+ // is a perfect match rather than 0.0
661
+ if (conf.corr_crit!="SSD")
662
+ res.cost = 1.0-res.cost;
663
+
664
+ // append optimization results to results vectors
665
+ util::append_results(img_num, ss, res, num_ss);
666
+
667
+ // update progress bar
668
+ int progress = current_progress.fetch_add(1);
669
+ util::update_progress_bar(bar, progress, num_ss, prev_pct);
670
+
671
+ }
672
+ }
673
+ bar.mark_as_completed();
674
+ indicators::show_console_cursor(true);
675
+ }
676
+
677
+ }