pivtools 0.1.3__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.
Files changed (127) hide show
  1. pivtools-0.1.3.dist-info/METADATA +222 -0
  2. pivtools-0.1.3.dist-info/RECORD +127 -0
  3. pivtools-0.1.3.dist-info/WHEEL +5 -0
  4. pivtools-0.1.3.dist-info/entry_points.txt +3 -0
  5. pivtools-0.1.3.dist-info/top_level.txt +3 -0
  6. pivtools_cli/__init__.py +5 -0
  7. pivtools_cli/_build_marker.c +25 -0
  8. pivtools_cli/_build_marker.cp311-win_amd64.pyd +0 -0
  9. pivtools_cli/cli.py +225 -0
  10. pivtools_cli/example.py +139 -0
  11. pivtools_cli/lib/PIV_2d_cross_correlate.c +334 -0
  12. pivtools_cli/lib/PIV_2d_cross_correlate.h +22 -0
  13. pivtools_cli/lib/common.h +36 -0
  14. pivtools_cli/lib/interp2custom.c +146 -0
  15. pivtools_cli/lib/interp2custom.h +48 -0
  16. pivtools_cli/lib/peak_locate_gsl.c +711 -0
  17. pivtools_cli/lib/peak_locate_gsl.h +40 -0
  18. pivtools_cli/lib/peak_locate_gsl_print.c +736 -0
  19. pivtools_cli/lib/peak_locate_lm.c +751 -0
  20. pivtools_cli/lib/peak_locate_lm.h +27 -0
  21. pivtools_cli/lib/xcorr.c +342 -0
  22. pivtools_cli/lib/xcorr.h +31 -0
  23. pivtools_cli/lib/xcorr_cache.c +78 -0
  24. pivtools_cli/lib/xcorr_cache.h +26 -0
  25. pivtools_cli/piv/interp2custom/interp2custom.py +69 -0
  26. pivtools_cli/piv/piv.py +240 -0
  27. pivtools_cli/piv/piv_backend/base.py +825 -0
  28. pivtools_cli/piv/piv_backend/cpu_instantaneous.py +1005 -0
  29. pivtools_cli/piv/piv_backend/factory.py +28 -0
  30. pivtools_cli/piv/piv_backend/gpu_instantaneous.py +15 -0
  31. pivtools_cli/piv/piv_backend/infilling.py +445 -0
  32. pivtools_cli/piv/piv_backend/outlier_detection.py +306 -0
  33. pivtools_cli/piv/piv_backend/profile_cpu_instantaneous.py +230 -0
  34. pivtools_cli/piv/piv_result.py +40 -0
  35. pivtools_cli/piv/save_results.py +342 -0
  36. pivtools_cli/piv_cluster/cluster.py +108 -0
  37. pivtools_cli/preprocessing/filters.py +399 -0
  38. pivtools_cli/preprocessing/preprocess.py +79 -0
  39. pivtools_cli/tests/helpers.py +107 -0
  40. pivtools_cli/tests/instantaneous_piv/test_piv_integration.py +167 -0
  41. pivtools_cli/tests/instantaneous_piv/test_piv_integration_multi.py +553 -0
  42. pivtools_cli/tests/preprocessing/test_filters.py +41 -0
  43. pivtools_core/__init__.py +5 -0
  44. pivtools_core/config.py +703 -0
  45. pivtools_core/config.yaml +135 -0
  46. pivtools_core/image_handling/__init__.py +0 -0
  47. pivtools_core/image_handling/load_images.py +464 -0
  48. pivtools_core/image_handling/readers/__init__.py +53 -0
  49. pivtools_core/image_handling/readers/generic_readers.py +50 -0
  50. pivtools_core/image_handling/readers/lavision_reader.py +190 -0
  51. pivtools_core/image_handling/readers/registry.py +24 -0
  52. pivtools_core/paths.py +49 -0
  53. pivtools_core/vector_loading.py +248 -0
  54. pivtools_gui/__init__.py +3 -0
  55. pivtools_gui/app.py +687 -0
  56. pivtools_gui/calibration/__init__.py +0 -0
  57. pivtools_gui/calibration/app/__init__.py +0 -0
  58. pivtools_gui/calibration/app/views.py +1186 -0
  59. pivtools_gui/calibration/calibration_planar/planar_calibration_production.py +570 -0
  60. pivtools_gui/calibration/vector_calibration_production.py +544 -0
  61. pivtools_gui/config.py +703 -0
  62. pivtools_gui/image_handling/__init__.py +0 -0
  63. pivtools_gui/image_handling/load_images.py +464 -0
  64. pivtools_gui/image_handling/readers/__init__.py +53 -0
  65. pivtools_gui/image_handling/readers/generic_readers.py +50 -0
  66. pivtools_gui/image_handling/readers/lavision_reader.py +190 -0
  67. pivtools_gui/image_handling/readers/registry.py +24 -0
  68. pivtools_gui/masking/__init__.py +0 -0
  69. pivtools_gui/masking/app/__init__.py +0 -0
  70. pivtools_gui/masking/app/views.py +123 -0
  71. pivtools_gui/paths.py +49 -0
  72. pivtools_gui/piv_runner.py +261 -0
  73. pivtools_gui/pivtools.py +58 -0
  74. pivtools_gui/plotting/__init__.py +0 -0
  75. pivtools_gui/plotting/app/__init__.py +0 -0
  76. pivtools_gui/plotting/app/views.py +1671 -0
  77. pivtools_gui/plotting/plot_maker.py +220 -0
  78. pivtools_gui/post_processing/POD/__init__.py +0 -0
  79. pivtools_gui/post_processing/POD/app/__init__.py +0 -0
  80. pivtools_gui/post_processing/POD/app/views.py +647 -0
  81. pivtools_gui/post_processing/POD/pod_decompose.py +979 -0
  82. pivtools_gui/post_processing/POD/views.py +1096 -0
  83. pivtools_gui/post_processing/__init__.py +0 -0
  84. pivtools_gui/static/404.html +1 -0
  85. pivtools_gui/static/_next/static/chunks/117-d5793c8e79de5511.js +2 -0
  86. pivtools_gui/static/_next/static/chunks/484-cfa8b9348ce4f00e.js +1 -0
  87. pivtools_gui/static/_next/static/chunks/869-320a6b9bdafbb6d3.js +1 -0
  88. pivtools_gui/static/_next/static/chunks/app/_not-found/page-12f067ceb7415e55.js +1 -0
  89. pivtools_gui/static/_next/static/chunks/app/layout-b907d5f31ac82e9d.js +1 -0
  90. pivtools_gui/static/_next/static/chunks/app/page-334cc4e8444cde2f.js +1 -0
  91. pivtools_gui/static/_next/static/chunks/fd9d1056-ad15f396ddf9b7e5.js +1 -0
  92. pivtools_gui/static/_next/static/chunks/framework-f66176bb897dc684.js +1 -0
  93. pivtools_gui/static/_next/static/chunks/main-a1b3ced4d5f6d998.js +1 -0
  94. pivtools_gui/static/_next/static/chunks/main-app-8a63c6f5e7baee11.js +1 -0
  95. pivtools_gui/static/_next/static/chunks/pages/_app-72b849fbd24ac258.js +1 -0
  96. pivtools_gui/static/_next/static/chunks/pages/_error-7ba65e1336b92748.js +1 -0
  97. pivtools_gui/static/_next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
  98. pivtools_gui/static/_next/static/chunks/webpack-4a8ca7c99e9bb3d8.js +1 -0
  99. pivtools_gui/static/_next/static/css/7d3f2337d7ea12a5.css +3 -0
  100. pivtools_gui/static/_next/static/vQeR20OUdSSKlK4vukC4q/_buildManifest.js +1 -0
  101. pivtools_gui/static/_next/static/vQeR20OUdSSKlK4vukC4q/_ssgManifest.js +1 -0
  102. pivtools_gui/static/file.svg +1 -0
  103. pivtools_gui/static/globe.svg +1 -0
  104. pivtools_gui/static/grid.svg +8 -0
  105. pivtools_gui/static/index.html +1 -0
  106. pivtools_gui/static/index.txt +8 -0
  107. pivtools_gui/static/next.svg +1 -0
  108. pivtools_gui/static/vercel.svg +1 -0
  109. pivtools_gui/static/window.svg +1 -0
  110. pivtools_gui/stereo_reconstruction/__init__.py +0 -0
  111. pivtools_gui/stereo_reconstruction/app/__init__.py +0 -0
  112. pivtools_gui/stereo_reconstruction/app/views.py +1985 -0
  113. pivtools_gui/stereo_reconstruction/stereo_calibration_production.py +606 -0
  114. pivtools_gui/stereo_reconstruction/stereo_reconstruction_production.py +544 -0
  115. pivtools_gui/utils.py +63 -0
  116. pivtools_gui/vector_loading.py +248 -0
  117. pivtools_gui/vector_merging/__init__.py +1 -0
  118. pivtools_gui/vector_merging/app/__init__.py +1 -0
  119. pivtools_gui/vector_merging/app/views.py +759 -0
  120. pivtools_gui/vector_statistics/app/__init__.py +1 -0
  121. pivtools_gui/vector_statistics/app/views.py +710 -0
  122. pivtools_gui/vector_statistics/ensemble_statistics.py +49 -0
  123. pivtools_gui/vector_statistics/instantaneous_statistics.py +311 -0
  124. pivtools_gui/video_maker/__init__.py +0 -0
  125. pivtools_gui/video_maker/app/__init__.py +0 -0
  126. pivtools_gui/video_maker/app/views.py +436 -0
  127. pivtools_gui/video_maker/video_maker.py +662 -0
