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.
- pivtools-0.1.3.dist-info/METADATA +222 -0
- pivtools-0.1.3.dist-info/RECORD +127 -0
- pivtools-0.1.3.dist-info/WHEEL +5 -0
- pivtools-0.1.3.dist-info/entry_points.txt +3 -0
- pivtools-0.1.3.dist-info/top_level.txt +3 -0
- pivtools_cli/__init__.py +5 -0
- pivtools_cli/_build_marker.c +25 -0
- pivtools_cli/_build_marker.cp311-win_amd64.pyd +0 -0
- pivtools_cli/cli.py +225 -0
- pivtools_cli/example.py +139 -0
- pivtools_cli/lib/PIV_2d_cross_correlate.c +334 -0
- pivtools_cli/lib/PIV_2d_cross_correlate.h +22 -0
- pivtools_cli/lib/common.h +36 -0
- pivtools_cli/lib/interp2custom.c +146 -0
- pivtools_cli/lib/interp2custom.h +48 -0
- pivtools_cli/lib/peak_locate_gsl.c +711 -0
- pivtools_cli/lib/peak_locate_gsl.h +40 -0
- pivtools_cli/lib/peak_locate_gsl_print.c +736 -0
- pivtools_cli/lib/peak_locate_lm.c +751 -0
- pivtools_cli/lib/peak_locate_lm.h +27 -0
- pivtools_cli/lib/xcorr.c +342 -0
- pivtools_cli/lib/xcorr.h +31 -0
- pivtools_cli/lib/xcorr_cache.c +78 -0
- pivtools_cli/lib/xcorr_cache.h +26 -0
- pivtools_cli/piv/interp2custom/interp2custom.py +69 -0
- pivtools_cli/piv/piv.py +240 -0
- pivtools_cli/piv/piv_backend/base.py +825 -0
- pivtools_cli/piv/piv_backend/cpu_instantaneous.py +1005 -0
- pivtools_cli/piv/piv_backend/factory.py +28 -0
- pivtools_cli/piv/piv_backend/gpu_instantaneous.py +15 -0
- pivtools_cli/piv/piv_backend/infilling.py +445 -0
- pivtools_cli/piv/piv_backend/outlier_detection.py +306 -0
- pivtools_cli/piv/piv_backend/profile_cpu_instantaneous.py +230 -0
- pivtools_cli/piv/piv_result.py +40 -0
- pivtools_cli/piv/save_results.py +342 -0
- pivtools_cli/piv_cluster/cluster.py +108 -0
- pivtools_cli/preprocessing/filters.py +399 -0
- pivtools_cli/preprocessing/preprocess.py +79 -0
- pivtools_cli/tests/helpers.py +107 -0
- pivtools_cli/tests/instantaneous_piv/test_piv_integration.py +167 -0
- pivtools_cli/tests/instantaneous_piv/test_piv_integration_multi.py +553 -0
- pivtools_cli/tests/preprocessing/test_filters.py +41 -0
- pivtools_core/__init__.py +5 -0
- pivtools_core/config.py +703 -0
- pivtools_core/config.yaml +135 -0
- pivtools_core/image_handling/__init__.py +0 -0
- pivtools_core/image_handling/load_images.py +464 -0
- pivtools_core/image_handling/readers/__init__.py +53 -0
- pivtools_core/image_handling/readers/generic_readers.py +50 -0
- pivtools_core/image_handling/readers/lavision_reader.py +190 -0
- pivtools_core/image_handling/readers/registry.py +24 -0
- pivtools_core/paths.py +49 -0
- pivtools_core/vector_loading.py +248 -0
- pivtools_gui/__init__.py +3 -0
- pivtools_gui/app.py +687 -0
- pivtools_gui/calibration/__init__.py +0 -0
- pivtools_gui/calibration/app/__init__.py +0 -0
- pivtools_gui/calibration/app/views.py +1186 -0
- pivtools_gui/calibration/calibration_planar/planar_calibration_production.py +570 -0
- pivtools_gui/calibration/vector_calibration_production.py +544 -0
- pivtools_gui/config.py +703 -0
- pivtools_gui/image_handling/__init__.py +0 -0
- pivtools_gui/image_handling/load_images.py +464 -0
- pivtools_gui/image_handling/readers/__init__.py +53 -0
- pivtools_gui/image_handling/readers/generic_readers.py +50 -0
- pivtools_gui/image_handling/readers/lavision_reader.py +190 -0
- pivtools_gui/image_handling/readers/registry.py +24 -0
- pivtools_gui/masking/__init__.py +0 -0
- pivtools_gui/masking/app/__init__.py +0 -0
- pivtools_gui/masking/app/views.py +123 -0
- pivtools_gui/paths.py +49 -0
- pivtools_gui/piv_runner.py +261 -0
- pivtools_gui/pivtools.py +58 -0
- pivtools_gui/plotting/__init__.py +0 -0
- pivtools_gui/plotting/app/__init__.py +0 -0
- pivtools_gui/plotting/app/views.py +1671 -0
- pivtools_gui/plotting/plot_maker.py +220 -0
- pivtools_gui/post_processing/POD/__init__.py +0 -0
- pivtools_gui/post_processing/POD/app/__init__.py +0 -0
- pivtools_gui/post_processing/POD/app/views.py +647 -0
- pivtools_gui/post_processing/POD/pod_decompose.py +979 -0
- pivtools_gui/post_processing/POD/views.py +1096 -0
- pivtools_gui/post_processing/__init__.py +0 -0
- pivtools_gui/static/404.html +1 -0
- pivtools_gui/static/_next/static/chunks/117-d5793c8e79de5511.js +2 -0
- pivtools_gui/static/_next/static/chunks/484-cfa8b9348ce4f00e.js +1 -0
- pivtools_gui/static/_next/static/chunks/869-320a6b9bdafbb6d3.js +1 -0
- pivtools_gui/static/_next/static/chunks/app/_not-found/page-12f067ceb7415e55.js +1 -0
- pivtools_gui/static/_next/static/chunks/app/layout-b907d5f31ac82e9d.js +1 -0
- pivtools_gui/static/_next/static/chunks/app/page-334cc4e8444cde2f.js +1 -0
- pivtools_gui/static/_next/static/chunks/fd9d1056-ad15f396ddf9b7e5.js +1 -0
- pivtools_gui/static/_next/static/chunks/framework-f66176bb897dc684.js +1 -0
- pivtools_gui/static/_next/static/chunks/main-a1b3ced4d5f6d998.js +1 -0
- pivtools_gui/static/_next/static/chunks/main-app-8a63c6f5e7baee11.js +1 -0
- pivtools_gui/static/_next/static/chunks/pages/_app-72b849fbd24ac258.js +1 -0
- pivtools_gui/static/_next/static/chunks/pages/_error-7ba65e1336b92748.js +1 -0
- pivtools_gui/static/_next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
- pivtools_gui/static/_next/static/chunks/webpack-4a8ca7c99e9bb3d8.js +1 -0
- pivtools_gui/static/_next/static/css/7d3f2337d7ea12a5.css +3 -0
- pivtools_gui/static/_next/static/vQeR20OUdSSKlK4vukC4q/_buildManifest.js +1 -0
- pivtools_gui/static/_next/static/vQeR20OUdSSKlK4vukC4q/_ssgManifest.js +1 -0
- pivtools_gui/static/file.svg +1 -0
- pivtools_gui/static/globe.svg +1 -0
- pivtools_gui/static/grid.svg +8 -0
- pivtools_gui/static/index.html +1 -0
- pivtools_gui/static/index.txt +8 -0
- pivtools_gui/static/next.svg +1 -0
- pivtools_gui/static/vercel.svg +1 -0
- pivtools_gui/static/window.svg +1 -0
- pivtools_gui/stereo_reconstruction/__init__.py +0 -0
- pivtools_gui/stereo_reconstruction/app/__init__.py +0 -0
- pivtools_gui/stereo_reconstruction/app/views.py +1985 -0
- pivtools_gui/stereo_reconstruction/stereo_calibration_production.py +606 -0
- pivtools_gui/stereo_reconstruction/stereo_reconstruction_production.py +544 -0
- pivtools_gui/utils.py +63 -0
- pivtools_gui/vector_loading.py +248 -0
- pivtools_gui/vector_merging/__init__.py +1 -0
- pivtools_gui/vector_merging/app/__init__.py +1 -0
- pivtools_gui/vector_merging/app/views.py +759 -0
- pivtools_gui/vector_statistics/app/__init__.py +1 -0
- pivtools_gui/vector_statistics/app/views.py +710 -0
- pivtools_gui/vector_statistics/ensemble_statistics.py +49 -0
- pivtools_gui/vector_statistics/instantaneous_statistics.py +311 -0
- pivtools_gui/video_maker/__init__.py +0 -0
- pivtools_gui/video_maker/app/__init__.py +0 -0
- pivtools_gui/video_maker/app/views.py +436 -0
- pivtools_gui/video_maker/video_maker.py +662 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#ifndef PEAK_LOCATE_LM_H
|
|
2
|
+
#define PEAK_LOCATE_LM_H
|
|
3
|
+
|
|
4
|
+
/**** defines ****/
|
|
5
|
+
/* Peak localization window size (odd numbers only: 3, 5, 7, 9, ...)
|
|
6
|
+
* 5×5 is optimal for most PIV applications
|
|
7
|
+
* 7×7 provides more robustness for noisy data but is ~2x slower
|
|
8
|
+
* 3×3 is faster but less accurate
|
|
9
|
+
*/
|
|
10
|
+
#define PKSIZE_X 5
|
|
11
|
+
#define PKSIZE_Y 5
|
|
12
|
+
|
|
13
|
+
/******************************************************************************
|
|
14
|
+
* Fast Levenberg-Marquardt peak localization
|
|
15
|
+
*
|
|
16
|
+
* Drop-in replacement for GSL-based lsqpeaklocate
|
|
17
|
+
* Optimized for PIV correlation peak fitting with:
|
|
18
|
+
* - No external dependencies (GSL-free)
|
|
19
|
+
* - Direct Jacobian computation
|
|
20
|
+
* - Fast convergence for PIV peaks
|
|
21
|
+
* - Reduced iteration count
|
|
22
|
+
*****************************************************************************/
|
|
23
|
+
|
|
24
|
+
/* Main peak localization function - compatible with existing interface */
|
|
25
|
+
void lsqpeaklocate_lm(const float *xcorr, const int *N, float *peak_loc, int nPeaks, int iFitType, float *std_dev);
|
|
26
|
+
|
|
27
|
+
#endif
|
pivtools_cli/lib/xcorr.c
ADDED
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
#include "xcorr.h"
|
|
2
|
+
#include "common.h"
|
|
3
|
+
#include <fftw3.h>
|
|
4
|
+
#include <stdlib.h>
|
|
5
|
+
#include <string.h>
|
|
6
|
+
#include <omp.h>
|
|
7
|
+
|
|
8
|
+
/****************************************************
|
|
9
|
+
* unsigned convolve(a, b, c, N)
|
|
10
|
+
* convolve a with b and store the result in c
|
|
11
|
+
* accomplished using zero padded cross-correlation of a and b
|
|
12
|
+
*/
|
|
13
|
+
unsigned convolve(const float *fA, const float *fB, float *fC, const int *N)
|
|
14
|
+
{
|
|
15
|
+
int Npad[2];
|
|
16
|
+
int Nvox;
|
|
17
|
+
int i, j;
|
|
18
|
+
float *fApad, *fBpad, *fCpad;
|
|
19
|
+
unsigned uError;
|
|
20
|
+
|
|
21
|
+
/* allocate memory for padded versions of a, b and c */
|
|
22
|
+
Npad[0] = N[0]*2;
|
|
23
|
+
Npad[1] = N[1]*2;
|
|
24
|
+
Nvox = Npad[0] * Npad[1];
|
|
25
|
+
|
|
26
|
+
fApad = (float*)malloc(Nvox * sizeof(float));
|
|
27
|
+
fBpad = (float*)malloc(Nvox * sizeof(float));
|
|
28
|
+
fCpad = (float*)malloc(Nvox * sizeof(float));
|
|
29
|
+
if(!fApad || !fBpad || !fCpad)
|
|
30
|
+
{
|
|
31
|
+
if(fApad) free(fApad);
|
|
32
|
+
if(fBpad) free(fBpad);
|
|
33
|
+
if(fCpad) free(fCpad);
|
|
34
|
+
return ERROR_NOMEM;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* Copy input to zero-padded arrays (row-major) */
|
|
38
|
+
memset(fApad, 0, Nvox * sizeof(float));
|
|
39
|
+
memset(fBpad, 0, Nvox * sizeof(float));
|
|
40
|
+
for(i = 0; i < N[0]; ++i) /* rows */
|
|
41
|
+
{
|
|
42
|
+
for(j = 0; j < N[1]; ++j) /* columns */
|
|
43
|
+
{
|
|
44
|
+
fApad[SUB2IND_2D(i+N[0]/2, j+N[1]/2, Npad[1])] =
|
|
45
|
+
fA[SUB2IND_2D(i, j, N[1])];
|
|
46
|
+
fBpad[SUB2IND_2D(i+N[0]/2, j+N[1]/2, Npad[1])] =
|
|
47
|
+
fB[SUB2IND_2D(i, j, N[1])];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* cross-correlate */
|
|
52
|
+
uError = xcorr(fApad, fBpad, fCpad, Npad);
|
|
53
|
+
|
|
54
|
+
/* copy centre of fCpad into fC */
|
|
55
|
+
for(i = 0; i < N[0]; ++i)
|
|
56
|
+
{
|
|
57
|
+
for(j = 0; j < N[1]; ++j)
|
|
58
|
+
{
|
|
59
|
+
fC[SUB2IND_2D(i, j, N[1])] =
|
|
60
|
+
fCpad[SUB2IND_2D(i+N[0]/2, j+N[1]/2, Npad[1])];
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/* free memory */
|
|
65
|
+
free(fApad);
|
|
66
|
+
free(fBpad);
|
|
67
|
+
free(fCpad);
|
|
68
|
+
|
|
69
|
+
return uError;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
/****************************************************
|
|
74
|
+
* unsigned xcorr_create_plan(N, planstruct)
|
|
75
|
+
*
|
|
76
|
+
* create plans for cross-correlation
|
|
77
|
+
* xcorr_plan is NOT thread safe
|
|
78
|
+
* so run it inside an OMP critical directive
|
|
79
|
+
*
|
|
80
|
+
*/
|
|
81
|
+
|
|
82
|
+
unsigned xcorr_create_plan(const int *N, sPlan *pPlanStruct)
|
|
83
|
+
{
|
|
84
|
+
fftwf_plan plan_AB_fft;
|
|
85
|
+
fftwf_plan plan_C_ifft;
|
|
86
|
+
fftwf_real *ab_copy;
|
|
87
|
+
fftwf_real *c_copy;
|
|
88
|
+
fftwf_complex *AB_copy;
|
|
89
|
+
fftwf_complex *C;
|
|
90
|
+
unsigned numel, numel_fft;
|
|
91
|
+
int Nbackwards[2];
|
|
92
|
+
|
|
93
|
+
if(!pPlanStruct)
|
|
94
|
+
return ERROR_NOMEM;
|
|
95
|
+
|
|
96
|
+
/* initialise some bits we need */
|
|
97
|
+
numel = N[0] * N[1];
|
|
98
|
+
numel_fft = N[1] * (N[0]/2+1);
|
|
99
|
+
Nbackwards[0] = N[1];
|
|
100
|
+
Nbackwards[1] = N[0];
|
|
101
|
+
|
|
102
|
+
/* allocate memory using fftw's aligned allocation for SIMD optimization
|
|
103
|
+
* fftwf_alloc_* ensures proper alignment for SSE/AVX instructions
|
|
104
|
+
*/
|
|
105
|
+
ab_copy = (fftwf_real *) fftwf_alloc_real(numel * 2);
|
|
106
|
+
AB_copy = (fftwf_complex *) fftwf_alloc_complex(numel_fft * 2); // column major format
|
|
107
|
+
C = (fftwf_complex *) fftwf_alloc_complex(numel_fft );
|
|
108
|
+
c_copy = (fftwf_real *) fftwf_alloc_real(numel);
|
|
109
|
+
if(!ab_copy || !AB_copy || !C || !c_copy)
|
|
110
|
+
{
|
|
111
|
+
if(ab_copy) fftwf_free(ab_copy);
|
|
112
|
+
if(AB_copy) fftwf_free(AB_copy);
|
|
113
|
+
if(C) fftwf_free(C);
|
|
114
|
+
if(c_copy) fftwf_free(c_copy);
|
|
115
|
+
return ERROR_NOMEM;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/* create plans with FFTW_MEASURE for optimal performance
|
|
119
|
+
* FFTW_MEASURE takes more time initially but creates much faster plans
|
|
120
|
+
* The plans are reused many times, so this cost is amortized
|
|
121
|
+
*/
|
|
122
|
+
//fftwf_plan_with_nthreads(1);
|
|
123
|
+
plan_AB_fft = fftwf_plan_many_dft_r2c( 2, Nbackwards, 2,
|
|
124
|
+
ab_copy, NULL,
|
|
125
|
+
1, N[0]*N[1],
|
|
126
|
+
AB_copy, NULL,
|
|
127
|
+
1, N[1]*(N[0]/2+1),
|
|
128
|
+
FFTW_MEASURE | FFTW_DESTROY_INPUT);
|
|
129
|
+
plan_C_ifft = fftwf_plan_dft_c2r_2d( N[1], N[0], C, c_copy, FFTW_MEASURE | FFTW_DESTROY_INPUT );
|
|
130
|
+
if(!plan_AB_fft || !plan_C_ifft)
|
|
131
|
+
{
|
|
132
|
+
fftwf_free(AB_copy);
|
|
133
|
+
fftwf_free(c_copy);
|
|
134
|
+
fftwf_free(ab_copy);
|
|
135
|
+
fftwf_free(C);
|
|
136
|
+
if(!plan_C_ifft)
|
|
137
|
+
return ERROR_NOPLAN_BWD;
|
|
138
|
+
return ERROR_NOPLAN_FWD;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/* put into output structure */
|
|
142
|
+
pPlanStruct->plan_AB_fft = plan_AB_fft;
|
|
143
|
+
pPlanStruct->plan_C_ifft = plan_C_ifft;
|
|
144
|
+
pPlanStruct->ab_copy = ab_copy;
|
|
145
|
+
pPlanStruct->AB_copy = AB_copy;
|
|
146
|
+
pPlanStruct->C = C;
|
|
147
|
+
pPlanStruct->c_copy = c_copy;
|
|
148
|
+
pPlanStruct->N[0] = N[0];
|
|
149
|
+
pPlanStruct->N[1] = N[1];
|
|
150
|
+
|
|
151
|
+
return ERROR_NONE;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/****************************************************
|
|
155
|
+
* unsigned xcorr_destroy_plan(planstruct)
|
|
156
|
+
*
|
|
157
|
+
* destroy plans created by xcorr_create_plan
|
|
158
|
+
* xcorr_destroy_plan is NOT thread safe
|
|
159
|
+
* so call it from an OMP critical section
|
|
160
|
+
*/
|
|
161
|
+
|
|
162
|
+
unsigned xcorr_destroy_plan(sPlan *pPlanStruct)
|
|
163
|
+
{
|
|
164
|
+
if(!pPlanStruct)
|
|
165
|
+
return ERROR_NOMEM;
|
|
166
|
+
|
|
167
|
+
/* destroy plans first */
|
|
168
|
+
if(pPlanStruct->plan_AB_fft) fftwf_destroy_plan(pPlanStruct->plan_AB_fft);
|
|
169
|
+
if(pPlanStruct->plan_C_ifft) fftwf_destroy_plan(pPlanStruct->plan_C_ifft);
|
|
170
|
+
|
|
171
|
+
/* deallocate memory*/
|
|
172
|
+
if(pPlanStruct->ab_copy) fftwf_free(pPlanStruct->ab_copy);
|
|
173
|
+
if(pPlanStruct->AB_copy) fftwf_free(pPlanStruct->AB_copy);
|
|
174
|
+
if(pPlanStruct->C) fftwf_free(pPlanStruct->C);
|
|
175
|
+
if(pPlanStruct->c_copy) fftwf_free(pPlanStruct->c_copy);
|
|
176
|
+
|
|
177
|
+
return ERROR_NONE;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/****************************************************
|
|
181
|
+
* unsigned xcorr_preplanned(a, b, c, sPlan)
|
|
182
|
+
* same as xcorr(a,b,c,sPlan->N), but with preplanned FFTs
|
|
183
|
+
*
|
|
184
|
+
* calculates c = fftshift( IFFT(FFT(a) .* FFT(b)') )
|
|
185
|
+
* xcorr_preplanned is thread safe
|
|
186
|
+
* HOWEVER, you cannot use plans generated by other threads
|
|
187
|
+
*
|
|
188
|
+
*/
|
|
189
|
+
|
|
190
|
+
unsigned xcorr_preplanned(const float *a, const float *b, float *c, sPlan *pPlanStruct)
|
|
191
|
+
{
|
|
192
|
+
fftwf_plan plan_AB_fft;
|
|
193
|
+
fftwf_plan plan_C_ifft;
|
|
194
|
+
fftwf_real *ab_copy;
|
|
195
|
+
fftwf_real *c_copy;
|
|
196
|
+
fftwf_complex *AB_copy;
|
|
197
|
+
fftwf_complex *C;
|
|
198
|
+
unsigned numel, numel_fft;
|
|
199
|
+
int N[2];
|
|
200
|
+
int jswap, j;
|
|
201
|
+
float mul;
|
|
202
|
+
|
|
203
|
+
if(!pPlanStruct)
|
|
204
|
+
return ERROR_NOMEM;
|
|
205
|
+
|
|
206
|
+
/* load plan from sPlanStruct */
|
|
207
|
+
N[0] = pPlanStruct->N[0];
|
|
208
|
+
N[1] = pPlanStruct->N[1];
|
|
209
|
+
plan_AB_fft = pPlanStruct->plan_AB_fft;
|
|
210
|
+
plan_C_ifft = pPlanStruct->plan_C_ifft;
|
|
211
|
+
ab_copy = pPlanStruct->ab_copy;
|
|
212
|
+
AB_copy = pPlanStruct->AB_copy;
|
|
213
|
+
C = pPlanStruct->C;
|
|
214
|
+
c_copy = pPlanStruct->c_copy;
|
|
215
|
+
|
|
216
|
+
/****
|
|
217
|
+
* initialise variables to be used for later
|
|
218
|
+
*/
|
|
219
|
+
numel = N[0] * N[1];
|
|
220
|
+
numel_fft = N[1] * (N[0]/2+1);
|
|
221
|
+
|
|
222
|
+
/****
|
|
223
|
+
* copy in a and b, should be done after planning stage as fftwf_plan can overwrite
|
|
224
|
+
* input array
|
|
225
|
+
*/
|
|
226
|
+
memcpy(&ab_copy[0 ], a, numel*sizeof(float));
|
|
227
|
+
memcpy(&ab_copy[numel], b, numel*sizeof(float));
|
|
228
|
+
|
|
229
|
+
/****
|
|
230
|
+
* execute forward transform
|
|
231
|
+
*/
|
|
232
|
+
fftwf_execute(plan_AB_fft);
|
|
233
|
+
|
|
234
|
+
/****
|
|
235
|
+
* do C = A .* conj(B)
|
|
236
|
+
*/
|
|
237
|
+
multiply_conjugate((const fftwf_complex*)&AB_copy[0], (const fftwf_complex*)&AB_copy[numel_fft], C, numel_fft);
|
|
238
|
+
|
|
239
|
+
/***
|
|
240
|
+
* execute backward transform and renormalise
|
|
241
|
+
*/
|
|
242
|
+
fftwf_execute(plan_C_ifft);
|
|
243
|
+
|
|
244
|
+
/***
|
|
245
|
+
* Copy ifft result into c, after shifting data with fftshift
|
|
246
|
+
* For row-major arrays with shape [N[0], N[1]]:
|
|
247
|
+
* - N[0] = number of rows (height)
|
|
248
|
+
* - N[1] = number of columns (width)
|
|
249
|
+
* fftshift swaps quadrants: moves zero-frequency to center
|
|
250
|
+
*/
|
|
251
|
+
// Renormalise first
|
|
252
|
+
mul = 1.0f / (float)numel;
|
|
253
|
+
for(j=0; j<(int)numel; ++j) { c_copy[j] = c_copy[j] * mul; }
|
|
254
|
+
|
|
255
|
+
/* Perform fftshift for row-major data
|
|
256
|
+
* For each row, swap left/right halves
|
|
257
|
+
* Also swap top/bottom halves
|
|
258
|
+
*/
|
|
259
|
+
for(int row = 0; row < N[0]; ++row)
|
|
260
|
+
{
|
|
261
|
+
int row_swap = (row + N[0]/2) % N[0];
|
|
262
|
+
/* Copy left half of swapped row to right half of output */
|
|
263
|
+
memcpy(&c[SUB2IND_2D(row, N[1]/2, N[1])],
|
|
264
|
+
&c_copy[SUB2IND_2D(row_swap, 0, N[1])],
|
|
265
|
+
N[1]/2 * sizeof(float));
|
|
266
|
+
/* Copy right half of swapped row to left half of output */
|
|
267
|
+
memcpy(&c[SUB2IND_2D(row, 0, N[1])],
|
|
268
|
+
&c_copy[SUB2IND_2D(row_swap, N[1]/2, N[1])],
|
|
269
|
+
N[1]/2 * sizeof(float));
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return ERROR_NONE;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/****************************************************
|
|
276
|
+
* unsigned xcorr(a, b, c, N)
|
|
277
|
+
*
|
|
278
|
+
* calculates c = fftshift( IFFT(FFT(a) .* FFT(b)') )
|
|
279
|
+
*
|
|
280
|
+
* a, b, and c are 2D arrays of size N[0]xN[1] supplied in row-major (C-contiguous) format
|
|
281
|
+
* where N[0] = number of rows (height), N[1] = number of columns (width)
|
|
282
|
+
*
|
|
283
|
+
* return value is the error code. return value of 0 means success.
|
|
284
|
+
* NOTE: everything apart from fftwf_execute in fftwf_ library is NOT thread safe
|
|
285
|
+
* so must encapsulate it in an omp critical section
|
|
286
|
+
*
|
|
287
|
+
*/
|
|
288
|
+
|
|
289
|
+
unsigned xcorr(const float *a, const float *b, float *c, const int *N)
|
|
290
|
+
{
|
|
291
|
+
sPlan spPlan;
|
|
292
|
+
unsigned uError;
|
|
293
|
+
|
|
294
|
+
/* create plan */
|
|
295
|
+
#pragma omp critical
|
|
296
|
+
{
|
|
297
|
+
uError = xcorr_create_plan(N, &spPlan);
|
|
298
|
+
}
|
|
299
|
+
if(uError != ERROR_NONE)
|
|
300
|
+
return uError;
|
|
301
|
+
|
|
302
|
+
/* cross-correlate with xcorr_preplanned */
|
|
303
|
+
uError = xcorr_preplanned(a, b, c, &spPlan);
|
|
304
|
+
|
|
305
|
+
/* destroy plan */
|
|
306
|
+
#pragma omp critical
|
|
307
|
+
{
|
|
308
|
+
xcorr_destroy_plan(&spPlan);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
return uError;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
/****************************************************
|
|
316
|
+
* void multiply_conjugate(const fftwf_complex *A, const fftwf_complex *B, fftwf_complex *C, int N)
|
|
317
|
+
*
|
|
318
|
+
* performs C = A .* conj(B)
|
|
319
|
+
*
|
|
320
|
+
* A, B and C are treated as 1xN vectors of fftwf_complex
|
|
321
|
+
* Optimized with restrict pointers for better compiler optimization
|
|
322
|
+
*/
|
|
323
|
+
|
|
324
|
+
void multiply_conjugate(const fftwf_complex * restrict A, const fftwf_complex * restrict B, fftwf_complex * restrict C, int N)
|
|
325
|
+
{
|
|
326
|
+
int i;
|
|
327
|
+
|
|
328
|
+
/* Use restrict to help compiler optimize and enable SIMD vectorization */
|
|
329
|
+
#pragma omp simd
|
|
330
|
+
for(i=0; i<N; ++i)
|
|
331
|
+
{
|
|
332
|
+
const float Ar = A[i][0];
|
|
333
|
+
const float Ai = A[i][1];
|
|
334
|
+
const float Br = B[i][0];
|
|
335
|
+
const float Bi = B[i][1];
|
|
336
|
+
|
|
337
|
+
C[i][0] = Ar*Br + Ai*Bi;
|
|
338
|
+
C[i][1] = Ai*Br - Ar*Bi;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return;
|
|
342
|
+
}
|
pivtools_cli/lib/xcorr.h
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#ifndef XCORR_H
|
|
2
|
+
#define XCORR_H
|
|
3
|
+
|
|
4
|
+
#include <fftw3.h>
|
|
5
|
+
|
|
6
|
+
/**** defines ****/
|
|
7
|
+
#define fftwf_real float
|
|
8
|
+
|
|
9
|
+
/**** data structures ****/
|
|
10
|
+
typedef struct _sPlan
|
|
11
|
+
{
|
|
12
|
+
fftwf_plan plan_AB_fft;
|
|
13
|
+
fftwf_plan plan_C_ifft;
|
|
14
|
+
fftwf_real *ab_copy;
|
|
15
|
+
fftwf_real *c_copy;
|
|
16
|
+
fftwf_complex *AB_copy;
|
|
17
|
+
fftwf_complex *C;
|
|
18
|
+
int N[2];
|
|
19
|
+
} sPlan;
|
|
20
|
+
|
|
21
|
+
/**** functions ****/
|
|
22
|
+
unsigned convolve(const float *w1, const float *w2, float *conv, const int *N);
|
|
23
|
+
unsigned xcorr(const float *w1, const float *w2, float *corr, const int *N);
|
|
24
|
+
|
|
25
|
+
unsigned xcorr_create_plan(const int *N, sPlan *pPlanStruct);
|
|
26
|
+
unsigned xcorr_destroy_plan(sPlan *pPlanStruct);
|
|
27
|
+
unsigned xcorr_preplanned(const float *w1, const float *w2, float *corr, sPlan *pPlanStruct);
|
|
28
|
+
|
|
29
|
+
void multiply_conjugate(const fftwf_complex * restrict A, const fftwf_complex * restrict B, fftwf_complex * restrict C, int N);
|
|
30
|
+
|
|
31
|
+
#endif
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
#include "xcorr_cache.h"
|
|
2
|
+
#include "common.h"
|
|
3
|
+
#include <stdlib.h>
|
|
4
|
+
#include <string.h>
|
|
5
|
+
#include <stdio.h>
|
|
6
|
+
#include <sys/stat.h>
|
|
7
|
+
#include <errno.h>
|
|
8
|
+
#include <omp.h>
|
|
9
|
+
|
|
10
|
+
/******************************************************************************
|
|
11
|
+
* FFTW Wisdom Cache and Plan Reuse
|
|
12
|
+
*
|
|
13
|
+
* Implements persistent caching of FFTW plans to avoid expensive planning
|
|
14
|
+
* on repeated runs. Key optimizations:
|
|
15
|
+
*
|
|
16
|
+
* - Save/load FFTW wisdom to disk for persistent optimization
|
|
17
|
+
* - Cache plans by window size to avoid recreating identical plans
|
|
18
|
+
* - Thread-safe plan cache with minimal locking
|
|
19
|
+
*****************************************************************************/
|
|
20
|
+
|
|
21
|
+
/* Global wisdom management */
|
|
22
|
+
static omp_lock_t wisdom_lock;
|
|
23
|
+
static int wisdom_initialized = 0;
|
|
24
|
+
|
|
25
|
+
/* Initialize wisdom system */
|
|
26
|
+
void xcorr_cache_init(const char *wisdom_file)
|
|
27
|
+
{
|
|
28
|
+
if(!wisdom_initialized) {
|
|
29
|
+
omp_init_lock(&wisdom_lock);
|
|
30
|
+
wisdom_initialized = 1;
|
|
31
|
+
|
|
32
|
+
/* Try to load existing wisdom */
|
|
33
|
+
if(wisdom_file && wisdom_file[0] != '\0') {
|
|
34
|
+
omp_set_lock(&wisdom_lock);
|
|
35
|
+
FILE *f = fopen(wisdom_file, "r");
|
|
36
|
+
if(f) {
|
|
37
|
+
fftwf_import_wisdom_from_file(f);
|
|
38
|
+
fclose(f);
|
|
39
|
+
}
|
|
40
|
+
omp_unset_lock(&wisdom_lock);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/* Save wisdom to disk */
|
|
46
|
+
void xcorr_cache_save_wisdom(const char *wisdom_file)
|
|
47
|
+
{
|
|
48
|
+
if(!wisdom_initialized || !wisdom_file || wisdom_file[0] == '\0')
|
|
49
|
+
return;
|
|
50
|
+
|
|
51
|
+
omp_set_lock(&wisdom_lock);
|
|
52
|
+
FILE *f = fopen(wisdom_file, "w");
|
|
53
|
+
if(f) {
|
|
54
|
+
fftwf_export_wisdom_to_file(f);
|
|
55
|
+
fclose(f);
|
|
56
|
+
}
|
|
57
|
+
omp_unset_lock(&wisdom_lock);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/* Cleanup wisdom system */
|
|
61
|
+
void xcorr_cache_cleanup(void)
|
|
62
|
+
{
|
|
63
|
+
if(wisdom_initialized) {
|
|
64
|
+
omp_destroy_lock(&wisdom_lock);
|
|
65
|
+
wisdom_initialized = 0;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/* Get default wisdom file path (in user's home or temp directory) */
|
|
70
|
+
void xcorr_cache_get_default_wisdom_path(char *path, size_t max_len)
|
|
71
|
+
{
|
|
72
|
+
const char *home = getenv("HOME");
|
|
73
|
+
if(home) {
|
|
74
|
+
snprintf(path, max_len, "%s/.pypivtools_fftw_wisdom", home);
|
|
75
|
+
} else {
|
|
76
|
+
snprintf(path, max_len, "/tmp/.pypivtools_fftw_wisdom");
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#ifndef XCORR_CACHE_H
|
|
2
|
+
#define XCORR_CACHE_H
|
|
3
|
+
|
|
4
|
+
#include <fftw3.h>
|
|
5
|
+
#include <stddef.h>
|
|
6
|
+
|
|
7
|
+
/******************************************************************************
|
|
8
|
+
* FFTW Wisdom Cache
|
|
9
|
+
*
|
|
10
|
+
* Provides persistent caching of FFTW optimization data (wisdom) to avoid
|
|
11
|
+
* expensive plan creation on repeated runs.
|
|
12
|
+
*****************************************************************************/
|
|
13
|
+
|
|
14
|
+
/* Initialize the wisdom cache system */
|
|
15
|
+
void xcorr_cache_init(const char *wisdom_file);
|
|
16
|
+
|
|
17
|
+
/* Save current wisdom to disk */
|
|
18
|
+
void xcorr_cache_save_wisdom(const char *wisdom_file);
|
|
19
|
+
|
|
20
|
+
/* Cleanup wisdom cache */
|
|
21
|
+
void xcorr_cache_cleanup(void);
|
|
22
|
+
|
|
23
|
+
/* Get default wisdom file path */
|
|
24
|
+
void xcorr_cache_get_default_wisdom_path(char *path, size_t max_len);
|
|
25
|
+
|
|
26
|
+
#endif
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import ctypes
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
KERNEL_LANCZOS = 0
|
|
7
|
+
KERNEL_GAUSSIAN = 1
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Interp2Custom:
|
|
11
|
+
def __init__(self, lib_path=None):
|
|
12
|
+
if lib_path is None:
|
|
13
|
+
# Use platform-appropriate library extension
|
|
14
|
+
lib_extension = ".dll" if os.name == "nt" else ".so"
|
|
15
|
+
lib_path = os.path.join(
|
|
16
|
+
os.path.dirname(__file__), "../lib", f"libinterp2custom{lib_extension}"
|
|
17
|
+
)
|
|
18
|
+
self.lib = ctypes.CDLL(os.path.abspath(lib_path))
|
|
19
|
+
|
|
20
|
+
# Set C function signatures
|
|
21
|
+
self.lib.interp2custom.argtypes = [
|
|
22
|
+
np.ctypeslib.ndpointer(dtype=np.float32, flags="F_CONTIGUOUS"), # y
|
|
23
|
+
np.ctypeslib.ndpointer(dtype=np.uintp, flags="F_CONTIGUOUS"), # N
|
|
24
|
+
np.ctypeslib.ndpointer(dtype=np.float32, flags="F_CONTIGUOUS"), # f_i
|
|
25
|
+
np.ctypeslib.ndpointer(dtype=np.float32, flags="F_CONTIGUOUS"), # f_j
|
|
26
|
+
np.ctypeslib.ndpointer(
|
|
27
|
+
dtype=np.float32, flags="F_CONTIGUOUS"
|
|
28
|
+
), # yi (output)
|
|
29
|
+
ctypes.c_int, # n_interp
|
|
30
|
+
]
|
|
31
|
+
self.lib.interp2custom.restype = None
|
|
32
|
+
|
|
33
|
+
def interpolate(
|
|
34
|
+
self, y, i, j, kernel_type="lanczos", kernel_size=4, gaussian_std=0.65
|
|
35
|
+
):
|
|
36
|
+
# -------------------
|
|
37
|
+
# Validate inputs
|
|
38
|
+
# -------------------
|
|
39
|
+
if y.ndim != 2:
|
|
40
|
+
raise ValueError("y must be a 2D array")
|
|
41
|
+
if y.dtype != np.float32:
|
|
42
|
+
y = y.astype(np.float32)
|
|
43
|
+
if i.shape != j.shape:
|
|
44
|
+
raise ValueError("i and j must have the same shape")
|
|
45
|
+
if i.dtype != np.float32:
|
|
46
|
+
i = i.astype(np.float32)
|
|
47
|
+
if j.dtype != np.float32:
|
|
48
|
+
j = j.astype(np.float32)
|
|
49
|
+
|
|
50
|
+
# Kernel type as int
|
|
51
|
+
if kernel_type.lower() == "lanczos":
|
|
52
|
+
ktype = KERNEL_LANCZOS
|
|
53
|
+
elif kernel_type.lower() == "gaussian":
|
|
54
|
+
ktype = KERNEL_GAUSSIAN
|
|
55
|
+
else:
|
|
56
|
+
raise ValueError(f"Unknown kernel type '{kernel_type}'")
|
|
57
|
+
|
|
58
|
+
self.lib.interp1custom_generatelut(
|
|
59
|
+
ktype, kernel_size, ctypes.c_float(gaussian_std)
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
N = np.asfortranarray([y.shape[0], y.shape[1]], dtype=np.uintp)
|
|
63
|
+
n_interp = i.size
|
|
64
|
+
yi = np.asfortranarray(np.zeros_like(i, dtype=np.float32))
|
|
65
|
+
y = np.asfortranarray(y)
|
|
66
|
+
i = np.asfortranarray(i)
|
|
67
|
+
j = np.asfortranarray(j)
|
|
68
|
+
self.lib.interp2custom(y, N, i, j, yi, n_interp)
|
|
69
|
+
return yi
|