Skip to content

SegmenterNumpy

SegmenterNumpy

A class for segmenting and reconstructing input data using windowing techniques.

Supports Weighted Overlap-Add (WOLA) and Overlap-Add (OLA) methods.

Attributes:

Name Type Description
window Window

A class containing hop size, segment size, and windows.

Source code in src/libsegmenter/backends/SegmenterNumpy.py
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
class SegmenterNumpy:
    """
    A class for segmenting and reconstructing input data using windowing techniques.

    Supports Weighted Overlap-Add (WOLA) and Overlap-Add (OLA) methods.

    Attributes:
        window (Window): A class containing hop size, segment size, and windows.

    """

    def __init__(self, window: Window) -> None:
        """
        Initializes the SegmenterNumpy instance.

        Args:
            window (Window): A window object containing segmentation parameters.

        """
        self.window = window

    def segment(self, x: NDArray[T]) -> NDArray[T]:
        """
        Segments the input signal into overlapping windows using the window parameters.

        Args:
            x (np.ndarray): Input array, either 1D (sequence) or 2D (batch).

        Returns:
            Segmented data of shape (batch_size, num_segments, segment_size).

        Raises:
            ValueError: If types are incorrect.
            ValueError: If input dimensions are invalid.

        """
        if x.ndim not in {1, 2}:
            raise ValueError(f"Only supports 1D or 2D inputs, provided {x.ndim}D.")

        batch_size = x.shape[0] if x.ndim == 2 else None
        num_samples = x.shape[-1]

        if batch_size is None:
            x = x.reshape(1, -1)  # Convert to batch format for consistency

        num_segments = compute_num_segments(
            num_samples, self.window.hop_size, self.window.analysis_window.shape[-1]
        )

        if num_segments <= 0:
            raise ValueError(
                "Input signal is too short for segmentation with the given num_samples "
                + f"({num_samples}), hop size "
                + f"({self.window.hop_size}) and segment size "
                + f"({self.window.analysis_window.shape[-1]})."
            )

        # Pre-allocation
        y = np.zeros(
            (
                batch_size if batch_size is not None else 1,
                num_segments,
                self.window.analysis_window.shape[-1],
            ),
            dtype=x.dtype,
        )

        # Windowing
        for k in range(num_segments):
            start_idx = k * self.window.hop_size
            y[:, k, :] = np.multiply(
                x[:, start_idx : start_idx + self.window.analysis_window.shape[-1]],
                self.window.analysis_window,
            )

        return y.squeeze(0) if batch_size is None else y

    def unsegment(self, y: NDArray[T]) -> NDArray[T]:
        """
        Reconstructs the original signal from segmented data using synthesis windowing.

        Args:
            y (np.ndarray): Segmented data with shape (batch_size, num_segments,
                            segment_size) or (num_segments, segment_size) for a single
                            sequence.

        Returns:
            Reconstructed signal.

        """
        if self.window.synthesis_window is None:
            raise ValueError("Given windowing scheme does not support unsegmenting.")

        if y.ndim not in {2, 3}:
            raise ValueError(f"Only supports 2D or 3D inputs, provided {y.ndim}D.")

        batch_size = y.shape[0] if y.ndim == 3 else None
        num_segments = y.shape[-2]
        segment_size = y.shape[-1]

        if batch_size is None:
            y = y.reshape(1, num_segments, -1)  # Convert to batch format

        num_samples = compute_num_samples(
            num_segments, self.window.hop_size, segment_size
        )

        if num_samples <= 0:
            raise ValueError(
                "Invalid segment structure, possibly due to incorrect windowing "
                + "parameters."
            )

        # Efficient numpy array allocation
        x = np.zeros(
            (batch_size if batch_size is not None else 1, num_samples), dtype=y.dtype
        )

        # Vectorized accumulation
        for k in range(num_segments):
            start_idx = k * self.window.hop_size
            x[:, start_idx : start_idx + segment_size] += np.multiply(
                y[:, k, :], self.window.synthesis_window
            )

        return x.squeeze(0) if batch_size is None else x

__init__(window)

Initializes the SegmenterNumpy instance.

Parameters:

Name Type Description Default
window Window

A window object containing segmentation parameters.

