DASPy-toolbox 1.0.0__py3-none-any.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 (49) hide show
  1. DASPy_toolbox-1.0.0.dist-info/LICENSE.txt +1 -0
  2. DASPy_toolbox-1.0.0.dist-info/METADATA +85 -0
  3. DASPy_toolbox-1.0.0.dist-info/RECORD +49 -0
  4. DASPy_toolbox-1.0.0.dist-info/WHEEL +5 -0
  5. DASPy_toolbox-1.0.0.dist-info/entry_points.txt +2 -0
  6. DASPy_toolbox-1.0.0.dist-info/top_level.txt +1 -0
  7. daspy/__init__.py +4 -0
  8. daspy/advanced_tools/__init__.py +0 -0
  9. daspy/advanced_tools/channel.py +354 -0
  10. daspy/advanced_tools/decomposition.py +165 -0
  11. daspy/advanced_tools/denoising.py +276 -0
  12. daspy/advanced_tools/fdct.py +789 -0
  13. daspy/advanced_tools/strain2vel.py +245 -0
  14. daspy/basic_tools/__init__.py +0 -0
  15. daspy/basic_tools/filter.py +257 -0
  16. daspy/basic_tools/freqattributes.py +117 -0
  17. daspy/basic_tools/preprocessing.py +238 -0
  18. daspy/basic_tools/visualization.py +186 -0
  19. daspy/core/__init__.py +4 -0
  20. daspy/core/collection.py +279 -0
  21. daspy/core/dasdatetime.py +72 -0
  22. daspy/core/example.pkl +0 -0
  23. daspy/core/make_example.py +32 -0
  24. daspy/core/read.py +544 -0
  25. daspy/core/section.py +1319 -0
  26. daspy/core/write.py +282 -0
  27. daspy/seismic_detection/__init__.py +1 -0
  28. daspy/seismic_detection/calc_travel_time.py +23 -0
  29. daspy/seismic_detection/core.py +119 -0
  30. daspy/seismic_detection/detection.py +12 -0
  31. daspy/seismic_detection/gamma/__init__.py +13 -0
  32. daspy/seismic_detection/gamma/_base.py +549 -0
  33. daspy/seismic_detection/gamma/_bayesian_mixture.py +875 -0
  34. daspy/seismic_detection/gamma/_gaussian_mixture.py +866 -0
  35. daspy/seismic_detection/gamma/app.py +192 -0
  36. daspy/seismic_detection/gamma/seismic_ops.py +478 -0
  37. daspy/seismic_detection/gamma/utils.py +512 -0
  38. daspy/seismic_detection/location.py +266 -0
  39. daspy/seismic_detection/magnitude.py +43 -0
  40. daspy/seismic_detection/phase_picking.py +67 -0
  41. daspy/structure_imaging/__init__.py +0 -0
  42. daspy/structure_imaging/ambient_noise.py +4 -0
  43. daspy/structure_imaging/dispersion.py +27 -0
  44. daspy/structure_imaging/fault_zone.py +59 -0
  45. daspy/structure_imaging/inversion.py +6 -0
  46. daspy/traffic_monitoring/JamDetection.py +6 -0
  47. daspy/traffic_monitoring/SpeedMeasurement.py +6 -0
  48. daspy/traffic_monitoring/VehicleDetection.py +6 -0
  49. daspy/traffic_monitoring/__init__.py +0 -0