@@ -0,0 +1,553 @@
1
+ import unittest
2
+ from pathlib import Path
3
+ from unittest import skip
4
+ from unittest.mock import patch
5
+
6
+ import dask.array as da
7
+ import h5py
8
+ import numpy as np
9
+ import pytest
10
+ import tifffile
11
+ from scipy.io import loadmat
12
+
13
+ from pivtools_cli.config import Config
14
+ from pivtools_cli.piv.piv_backend.base import CrossCorrelator
15
+ from pivtools_cli.piv.piv_backend.cpu_instantaneous import InstantaneousCorrelatorCPU
16
+ from pivtools_cli.piv.piv_backend.factory import make_correlator_backend
17
+ from pivtools_cli.tests.helpers import assert_arrays_close
18
+
19
+
20
+ def _fake_inpaint_biharm(self, x: np.ndarray) -> np.ndarray:
21
+ return np.nan_to_num(x, nan=1.0)
22
+
23
+
24
+ class InstantaneousPIVTestCase(unittest.TestCase):
25
+
26
+ def setUp(self):
27
+ test_dir = Path(__file__).parent
28
+ config_path = test_dir / "config.yaml"
29
+ self.config = Config(config_path)
30
+ self.config.data["paths"]["base_path"] = str(
31
+ test_dir.parent / "data" / "instantaneous_piv"
32
+ )
33
+ camera_path = self.config.base_path / self.config.cameras[0]
34
+ file_paths = [
35
+ camera_path / (self.config.image_format % 1),
36
+ camera_path / (self.config.image_format.replace("_A", "_B") % 1),
37
+ ]
38
+
39
+ self.img_pair = np.stack(
40
+ [
41
+ tifffile.imread(file_paths[0]).astype(self.config.image_dtype),
42
+ tifffile.imread(file_paths[1]).astype(self.config.image_dtype),
43
+ ],
44
+ axis=0,
45
+ )
46
+ C, H, W = self.img_pair.shape
47
+ self.img_pair = self.img_pair.reshape((1, C, H, W))
48
+ self.base_path = Path(self.config.data["paths"]["base_path"]) / "Matlab"
49
+ patcher = patch(
50
+ "pivtools_cli.piv.piv_backend.cpu_instantaneous.InstantaneousCorrelatorCPU._inpaint_nans_biharm",
51
+ new=_fake_inpaint_biharm,
52
+ )
53
+
54
+ self._mock_inpaint = patcher.start()
55
+
56
+ # def tearDown(self):
57
+ # self._mock_inpaint.stop()
58
+
59
+ def test_x_peaks(self):
60
+ self.config.data["piv"]["secondary_peak"] = False
61
+ cpu_backend = make_correlator_backend(config=self.config)
62
+ piv_result = cpu_backend.correlate_batch(self.img_pair, self.config)
63
+ for i in range(1, 2): # len(self.config.window_sizes)):
64
+ peak_x_mat_file = self.base_path / f"MATLAB_peak_loc_x_{i}.mat"
65
+
66
+ with h5py.File(peak_x_mat_file, "r") as f:
67
+
68
+ matlab_peaks = np.array(f["peak_loc_x"], dtype=np.float32)
69
+ matlab_peaks = np.transpose(matlab_peaks, (2, 1, 0))
70
+ # compare_matrices(
71
+ # matlab_peaks[0], piv_result.passes[i - 1].peak_loc_x[0]
72
+ # )
73
+ assert_arrays_close(
74
+ self,
75
+ matlab_peaks,
76
+ piv_result.passes[i - 1].peak_loc_x,
77
+ tol=0.001,
78
+ )
79
+
80
+ def test_x_peaks_after_bulk(self):
81
+ self.config.data["piv"]["secondary_peak"] = False
82
+ cpu_backend = make_correlator_backend(config=self.config)
83
+ piv_result = cpu_backend.correlate_batch(self.img_pair, self.config)
84
+ for i in range(1, 3): # len(self.config.window_sizes) + 1):
85
+ peak_x_mat_file = self.base_path / f"MATLAB_peak_loc_x_after_bulk_{i}.mat"
86
+
87
+ with h5py.File(peak_x_mat_file, "r") as f:
88
+
89
+ matlab_peaks = np.array(f["peak_loc_x"], dtype=np.float32)
90
+ matlab_peaks = np.transpose(matlab_peaks, (2, 1, 0))
91
+ compare_matrices(
92
+ matlab_peaks[0], piv_result.passes[i - 1].peak_loc_x_after_bulk[0]
93
+ )
94
+ assert_arrays_close(
95
+ self,
96
+ matlab_peaks,
97
+ piv_result.passes[i - 1].peak_loc_x_after_bulk,
98
+ tol=0.001,
99
+ )
100
+
101
+ def test_y_peaks_after_bulk(self):
102
+ self.config.data["piv"]["secondary_peak"] = False
103
+ cpu_backend = make_correlator_backend(config=self.config)
104
+ piv_result = cpu_backend.correlate_batch(self.img_pair, self.config)
105
+ for i in range(1, 3): # len(self.config.window_sizes) + 1):
106
+ peak_y_mat_file = self.base_path / f"MATLAB_peak_loc_y _after_bulk_{i}.mat"
107
+
108
+ with h5py.File(peak_y_mat_file, "r") as f:
109
+
110
+ matlab_peaks = np.array(f["peak_loc_y"], dtype=np.float32)
111
+ matlab_peaks = np.transpose(matlab_peaks, (2, 1, 0))
112
+ compare_matrices(
113
+ matlab_peaks[0], piv_result.passes[i - 1].peak_loc_y_after_bulk[0]
114
+ )
115
+ assert_arrays_close(
116
+ self,
117
+ matlab_peaks,
118
+ piv_result.passes[i - 1].peak_loc_y_after_bulk,
119
+ tol=0.001,
120
+ )
121
+
122
+ def test_y_peaks(self):
123
+ self.config.data["piv"]["secondary_peak"] = False
124
+ cpu_backend = make_correlator_backend(config=self.config)
125
+ piv_result = cpu_backend.correlate_batch(self.img_pair, self.config)
126
+ for i in range(1, len(self.config.window_sizes) + 1):
127
+ peak_y_mat_file = self.base_path / f"MATLAB_peak_loc_y_{i}.mat"
128
+ with h5py.File(peak_y_mat_file, "r") as f:
129
+
130
+ matlab_peaks = np.array(f["peak_loc_y"], dtype=np.float32)
131
+ matlab_peaks = np.transpose(matlab_peaks, (2, 1, 0))
132
+ assert_arrays_close(
133
+ self, matlab_peaks, piv_result.passes[i - 1].peak_loc_y, tol=0.0001
134
+ )
135
+
136
+ def test_a_prime(self):
137
+ self.config.data["piv"]["secondary_peak"] = False
138
+ cpu_backend = make_correlator_backend(config=self.config)
139
+ piv_result = cpu_backend.correlate_batch(self.img_pair, self.config)
140
+ for i in range(2, len(self.config.window_sizes) + 1):
141
+ peak_y_mat_file = self.base_path / f"MATLAB_A_prime_{i}.mat"
142
+ with h5py.File(peak_y_mat_file, "r") as f:
143
+
144
+ matlab_image_a = np.array(f["A_prime"], dtype=np.float32)
145
+ compare_matrices(
146
+ matlab_image_a.T[0],
147
+ piv_result.passes[i - 1].image_a_prime[0],
148
+ )
149
+ assert_arrays_close(
150
+ self,
151
+ matlab_image_a.T,
152
+ piv_result.passes[i - 1].image_a_prime,
153
+ tol=0.01,
154
+ )
155
+
156
+ def test_a_prime_subset(self):
157
+ self.config.data["piv"]["secondary_peak"] = False
158
+ cpu_backend = make_correlator_backend(config=self.config)
159
+ piv_result = cpu_backend.correlate_batch(self.img_pair, self.config)
160
+ for i in range(2, 3): # self.MAX_PASS): # len(self.config.window_sizes)+1):
161
+ peak_y_mat_file = self.base_path / f"MATLAB_a_prime_subset_{i}.mat"
162
+ with h5py.File(peak_y_mat_file, "r") as f:
163
+
164
+ matlab_image_a = np.array(f["A_prime_subset"], dtype=np.float32)
165
+ compare_matrices(
166
+ matlab_image_a.T[0],
167
+ piv_result.passes[i - 1].image_a_prime_subset[0],
168
+ )
169
+ assert_arrays_close(
170
+ self,
171
+ matlab_image_a.T,
172
+ piv_result.passes[i - 1].image_a_prime_subset,
173
+ tol=0.005,
174
+ )
175
+
176
+ def test_a(self):
177
+ self.config.data["piv"]["secondary_peak"] = False
178
+ cpu_backend = make_correlator_backend(config=self.config)
179
+ piv_result = cpu_backend.correlate_batch(self.img_pair, self.config)
180
+ for i in range(2, 3): # self.MAX_PASS): # len(self.config.window_sizes)+1):
181
+ peak_y_mat_file = self.base_path / f"MATLAB_image_a_{i}.mat"
182
+ with h5py.File(peak_y_mat_file, "r") as f:
183
+
184
+ matlab_image_a = np.array(f["A"], dtype=np.float32)
185
+ compare_matrices(
186
+ matlab_image_a.T[2], piv_result.passes[i - 1].image_a_before[2]
187
+ )
188
+ assert_arrays_close(
189
+ self,
190
+ matlab_image_a.T,
191
+ piv_result.passes[i - 1].image_a_before,
192
+ tol=0.05,
193
+ )
194
+
195
+ def test_a_mesh(self):
196
+ self.config.data["piv"]["secondary_peak"] = False
197
+ cpu_backend = make_correlator_backend(config=self.config)
198
+ piv_result = cpu_backend.correlate_batch(self.img_pair, self.config)
199
+ for i in range(2, 3): # self.MAX_PASS): # len(self.config.window_sizes)+1):
200
+ peak_y_mat_file = self.base_path / f"MATLAB_image_mesh_A_{i}.mat"
201
+ with h5py.File(peak_y_mat_file, "r") as f:
202
+
203
+ matlab_image_mesh_a = np.array(f["im_mesh_A"], dtype=np.float32)
204
+ matlab_image_mesh_a = np.transpose(matlab_image_mesh_a, (2, 1, 0))
205
+ print(matlab_image_mesh_a.shape)
206
+ print(piv_result.passes[i - 1].im_mesh_A.shape)
207
+ ii, jj = 10, 20
208
+ print(
209
+ "Python mesh:",
210
+ piv_result.passes[i - 1].im_mesh_A[ii, jj, 0],
211
+ piv_result.passes[i - 1].im_mesh_A[ii, jj, 1],
212
+ )
213
+ print("MATLAB mesh:", matlab_image_mesh_a[ii, jj, :])
214
+ compare_matrices(
215
+ matlab_image_mesh_a.T[2], piv_result.passes[i - 1].im_mesh_A[2] + 1
216
+ )
217
+ assert_arrays_close(
218
+ self,
219
+ matlab_image_mesh_a,
220
+ piv_result.passes[i - 1].im_mesh_A + 1,
221
+ tol=0.0005,
222
+ )
223
+
224
+ def test_peak_height(self):
225
+ self.config.data["piv"]["secondary_peak"] = False
226
+ cpu_backend = make_correlator_backend(config=self.config)
227
+ piv_result = cpu_backend.correlate_batch(self.img_pair, self.config)
228
+ for i in range(1, self.MAX_PASS): # len(self.config.window_sizes)+1):
229
+ peak_height_mat_file = self.base_path / f"MATLAB_peak_height_{i}.mat"
230
+ with h5py.File(peak_height_mat_file, "r") as f:
231
+ matlab_peaks = np.array(f["peak_height"], dtype=np.float32)
232
+
233
+ matlab_peaks = np.transpose(matlab_peaks, (2, 1, 0))
234
+ print("PEAKS", matlab_peaks[0, :, :])
235
+ assert_arrays_close(
236
+ self, matlab_peaks, piv_result.passes[i - 1].peak_mag, tol=0.0001
237
+ )
238
+
239
+ def test_ux_mat(self):
240
+ self.config.data["piv"]["secondary_peak"] = False
241
+ cpu_backend = make_correlator_backend(config=self.config)
242
+ piv_result = cpu_backend.correlate_batch(self.img_pair, self.config)
243
+ for i in range(1, 4): # len(self.config.window_sizes)+1):
244
+ ux_mat_file = self.base_path / f"MATLAB_ux_mat_{i}.mat"
245
+
246
+ with h5py.File(ux_mat_file, "r") as f:
247
+ matlab_ux_mat = np.array(f["ux_mat"], dtype=np.float32)
248
+ matlab_ux_mat = np.transpose(matlab_ux_mat, (1, 0))
249
+ print("UX", matlab_ux_mat[0], piv_result.passes[i - 1].ux_mat[0])
250
+ assert_arrays_close(
251
+ self, matlab_ux_mat, piv_result.passes[i - 1].ux_mat, tol=0.0001
252
+ )
253
+
254
+ def test_uy_mat(self):
255
+ self.config.data["piv"]["secondary_peak"] = False
256
+ cpu_backend = make_correlator_backend(config=self.config)
257
+ piv_result = cpu_backend.correlate_batch(self.img_pair, self.config)
258
+ for i in range(1, 5): # len(self.config.window_sizes)+1):
259
+ uy_mat_file = self.base_path / f"MATLAB_uy_mat_{i}.mat"
260
+ with h5py.File(uy_mat_file, "r") as f:
261
+ matlab_uy_mat = np.array(f["uy_mat"], dtype=np.float32)
262
+ matlab_uy_mat = np.transpose(matlab_uy_mat, (1, 0))
263
+ compare_matrices(matlab_uy_mat, piv_result.passes[i - 1].uy_mat)
264
+ assert_arrays_close(
265
+ self, matlab_uy_mat, piv_result.passes[i - 1].uy_mat, tol=1.001
266
+ )
267
+
268
+ def test_ux_mat_secondary(self):
269
+ self.config.data["piv"]["secondary_peak"] = True
270
+ cpu_backend = make_correlator_backend(config=self.config)
271
+ piv_result = cpu_backend.correlate_batch(self.img_pair, self.config)
272
+ for i in range(1, 3): # len(self.config.window_sizes)+1):
273
+ ux_mat_file = self.base_path / f"MATLAB_ux_mat_{i}_secondary.mat"
274
+ with h5py.File(ux_mat_file, "r") as f:
275
+ matlab_ux_mat = np.array(f["ux_mat"], dtype=np.float32)
276
+ matlab_ux_mat = np.transpose(matlab_ux_mat, (1, 0))
277
+ assert_arrays_close(
278
+ self, matlab_ux_mat, piv_result.passes[i - 1].ux_mat, tol=0.0001
279
+ )
280
+
281
+ def test_uy_mat_secondary(self):
282
+ self.config.data["piv"]["secondary_peak"] = True
283
+ cpu_backend = make_correlator_backend(config=self.config)
284
+ piv_result = cpu_backend.correlate_batch(self.img_pair, self.config)
285
+ for i in range(1, 3): # len(self.config.window_sizes)+1):
286
+ uy_mat_file = self.base_path / f"MATLAB_uy_mat_{i}_secondary.mat"
287
+ with h5py.File(uy_mat_file, "r") as f:
288
+ matlab_uy_mat = np.array(f["uy_mat"], dtype=np.float32)
289
+ matlab_uy_mat = np.transpose(matlab_uy_mat, (1, 0))
290
+
291
+ assert_arrays_close(
292
+ self, matlab_uy_mat, piv_result.passes[i - 1].uy_mat, tol=0.0001
293
+ )
294
+ assert True
295
+
296
+ def test_nan(self):
297
+ self.config.data["piv"]["secondary_peak"] = False
298
+ cpu_backend = make_correlator_backend(config=self.config)
299
+ piv_result = cpu_backend.correlate_batch(self.img_pair, self.config)
300
+
301
+ for i in range(1, self.MAX_PASS): # len(self.config.window_sizes)+1):
302
+ nan_file = self.base_path / f"MATLAB_nan_{i}.mat"
303
+ with h5py.File(nan_file, "r") as f:
304
+ matlab_nan = np.array(f["nan_mask"], dtype=bool)
305
+
306
+ matlab_nan = np.transpose(matlab_nan, (1, 0))
307
+ python_nan = piv_result.passes[i - 1].nan_mask
308
+ try:
309
+ np.testing.assert_array_equal(matlab_nan, python_nan)
310
+ except AssertionError:
311
+ diff = matlab_nan != python_nan
312
+ idx = np.argwhere(diff)
313
+
314
+ print(f"nan_mask mismatch: {len(idx)} elements differ")
315
+ for r, c in idx[:10]:
316
+ print(
317
+ f"({r},{c}): MATLAB={matlab_nan[r,c]}, Python={python_nan[r,c]}"
318
+ )
319
+ raise
320
+
321
+ def test_Q_mat_secondary(self):
322
+
323
+ self.config.data["piv"]["secondary_peak"] = True
324
+ cpu_backend = make_correlator_backend(config=self.config)
325
+ piv_result = cpu_backend.correlate_batch(self.img_pair, self.config)
326
+ for i in range(1, self.MAX_PASS): # len(self.config.window_sizes)+1):
327
+ q_mat_file = self.base_path / f"MATLAB_Qmat_{i}.mat"
328
+ with h5py.File(q_mat_file, "r") as f:
329
+ matlab_q_mat = np.array(f["Q_mat"], dtype=np.float32)
330
+ matlab_q_mat = np.transpose(matlab_q_mat, (0, 2, 1))
331
+ assert_arrays_close(
332
+ self, matlab_q_mat, piv_result.passes[i - 1].Q_mat, tol=0.0001
333
+ )
334
+
335
+ def test_delta_ab_gauss(self):
336
+
337
+ self.config.data["piv"]["secondary_peak"] = False
338
+ cpu_backend = make_correlator_backend(config=self.config)
339
+ piv_result = cpu_backend.correlate_batch(self.img_pair, self.config)
340
+ for i in range(2, 3): # self.MAX_PASS): # len(self.config.window_sizes)+1):
341
+ q_mat_file = self.base_path / f"MATLAB_delta_ab_old_gauss_{i}.mat"
342
+ with h5py.File(q_mat_file, "r") as f:
343
+ matlab_delta_ab_old = np.array(f["delta_ab_old"], dtype=np.float32)
344
+ matlab_delta_ab_old = np.transpose(matlab_delta_ab_old, (2, 1, 0))
345
+ print(matlab_delta_ab_old.shape)
346
+
347
+ compare_matrices(
348
+ matlab_delta_ab_old[0],
349
+ piv_result.passes[i - 1].delta_ab_old_gauss[0],
350
+ )
351
+ assert_arrays_close(
352
+ self,
353
+ matlab_delta_ab_old,
354
+ piv_result.passes[i - 1].delta_ab_old_gauss,
355
+ tol=0.0001,
356
+ )
357
+
358
+ def test_delta_ab_dense(self):
359
+
360
+ self.config.data["piv"]["secondary_peak"] = False
361
+ cpu_backend = make_correlator_backend(config=self.config)
362
+ piv_result = cpu_backend.correlate_batch(self.img_pair, self.config)
363
+ for i in range(2, 3): # self.MAX_PASS): # len(self.config.window_sizes)+1):
364
+ q_mat_file = self.base_path / f"MATLAB_delta_ab_dense_{i}.mat"
365
+ with h5py.File(q_mat_file, "r") as f:
366
+ matlab_delta_ab_dense = np.array(f["delta_ab_dense"], dtype=np.float32)
367
+ matlab_delta_ab_dense = np.transpose(matlab_delta_ab_dense, (2, 1, 0))
368
+ for numb in range(1):
369
+ compare_matrices(
370
+ matlab_delta_ab_dense[numb],
371
+ piv_result.passes[i - 1].delta_ab_dense_test[numb],
372
+ )
373
+
374
+ assert_arrays_close(
375
+ self,
376
+ matlab_delta_ab_dense,
377
+ piv_result.passes[i - 1].delta_ab_dense_test,
378
+ tol=0.001,
379
+ )
380
+
381
+ def test_delta_ab_filt(self):
382
+
383
+ self.config.data["piv"]["secondary_peak"] = False
384
+ cpu_backend = make_correlator_backend(config=self.config)
385
+ piv_result = cpu_backend.correlate_batch(self.img_pair, self.config)
386
+ for i in range(2, 3): # self.MAX_PASS): # len(self.config.window_sizes)+1):
387
+ q_mat_file = self.base_path / f"MATLAB_delta_ab_filt_{i}.mat"
388
+ with h5py.File(q_mat_file, "r") as f:
389
+ matlab_delta_ab_filt = np.array(f["delta_ab_filt"], dtype=np.float32)
390
+ matlab_delta_ab_filt = np.transpose(matlab_delta_ab_filt, (2, 1, 0))
391
+ assert_arrays_close(
392
+ self,
393
+ matlab_delta_ab_filt,
394
+ piv_result.passes[i - 1].delta_ab_filt,
395
+ tol=0.0001,
396
+ )
397
+
398
+ def test_delta_ab_pred_test(self):
399
+
400
+ self.config.data["piv"]["secondary_peak"] = False
401
+ cpu_backend = make_correlator_backend(config=self.config)
402
+ piv_result = cpu_backend.correlate_batch(self.img_pair, self.config)
403
+ for i in range(2, 3): # self.MAX_PASS): # len(self.config.window_sizes)+1):
404
+ q_mat_file = self.base_path / f"MATLAB_delta_ab_pred_test_{i}.mat"
405
+ with h5py.File(q_mat_file, "r") as f:
406
+ matlab_delta_ab_pred = np.array(f["delta_ab_pred"], dtype=np.float32)
407
+ matlab_delta_ab_pred = np.transpose(matlab_delta_ab_pred, (2, 1, 0))
408
+ print(f"MATLAB shape {i}", matlab_delta_ab_pred.shape)
409
+ # print("MATLAB", matlab_delta_ab_pred.shape)
410
+ # print("PYTHON", piv_result.passes[i - 1].delta_ab_pred_test.shape)
411
+ for numb in range(8):
412
+ compare_matrices(
413
+ matlab_delta_ab_pred[numb],
414
+ piv_result.passes[i - 1].delta_ab_pred_test[numb],
415
+ )
416
+ assert_arrays_close(
417
+ self,
418
+ matlab_delta_ab_pred,
419
+ piv_result.passes[i - 1].delta_ab_pred_test,
420
+ tol=0.0001,
421
+ )
422
+
423
+ def test_delta_ab_old(self):
424
+
425
+ self.config.data["piv"]["secondary_peak"] = False
426
+ cpu_backend = make_correlator_backend(config=self.config)
427
+ piv_result = cpu_backend.correlate_batch(self.img_pair, self.config)
428
+ print("PIV", piv_result.passes[0].delta_ab_old[:, 2, 0])
429
+ for i in range(1, 3): # self.MAX_PASS): # len(self.config.window_sizes)+1):
430
+ q_mat_file = self.base_path / f"MATLAB_delta_ab_old_padded_{i}.mat"
431
+ with h5py.File(q_mat_file, "r") as f:
432
+ matlab_delta_ab_old = np.array(f["delta_ab_old"], dtype=np.float32)
433
+ matlab_delta_ab_old = np.transpose(matlab_delta_ab_old, (2, 1, 0))
434
+ print("MATLAB shape", matlab_delta_ab_old.shape)
435
+ # print("MATLAB", matlab_delta_ab_pred.shape)
436
+ # print("PYTHON", piv_result.passes[i - 1].delta_ab_pred_test.shape)
437
+ for numb in range(1):
438
+ compare_matrices(
439
+ matlab_delta_ab_old[numb],
440
+ piv_result.passes[i - 1].delta_ab_old[numb],
441
+ )
442
+ print(matlab_delta_ab_old[:, 0, 0])
443
+ print(matlab_delta_ab_old[:, 1, 0])
444
+ print(matlab_delta_ab_old[:, 2, 0])
445
+
446
+ print(piv_result.passes[i - 1].delta_ab_old[:, 0, 0])
447
+ print(piv_result.passes[i - 1].delta_ab_old[:, 1, 0])
448
+ print(piv_result.passes[i - 1].delta_ab_old[:, 2, 0])
449
+
450
+ assert_arrays_close(
451
+ self,
452
+ matlab_delta_ab_old,
453
+ piv_result.passes[i - 1].delta_ab_old,
454
+ tol=0.01,
455
+ )
456
+
457
+ def test_delta_ab_old_not_padded(self):
458
+
459
+ self.config.data["piv"]["secondary_peak"] = False
460
+ cpu_backend = make_correlator_backend(config=self.config)
461
+ piv_result = cpu_backend.correlate_batch(self.img_pair, self.config)
462
+ for i in range(1, 3): # self.MAX_PASS): # len(self.config.window_sizes)+1):
463
+ q_mat_file = self.base_path / f"MATLAB_delta_ab_old_not_padded_{i}.mat"
464
+ with h5py.File(q_mat_file, "r") as f:
465
+ matlab_delta_ab_old = np.array(f["delta_ab_old"], dtype=np.float32)
466
+ matlab_delta_ab_old = np.transpose(matlab_delta_ab_old, (2, 1, 0))
467
+ print("MATLAB shape", matlab_delta_ab_old.shape)
468
+ # print("MATLAB", matlab_delta_ab_pred.shape)
469
+ # print("PYTHON", piv_result.passes[i - 1].delta_ab_pred_test.shape)
470
+ for numb in range(3):
471
+ compare_matrices(
472
+ matlab_delta_ab_old[numb],
473
+ piv_result.passes[i - 1].delta_ab_old_not_padded[numb],
474
+ )
475
+ assert_arrays_close(
476
+ self,
477
+ matlab_delta_ab_old,
478
+ piv_result.passes[i - 1].delta_ab_old_not_padded,
479
+ tol=0.0001,
480
+ )
481
+
482
+
483
+ import numpy as np
484
+ import pandas as pd
485
+
486
+
487
+ def compare_matrices(matlab_uy, python_uy, precision=6):
488
+ """
489
+ Compare two UY matrices (MATLAB vs Python) and summarize differences.
490
+
491
+ Parameters
492
+ ----------
493
+ matlab_uy : 2D array_like
494
+ MATLAB UY matrix.
495
+ python_uy : 2D array_like
496
+ Python UY matrix (same shape as matlab_uy).
497
+ precision : int
498
+ Number of decimal places to print.
499
+
500
+ Returns
501
+ -------
502
+ summary_df : pandas DataFrame
503
+ A table with MATLAB, Python, Diff, and Relative Diff for each element.
504
+ """
505
+ if matlab_uy.shape != python_uy.shape:
506
+ raise ValueError(
507
+ f"Matrices must have the same shape: {matlab_uy.shape} != {python_uy.shape}"
508
+ )
509
+
510
+ # Flatten for element-wise comparison
511
+ matlab_flat = matlab_uy.ravel()
512
+ python_flat = python_uy.ravel()
513
+
514
+ # Differences
515
+ diff = python_flat - matlab_flat
516
+ rel_diff = np.where(matlab_flat != 0, diff / matlab_flat, 0)
517
+
518
+ # Prepare a DataFrame for easy inspection
519
+ summary_df = pd.DataFrame(
520
+ {
521
+ "MATLAB": np.round(matlab_flat, precision),
522
+ "Python": np.round(python_flat, precision),
523
+ "Diff": np.round(diff, precision),
524
+ "RelDiff (%)": np.round(100 * rel_diff, precision),
525
+ }
526
+ )
527
+
528
+ # Summary stats
529
+ stats = {
530
+ "Mean Absolute Diff": np.mean(np.abs(diff)),
531
+ "Max Absolute Diff": np.max(np.abs(diff)),
532
+ "Min Absolute Diff": np.min(np.abs(diff)),
533
+ "Mean Relative Diff (%)": np.mean(np.abs(rel_diff)) * 100,
534
+ "Max Relative Diff (%)": np.max(np.abs(rel_diff)) * 100,
535
+ "Min Relative Diff (%)": np.min(np.abs(rel_diff)) * 100,
536
+ }
537
+
538
+ print("=== Full Element-wise Comparison ===")
539
+ pd.set_option("display.max_rows", None)
540
+
541
+ # Show all columns
542
+ pd.set_option("display.max_columns", None)
543
+ print(summary_df)
544
+ print("\n=== Summary Statistics ===")
545
+ for k, v in stats.items():
546
+ print(f"{k}: {v:.6f}")
547
+
548
+ print()
549
+ return summary_df, stats
550
+
551
+
552
+ # Example usage:
553
+ # summary, stats = compare_uy_matrices(matlab_uy_mat, piv_result.passes[i-1].uy_mat)
@@ -0,0 +1,41 @@
1
+ import unittest
2
+ import numpy as np
3
+ import dask.array as da
4
+ from pivtools_cli.preprocessing.filters import (
5
+ filter_images,
6
+ time_filter, pod_filter, clip_filter, invert_filter,
7
+ levelize_filter, lmax_filter, maxnorm_filter,
8
+ )
9
+ from scipy.ndimage import maximum_filter
10
+
11
+ class FilterTestCase(unittest.TestCase):
12
+ def setUp(self):
13
+ rng = np.random.default_rng(42)
14
+ arr = rng.normal(loc=100.0, scale=20.0, size=(3, 2, 4, 4)).astype(np.float32)
15
+ self.images = da.from_array(arr, chunks=(2, 2, 4, 4))
16
+ white = rng.uniform(low=0.5, high=2.0, size=(4, 4)).astype(np.float32)
17
+ self.white = da.from_array(white, chunks=(4, 4))
18
+
19
+ def test_clip_with_explicit_threshold(self):
20
+ out = clip_filter(self.images, threshold=(10, 20)).compute()
21
+ self.assertTrue(out.min() >= 10)
22
+ self.assertTrue(out.max() <= 20)
23
+
24
+ def test_invert_with_offset(self):
25
+ offset = 200.0
26
+ out = invert_filter(self.images, offset=offset).compute()
27
+ expected = offset - self.images.compute()
28
+ np.testing.assert_allclose(out, expected)
29
+
30
+ def test_levelize_with_white_image(self):
31
+ out = levelize_filter(self.images, self.white).compute()
32
+ expected = self.images.compute() / self.white.compute()
33
+ np.testing.assert_allclose(out, expected)
34
+
35
+ def test_lmax_filter(self):
36
+ size = (3, 3)
37
+ out = lmax_filter(self.images, size=size).compute()
38
+ for i in range(out.shape[0]):
39
+ for j in range(out.shape[1]):
40
+ local_max = maximum_filter(self.images[i, j].compute(), size=size)
41
+ np.testing.assert_allclose(out[i, j], local_max)
@@ -0,0 +1,5 @@
1
+ """
2
+ PIVTOOLs Core - Shared utilities for PIVTOOLs packages
3
+ """
4
+
5
+ __version__ = "0.1.0"