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.
- 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-win_amd64.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-win_amd64.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,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
|
+
|