@@ -0,0 +1,789 @@
1
+ # Purpose: Fast Discrete Curvelet Transform
2
+ # Author: Minzhe Hu
3
+ # Date: 2024.4.11
4
+ # Email: hmz2018@mail.ustc.edu.cn
5
+ # Modified from
6
+ # http://www.curvelet.org/download-secure.php?file=CurveLab-2.1.3.tar.gz
7
+ # (matlab version)
8
+ import numpy as np
9
+ from numpy.fft import fftshift, ifftshift, fft2, ifft2
10
+
11
+
12
+ def _round(x):
13
+ return np.round(x).astype(int)
14
+
15
+
16
+ def _floor(x):
17
+ return np.floor(x).astype(int)
18
+
19
+
20
+ def _ceil(x):
21
+ return np.ceil(x).astype(int)
22
+
23
+
24
+ def fdct_wrapping_window(x):
25
+ """
26
+ Creates the two halves of a C**inf compactly supported window.
27
+
28
+ :param x: vector or matrix of abscissae, the relevant ones from 0 to 1.
29
+ :return: vector or matrix containing samples of the left, resp. right half
30
+ of the window.
31
+ """
32
+
33
+ # Initialize the variables
34
+ wr = np.zeros_like(x)
35
+ wl = np.zeros_like(x)
36
+
37
+ # Set values close to zero to zero
38
+ x[np.abs(x) < 2**-52] = 0
39
+
40
+ # Calculate wr and wl
41
+ wr[(x > 0) & (x < 1)] = np.exp(
42
+ 1 - 1. / (1 - np.exp(1 - 1. / x[(x > 0) & (x < 1)])))
43
+ wr[x <= 0] = 1
44
+ wl[(x > 0) & (x < 1)] = np.exp(
45
+ 1 - 1. / (1 - np.exp(1 - 1. / (1 - x[(x > 0) & (x < 1)]))))
46
+ wl[x >= 1] = 1
47
+
48
+ # Normalize wr and wl
49
+ normalization = np.sqrt(wl**2 + wr**2)
50
+ wr = wr / normalization
51
+ wl = wl / normalization
52
+
53
+ return wl, wr
54
+
55
+
56
+ def fdct_wrapping(x, is_real=False, finest=2,
57
+ nbscales=None, nbangles_coarse=16):
58
+ """
59
+ Fast Discrete Curvelet Transform via wedge wrapping.
60
+
61
+ :param x: np.array. M-by-N matrix.
62
+ :param is_real: bool. Type of the transform, False for complex-valued
63
+ curvelets and True for real-valued curvelets.
64
+ :param finest: int. Chooses one of two possibilities for the coefficients at
65
+ the finest level: 1 for curvelets and 2 for wavelets.
66
+ :param nbscales: int. Number of scales including the coarsest wavele
67
+ level. Default set to ceil(log2(min(M,N)) - 3).
68
+ :param nbangles_coarse: int. Number of angles at the 2nd coarsest level,
69
+ minimum 8, must be a multiple of 4.
70
+ :return: 2-D list of np.ndarray. Array of curvelet coefficients.
71
+ C[j][l][k1,k2] is the coefficient at scale j(from finest to coarsest
72
+ scale), angle l(starts at the top-left corner and increases clockwise),
73
+ position k1, k2(size varies with j and l). If is_real is 1, there are
74
+ two types of curvelets, 'cosine' and 'sine'. For a given scale j, the
75
+ 'cosine' coefficients are stored in the first two quadrants (low values
76
+ of l), the 'sine' coefficients in the last two quadrants (high values of
77
+ l).
78
+ """
79
+ X = fftshift(fft2(ifftshift(x))) / np.sqrt(x.size)
80
+ N1, N2 = X.shape
81
+ if nbscales is None:
82
+ nbscales = _ceil(np.log2(min(N1, N2)) - 3)
83
+
84
+ # Initialization: data structure
85
+ nbangles = [1] + [nbangles_coarse * 2 ** ((nbscales - i) // 2)
86
+ for i in range(nbscales, 1, -1)]
87
+ if finest == 2:
88
+ nbangles[-1] = 1
89
+
90
+ C = []
91
+ for j in range(nbscales):
92
+ C.append([None] * nbangles[j])
93
+
94
+ # Loop: pyramidal scale decomposition
95
+ M1 = N1 / 3
96
+ M2 = N2 / 3
97
+
98
+ if finest == 1:
99
+ # Initialization: smooth periodic extension of high frequencies
100
+ bigN1 = 2 * _floor(2 * M1) + 1
101
+ bigN2 = 2 * _floor(2 * M2) + 1
102
+ equiv_index_1 = (_floor(N1 / 2) - _floor(2 * M1) +
103
+ np.arange(bigN1)) % N1
104
+ equiv_index_2 = (_floor(N2 / 2) - _floor(2 * M2) +
105
+ np.arange(bigN2)) % N2
106
+ X = X[np.ix_(equiv_index_1, equiv_index_2)]
107
+
108
+ window_length_1 = _floor(2 * M1) - _floor(M1) - (N1 % 3 == 0)
109
+ window_length_2 = _floor(2 * M2) - _floor(M2) - (N2 % 3 == 0)
110
+ coord_1 = np.linspace(0, 1, window_length_1)
111
+ coord_2 = np.linspace(0, 1, window_length_2)
112
+ wl_1, wr_1 = fdct_wrapping_window(coord_1)
113
+ wl_2, wr_2 = fdct_wrapping_window(coord_2)
114
+
115
+ lowpass_1 = np.concatenate((wl_1, np.ones(2 * _floor(M1) + 1), wr_1))
116
+ if N1 % 3 == 0:
117
+ lowpass_1 = np.concatenate(([0], lowpass_1, [0]))
118
+
119
+ lowpass_2 = np.concatenate((wl_2, np.ones(2 * _floor(M2) + 1), wr_2))
120
+ if N2 % 3 == 0:
121
+ lowpass_2 = np.concatenate(([0], lowpass_2, [0]))
122
+
123
+ lowpass = np.outer(lowpass_1, lowpass_2)
124
+ Xlow = X * lowpass
125
+ scales = np.arange(nbscales, 1, -1)
126
+
127
+ else:
128
+ M1 /= 2
129
+ M2 /= 2
130
+
131
+ window_length_1 = _floor(2 * M1) - _floor(M1)
132
+ window_length_2 = _floor(2 * M2) - _floor(M2)
133
+ coord_1 = np.linspace(0, 1, window_length_1)
134
+ coord_2 = np.linspace(0, 1, window_length_2)
135
+ wl_1, wr_1 = fdct_wrapping_window(coord_1)
136
+ wl_2, wr_2 = fdct_wrapping_window(coord_2)
137
+
138
+ lowpass_1 = np.concatenate((wl_1, np.ones(2 * _floor(M1) + 1), wr_1))
139
+ lowpass_2 = np.concatenate((wl_2, np.ones(2 * _floor(M2) + 1), wr_2))
140
+ lowpass = np.outer(lowpass_1, lowpass_2)
141
+ hipass = np.sqrt(1 - lowpass ** 2)
142
+
143
+ Xlow_index_1 = np.arange(-_floor(2 * M1),
144
+ _floor(2 * M1) + 1) + _ceil((N1 + 1) / 2) - 1
145
+ Xlow_index_2 = np.arange(-_floor(2 * M2),
146
+ _floor(2 * M2) + 1) + _ceil((N2 + 1) / 2) - 1
147
+ Xlow = X[np.ix_(Xlow_index_1, Xlow_index_2)] * lowpass
148
+ Xhi = X.copy()
149
+ Xhi[np.ix_(Xlow_index_1, Xlow_index_2)] *= hipass
150
+
151
+ C[nbscales - 1][0] = fftshift(ifft2(ifftshift(Xhi))
152
+ ) * np.sqrt(Xhi.size)
153
+ if is_real:
154
+ C[nbscales - 1][0] = C[nbscales - 1][0].real
155
+
156
+ scales = np.arange(nbscales - 1, 1, -1)
157
+ for j in scales - 1:
158
+ M1 /= 2
159
+ M2 /= 2
160
+ window_length_1 = _floor(2 * M1) - _floor(M1)
161
+ window_length_2 = _floor(2 * M2) - _floor(M2)
162
+ coord_1 = np.linspace(0, 1, window_length_1)
163
+ coord_2 = np.linspace(0, 1, window_length_2)
164
+ wl_1, wr_1 = fdct_wrapping_window(coord_1)
165
+ wl_2, wr_2 = fdct_wrapping_window(coord_2)
166
+
167
+ lowpass_1 = np.concatenate((wl_1, np.ones(2 * _floor(M1) + 1), wr_1))
168
+ lowpass_2 = np.concatenate((wl_2, np.ones(2 * _floor(M2) + 1), wr_2))
169
+ lowpass = np.outer(lowpass_1, lowpass_2)
170
+ hipass = np.sqrt(1 - lowpass ** 2)
171
+
172
+ Xhi = Xlow.copy()
173
+ Xlow_index_1 = np.arange(-_floor(2 * M1),
174
+ _floor(2 * M1) + 1) + _floor(4 * M1)
175
+ Xlow_index_2 = np.arange(-_floor(2 * M2),
176
+ _floor(2 * M2) + 1) + _floor(4 * M2)
177
+ Xlow = Xlow[np.ix_(Xlow_index_1, Xlow_index_2)]
178
+ Xhi[np.ix_(Xlow_index_1, Xlow_index_2)] = Xlow * hipass
179
+ Xlow *= lowpass
180
+
181
+ # Loop: angular decomposition
182
+ l = -1
183
+ nbquadrants = 2 + 2 * (not is_real)
184
+ nbangles_perquad = nbangles[j] // 4
185
+ for quadrant in range(1, nbquadrants + 1):
186
+ M_horiz = (M1, M2)[quadrant % 2]
187
+ M_vert = (M2, M1)[quadrant % 2]
188
+ wedge_ticks_left = _round(
189
+ np.linspace(
190
+ 0,
191
+ 1,
192
+ nbangles_perquad +
193
+ 1) *
194
+ _floor(
195
+ 4 *
196
+ M_horiz) +
197
+ 1)
198
+ wedge_ticks_right = 2 * _floor(4 * M_horiz) + 2 - wedge_ticks_left
199
+ if nbangles_perquad % 2:
200
+ wedge_ticks = np.concatenate(
201
+ (wedge_ticks_left, wedge_ticks_right[::-1]))
202
+ else:
203
+ wedge_ticks = np.concatenate(
204
+ (wedge_ticks_left, wedge_ticks_right[-2::-1]))
205
+
206
+ wedge_endpoints = wedge_ticks[1:-1:2]
207
+ wedge_midpoints = (wedge_endpoints[:-1] + wedge_endpoints[1:]) / 2
208
+ # Left corner wedge
209
+ l += 1
210
+ first_wedge_endpoint_vert = _round(
211
+ _floor(4 * M_vert) / nbangles_perquad + 1)
212
+ length_corner_wedge = _floor(4 * M_vert) - _floor(M_vert) + \
213
+ _ceil(first_wedge_endpoint_vert / 4)
214
+ Y_corner = np.arange(length_corner_wedge) + 1
215
+ XX, YY = np.meshgrid(
216
+ np.arange(2 * _floor(4 * M_horiz) + 1) + 1, Y_corner)
217
+ width_wedge = wedge_endpoints[1] + wedge_endpoints[0] - 1
218
+ slope_wedge = (_floor(4 * M_horiz) + 1 -
219
+ wedge_endpoints[0]) / _floor(4 * M_vert)
220
+ left_line = _round(
221
+ 2 - wedge_endpoints[0] + slope_wedge * (Y_corner - 1))
222
+ wrapped_data = np.zeros(
223
+ (length_corner_wedge, width_wedge), dtype=complex)
224
+ wrapped_XX = np.zeros(
225
+ (length_corner_wedge, width_wedge), dtype=int)
226
+ wrapped_YY = np.zeros(
227
+ (length_corner_wedge, width_wedge), dtype=int)
228
+ first_row = _floor(4 * M_vert) + 2 - \
229
+ _ceil((length_corner_wedge + 1) / 2) + \
230
+ (length_corner_wedge + 1) % 2 * (quadrant - 2 == quadrant % 2)
231
+ first_col = _floor(4 * M_horiz) + 2 - _ceil((width_wedge + 1) / 2) \
232
+ + (width_wedge + 1) % 2 * (quadrant - 3 == (quadrant - 3) % 2)
233
+ for row in Y_corner - 1:
234
+ cols = left_line[row] + \
235
+ (np.arange(width_wedge) - (left_line[row] - first_col)) \
236
+ % width_wedge
237
+ admissible_cols = _round(0.5 * (cols + 1 + abs(cols - 1))) - 1
238
+ new_row = (row - first_row + 1) % length_corner_wedge
239
+ wrapped_data[new_row, :] = Xhi[row,
240
+ admissible_cols] * (cols > 0)
241
+ wrapped_XX[new_row, :] = XX[row, admissible_cols]
242
+ wrapped_YY[new_row, :] = YY[row, admissible_cols]
243
+ slope_wedge_right = (_floor(4 * M_horiz) + 1 -
244
+ wedge_midpoints[0]) / _floor(4 * M_vert)
245
+ mid_line_right = wedge_midpoints[0] + \
246
+ slope_wedge_right * (wrapped_YY - 1)
247
+ coord_right = 0.5 + _floor(4 * M_vert) / \
248
+ (wedge_endpoints[1] - wedge_endpoints[0]) * \
249
+ (wrapped_XX - mid_line_right) / \
250
+ (_floor(4 * M_vert) + 1 - wrapped_YY)
251
+ C2 = 1 / (1 / (2 * (_floor(4 * M_horiz)) / (wedge_endpoints[0] -
252
+ 1) - 1) + 1 / (2 * (_floor(4 * M_vert)) / (
253
+ first_wedge_endpoint_vert - 1) - 1))
254
+ C1 = C2 / (2 * (_floor(4 * M_vert)) /
255
+ (first_wedge_endpoint_vert - 1) - 1)
256
+ wrapped_XX[(wrapped_XX - 1) / _floor(4 * M_horiz) +
257
+ (wrapped_YY - 1) / _floor(4 * M_vert) == 2] += 1
258
+ coord_corner = C1 + C2 * ((wrapped_XX - 1) / _floor(4 * M_horiz) -
259
+ (wrapped_YY - 1) / _floor(4 * M_vert)) / (2 -
260
+ ((wrapped_XX - 1) / _floor(4 * M_horiz) + (wrapped_YY - 1) /
261
+ _floor(4 * M_vert)))
262
+ wl_left, _ = fdct_wrapping_window(coord_corner)
263
+ _, wr_right = fdct_wrapping_window(coord_right)
264
+ wrapped_data = wrapped_data * wl_left * wr_right
265
+ if not is_real:
266
+ wrapped_data = np.rot90(wrapped_data, -(quadrant - 1))
267
+ C[j][l] = fftshift(ifft2(ifftshift(wrapped_data))) * \
268
+ np.sqrt(wrapped_data.size)
269
+ else:
270
+ wrapped_data = np.rot90(wrapped_data, -(quadrant - 1))
271
+ x = fftshift(ifft2(ifftshift(wrapped_data))) * \
272
+ np.sqrt(wrapped_data.size)
273
+ C[j][l] = np.sqrt(2) * x.real
274
+ C[j][l + nbangles[j] // 2] = np.sqrt(2) * x.imag
275
+
276
+ # Regular wedges
277
+ length_wedge = _floor(4 * M_vert) - _floor(M_vert)
278
+ Y = np.arange(length_wedge) + 1
279
+ first_row = _floor(4 * M_vert) + 2 - _ceil((length_wedge + 1) / 2) \
280
+ + (length_wedge + 1) % 2 * (quadrant - 2 == quadrant % 2)
281
+ for subl in range(1, nbangles_perquad - 1):
282
+ l += 1
283
+ width_wedge = wedge_endpoints[subl +
284
+ 1] - wedge_endpoints[subl - 1] + 1
285
+ slope_wedge = ((_floor(4 * M_horiz) + 1) -
286
+ wedge_endpoints[subl]) / _floor(4 * M_vert)
287
+ left_line = _round(
288
+ wedge_endpoints[subl - 1] + slope_wedge * (Y - 1))
289
+ wrapped_data = np.zeros(
290
+ (length_wedge, width_wedge), dtype=complex)
291
+ wrapped_XX = np.zeros((length_wedge, width_wedge), dtype=int)
292
+ wrapped_YY = np.zeros((length_wedge, width_wedge), dtype=int)
293
+ first_col = _floor(4 * M_horiz) + 2 - \
294
+ _ceil((width_wedge + 1) / 2) + \
295
+ (width_wedge + 1) % 2 * (quadrant - 3 == (quadrant - 3) % 2)
296
+ for row in Y - 1:
297
+ cols = left_line[row] + (np.arange(width_wedge) -
298
+ (left_line[row] - first_col)) % width_wedge - 1
299
+ new_row = (row - first_row + 1) % length_wedge
300
+ wrapped_data[new_row, :] = Xhi[row, cols]
301
+ wrapped_XX[new_row, :] = XX[row, cols]
302
+ wrapped_YY[new_row, :] = YY[row, cols]
303
+ slope_wedge_left = ((_floor(4 * M_horiz) + 1) -
304
+ wedge_midpoints[subl - 1]) / _floor(4 * M_vert)
305
+ mid_line_left = wedge_midpoints[subl - 1] + \
306
+ slope_wedge_left * (wrapped_YY - 1)
307
+ coord_left = 0.5 + _floor(4 * M_vert) / \
308
+ (wedge_endpoints[subl] - wedge_endpoints[subl - 1]) * \
309
+ (wrapped_XX - mid_line_left) / \
310
+ (_floor(4 * M_vert) + 1 - wrapped_YY)
311
+ slope_wedge_right = ((_floor(4 * M_horiz) + 1) -
312
+ wedge_midpoints[subl]) / _floor(4 * M_vert)
313
+ mid_line_right = wedge_midpoints[subl] + \
314
+ slope_wedge_right * (wrapped_YY - 1)
315
+ coord_right = 0.5 + _floor(4 * M_vert) / \
316
+ (wedge_endpoints[subl + 1] - wedge_endpoints[subl]) * \
317
+ (wrapped_XX - mid_line_right) / \
318
+ (_floor(4 * M_vert) + 1 - wrapped_YY)
319
+
320
+ wl_left, _ = fdct_wrapping_window(coord_left)
321
+ _, wr_right = fdct_wrapping_window(coord_right)
322
+ wrapped_data = wrapped_data * wl_left * wr_right
323
+ if not is_real:
324
+ wrapped_data = np.rot90(wrapped_data, -(quadrant - 1))
325
+ C[j][l] = fftshift(ifft2(ifftshift(wrapped_data))) * \
326
+ np.sqrt(wrapped_data.size)
327
+ else:
328
+ wrapped_data = np.rot90(wrapped_data, -(quadrant - 1))
329
+ x = fftshift(ifft2(ifftshift(wrapped_data))) * \
330
+ np.sqrt(wrapped_data.size)
331
+ C[j][l] = np.sqrt(2) * x.real
332
+ C[j][l + nbangles[j] // 2] = np.sqrt(2) * x.imag
333
+
334
+ # Right corner wedge
335
+ l += 1
336
+ width_wedge = 4 * _floor(4 * M_horiz) + 3 - \
337
+ wedge_endpoints[-1] - wedge_endpoints[-2]
338
+ slope_wedge = ((_floor(4 * M_horiz) + 1) -
339
+ wedge_endpoints[-1]) / _floor(4 * M_vert)
340
+ left_line = _round(
341
+ wedge_endpoints[-2] + slope_wedge * (Y_corner - 1))
342
+ wrapped_data = np.zeros((length_corner_wedge, width_wedge),
343
+ dtype=complex)
344
+ wrapped_XX = np.zeros((length_corner_wedge, width_wedge), dtype=int)
345
+ wrapped_YY = np.zeros((length_corner_wedge, width_wedge), dtype=int)
346
+ first_row = _floor(4 * M_vert) + 2 - \
347
+ _ceil((length_corner_wedge + 1) / 2) + \
348
+ (length_corner_wedge + 1) % 2 * (quadrant - 2 == quadrant % 2)
349
+ first_col = _floor(4 * M_horiz) + 2 - _ceil((width_wedge + 1) / 2) + \
350
+ (width_wedge + 1) % 2 * (quadrant - 3 == (quadrant - 3) % 2)
351
+ for row in Y_corner - 1:
352
+ cols = left_line[row] + (np.arange(width_wedge) -
353
+ (left_line[row] - first_col)) % width_wedge
354
+ admissible_cols = _round(0.5 * (cols + 2 * _floor(4 * M_horiz)
355
+ + 1 - np.abs(cols - (2 * _floor(4 * M_horiz) + 1)))) - 1
356
+ new_row = (row - first_row + 1) % length_corner_wedge
357
+ wrapped_data[new_row, :] = Xhi[row, admissible_cols] * \
358
+ (cols <= (2 * _floor(4 * M_horiz) + 1))
359
+ wrapped_XX[new_row, :] = XX[row, admissible_cols]
360
+ wrapped_YY[new_row, :] = YY[row, admissible_cols]
361
+
362
+ slope_wedge_left = ((_floor(4 * M_horiz) + 1) -
363
+ wedge_midpoints[-1]) / _floor(4 * M_vert)
364
+ mid_line_left = wedge_midpoints[-1] + \
365
+ slope_wedge_left * (wrapped_YY - 1)
366
+ coord_left = 0.5 + _floor(4 * M_vert) / \
367
+ (wedge_endpoints[-1] - wedge_endpoints[-2]) * \
368
+ (wrapped_XX - mid_line_left) / \
369
+ (_floor(4 * M_vert) + 1 - wrapped_YY)
370
+ C2 = -1 / (2 * (_floor(4 * M_horiz)) / (wedge_endpoints[-1] - 1) -
371
+ 1 + 1 / (2 * (_floor(4 * M_vert)) /
372
+ (first_wedge_endpoint_vert - 1) - 1))
373
+ C1 = -C2 * (2 * (_floor(4 * M_horiz)) /
374
+ (wedge_endpoints[-1] - 1) - 1)
375
+ wrapped_XX[(wrapped_XX - 1) / _floor(4 * M_horiz) ==
376
+ (wrapped_YY - 1) / _floor(4 * M_vert)] -= 1
377
+ coord_corner = C1 + C2 * (2 - ((wrapped_XX - 1) /
378
+ _floor(4 * M_horiz) + (wrapped_YY - 1) / _floor(4 * M_vert))) \
379
+ / ((wrapped_XX - 1) / _floor(4 * M_horiz) - (wrapped_YY - 1) /
380
+ _floor(4 * M_vert))
381
+ wl_left, _ = fdct_wrapping_window(coord_left)
382
+ _, wr_right = fdct_wrapping_window(coord_corner)
383
+ wrapped_data = wrapped_data * wl_left * wr_right
384
+ if not is_real:
385
+ wrapped_data = np.rot90(wrapped_data, -(quadrant - 1))
386
+ C[j][l] = fftshift(ifft2(ifftshift(wrapped_data))
387
+ ) * np.sqrt(wrapped_data.size)
388
+ else:
389
+ wrapped_data = np.rot90(wrapped_data, -(quadrant - 1))
390
+ x = fftshift(ifft2(ifftshift(wrapped_data))) * \
391
+ np.sqrt(wrapped_data.size)
392
+ C[j][l] = np.sqrt(2) * x.real
393
+ C[j][l + nbangles[j] // 2] = np.sqrt(2) * x.imag
394
+
395
+ if quadrant < nbquadrants:
396
+ Xhi = np.rot90(Xhi)
397
+ # Coarsest wavelet level
398
+ C[0][0] = fftshift(ifft2(ifftshift(Xlow))) * np.sqrt(Xlow.size)
399
+ if is_real:
400
+ C[0][0] = C[0][0].real
401
+
402
+ return C
403
+
404
+
405
+ def ifdct_wrapping(C, is_real=False, size=None):
406
+ """
407
+ Inverse Fast Discrete Curvelet Transform via wedge wrapping. This is in fact
408
+ the adjoint, also the pseudo-inverse
409
+
410
+ :param C: 2-D list of np.ndarray. Array of curvelet coefficients.
411
+ :param is_real: bool. Type of the transform, False for complex-valued
412
+ curvelets and True for real-valued curvelets.
413
+ :param size: tuple of ints. Size of the image to be recovered (not necessary
414
+ if finest = 2)
415
+ :return: 2-D np.ndarray.
416
+ """
417
+ nbscales = len(C)
418
+ nbangles_coarse = len(C[1])
419
+ nbangles = [1] + [nbangles_coarse * 2 ** ((nbscales - i) // 2)
420
+ for i in range(nbscales, 1, -1)]
421
+ if len(C[-1]) == 1:
422
+ finest = 2
423
+ nbangles[nbscales - 1] = 1
424
+ else:
425
+ finest = 1
426
+
427
+ if size is None:
428
+ if finest == 1:
429
+ raise ValueError("Require output size.")
430
+ else:
431
+ N1, N2 = C[-1][0].shape
432
+ else:
433
+ N1, N2 = size
434
+
435
+ M1 = N1 / 3
436
+ M2 = N2 / 3
437
+
438
+ if finest == 1:
439
+ # Initialization: preparing the lowpass filter at finest scale
440
+ window_length_1 = _floor(2 * M1) - _floor(M1) - (N1 % 3 == 0)
441
+ window_length_2 = _floor(2 * M2) - _floor(M2) - (N2 % 3 == 0)
442
+ coord_1 = np.linspace(0, 1, window_length_1)
443
+ coord_2 = np.linspace(0, 1, window_length_2)
444
+ wl_1, wr_1 = fdct_wrapping_window(coord_1)
445
+ wl_2, wr_2 = fdct_wrapping_window(coord_2)
446
+
447
+ lowpass_1 = np.concatenate((wl_1, np.ones(2 * _floor(M1) + 1), wr_1))
448
+ if N1 % 3 == 0:
449
+ lowpass_1 = np.concatenate(([0], lowpass_1, [0]))
450
+
451
+ lowpass_2 = np.concatenate((wl_2, np.ones(2 * _floor(M2) + 1), wr_2))
452
+ if N2 % 3 == 0:
453
+ lowpass_2 = np.concatenate(([0], lowpass_2, [0]))
454
+
455
+ lowpass = np.outer(lowpass_1, lowpass_2)
456
+ scales = np.arange(nbscales, 1, -1)
457
+ else:
458
+ M1 /= 2
459
+ M2 /= 2
460
+
461
+ window_length_1 = _floor(2 * M1) - _floor(M1)
462
+ window_length_2 = _floor(2 * M2) - _floor(M2)
463
+ coord_1 = np.linspace(0, 1, window_length_1)
464
+ coord_2 = np.linspace(0, 1, window_length_2)
465
+ wl_1, wr_1 = fdct_wrapping_window(coord_1)
466
+ wl_2, wr_2 = fdct_wrapping_window(coord_2)
467
+
468
+ lowpass_1 = np.concatenate((wl_1, np.ones(2 * _floor(M1) + 1), wr_1))
469
+ lowpass_2 = np.concatenate((wl_2, np.ones(2 * _floor(M2) + 1), wr_2))
470
+ lowpass = np.outer(lowpass_1, lowpass_2)
471
+ hipass_finest = np.sqrt(1 - lowpass ** 2)
472
+
473
+ scales = np.arange(nbscales - 1, 1, -1)
474
+
475
+ bigN1 = 2 * _floor(2 * M1) + 1
476
+ bigN2 = 2 * _floor(2 * M2) + 1
477
+ X = np.zeros((bigN1, bigN2), dtype=complex)
478
+
479
+ # Loop: pyramidal reconstruction
480
+
481
+ Xj_topleft_1 = 1
482
+ Xj_topleft_2 = 1
483
+ for j in scales - 1:
484
+ M1 /= 2
485
+ M2 /= 2
486
+
487
+ window_length_1 = _floor(2 * M1) - _floor(M1)
488
+ window_length_2 = _floor(2 * M2) - _floor(M2)
489
+ coord_1 = np.linspace(0, 1, window_length_1)
490
+ coord_2 = np.linspace(0, 1, window_length_2)
491
+ wl_1, wr_1 = fdct_wrapping_window(coord_1)
492
+ wl_2, wr_2 = fdct_wrapping_window(coord_2)
493
+
494
+ lowpass_1 = np.concatenate((wl_1, np.ones(2 * _floor(M1) + 1), wr_1))
495
+ lowpass_2 = np.concatenate((wl_2, np.ones(2 * _floor(M2) + 1), wr_2))
496
+ lowpass_next = np.outer(lowpass_1, lowpass_2)
497
+ hipass = np.sqrt(1 - lowpass_next ** 2)
498
+ Xj = np.zeros((2 * _floor(4 * M1) + 1, 2 * _floor(4 * M2) + 1),
499
+ dtype=complex)
500
+
501
+ # Loop: angles
502
+ l = -1
503
+ nbquadrants = 2 + 2 * (not is_real)
504
+ nbangles_perquad = nbangles[j] // 4
505
+ for quadrant in range(1, nbquadrants + 1):
506
+ M_horiz = (M1, M2)[quadrant % 2]
507
+ M_vert = (M2, M1)[quadrant % 2]
508
+ wedge_ticks_left = _round(np.linspace(0, 1, nbangles_perquad + 1) *
509
+ _floor(4 * M_horiz) + 1)
510
+ wedge_ticks_right = 2 * _floor(4 * M_horiz) + 2 - wedge_ticks_left
511
+ if nbangles_perquad % 2:
512
+ wedge_ticks = np.concatenate(
513
+ (wedge_ticks_left, wedge_ticks_right[::-1]))
514
+ else:
515
+ wedge_ticks = np.concatenate(
516
+ (wedge_ticks_left, wedge_ticks_right[-2::-1]))
517
+ wedge_endpoints = wedge_ticks[1:-1:2]
518
+ wedge_midpoints = (wedge_endpoints[:-1] + wedge_endpoints[1:]) / 2
519
+
520
+ # Left corner wedge
521
+ l += 1
522
+ first_wedge_endpoint_vert = _round(_floor(4 * M_vert) /
523
+ nbangles_perquad + 1)
524
+ length_corner_wedge = _floor(4 * M_vert) - _floor(M_vert) + \
525
+ _ceil(first_wedge_endpoint_vert / 4)
526
+ Y_corner = np.arange(length_corner_wedge) + 1
527
+ [XX, YY] = np.meshgrid(np.arange(1, 2 * _floor(4 * M_horiz) + 2),
528
+ Y_corner)
529
+ width_wedge = wedge_endpoints[1] + wedge_endpoints[0] - 1
530
+ slope_wedge = (_floor(4 * M_horiz) + 1 -
531
+ wedge_endpoints[0]) / _floor(4 * M_vert)
532
+ left_line = _round(2 - wedge_endpoints[0] +
533
+ slope_wedge * (Y_corner - 1))
534
+ wrapped_XX = np.zeros((length_corner_wedge, width_wedge), dtype=int)
535
+ wrapped_YY = np.zeros((length_corner_wedge, width_wedge), dtype=int)
536
+ first_row = _floor(4 * M_vert) + \
537
+ 2 - _ceil((length_corner_wedge + 1) / 2) + \
538
+ (length_corner_wedge + 1) % 2 * (quadrant - 2 == quadrant % 2)
539
+ first_col = _floor(4 * M_horiz) + 2 - _ceil((width_wedge + 1) / 2) \
540
+ + (width_wedge + 1) % 2 * (quadrant - 3 == (quadrant - 3) % 2)
541
+ for row in Y_corner - 1:
542
+ cols = left_line[row] + (np.arange(width_wedge) -
543
+ (left_line[row] - first_col)) % width_wedge
544
+ new_row = (row - first_row + 1) % length_corner_wedge
545
+ admissible_cols = _round(0.5 * (cols + 1 + abs(cols - 1))) - 1
546
+ wrapped_XX[new_row, :] = XX[row, admissible_cols]
547
+ wrapped_YY[new_row, :] = YY[row, admissible_cols]
548
+
549
+ slope_wedge_right = (_floor(4 * M_horiz) + 1 - wedge_midpoints[0]) \
550
+ / _floor(4 * M_vert)
551
+ mid_line_right = wedge_midpoints[0] + \
552
+ slope_wedge_right * (wrapped_YY - 1)
553
+ coord_right = 0.5 + _floor(4 * M_vert) / (wedge_endpoints[1] -
554
+ wedge_endpoints[0]) * (wrapped_XX - mid_line_right) / \
555
+ (_floor(4 * M_vert) + 1 - wrapped_YY)
556
+ C2 = 1 / (1 / (2 * (_floor(4 * M_horiz)) /
557
+ (wedge_endpoints[0] - 1) - 1) + 1 / (2 * (_floor(4 * M_vert))
558
+ / (first_wedge_endpoint_vert - 1) - 1))
559
+ C1 = C2 / (2 * (_floor(4 * M_vert)) /
560
+ (first_wedge_endpoint_vert - 1) - 1)
561
+ wrapped_XX[(wrapped_XX - 1) / _floor(4 * M_horiz) +
562
+ (wrapped_YY - 1) / _floor(4 * M_vert) == 2] += 1
563
+ coord_corner = C1 + C2 * ((wrapped_XX - 1) / _floor(4 * M_horiz) -
564
+ (wrapped_YY - 1) / _floor(4 * M_vert)) / (2 - ((wrapped_XX - 1)
565
+ / _floor(4 * M_horiz) + (wrapped_YY - 1) / _floor(4 * M_vert)))
566
+ wl_left, _ = fdct_wrapping_window(coord_corner)
567
+ _, wr_right = fdct_wrapping_window(coord_right)
568
+
569
+ if not is_real:
570
+ wrapped_data = fftshift(fft2(ifftshift(C[j][l]))) / \
571
+ np.sqrt(C[j][l].size)
572
+ wrapped_data = np.rot90(wrapped_data, quadrant - 1)
573
+ else:
574
+ x = C[j][l] + 1j * C[j][l + nbangles[j] // 2]
575
+ wrapped_data = fftshift(fft2(ifftshift(x))) / \
576
+ np.sqrt(x.size * 2)
577
+ wrapped_data = np.rot90(wrapped_data, quadrant - 1)
578
+
579
+ wrapped_data = wrapped_data * wl_left * wr_right
580
+ # Unwrapping data
581
+ for row in Y_corner - 1:
582
+ cols = left_line[row] + (np.arange(width_wedge) -
583
+ (left_line[row] - first_col)) % width_wedge
584
+ admissible_cols = _round(0.5 * (cols + 1 + abs(cols - 1))) - 1
585
+ new_row = (row - first_row + 1) % length_corner_wedge
586
+ Xj[row, admissible_cols] += wrapped_data[new_row, :]
587
+ # We use the following property: in an assignment A(B) = C where
588
+ # B and C are vectors, if some value x repeats in B, then the
589
+ # last occurrence of x is the one corresponding to the eventual
590
+ # assignment.
591
+
592
+ # Regular wedges
593
+ length_wedge = _floor(4 * M_vert) - _floor(M_vert)
594
+ Y = np.arange(length_wedge) + 1
595
+ first_row = _floor(4 * M_vert) + 2 - _ceil((length_wedge + 1) / 2) \
596
+ + (length_wedge + 1) % 2 * (quadrant - 2 == quadrant % 2)
597
+ for subl in range(1, nbangles_perquad - 1):
598
+ l += 1
599
+ width_wedge = wedge_endpoints[subl + 1] - \
600
+ wedge_endpoints[subl - 1] + 1
601
+ slope_wedge = ((_floor(4 * M_horiz) + 1) -
602
+ wedge_endpoints[subl]) / _floor(4 * M_vert)
603
+ left_line = _round(wedge_endpoints[subl - 1] +
604
+ slope_wedge * (Y - 1))
605
+ wrapped_XX = np.zeros((length_wedge, width_wedge), dtype=int)
606
+ wrapped_YY = np.zeros((length_wedge, width_wedge), dtype=int)
607
+ first_col = _floor(4 * M_horiz) + 2 - \
608
+ _ceil((width_wedge + 1) / 2) + \
609
+ (width_wedge + 1) % 2 * (quadrant - 3 == (quadrant - 3) % 2)
610
+ for row in Y - 1:
611
+ cols = left_line[row] + (np.arange(width_wedge) -
612
+ (left_line[row] - first_col)) % width_wedge - 1
613
+ new_row = (row - first_row + 1) % length_wedge
614
+ wrapped_XX[new_row, :] = XX[row, cols]
615
+ wrapped_YY[new_row, :] = YY[row, cols]
616
+
617
+ slope_wedge_left = ((_floor(4 * M_horiz) + 1) -
618
+ wedge_midpoints[subl - 1]) / _floor(4 * M_vert)
619
+ mid_line_left = wedge_midpoints[subl - 1] + \
620
+ slope_wedge_left * (wrapped_YY - 1)
621
+ coord_left = 0.5 + _floor(4 * M_vert) / (wedge_endpoints[subl]
622
+ - wedge_endpoints[subl - 1]) * \
623
+ (wrapped_XX - mid_line_left) / \
624
+ (_floor(4 * M_vert) + 1 - wrapped_YY)
625
+ slope_wedge_right = ((_floor(4 * M_horiz) + 1) -
626
+ wedge_midpoints[subl]) / _floor(4 * M_vert)
627
+ mid_line_right = wedge_midpoints[subl] + \
628
+ slope_wedge_right * (wrapped_YY - 1)
629
+ coord_right = 0.5 + _floor(4 * M_vert) / \
630
+ (wedge_endpoints[subl + 1] - wedge_endpoints[subl]) * \
631
+ (wrapped_XX - mid_line_right) / \
632
+ (_floor(4 * M_vert) + 1 - wrapped_YY)
633
+ wl_left, _ = fdct_wrapping_window(coord_left)
634
+ _, wr_right = fdct_wrapping_window(coord_right)
635
+ if not is_real:
636
+ wrapped_data = fftshift(fft2(ifftshift(C[j][l]))) / \
637
+ np.sqrt(C[j][l].size)
638
+ wrapped_data = np.rot90(wrapped_data, quadrant - 1)
639
+ else:
640
+ x = C[j][l] + 1j * C[j][l + nbangles[j] // 2]
641
+ wrapped_data = fftshift(
642
+ fft2(ifftshift(x))) / np.sqrt(x.size * 2)
643
+ wrapped_data = np.rot90(wrapped_data, quadrant - 1)
644
+
645
+ wrapped_data = wrapped_data * wl_left * wr_right
646
+
647
+ # Unwrapping data
648
+ for row in Y - 1:
649
+ cols = left_line[row] + (np.arange(width_wedge) -
650
+ (left_line[row] - first_col)) % width_wedge - 1
651
+ new_row = (row + 1 - first_row) % length_wedge
652
+ Xj[row, cols] += wrapped_data[new_row, :]
653
+
654
+ # Right corner wedge
655
+ l += 1
656
+ width_wedge = 4 * _floor(4 * M_horiz) + 3 - \
657
+ wedge_endpoints[-1] - wedge_endpoints[-2]
658
+ slope_wedge = ((_floor(4 * M_horiz) + 1) -
659
+ wedge_endpoints[-1]) / _floor(4 * M_vert)
660
+ left_line = _round(
661
+ wedge_endpoints[-2] + slope_wedge * (Y_corner - 1))
662
+ wrapped_XX = np.zeros(
663
+ (length_corner_wedge, width_wedge), dtype=int)
664
+ wrapped_YY = np.zeros(
665
+ (length_corner_wedge, width_wedge), dtype=int)
666
+ first_row = _floor(4 * M_vert) + 2 - \
667
+ _ceil((length_corner_wedge + 1) / 2) + \
668
+ (length_corner_wedge + 1) % 2 * (quadrant - 2 == quadrant % 2)
669
+ first_col = _floor(4 * M_horiz) + 2 - _ceil((width_wedge + 1) / 2) \
670
+ + (width_wedge + 1) % 2 * (quadrant - 3 == (quadrant - 3) % 2)
671
+ for row in Y_corner - 1:
672
+ cols = left_line[row] + (np.arange(width_wedge) -
673
+ (left_line[row] - first_col)) % width_wedge
674
+ admissible_cols = _round(0.5 * (cols + 2 * _floor(4 * M_horiz)
675
+ + 1 - np.abs(cols - (2 * _floor(4 * M_horiz) + 1)))) - 1
676
+ new_row = (row - first_row + 1) % length_corner_wedge
677
+ wrapped_XX[new_row, :] = XX[row, admissible_cols]
678
+ wrapped_YY[new_row, :] = YY[row, admissible_cols]
679
+
680
+ slope_wedge_left = ((_floor(4 * M_horiz) + 1) -
681
+ wedge_midpoints[-1]) / _floor(4 * M_vert)
682
+ mid_line_left = wedge_midpoints[-1] + \
683
+ slope_wedge_left * (wrapped_YY - 1)
684
+ coord_left = 0.5 + _floor(4 * M_vert) / \
685
+ (wedge_endpoints[-1] - wedge_endpoints[-2]) * \
686
+ (wrapped_XX - mid_line_left) / \
687
+ (_floor(4 * M_vert) + 1 - wrapped_YY)
688
+ C2 = -1 / (2 * (_floor(4 * M_horiz)) / (wedge_endpoints[-1] - 1)
689
+ - 1 + 1 / (2 * (_floor(4 * M_vert)) /
690
+ (first_wedge_endpoint_vert - 1) - 1))
691
+ C1 = -C2 * (2 * (_floor(4 * M_horiz)) /
692
+ (wedge_endpoints[-1] - 1) - 1)
693
+
694
+ wrapped_XX[(wrapped_XX - 1) / _floor(4 * M_horiz) ==
695
+ (wrapped_YY - 1) / _floor(4 * M_vert)] -= 1
696
+ coord_corner = C1 + C2 * (2 - ((wrapped_XX - 1) /
697
+ _floor(4 * M_horiz) + (wrapped_YY - 1) / _floor(4 * M_vert))) \
698
+ / ((wrapped_XX - 1) / _floor(4 * M_horiz) - (wrapped_YY - 1) /
699
+ _floor(4 * M_vert))
700
+ wl_left, _ = fdct_wrapping_window(coord_left)
701
+ _, wr_right = fdct_wrapping_window(coord_corner)
702
+
703
+ if not is_real:
704
+ wrapped_data = fftshift(
705
+ fft2(ifftshift(C[j][l]))) / np.sqrt(C[j][l].size)
706
+ wrapped_data = np.rot90(wrapped_data, quadrant - 1)
707
+ else:
708
+ x = C[j][l] + 1j * C[j][l + nbangles[j] // 2]
709
+ wrapped_data = fftshift(
710
+ fft2(ifftshift(x))) / np.sqrt(x.size * 2)
711
+ wrapped_data = np.rot90(wrapped_data, quadrant - 1)
712
+
713
+ wrapped_data = wrapped_data * wl_left * wr_right
714
+
715
+ # Unwrapping data
716
+ for row in Y_corner - 1:
717
+ cols = left_line[row] + (np.arange(width_wedge) -
718
+ (left_line[row] - first_col)) % width_wedge
719
+ admissible_cols = _round(1 / 2 * (cols + 2 * _floor(4 * M_horiz)
720
+ + 1 - abs(cols - (2 * _floor(4 * M_horiz) + 1)))) - 1
721
+ new_row = (row + 1 - first_row) % length_corner_wedge
722
+ Xj[row, np.flip(admissible_cols)] += wrapped_data[new_row, ::-1]
723
+ # We use the following property: in an assignment A[B] = C where
724
+ # B and C are vectors, if some value x repeats in B, then the
725
+ # last occurrence of x is the one corresponding to the eventual
726
+ # assignment.
727
+
728
+ Xj = np.rot90(Xj)
729
+
730
+ Xj *= lowpass
731
+ Xj_index1 = np.arange(-_floor(2 * M1),
732
+ _floor(2 * M1) + 1) + _floor(4 * M1)
733
+ Xj_index2 = np.arange(-_floor(2 * M2),
734
+ _floor(2 * M2) + 1) + _floor(4 * M2)
735
+
736
+ Xj[np.ix_(Xj_index1, Xj_index2)] *= hipass
737
+
738
+ loc_1 = Xj_topleft_1 + np.arange(2 * _floor(4 * M1) + 1) - 1
739
+ loc_2 = Xj_topleft_2 + np.arange(2 * _floor(4 * M2) + 1) - 1
740
+ X[np.ix_(loc_1, loc_2)] += Xj
741
+
742
+ # Preparing for loop reentry or exit
743
+ Xj_topleft_1 += _floor(4 * M1) - _floor(2 * M1)
744
+ Xj_topleft_2 += _floor(4 * M2) - _floor(2 * M2)
745
+
746
+ lowpass = lowpass_next
747
+
748
+ if is_real:
749
+ Y = X
750
+ X = np.rot90(X, 2)
751
+ X = X + np.conj(Y)
752
+
753
+ # Coarsest wavelet level
754
+ M1 = M1 / 2
755
+ M2 = M2 / 2
756
+ Xj = fftshift(fft2(ifftshift(C[0][0]))) / np.sqrt(C[0][0].size)
757
+ loc_1 = Xj_topleft_1 + np.arange(2 * _floor(4 * M1) + 1) - 1
758
+ loc_2 = Xj_topleft_2 + np.arange(2 * _floor(4 * M2) + 1) - 1
759
+ X[np.ix_(loc_1, loc_2)] += Xj * lowpass
760
+
761
+ # Finest level
762
+ M1 = N1 / 3
763
+ M2 = N2 / 3
764
+ if finest == 1:
765
+ # Folding back onto N1-by-N2 matrix
766
+ shift_1 = _floor(2 * M1) - _floor(N1 / 2)
767
+ shift_2 = _floor(2 * M2) - _floor(N2 / 2)
768
+ Y = X[:, np.arange(N2) + shift_2]
769
+ Y[:, np.arange(N2 - shift_2, N2)] += X[:, :shift_2]
770
+ Y[:, :shift_2] += X[:, N2 + shift_2:N2 + 2 * shift_2]
771
+ X = Y[np.arange(N1) + shift_1, :]
772
+ X[np.arange(N1 - shift_1, N1), :] += Y[:shift_1, :]
773
+ X[:shift_1, :] += Y[N1 + shift_1:N1 + 2 * shift_1, :]
774
+ else:
775
+ # Extension to a N1-by-N2 matrix
776
+ Y = fftshift(fft2(ifftshift(C[nbscales - 1][0]))) / \
777
+ np.sqrt(C[nbscales - 1][0].size)
778
+ X_topleft_1 = _ceil((N1 + 1) / 2) - _floor(M1)
779
+ X_topleft_2 = _ceil((N2 + 1) / 2) - _floor(M2)
780
+ loc_1 = X_topleft_1 + np.arange(2 * _floor(M1) + 1) - 1
781
+ loc_2 = X_topleft_2 + np.arange(2 * _floor(M2) + 1) - 1
782
+ Y[np.ix_(loc_1, loc_2)] = Y[np.ix_(loc_1, loc_2)] * hipass_finest + X
783
+ X = Y
784
+
785
+ x = fftshift(ifft2(ifftshift(X))) * np.sqrt(X.size)
786
+ if is_real:
787
+ x = np.real(x)
788
+
789
+ return x