CoolFace
Apppublic

undetectable/voice-clone

sourceHugging Facemitupdated 2y agoView on Hugging Face
1likes
utils.py2608 linesDownload Raw Back to packages
1#!/usr/bin/env python2# -*- coding: utf-8 -*-3"""Utility functions"""4 5from __future__ import annotations6 7import scipy.ndimage8import scipy.sparse9 10import numpy as np11import numba12from numpy.lib.stride_tricks import as_strided13 14from .._cache import cache15from .exceptions import ParameterError16from .deprecation import Deprecated17from numpy.typing import ArrayLike, DTypeLike18from typing import (19    Any,20    Callable,21    Iterable,22    List,23    Dict,24    Optional,25    Sequence,26    Tuple,27    TypeVar,28    Union,29    overload,30)31from typing_extensions import Literal32from .._typing import _SequenceLike, _FloatLike_co, _ComplexLike_co33 34# Constrain STFT block sizes to 256 KB35MAX_MEM_BLOCK = 2**8 * 2**1036 37__all__ = [38    "MAX_MEM_BLOCK",39    "frame",40    "pad_center",41    "expand_to",42    "fix_length",43    "valid_audio",44    "valid_int",45    "is_positive_int",46    "valid_intervals",47    "fix_frames",48    "axis_sort",49    "localmax",50    "localmin",51    "normalize",52    "peak_pick",53    "sparsify_rows",54    "shear",55    "stack",56    "fill_off_diagonal",57    "index_to_slice",58    "sync",59    "softmask",60    "buf_to_float",61    "tiny",62    "cyclic_gradient",63    "dtype_r2c",64    "dtype_c2r",65    "count_unique",66    "is_unique",67    "abs2",68    "phasor",69]70 71 72def frame(73    x: np.ndarray,74    *,75    frame_length: int,76    hop_length: int,77    axis: int = -1,78    writeable: bool = False,79    subok: bool = False,80) -> np.ndarray:81    """Slice a data array into (overlapping) frames.82 83    This implementation uses low-level stride manipulation to avoid84    making a copy of the data.  The resulting frame representation85    is a new view of the same input data.86 87    For example, a one-dimensional input ``x = [0, 1, 2, 3, 4, 5, 6]``88    can be framed with frame length 3 and hop length 2 in two ways.89    The first (``axis=-1``), results in the array ``x_frames``::90 91        [[0, 2, 4],92         [1, 3, 5],93         [2, 4, 6]]94 95    where each column ``x_frames[:, i]`` contains a contiguous slice of96    the input ``x[i * hop_length : i * hop_length + frame_length]``.97 98    The second way (``axis=0``) results in the array ``x_frames``::99 100        [[0, 1, 2],101         [2, 3, 4],102         [4, 5, 6]]103 104    where each row ``x_frames[i]`` contains a contiguous slice of the input.105 106    This generalizes to higher dimensional inputs, as shown in the examples below.107    In general, the framing operation increments by 1 the number of dimensions,108    adding a new "frame axis" either before the framing axis (if ``axis < 0``)109    or after the framing axis (if ``axis >= 0``).110 111    Parameters112    ----------113    x : np.ndarray114        Array to frame115    frame_length : int > 0 [scalar]116        Length of the frame117    hop_length : int > 0 [scalar]118        Number of steps to advance between frames119    axis : int120        The axis along which to frame.121    writeable : bool122        If ``True``, then the framed view of ``x`` is read-only.123        If ``False``, then the framed view is read-write.  Note that writing to the framed view124        will also write to the input array ``x`` in this case.125    subok : bool126        If True, sub-classes will be passed-through, otherwise the returned array will be127        forced to be a base-class array (default).128 129    Returns130    -------131    x_frames : np.ndarray [shape=(..., frame_length, N_FRAMES, ...)]132        A framed view of ``x``, for example with ``axis=-1`` (framing on the last dimension)::133 134            x_frames[..., j] == x[..., j * hop_length : j * hop_length + frame_length]135 136        If ``axis=0`` (framing on the first dimension), then::137 138            x_frames[j] = x[j * hop_length : j * hop_length + frame_length]139 140    Raises141    ------142    ParameterError143        If ``x.shape[axis] < frame_length``, there is not enough data to fill one frame.144 145        If ``hop_length < 1``, frames cannot advance.146 147    See Also148    --------149    numpy.lib.stride_tricks.as_strided150 151    Examples152    --------153    Extract 2048-sample frames from monophonic signal with a hop of 64 samples per frame154 155    >>> y, sr = librosa.load(librosa.ex('trumpet'))156    >>> frames = librosa.util.frame(y, frame_length=2048, hop_length=64)157    >>> frames158    array([[-1.407e-03, -2.604e-02, ..., -1.795e-05, -8.108e-06],159           [-4.461e-04, -3.721e-02, ..., -1.573e-05, -1.652e-05],160           ...,161           [ 7.960e-02, -2.335e-01, ..., -6.815e-06,  1.266e-05],162           [ 9.568e-02, -1.252e-01, ...,  7.397e-06, -1.921e-05]],163          dtype=float32)164    >>> y.shape165    (117601,)166 167    >>> frames.shape168    (2048, 1806)169 170    Or frame along the first axis instead of the last:171 172    >>> frames = librosa.util.frame(y, frame_length=2048, hop_length=64, axis=0)173    >>> frames.shape174    (1806, 2048)175 176    Frame a stereo signal:177 178    >>> y, sr = librosa.load(librosa.ex('trumpet', hq=True), mono=False)179    >>> y.shape180    (2, 117601)181    >>> frames = librosa.util.frame(y, frame_length=2048, hop_length=64)182    (2, 2048, 1806)183 184    Carve an STFT into fixed-length patches of 32 frames with 50% overlap185 186    >>> y, sr = librosa.load(librosa.ex('trumpet'))187    >>> S = np.abs(librosa.stft(y))188    >>> S.shape189    (1025, 230)190    >>> S_patch = librosa.util.frame(S, frame_length=32, hop_length=16)191    >>> S_patch.shape192    (1025, 32, 13)193    >>> # The first patch contains the first 32 frames of S194    >>> np.allclose(S_patch[:, :, 0], S[:, :32])195    True196    >>> # The second patch contains frames 16 to 16+32=48, and so on197    >>> np.allclose(S_patch[:, :, 1], S[:, 16:48])198    True199    """200 201    # This implementation is derived from numpy.lib.stride_tricks.sliding_window_view (1.20.0)202    # https://numpy.org/doc/stable/reference/generated/numpy.lib.stride_tricks.sliding_window_view.html203 204    x = np.array(x, copy=False, subok=subok)205 206    if x.shape[axis] < frame_length:207        raise ParameterError(208            f"Input is too short (n={x.shape[axis]:d}) for frame_length={frame_length:d}"209        )210 211    if hop_length < 1:212        raise ParameterError(f"Invalid hop_length: {hop_length:d}")213 214    # put our new within-frame axis at the end for now215    out_strides = x.strides + tuple([x.strides[axis]])216 217    # Reduce the shape on the framing axis218    x_shape_trimmed = list(x.shape)219    x_shape_trimmed[axis] -= frame_length - 1220 221    out_shape = tuple(x_shape_trimmed) + tuple([frame_length])222    xw = as_strided(223        x, strides=out_strides, shape=out_shape, subok=subok, writeable=writeable224    )225 226    if axis < 0:227        target_axis = axis - 1228    else:229        target_axis = axis + 1230 231    xw = np.moveaxis(xw, -1, target_axis)232 233    # Downsample along the target axis234    slices = [slice(None)] * xw.ndim235    slices[axis] = slice(0, None, hop_length)236    return xw[tuple(slices)]237 238 239@cache(level=20)240def valid_audio(y: np.ndarray, *, mono: Union[bool, Deprecated] = Deprecated()) -> bool:241    """Determine whether a variable contains valid audio data.242 243    The following conditions must be satisfied:244 245    - ``type(y)`` is ``np.ndarray``246    - ``y.dtype`` is floating-point247    - ``y.ndim != 0`` (must have at least one dimension)248    - ``np.isfinite(y).all()`` samples must be all finite values249 250    If ``mono`` is specified, then we additionally require251    - ``y.ndim == 1``252 253    Parameters254    ----------255    y : np.ndarray256        The input data to validate257 258    mono : bool259        Whether or not to require monophonic audio260 261        .. warning:: The ``mono`` parameter is deprecated in version 0.9 and will be262          removed in 0.10.263 264    Returns265    -------266    valid : bool267        True if all tests pass268 269    Raises270    ------271    ParameterError272        In any of the conditions specified above fails273 274    Notes275    -----276    This function caches at level 20.277 278    Examples279    --------280    >>> # By default, valid_audio allows only mono signals281    >>> filepath = librosa.ex('trumpet', hq=True)282    >>> y_mono, sr = librosa.load(filepath, mono=True)283    >>> y_stereo, _ = librosa.load(filepath, mono=False)284    >>> librosa.util.valid_audio(y_mono), librosa.util.valid_audio(y_stereo)285    True, False286 287    >>> # To allow stereo signals, set mono=False288    >>> librosa.util.valid_audio(y_stereo, mono=False)289    True290 291    See Also292    --------293    numpy.float32294    """295 296    if not isinstance(y, np.ndarray):297        raise ParameterError("Audio data must be of type numpy.ndarray")298 299    if not np.issubdtype(y.dtype, np.floating):300        raise ParameterError("Audio data must be floating-point")301 302    if y.ndim == 0:303        raise ParameterError(304            f"Audio data must be at least one-dimensional, given y.shape={y.shape}"305        )306 307    if isinstance(mono, Deprecated):308        mono = False309 310    if mono and y.ndim != 1:311        raise ParameterError(312            f"Invalid shape for monophonic audio: ndim={y.ndim:d}, shape={y.shape}"313        )314 315    if not np.isfinite(y).all():316        raise ParameterError("Audio buffer is not finite everywhere")317 318    return True319 320 321def valid_int(x: float, *, cast: Optional[Callable[[float], float]] = None) -> int:322    """Ensure that an input value is integer-typed.323    This is primarily useful for ensuring integrable-valued324    array indices.325 326    Parameters327    ----------328    x : number329        A scalar value to be cast to int330    cast : function [optional]331        A function to modify ``x`` before casting.332        Default: `np.floor`333 334    Returns335    -------336    x_int : int337        ``x_int = int(cast(x))``338 339    Raises340    ------341    ParameterError342        If ``cast`` is provided and is not callable.343    """344 345    if cast is None:346        cast = np.floor347 348    if not callable(cast):349        raise ParameterError("cast parameter must be callable")350 351    return int(cast(x))352 353 354def is_positive_int(x: float) -> bool:355    """Checks that x is a positive integer, i.e. 1 or greater.356 357    Parameters358    ----------359    x : number360 361    Returns362    -------363    positive : bool364 365    """366 367    # Check type first to catch None values.368    return isinstance(x, (int, np.integer)) and (x > 0)369 370 371def valid_intervals(intervals: np.ndarray) -> bool:372    """Ensure that an array is a valid representation of time intervals:373 374        - intervals.ndim == 2375        - intervals.shape[1] == 2376        - intervals[i, 0] <= intervals[i, 1] for all i377 378    Parameters379    ----------380    intervals : np.ndarray [shape=(n, 2)]381        set of time intervals382 383    Returns384    -------385    valid : bool386        True if ``intervals`` passes validation.387    """388 389    if intervals.ndim != 2 or intervals.shape[-1] != 2:390        raise ParameterError("intervals must have shape (n, 2)")391 392    if np.any(intervals[:, 0] > intervals[:, 1]):393        raise ParameterError(f"intervals={intervals} must have non-negative durations")394 395    return True396 397 398def pad_center(399    data: np.ndarray, *, size: int, axis: int = -1, **kwargs: Any400) -> np.ndarray:401    """Pad an array to a target length along a target axis.402 403    This differs from `np.pad` by centering the data prior to padding,404    analogous to `str.center`405 406    Examples407    --------408    >>> # Generate a vector409    >>> data = np.ones(5)410    >>> librosa.util.pad_center(data, size=10, mode='constant')411    array([ 0.,  0.,  1.,  1.,  1.,  1.,  1.,  0.,  0.,  0.])412 413    >>> # Pad a matrix along its first dimension414    >>> data = np.ones((3, 5))415    >>> librosa.util.pad_center(data, size=7, axis=0)416    array([[ 0.,  0.,  0.,  0.,  0.],417           [ 0.,  0.,  0.,  0.,  0.],418           [ 1.,  1.,  1.,  1.,  1.],419           [ 1.,  1.,  1.,  1.,  1.],420           [ 1.,  1.,  1.,  1.,  1.],421           [ 0.,  0.,  0.,  0.,  0.],422           [ 0.,  0.,  0.,  0.,  0.]])423    >>> # Or its second dimension424    >>> librosa.util.pad_center(data, size=7, axis=1)425    array([[ 0.,  1.,  1.,  1.,  1.,  1.,  0.],426           [ 0.,  1.,  1.,  1.,  1.,  1.,  0.],427           [ 0.,  1.,  1.,  1.,  1.,  1.,  0.]])428 429    Parameters430    ----------431    data : np.ndarray432        Vector to be padded and centered433    size : int >= len(data) [scalar]434        Length to pad ``data``435    axis : int436        Axis along which to pad and center the data437    **kwargs : additional keyword arguments438        arguments passed to `np.pad`439 440    Returns441    -------442    data_padded : np.ndarray443        ``data`` centered and padded to length ``size`` along the444        specified axis445 446    Raises447    ------448    ParameterError449        If ``size < data.shape[axis]``450 451    See Also452    --------453    numpy.pad454    """455 456    kwargs.setdefault("mode", "constant")457 458    n = data.shape[axis]459 460    lpad = int((size - n) // 2)461 462    lengths = [(0, 0)] * data.ndim463    lengths[axis] = (lpad, int(size - n - lpad))464 465    if lpad < 0:466        raise ParameterError(467            f"Target size ({size:d}) must be at least input size ({n:d})"468        )469 470    return np.pad(data, lengths, **kwargs)471 472 473def expand_to(474    x: np.ndarray, *, ndim: int, axes: Union[int, slice, Sequence[int], Sequence[slice]]475) -> np.ndarray:476    """Expand the dimensions of an input array with477 478    Parameters479    ----------480    x : np.ndarray481        The input array482    ndim : int483        The number of dimensions to expand to.  Must be at least ``x.ndim``484    axes : int or slice485        The target axis or axes to preserve from x.486        All other axes will have length 1.487 488    Returns489    -------490    x_exp : np.ndarray491        The expanded version of ``x``, satisfying the following:492            ``x_exp[axes] == x``493            ``x_exp.ndim == ndim``494 495    See Also496    --------497    np.expand_dims498 499    Examples500    --------501    Expand a 1d array into an (n, 1) shape502 503    >>> x = np.arange(3)504    >>> librosa.util.expand_to(x, ndim=2, axes=0)505    array([[0],506       [1],507       [2]])508 509    Expand a 1d array into a (1, n) shape510 511    >>> librosa.util.expand_to(x, ndim=2, axes=1)512    array([[0, 1, 2]])513 514    Expand a 2d array into (1, n, m, 1) shape515 516    >>> x = np.vander(np.arange(3))517    >>> librosa.util.expand_to(x, ndim=4, axes=[1,2]).shape518    (1, 3, 3, 1)519    """520 521    # Force axes into a tuple522    axes_tup: Tuple[int]523    try:524        axes_tup = tuple(axes)  # type: ignore525    except TypeError:526        axes_tup = tuple([axes])  # type: ignore527 528    if len(axes_tup) != x.ndim:529        raise ParameterError(530            f"Shape mismatch between axes={axes_tup} and input x.shape={x.shape}"531        )532 533    if ndim < x.ndim:534        raise ParameterError(535            f"Cannot expand x.shape={x.shape} to fewer dimensions ndim={ndim}"536        )537 538    shape: List[int] = [1] * ndim539    for i, axi in enumerate(axes_tup):540        shape[axi] = x.shape[i]541 542    return x.reshape(shape)543 544 545def fix_length(546    data: np.ndarray, *, size: int, axis: int = -1, **kwargs: Any547) -> np.ndarray:548    """Fix the length an array ``data`` to exactly ``size`` along a target axis.549 550    If ``data.shape[axis] < n``, pad according to the provided kwargs.551    By default, ``data`` is padded with trailing zeros.552 553    Examples554    --------555    >>> y = np.arange(7)556    >>> # Default: pad with zeros557    >>> librosa.util.fix_length(y, size=10)558    array([0, 1, 2, 3, 4, 5, 6, 0, 0, 0])559    >>> # Trim to a desired length560    >>> librosa.util.fix_length(y, size=5)561    array([0, 1, 2, 3, 4])562    >>> # Use edge-padding instead of zeros563    >>> librosa.util.fix_length(y, size=10, mode='edge')564    array([0, 1, 2, 3, 4, 5, 6, 6, 6, 6])565 566    Parameters567    ----------568    data : np.ndarray569        array to be length-adjusted570    size : int >= 0 [scalar]571        desired length of the array572    axis : int, <= data.ndim573        axis along which to fix length574    **kwargs : additional keyword arguments575        Parameters to ``np.pad``576 577    Returns578    -------579    data_fixed : np.ndarray [shape=data.shape]580        ``data`` either trimmed or padded to length ``size``581        along the specified axis.582 583    See Also584    --------585    numpy.pad586    """587 588    kwargs.setdefault("mode", "constant")589 590    n = data.shape[axis]591 592    if n > size:593        slices = [slice(None)] * data.ndim594        slices[axis] = slice(0, size)595        return data[tuple(slices)]596 597    elif n < size:598        lengths = [(0, 0)] * data.ndim599        lengths[axis] = (0, size - n)600        return np.pad(data, lengths, **kwargs)601 602    return data603 604 605def fix_frames(606    frames: _SequenceLike[int],607    *,608    x_min: Optional[int] = 0,609    x_max: Optional[int] = None,610    pad: bool = True,611) -> np.ndarray:612    """Fix a list of frames to lie within [x_min, x_max]613 614    Examples615    --------616    >>> # Generate a list of frame indices617    >>> frames = np.arange(0, 1000.0, 50)618    >>> frames619    array([   0.,   50.,  100.,  150.,  200.,  250.,  300.,  350.,620            400.,  450.,  500.,  550.,  600.,  650.,  700.,  750.,621            800.,  850.,  900.,  950.])622    >>> # Clip to span at most 250623    >>> librosa.util.fix_frames(frames, x_max=250)624    array([  0,  50, 100, 150, 200, 250])625    >>> # Or pad to span up to 2500626    >>> librosa.util.fix_frames(frames, x_max=2500)627    array([   0,   50,  100,  150,  200,  250,  300,  350,  400,628            450,  500,  550,  600,  650,  700,  750,  800,  850,629            900,  950, 2500])630    >>> librosa.util.fix_frames(frames, x_max=2500, pad=False)631    array([  0,  50, 100, 150, 200, 250, 300, 350, 400, 450, 500,632           550, 600, 650, 700, 750, 800, 850, 900, 950])633 634    >>> # Or starting away from zero635    >>> frames = np.arange(200, 500, 33)636    >>> frames637    array([200, 233, 266, 299, 332, 365, 398, 431, 464, 497])638    >>> librosa.util.fix_frames(frames)639    array([  0, 200, 233, 266, 299, 332, 365, 398, 431, 464, 497])640    >>> librosa.util.fix_frames(frames, x_max=500)641    array([  0, 200, 233, 266, 299, 332, 365, 398, 431, 464, 497,642           500])643 644    Parameters645    ----------646    frames : np.ndarray [shape=(n_frames,)]647        List of non-negative frame indices648    x_min : int >= 0 or None649        Minimum allowed frame index650    x_max : int >= 0 or None651        Maximum allowed frame index652    pad : boolean653        If ``True``, then ``frames`` is expanded to span the full range654        ``[x_min, x_max]``655 656    Returns657    -------658    fixed_frames : np.ndarray [shape=(n_fixed_frames,), dtype=int]659        Fixed frame indices, flattened and sorted660 661    Raises662    ------663    ParameterError664        If ``frames`` contains negative values665    """666 667    frames = np.asarray(frames)668 669    if np.any(frames < 0):670        raise ParameterError("Negative frame index detected")671 672    # TODO: this whole function could be made more efficient673 674    if pad and (x_min is not None or x_max is not None):675        frames = np.clip(frames, x_min, x_max)676 677    if pad:678        pad_data = []679        if x_min is not None:680            pad_data.append(x_min)681        if x_max is not None:682            pad_data.append(x_max)683        frames = np.concatenate((np.asarray(pad_data), frames))684 685    if x_min is not None:686        frames = frames[frames >= x_min]687 688    if x_max is not None:689        frames = frames[frames <= x_max]690 691    unique: np.ndarray = np.unique(frames).astype(int)692    return unique693 694 695@overload696def axis_sort(697    S: np.ndarray,698    *,699    axis: int = ...,700    index: Literal[False] = ...,701    value: Optional[Callable[..., Any]] = ...,702) -> np.ndarray:703    ...704 705 706@overload707def axis_sort(708    S: np.ndarray,709    *,710    axis: int = ...,711    index: Literal[True],712    value: Optional[Callable[..., Any]] = ...,713) -> Tuple[np.ndarray, np.ndarray]:714    ...715 716 717def axis_sort(718    S: np.ndarray,719    *,720    axis: int = -1,721    index: bool = False,722    value: Optional[Callable[..., Any]] = None,723) -> Union[np.ndarray, Tuple[np.ndarray, np.ndarray]]:724    """Sort an array along its rows or columns.725 726    Examples727    --------728    Visualize NMF output for a spectrogram S729 730    >>> # Sort the columns of W by peak frequency bin731    >>> y, sr = librosa.load(librosa.ex('trumpet'))732    >>> S = np.abs(librosa.stft(y))733    >>> W, H = librosa.decompose.decompose(S, n_components=64)734    >>> W_sort = librosa.util.axis_sort(W)735 736    Or sort by the lowest frequency bin737 738    >>> W_sort = librosa.util.axis_sort(W, value=np.argmin)739 740    Or sort the rows instead of the columns741 742    >>> W_sort_rows = librosa.util.axis_sort(W, axis=0)743 744    Get the sorting index also, and use it to permute the rows of H745 746    >>> W_sort, idx = librosa.util.axis_sort(W, index=True)747    >>> H_sort = H[idx, :]748 749    >>> import matplotlib.pyplot as plt750    >>> fig, ax = plt.subplots(nrows=2, ncols=2)751    >>> img_w = librosa.display.specshow(librosa.amplitude_to_db(W, ref=np.max),752    ...                                  y_axis='log', ax=ax[0, 0])753    >>> ax[0, 0].set(title='W')754    >>> ax[0, 0].label_outer()755    >>> img_act = librosa.display.specshow(H, x_axis='time', ax=ax[0, 1])756    >>> ax[0, 1].set(title='H')757    >>> ax[0, 1].label_outer()758    >>> librosa.display.specshow(librosa.amplitude_to_db(W_sort,759    ...                                                  ref=np.max),760    ...                          y_axis='log', ax=ax[1, 0])761    >>> ax[1, 0].set(title='W sorted')762    >>> librosa.display.specshow(H_sort, x_axis='time', ax=ax[1, 1])763    >>> ax[1, 1].set(title='H sorted')764    >>> ax[1, 1].label_outer()765    >>> fig.colorbar(img_w, ax=ax[:, 0], orientation='horizontal')766    >>> fig.colorbar(img_act, ax=ax[:, 1], orientation='horizontal')767 768    Parameters769    ----------770    S : np.ndarray [shape=(d, n)]771        Array to be sorted772 773    axis : int [scalar]774        The axis along which to compute the sorting values775 776        - ``axis=0`` to sort rows by peak column index777        - ``axis=1`` to sort columns by peak row index778 779    index : boolean [scalar]780        If true, returns the index array as well as the permuted data.781 782    value : function783        function to return the index corresponding to the sort order.784        Default: `np.argmax`.785 786    Returns787    -------788    S_sort : np.ndarray [shape=(d, n)]789        ``S`` with the columns or rows permuted in sorting order790    idx : np.ndarray (optional) [shape=(d,) or (n,)]791        If ``index == True``, the sorting index used to permute ``S``.792        Length of ``idx`` corresponds to the selected ``axis``.793 794    Raises795    ------796    ParameterError797        If ``S`` does not have exactly 2 dimensions (``S.ndim != 2``)798    """799 800    if value is None:801        value = np.argmax802 803    if S.ndim != 2:804        raise ParameterError("axis_sort is only defined for 2D arrays")805 806    bin_idx = value(S, axis=np.mod(1 - axis, S.ndim))807    idx = np.argsort(bin_idx)808 809    sort_slice = [slice(None)] * S.ndim810    sort_slice[axis] = idx  # type: ignore811 812    if index:813        return S[tuple(sort_slice)], idx814    else:815        return S[tuple(sort_slice)]816 817 818@cache(level=40)819def normalize(820    S: np.ndarray,821    *,822    norm: Optional[float] = np.inf,823    axis: Optional[int] = 0,824    threshold: Optional[_FloatLike_co] = None,825    fill: Optional[bool] = None,826) -> np.ndarray:827    """Normalize an array along a chosen axis.828 829    Given a norm (described below) and a target axis, the input830    array is scaled so that::831 832        norm(S, axis=axis) == 1833 834    For example, ``axis=0`` normalizes each column of a 2-d array835    by aggregating over the rows (0-axis).836    Similarly, ``axis=1`` normalizes each row of a 2-d array.837 838    This function also supports thresholding small-norm slices:839    any slice (i.e., row or column) with norm below a specified840    ``threshold`` can be left un-normalized, set to all-zeros, or841    filled with uniform non-zero values that normalize to 1.842 843    Note: the semantics of this function differ from844    `scipy.linalg.norm` in two ways: multi-dimensional arrays845    are supported, but matrix-norms are not.846 847    Parameters848    ----------849    S : np.ndarray850        The array to normalize851 852    norm : {np.inf, -np.inf, 0, float > 0, None}853        - `np.inf`  : maximum absolute value854        - `-np.inf` : minimum absolute value855        - `0`    : number of non-zeros (the support)856        - float  : corresponding l_p norm857            See `scipy.linalg.norm` for details.858        - None : no normalization is performed859 860    axis : int [scalar]861        Axis along which to compute the norm.862 863    threshold : number > 0 [optional]864        Only the columns (or rows) with norm at least ``threshold`` are865        normalized.866 867        By default, the threshold is determined from868        the numerical precision of ``S.dtype``.869 870    fill : None or bool871        If None, then columns (or rows) with norm below ``threshold``872        are left as is.873 874        If False, then columns (rows) with norm below ``threshold``875        are set to 0.876 877        If True, then columns (rows) with norm below ``threshold``878        are filled uniformly such that the corresponding norm is 1.879 880        .. note:: ``fill=True`` is incompatible with ``norm=0`` because881            no uniform vector exists with l0 "norm" equal to 1.882 883    Returns884    -------885    S_norm : np.ndarray [shape=S.shape]886        Normalized array887 888    Raises889    ------890    ParameterError891        If ``norm`` is not among the valid types defined above892 893        If ``S`` is not finite894 895        If ``fill=True`` and ``norm=0``896 897    See Also898    --------899    scipy.linalg.norm900 901    Notes902    -----903    This function caches at level 40.904 905    Examples906    --------907    >>> # Construct an example matrix908    >>> S = np.vander(np.arange(-2.0, 2.0))909    >>> S910    array([[-8.,  4., -2.,  1.],911           [-1.,  1., -1.,  1.],912           [ 0.,  0.,  0.,  1.],913           [ 1.,  1.,  1.,  1.]])914    >>> # Max (l-infinity)-normalize the columns915    >>> librosa.util.normalize(S)916    array([[-1.   ,  1.   , -1.   ,  1.   ],917           [-0.125,  0.25 , -0.5  ,  1.   ],918           [ 0.   ,  0.   ,  0.   ,  1.   ],919           [ 0.125,  0.25 ,  0.5  ,  1.   ]])920    >>> # Max (l-infinity)-normalize the rows921    >>> librosa.util.normalize(S, axis=1)922    array([[-1.   ,  0.5  , -0.25 ,  0.125],923           [-1.   ,  1.   , -1.   ,  1.   ],924           [ 0.   ,  0.   ,  0.   ,  1.   ],925           [ 1.   ,  1.   ,  1.   ,  1.   ]])926    >>> # l1-normalize the columns927    >>> librosa.util.normalize(S, norm=1)928    array([[-0.8  ,  0.667, -0.5  ,  0.25 ],929           [-0.1  ,  0.167, -0.25 ,  0.25 ],930           [ 0.   ,  0.   ,  0.   ,  0.25 ],931           [ 0.1  ,  0.167,  0.25 ,  0.25 ]])932    >>> # l2-normalize the columns933    >>> librosa.util.normalize(S, norm=2)934    array([[-0.985,  0.943, -0.816,  0.5  ],935           [-0.123,  0.236, -0.408,  0.5  ],936           [ 0.   ,  0.   ,  0.   ,  0.5  ],937           [ 0.123,  0.236,  0.408,  0.5  ]])938 939    >>> # Thresholding and filling940    >>> S[:, -1] = 1e-308941    >>> S942    array([[ -8.000e+000,   4.000e+000,  -2.000e+000,943              1.000e-308],944           [ -1.000e+000,   1.000e+000,  -1.000e+000,945              1.000e-308],946           [  0.000e+000,   0.000e+000,   0.000e+000,947              1.000e-308],948           [  1.000e+000,   1.000e+000,   1.000e+000,949              1.000e-308]])950 951    >>> # By default, small-norm columns are left untouched952    >>> librosa.util.normalize(S)953    array([[ -1.000e+000,   1.000e+000,  -1.000e+000,954              1.000e-308],955           [ -1.250e-001,   2.500e-001,  -5.000e-001,956              1.000e-308],957           [  0.000e+000,   0.000e+000,   0.000e+000,958              1.000e-308],959           [  1.250e-001,   2.500e-001,   5.000e-001,960              1.000e-308]])961    >>> # Small-norm columns can be zeroed out962    >>> librosa.util.normalize(S, fill=False)963    array([[-1.   ,  1.   , -1.   ,  0.   ],964           [-0.125,  0.25 , -0.5  ,  0.   ],965           [ 0.   ,  0.   ,  0.   ,  0.   ],966           [ 0.125,  0.25 ,  0.5  ,  0.   ]])967    >>> # Or set to constant with unit-norm968    >>> librosa.util.normalize(S, fill=True)969    array([[-1.   ,  1.   , -1.   ,  1.   ],970           [-0.125,  0.25 , -0.5  ,  1.   ],971           [ 0.   ,  0.   ,  0.   ,  1.   ],972           [ 0.125,  0.25 ,  0.5  ,  1.   ]])973    >>> # With an l1 norm instead of max-norm974    >>> librosa.util.normalize(S, norm=1, fill=True)975    array([[-0.8  ,  0.667, -0.5  ,  0.25 ],976           [-0.1  ,  0.167, -0.25 ,  0.25 ],977           [ 0.   ,  0.   ,  0.   ,  0.25 ],978           [ 0.1  ,  0.167,  0.25 ,  0.25 ]])979    """980 981    # Avoid div-by-zero982    if threshold is None:983        threshold = tiny(S)984 985    elif threshold <= 0:986        raise ParameterError(f"threshold={threshold} must be strictly positive")987 988    if fill not in [None, False, True]:989        raise ParameterError(f"fill={fill} must be None or boolean")990 991    if not np.all(np.isfinite(S)):992        raise ParameterError("Input must be finite")993 994    # All norms only depend on magnitude, let's do that first995    mag = np.abs(S).astype(float)996 997    # For max/min norms, filling with 1 works998    fill_norm = 1999 1000    if norm is None:1001        return S1002 1003    elif norm == np.inf:1004        length = np.max(mag, axis=axis, keepdims=True)1005 1006    elif norm == -np.inf:1007        length = np.min(mag, axis=axis, keepdims=True)1008 1009    elif norm == 0:1010        if fill is True:1011            raise ParameterError("Cannot normalize with norm=0 and fill=True")1012 1013        length = np.sum(mag > 0, axis=axis, keepdims=True, dtype=mag.dtype)1014 1015    elif np.issubdtype(type(norm), np.number) and norm > 0:1016        length = np.sum(mag**norm, axis=axis, keepdims=True) ** (1.0 / norm)1017 1018        if axis is None:1019            fill_norm = mag.size ** (-1.0 / norm)1020        else:1021            fill_norm = mag.shape[axis] ** (-1.0 / norm)1022 1023    else:1024        raise ParameterError(f"Unsupported norm: {repr(norm)}")1025 1026    # indices where norm is below the threshold1027    small_idx = length < threshold1028 1029    Snorm = np.empty_like(S)1030    if fill is None:1031        # Leave small indices un-normalized1032        length[small_idx] = 1.01033        Snorm[:] = S / length1034 1035    elif fill:1036        # If we have a non-zero fill value, we locate those entries by1037        # doing a nan-divide.1038        # If S was finite, then length is finite (except for small positions)1039        length[small_idx] = np.nan1040        Snorm[:] = S / length1041        Snorm[np.isnan(Snorm)] = fill_norm1042    else:1043        # Set small values to zero by doing an inf-divide.1044        # This is safe (by IEEE-754) as long as S is finite.1045        length[small_idx] = np.inf1046        Snorm[:] = S / length1047 1048    return Snorm1049 1050 1051@numba.stencil1052def _localmax_sten(x):  # pragma: no cover1053    """Numba stencil for local maxima computation"""1054    return (x[0] > x[-1]) & (x[0] >= x[1])1055 1056 1057@numba.stencil1058def _localmin_sten(x):  # pragma: no cover1059    """Numba stencil for local minima computation"""1060    return (x[0] < x[-1]) & (x[0] <= x[1])1061 1062 1063@numba.guvectorize(1064    [1065        "void(int16[:], bool_[:])",1066        "void(int32[:], bool_[:])",1067        "void(int64[:], bool_[:])",1068        "void(float32[:], bool_[:])",1069        "void(float64[:], bool_[:])",1070    ],1071    "(n)->(n)",1072    cache=False,1073    nopython=True,1074)1075def _localmax(x, y):  # pragma: no cover1076    """Vectorized wrapper for the localmax stencil"""1077    y[:] = _localmax_sten(x)1078 1079 1080@numba.guvectorize(1081    [1082        "void(int16[:], bool_[:])",1083        "void(int32[:], bool_[:])",1084        "void(int64[:], bool_[:])",1085        "void(float32[:], bool_[:])",1086        "void(float64[:], bool_[:])",1087    ],1088    "(n)->(n)",1089    cache=False,1090    nopython=True,1091)1092def _localmin(x, y):  # pragma: no cover1093    """Vectorized wrapper for the localmin stencil"""1094    y[:] = _localmin_sten(x)1095 1096 1097def localmax(x: np.ndarray, *, axis: int = 0) -> np.ndarray:1098    """Find local maxima in an array1099 1100    An element ``x[i]`` is considered a local maximum if the following1101    conditions are met:1102 1103    - ``x[i] > x[i-1]``1104    - ``x[i] >= x[i+1]``1105 1106    Note that the first condition is strict, and that the first element1107    ``x[0]`` will never be considered as a local maximum.1108 1109    Examples1110    --------1111    >>> x = np.array([1, 0, 1, 2, -1, 0, -2, 1])1112    >>> librosa.util.localmax(x)1113    array([False, False, False,  True, False,  True, False,  True], dtype=bool)1114 1115    >>> # Two-dimensional example1116    >>> x = np.array([[1,0,1], [2, -1, 0], [2, 1, 3]])1117    >>> librosa.util.localmax(x, axis=0)1118    array([[False, False, False],1119           [ True, False, False],1120           [False,  True,  True]], dtype=bool)1121    >>> librosa.util.localmax(x, axis=1)1122    array([[False, False,  True],1123           [False, False,  True],1124           [False, False,  True]], dtype=bool)1125 1126    Parameters1127    ----------1128    x : np.ndarray [shape=(d1,d2,...)]1129        input vector or array1130    axis : int1131        axis along which to compute local maximality1132 1133    Returns1134    -------1135    m : np.ndarray [shape=x.shape, dtype=bool]1136        indicator array of local maximality along ``axis``1137 1138    See Also1139    --------1140    localmin1141    """1142    # Rotate the target axis to the end1143    xi = x.swapaxes(-1, axis)1144 1145    # Allocate the output array and rotate target axis1146    lmax = np.empty_like(x, dtype=bool)1147    lmaxi = lmax.swapaxes(-1, axis)1148 1149    # Call the vectorized stencil1150    _localmax(xi, lmaxi)1151 1152    # Handle the edge condition not covered by the stencil1153    lmaxi[..., -1] = xi[..., -1] > xi[..., -2]1154 1155    return lmax1156 1157 1158def localmin(x: np.ndarray, *, axis: int = 0) -> np.ndarray:1159    """Find local minima in an array1160 1161    An element ``x[i]`` is considered a local minimum if the following1162    conditions are met:1163 1164    - ``x[i] < x[i-1]``1165    - ``x[i] <= x[i+1]``1166 1167    Note that the first condition is strict, and that the first element1168    ``x[0]`` will never be considered as a local minimum.1169 1170    Examples1171    --------1172    >>> x = np.array([1, 0, 1, 2, -1, 0, -2, 1])1173    >>> librosa.util.localmin(x)1174    array([False,  True, False, False,  True, False,  True, False])1175 1176    >>> # Two-dimensional example1177    >>> x = np.array([[1,0,1], [2, -1, 0], [2, 1, 3]])1178    >>> librosa.util.localmin(x, axis=0)1179    array([[False, False, False],1180           [False,  True,  True],1181           [False, False, False]])1182 1183    >>> librosa.util.localmin(x, axis=1)1184    array([[False,  True, False],1185           [False,  True, False],1186           [False,  True, False]])1187 1188    Parameters1189    ----------1190    x : np.ndarray [shape=(d1,d2,...)]1191        input vector or array1192    axis : int1193        axis along which to compute local minimality1194 1195    Returns1196    -------1197    m : np.ndarray [shape=x.shape, dtype=bool]1198        indicator array of local minimality along ``axis``1199 1200    See Also

Showing the first 1,200 of 2608 lines. Download the file for the rest.