required
Source code in src/libsegmenter/backends/SegmenterNumpy.py
39
40
41
42
43
44
45
46
47
def __init__(self, window: Window) -> None:
    """
    Initializes the SegmenterNumpy instance.

    Args:
        window (Window): A window object containing segmentation parameters.

    """
    self.window = window

segment(x)

Segments the input signal into overlapping windows using the window parameters.

Parameters:

Name Type Description Default
x ndarray

Input array, either 1D (sequence) or 2D (batch).

required

Returns:

Type Description
NDArray[T]

Segmented data of shape (batch_size, num_segments, segment_size).

Raises:

Type Description
ValueError

If types are incorrect.

ValueError

If input dimensions are invalid.

Source code in src/libsegmenter/backends/SegmenterNumpy.py
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
def segment(self, x: NDArray[T]) -> NDArray[T]:
    """
    Segments the input signal into overlapping windows using the window parameters.

    Args:
        x (np.ndarray): Input array, either 1D (sequence) or 2D (batch).

    Returns:
        Segmented data of shape (batch_size, num_segments, segment_size).

    Raises:
        ValueError: If types are incorrect.
        ValueError: If input dimensions are invalid.

    """
    if x.ndim not in {1, 2}:
        raise ValueError(f"Only supports 1D or 2D inputs, provided {x.ndim}D.")

    batch_size = x.shape[0] if x.ndim == 2 else None
    num_samples = x.shape[-1]

    if batch_size is None:
        x = x.reshape(1, -1)  # Convert to batch format for consistency

    num_segments = compute_num_segments(
        num_samples, self.window.hop_size, self.window.analysis_window.shape[-1]
    )

    if num_segments <= 0:
        raise ValueError(
            "Input signal is too short for segmentation with the given num_samples "
            + f"({num_samples}), hop size "
            + f"({self.window.hop_size}) and segment size "
            + f"({self.window.analysis_window.shape[-1]})."
        )

    # Pre-allocation
    y = np.zeros(
        (
            batch_size if batch_size is not None else 1,
            num_segments,
            self.window.analysis_window.shape[-1],
        ),
        dtype=x.dtype,
    )

    # Windowing
    for k in range(num_segments):
        start_idx = k * self.window.hop_size
        y[:, k, :] = np.multiply(
            x[:, start_idx : start_idx + self.window.analysis_window.shape[-1]],
            self.window.analysis_window,
        )

    return y.squeeze(0) if batch_size is None else y

unsegment(y)

Reconstructs the original signal from segmented data using synthesis windowing.

Parameters:

Name Type Description Default
y ndarray

Segmented data with shape (batch_size, num_segments, segment_size) or (num_segments, segment_size) for a single sequence.

required

Returns:

Type Description
NDArray[T]

Reconstructed signal.

Source code in src/libsegmenter/backends/SegmenterNumpy.py
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
def unsegment(self, y: NDArray[T]) -> NDArray[T]:
    """
    Reconstructs the original signal from segmented data using synthesis windowing.

    Args:
        y (np.ndarray): Segmented data with shape (batch_size, num_segments,
                        segment_size) or (num_segments, segment_size) for a single
                        sequence.

    Returns:
        Reconstructed signal.

    """
    if self.window.synthesis_window is None:
        raise ValueError("Given windowing scheme does not support unsegmenting.")

    if y.ndim not in {2, 3}:
        raise ValueError(f"Only supports 2D or 3D inputs, provided {y.ndim}D.")

    batch_size = y.shape[0] if y.ndim == 3 else None
    num_segments = y.shape[-2]
    segment_size = y.shape[-1]

    if batch_size is None:
        y = y.reshape(1, num_segments, -1)  # Convert to batch format

    num_samples = compute_num_samples(
        num_segments, self.window.hop_size, segment_size
    )

    if num_samples <= 0:
        raise ValueError(
            "Invalid segment structure, possibly due to incorrect windowing "
            + "parameters."
        )

    # Efficient numpy array allocation
    x = np.zeros(
        (batch_size if batch_size is not None else 1, num_samples), dtype=y.dtype
    )

    # Vectorized accumulation
    for k in range(num_segments):
        start_idx = k * self.window.hop_size
        x[:, start_idx : start_idx + segment_size] += np.multiply(
            y[:, k, :], self.window.synthesis_window
        )

    return x.squeeze(0) if batch_size is None else x