|
14 | 14 |
|
15 | 15 | from . import _sigtools
|
16 | 16 |
|
| 17 | + |
17 | 18 | __all__ = ['kaiser_beta', 'kaiser_atten', 'kaiserord',
|
18 |
| - 'firwin', 'firwin2', 'remez', 'firls', 'minimum_phase'] |
| 19 | + 'firwin', 'firwin2', 'firwin_2d', 'remez', 'firls', 'minimum_phase'] |
19 | 20 |
|
20 | 21 |
|
21 | 22 | # Some notes on function parameters:
|
@@ -323,6 +324,7 @@ def firwin(numtaps, cutoff, *, width=None, window='hamming', pass_zero=True,
|
323 | 324 | See Also
|
324 | 325 | --------
|
325 | 326 | firwin2
|
| 327 | + firwin_2d |
326 | 328 | firls
|
327 | 329 | minimum_phase
|
328 | 330 | remez
|
@@ -1284,3 +1286,148 @@ def minimum_phase(h: np.ndarray,
|
1284 | 1286 | h_minimum = h_temp.real
|
1285 | 1287 | n_out = (n_half + len(h) % 2) if half else len(h)
|
1286 | 1288 | return h_minimum[:n_out]
|
| 1289 | + |
| 1290 | + |
| 1291 | +def firwin_2d(hsize, window, *, fc=None, fs=2, circular=False, |
| 1292 | + pass_zero=True, scale=True): |
| 1293 | + """ |
| 1294 | + 2D FIR filter design using the window method. |
| 1295 | +
|
| 1296 | + This function computes the coefficients of a 2D finite impulse response |
| 1297 | + filter. The filter is separable with linear phase; it will be designed |
| 1298 | + as a product of two 1D filters with dimensions defined by `hsize`. |
| 1299 | + Additionally, it can create approximately circularly symmetric 2-D windows. |
| 1300 | + |
| 1301 | + Parameters |
| 1302 | + ---------- |
| 1303 | + hsize : tuple or list of length 2 |
| 1304 | + Lengths of the filter in each dimension. `hsize[0]` specifies the |
| 1305 | + number of coefficients in the row direction and `hsize[1]` specifies |
| 1306 | + the number of coefficients in the column direction. |
| 1307 | + window : tuple or list of length 2 or string |
| 1308 | + Desired window to use for each 1D filter or a single window type |
| 1309 | + for creating circularly symmetric 2-D windows. Each element should be |
| 1310 | + a string or tuple of string and parameter values. See |
| 1311 | + `~scipy.signal.get_window` for a list of windows and required |
| 1312 | + parameters. |
| 1313 | + fc : float or 1-D array_like, optional |
| 1314 | + Cutoff frequency of the filter in the same units as `fs`. This defines |
| 1315 | + the frequency at which the filter's gain drops to approximately -6 dB |
| 1316 | + (half power) in a low-pass or high-pass filter. For multi-band filters, |
| 1317 | + `fc` can be an array of cutoff frequencies (i.e., band edges) in the |
| 1318 | + range [0, fs/2], with each band specified in pairs. Required if |
| 1319 | + `circular` is False. |
| 1320 | + fs : float, optional |
| 1321 | + The sampling frequency of the signal. Default is 2. |
| 1322 | + circular : bool, optional |
| 1323 | + Whether to create a circularly symmetric 2-D window. Default is ``False``. |
| 1324 | + pass_zero : {True, False, 'bandpass', 'lowpass', 'highpass', 'bandstop'}, optional |
| 1325 | + This parameter is directly passed to `firwin` for each scalar frequency axis. |
| 1326 | + Hence, if ``True``, the DC gain, i.e., the gain at frequency (0, 0), is 1. |
| 1327 | + If ``False``, the DC gain is 0 at frequency (0, 0) if `circular` is ``True``. |
| 1328 | + If `circular` is ``False`` the frequencies (0, f1) and (f0, 0) will |
| 1329 | + have gain 0. |
| 1330 | + It can also be a string argument for the desired filter type |
| 1331 | + (equivalent to ``btype`` in IIR design functions). |
| 1332 | + scale : bool, optional |
| 1333 | + This parameter is directly passed to `firwin` for each scalar frequency axis. |
| 1334 | + Set to ``True`` to scale the coefficients so that the frequency |
| 1335 | + response is exactly unity at a certain frequency on one frequency axis. |
| 1336 | + That frequency is either: |
| 1337 | + |
| 1338 | + - 0 (DC) if the first passband starts at 0 (i.e. pass_zero is ``True``) |
| 1339 | + - `fs`/2 (the Nyquist frequency) if the first passband ends at `fs`/2 |
| 1340 | + (i.e., the filter is a single band highpass filter); |
| 1341 | + center of first passband otherwise |
| 1342 | +
|
| 1343 | + Returns |
| 1344 | + ------- |
| 1345 | + filter_2d : (hsize[0], hsize[1]) ndarray |
| 1346 | + Coefficients of 2D FIR filter. |
| 1347 | +
|
| 1348 | + Raises |
| 1349 | + ------ |
| 1350 | + ValueError |
| 1351 | + - If `hsize` and `window` are not 2-element tuples or lists. |
| 1352 | + - If `cutoff` is None when `circular` is True. |
| 1353 | + - If `cutoff` is outside the range [0, `fs`/2] and `circular` is ``False``. |
| 1354 | + - If any of the elements in `window` are not recognized. |
| 1355 | + RuntimeError |
| 1356 | + If `firwin` fails to converge when designing the filter. |
| 1357 | +
|
| 1358 | + See Also |
| 1359 | + -------- |
| 1360 | + firwin: FIR filter design using the window method for 1d arrays. |
| 1361 | + get_window: Return a window of a given length and type. |
| 1362 | +
|
| 1363 | + Examples |
| 1364 | + -------- |
| 1365 | + Generate a 5x5 low-pass filter with cutoff frequency 0.1: |
| 1366 | +
|
| 1367 | + >>> import numpy as np |
| 1368 | + >>> from scipy.signal import get_window |
| 1369 | + >>> from scipy.signal import firwin_2d |
| 1370 | + >>> hsize = (5, 5) |
| 1371 | + >>> window = (("kaiser", 5.0), ("kaiser", 5.0)) |
| 1372 | + >>> fc = 0.1 |
| 1373 | + >>> filter_2d = firwin_2d(hsize, window, fc=fc) |
| 1374 | + >>> filter_2d |
| 1375 | + array([[0.00025366, 0.00401662, 0.00738617, 0.00401662, 0.00025366], |
| 1376 | + [0.00401662, 0.06360159, 0.11695714, 0.06360159, 0.00401662], |
| 1377 | + [0.00738617, 0.11695714, 0.21507283, 0.11695714, 0.00738617], |
| 1378 | + [0.00401662, 0.06360159, 0.11695714, 0.06360159, 0.00401662], |
| 1379 | + [0.00025366, 0.00401662, 0.00738617, 0.00401662, 0.00025366]]) |
| 1380 | +
|
| 1381 | + Generate a circularly symmetric 5x5 low-pass filter with Hamming window: |
| 1382 | +
|
| 1383 | + >>> filter_2d = firwin_2d((5, 5), 'hamming', fc=fc, circular=True) |
| 1384 | + >>> filter_2d |
| 1385 | + array([[-0.00020354, -0.00020354, -0.00020354, -0.00020354, -0.00020354], |
| 1386 | + [-0.00020354, 0.01506844, 0.09907658, 0.01506844, -0.00020354], |
| 1387 | + [-0.00020354, 0.09907658, -0.00020354, 0.09907658, -0.00020354], |
| 1388 | + [-0.00020354, 0.01506844, 0.09907658, 0.01506844, -0.00020354], |
| 1389 | + [-0.00020354, -0.00020354, -0.00020354, -0.00020354, -0.00020354]]) |
| 1390 | +
|
| 1391 | + Generate Plots comparing the product of two 1d filters with a circular |
| 1392 | + symmetric filter: |
| 1393 | +
|
| 1394 | + >>> import matplotlib.pyplot as plt |
| 1395 | + >>> hsize, fc = (50, 50), 0.05 |
| 1396 | + >>> window = (("kaiser", 5.0), ("kaiser", 5.0)) |
| 1397 | + >>> filter0_2d = firwin_2d(hsize, window, fc=fc) |
| 1398 | + >>> filter1_2d = firwin_2d((50, 50), 'hamming', fc=fc, circular=True) |
| 1399 | + ... |
| 1400 | + >>> fg, (ax0, ax1) = plt.subplots(1, 2, tight_layout=True, figsize=(6.5, 3.5)) |
| 1401 | + >>> ax0.set_title("Product of 2 Windows") |
| 1402 | + >>> im0 = ax0.imshow(filter0_2d, cmap='viridis', origin='lower', aspect='equal') |
| 1403 | + >>> fg.colorbar(im0, ax=ax0, shrink=0.7) |
| 1404 | + >>> ax1.set_title("Circular Window") |
| 1405 | + >>> im1 = ax1.imshow(filter1_2d, cmap='plasma', origin='lower', aspect='equal') |
| 1406 | + >>> fg.colorbar(im1, ax=ax1, shrink=0.7) |
| 1407 | + >>> plt.show() |
| 1408 | + """ |
| 1409 | + if len(hsize) != 2: |
| 1410 | + raise ValueError("hsize must be a 2-element tuple or list") |
| 1411 | + |
| 1412 | + if circular: |
| 1413 | + if fc is None: |
| 1414 | + raise ValueError("Cutoff frequency `fc` must be " |
| 1415 | + "provided when `circular` is True") |
| 1416 | + |
| 1417 | + n_r = max(hsize[0], hsize[1]) * 8 # oversample 1d window by factor 8 |
| 1418 | + |
| 1419 | + win_r = firwin(n_r, cutoff=fc, window=window, fs=fs) |
| 1420 | + |
| 1421 | + f1, f2 = np.meshgrid(np.linspace(-1, 1, hsize[0]), np.linspace(-1, 1, hsize[1])) |
| 1422 | + r = np.sqrt(f1**2 + f2**2) |
| 1423 | + |
| 1424 | + win_2d = np.interp(r, np.linspace(0, 1, n_r), win_r) |
| 1425 | + return win_2d |
| 1426 | + |
| 1427 | + if len(window) != 2: |
| 1428 | + raise ValueError("window must be a 2-element tuple or list") |
| 1429 | + |
| 1430 | + row_filter = firwin(hsize[0], cutoff=fc, window=window[0], fs=fs) |
| 1431 | + col_filter = firwin(hsize[1], cutoff=fc, window=window[1], fs=fs) |
| 1432 | + |
| 1433 | + return np.outer(row_filter, col_filter) |
0 commit comments