|
12 | 12 |
|
13 | 13 |
|
14 | 14 | __all__ = ["dwt", "idwt", "downcoef", "upcoef", "dwt_max_level", |
15 | | - "dwt_coeff_len"] |
| 15 | + "dwt_coeff_len", "pad"] |
16 | 16 |
|
17 | 17 |
|
18 | 18 | def dwt_max_level(data_len, filter_len): |
@@ -401,3 +401,80 @@ def upcoef(part, coeffs, wavelet, level=1, take=0): |
401 | 401 | if part not in 'ad': |
402 | 402 | raise ValueError("Argument 1 must be 'a' or 'd', not '%s'." % part) |
403 | 403 | return np.asarray(_upcoef(part == 'a', coeffs, wavelet, level, take)) |
| 404 | + |
| 405 | + |
| 406 | +def pad(x, pad_widths, mode): |
| 407 | + """Extend a 1D signal using a given boundary mode. |
| 408 | +
|
| 409 | + This is like `numpy.pad` but supports all PyWavelets boundary modes. |
| 410 | +
|
| 411 | + Parameters |
| 412 | + ---------- |
| 413 | + x : ndarray |
| 414 | + The array to pad |
| 415 | + pad_widths : {sequence, array_like, int} |
| 416 | + Number of values padded to the edges of each axis. |
| 417 | + ((before_1, after_1), … (before_N, after_N)) unique pad widths for each |
| 418 | + axis. ((before, after),) yields same before and after pad for each |
| 419 | + axis. (pad,) or int is a shortcut for before = after = pad width for |
| 420 | + all axes. |
| 421 | + mode : str, optional |
| 422 | + Signal extension mode, see Modes. |
| 423 | +
|
| 424 | + Returns |
| 425 | + ------- |
| 426 | + pad : ndarray |
| 427 | + Padded array of rank equal to array with shape increased according to |
| 428 | + `pad_width`. |
| 429 | +
|
| 430 | + """ |
| 431 | + if np.isscalar(pad_widths): |
| 432 | + pad_widths = (pad_widths, pad_widths) |
| 433 | + |
| 434 | + if x.ndim > 1: |
| 435 | + raise ValueError("This padding function is only for 1D signals.") |
| 436 | + |
| 437 | + if mode in ['symmetric', 'reflect']: |
| 438 | + xp = np.pad(x, pad_widths, mode=mode) |
| 439 | + elif mode in ['periodic', 'periodization']: |
| 440 | + if mode == 'periodization' and x.size % 2 == 1: |
| 441 | + raise ValueError("periodization expects an even length signal.") |
| 442 | + xp = np.pad(x, pad_widths, mode='wrap') |
| 443 | + elif mode == 'zeros': |
| 444 | + xp = np.pad(x, pad_widths, mode='constant', constant_values=0) |
| 445 | + elif mode == 'constant': |
| 446 | + xp = np.pad(x, pad_widths, mode='edge') |
| 447 | + elif mode == 'smooth': |
| 448 | + xp = np.pad(x, pad_widths, mode='linear_ramp', |
| 449 | + end_values=(x[0] + pad_widths[0] * (x[0] - x[1]), |
| 450 | + x[-1] + pad_widths[1] * (x[-1] - x[-2]))) |
| 451 | + elif mode == 'antisymmetric': |
| 452 | + # implement by flipping portions symmetric padding |
| 453 | + npad_l, npad_r = pad_widths |
| 454 | + xp = np.pad(x, pad_widths, mode='symmetric') |
| 455 | + r_edge = npad_l + x.size - 1 |
| 456 | + l_edge = npad_l |
| 457 | + # width of each reflected segment |
| 458 | + seg_width = x.size |
| 459 | + # flip reflected segments on the right of the original signal |
| 460 | + n = 1 |
| 461 | + while r_edge <= xp.size: |
| 462 | + segment_slice = slice(r_edge + 1, |
| 463 | + min(r_edge + 1 + seg_width, xp.size)) |
| 464 | + if n % 2: |
| 465 | + xp[segment_slice] *= -1 |
| 466 | + r_edge += seg_width |
| 467 | + n += 1 |
| 468 | + |
| 469 | + # flip reflected segments on the left of the original signal |
| 470 | + n = 1 |
| 471 | + while l_edge >= 0: |
| 472 | + segment_slice = slice(max(0, l_edge - seg_width), l_edge) |
| 473 | + if n % 2: |
| 474 | + xp[segment_slice] *= -1 |
| 475 | + l_edge -= seg_width |
| 476 | + n += 1 |
| 477 | + elif mode == 'antireflect': |
| 478 | + npad_l, npad_r = pad_widths |
| 479 | + xp = np.pad(x, pad_widths, mode='reflect', reflect_type='odd') |
| 480 | + return xp |
0 commit comments