Skip to content

zarrnii.plugins

Plugin API for segmentation and scaled-processing workflows, including hook markers, hook specifications, plugin-manager helpers, and bundled plugins.

Package exports

ZarrNii plugins package.

This package provides extensible plugin architectures for various image processing tasks such as segmentation, filtering, and analysis using the pluggy framework.

Plugin authors should depend on zarrnii and import hookimpl from :mod:zarrnii.plugins.

Classes

zarrnii.plugins.GaussianBiasFieldCorrection(sigma=5.0, mode='reflect')

Bias field correction plugin using multi-resolution processing.

This plugin estimates a smooth bias field at low resolution using simple smoothing (as a placeholder for more sophisticated methods like N4) and applies the correction to full resolution data by division.

Parameters:

  • sigma (float, default: 5.0 ) –

    Standard deviation for Gaussian smoothing (default: 5.0)

  • mode (str, default: 'reflect' ) –

    Boundary condition for smoothing (default: 'reflect')

Initialize bias field correction plugin.

Parameters:

  • sigma (float, default: 5.0 ) –

    Standard deviation for Gaussian smoothing

  • mode (str, default: 'reflect' ) –

    Boundary condition for smoothing

Source code in zarrnii/plugins/scaled_processing/gaussian_biasfield.py
29
30
31
32
33
34
35
36
37
38
def __init__(self, sigma: float = 5.0, mode: str = "reflect"):
    """
    Initialize bias field correction plugin.

    Args:
        sigma: Standard deviation for Gaussian smoothing
        mode: Boundary condition for smoothing
    """
    self.sigma = sigma
    self.mode = mode

Functions

zarrnii.plugins.GaussianBiasFieldCorrection.lowres_func(lowres_array)

Estimate bias field from low-resolution data.

This is a simplified bias field estimation using Gaussian smoothing. In practice, this could be replaced with more sophisticated methods like N4 bias field correction.

Parameters:

  • lowres_array (ndarray) –

    Downsampled input image

Returns:

  • ndarray

    Estimated bias field at low resolution

Source code in zarrnii/plugins/scaled_processing/gaussian_biasfield.py
 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
@hookimpl
def lowres_func(self, lowres_array: np.ndarray) -> np.ndarray:
    """
    Estimate bias field from low-resolution data.

    This is a simplified bias field estimation using Gaussian smoothing.
    In practice, this could be replaced with more sophisticated methods
    like N4 bias field correction.

    Args:
        lowres_array: Downsampled input image

    Returns:
        Estimated bias field at low resolution
    """
    if lowres_array.size == 0:
        raise ValueError("Input array is empty")

    # Handle different array dimensions
    if lowres_array.ndim < 2:
        raise ValueError("Input array must be at least 2D")

    # Store original shape
    original_shape = lowres_array.shape

    # For multi-dimensional arrays, work with the last 3 dimensions
    work_array = lowres_array
    if work_array.ndim > 3:
        # Flatten leading dimensions and work with spatial dimensions
        leading_shape = work_array.shape[:-3]
        spatial_shape = work_array.shape[-3:]
        work_array = work_array.reshape(-1, *spatial_shape)

    # Ensure we're working with float data
    if work_array.dtype.kind in ["i", "u"]:  # integer types
        work_array = work_array.astype(np.float32)

    # Simple bias field estimation: smooth the image to get low-frequency components
    if work_array.ndim == 2:
        smoothed = ndimage.gaussian_filter(
            work_array, sigma=self.sigma, mode=self.mode
        )
    else:
        # Apply smoothing to each volume if we have multiple
        if work_array.ndim == 4:  # batched 3D volumes
            smoothed = np.zeros_like(work_array)
            for i in range(work_array.shape[0]):
                smoothed[i] = ndimage.gaussian_filter(
                    work_array[i], sigma=self.sigma, mode=self.mode
                )
        else:  # single 3D volume
            smoothed = ndimage.gaussian_filter(
                work_array, sigma=self.sigma, mode=self.mode
            )

    # Reshape back to original shape if needed
    if original_shape != smoothed.shape:
        smoothed = smoothed.reshape(original_shape)

    # Avoid division by zero by adding small epsilon
    smoothed = np.maximum(smoothed, np.finfo(smoothed.dtype).eps)

    return smoothed
zarrnii.plugins.GaussianBiasFieldCorrection.highres_func(fullres_array, upsampled_output)

Apply bias field correction to full-resolution data.

This function takes the upsampled bias field (same size as fullres_array) and applies it to the full-resolution data by division.

Works with both dask arrays ("default" method) and plain NumPy arrays ("map_blocks" method) because only NumPy-compatible operations are used.

Parameters:

  • fullres_array

    Full-resolution array (dask or NumPy)

  • upsampled_output

    Upsampled bias field (same shape as fullres_array; dask or NumPy)

Returns:

  • Bias-corrected full-resolution array (same type as inputs)

Source code in zarrnii/plugins/scaled_processing/gaussian_biasfield.py
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
@hookimpl
def highres_func(self, fullres_array, upsampled_output):
    """
    Apply bias field correction to full-resolution data.

    This function takes the upsampled bias field (same size as fullres_array)
    and applies it to the full-resolution data by division.

    Works with both dask arrays (``"default"`` method) and plain NumPy arrays
    (``"map_blocks"`` method) because only NumPy-compatible operations are used.

    Args:
        fullres_array: Full-resolution array (dask or NumPy)
        upsampled_output: Upsampled bias field (same shape as fullres_array;
            dask or NumPy)

    Returns:
        Bias-corrected full-resolution array (same type as inputs)
    """
    # Avoid division by zero by clamping the bias field to a small epsilon.
    # np.maximum works for both dask arrays (via __array_ufunc__) and NumPy.
    epsilon = np.finfo(np.float32).eps
    corrected_array = fullres_array / np.maximum(upsampled_output, epsilon)

    return corrected_array
zarrnii.plugins.GaussianBiasFieldCorrection.scaled_processing_plugin_name()

Return the name of the algorithm.

Source code in zarrnii/plugins/scaled_processing/gaussian_biasfield.py
130
131
132
133
@hookimpl
def scaled_processing_plugin_name(self) -> str:
    """Return the name of the algorithm."""
    return "Gaussian Bias Field Correction"
zarrnii.plugins.GaussianBiasFieldCorrection.scaled_processing_plugin_description()

Return a description of the algorithm.

Source code in zarrnii/plugins/scaled_processing/gaussian_biasfield.py
135
136
137
138
139
140
141
142
@hookimpl
def scaled_processing_plugin_description(self) -> str:
    """Return a description of the algorithm."""
    return (
        "Multi-resolution bias field correction. Estimates smooth bias field "
        "at low resolution using Gaussian smoothing and applies correction "
        "to full resolution data by division."
    )

zarrnii.plugins.N4BiasFieldCorrection(spline_param=[2, 2, 2], convergence={'iters': [50, 50, 50, 50], 'tol': 1e-07}, shrink_factor=1)

N4 bias field correction plugin using multi-resolution processing.

This plugin estimates a smooth bias field at low resolution using the N4 bias field correction algorithm from ANTsPy and applies the correction to full resolution data by division.

Parameters:

  • spline_param (tuple[int, int, int], default: [2, 2, 2] ) –

    Spacing between knots for spline fitting (default: 200)

  • convergence (Optional[Dict[str, Any]], default: {'iters': [50, 50, 50, 50], 'tol': 1e-07} ) –

    Convergence criteria [iters, tol] (default: [50, 0.001])

  • shrink_factor (int, default: 1 ) –

    Shrink factor for processing (default: 1)

Initialize N4 bias field correction plugin.

Parameters:

  • spline_param (tuple[int, int, int], default: [2, 2, 2] ) –

    Spacing between knots for spline fitting

  • convergence (Optional[Dict[str, Any]], default: {'iters': [50, 50, 50, 50], 'tol': 1e-07} ) –

    Convergence criteria dict with 'iters' (list), 'tol'

  • shrink_factor (int, default: 1 ) –

    Shrink factor for processing

Raises:

  • ImportError

    If antspyx is not installed

Source code in zarrnii/plugins/scaled_processing/n4_biasfield.py
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
def __init__(
    self,
    spline_param: tuple[int, int, int] = [2, 2, 2],
    convergence: Optional[Dict[str, Any]] = {
        "iters": [50, 50, 50, 50],
        "tol": 1e-07,
    },
    shrink_factor: int = 1,
):
    """
    Initialize N4 bias field correction plugin.

    Args:
        spline_param: Spacing between knots for spline fitting
        convergence: Convergence criteria dict with 'iters' (list), 'tol'
        shrink_factor: Shrink factor for processing

    Raises:
        ImportError: If antspyx is not installed
    """
    if not HAS_ANTSPYX:
        raise ImportError(
            "antspyx is required for N4BiasFieldCorrection. "
            "Install it with: pip install 'zarrnii[n4]' "
            "or pip install antspyx"
        )

    self.spline_param = spline_param
    self.convergence = convergence
    self.shrink_factor = shrink_factor

Functions

zarrnii.plugins.N4BiasFieldCorrection.lowres_func(lowres_array)

Estimate bias field from low-resolution data using N4 algorithm.

This function uses ANTsPy's N4 bias field correction to estimate the bias field at low resolution.

Parameters:

  • lowres_array (ndarray) –

    Downsampled input image

Returns:

  • ndarray

    Estimated bias field at low resolution

Source code in zarrnii/plugins/scaled_processing/n4_biasfield.py
 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
@hookimpl
def lowres_func(self, lowres_array: np.ndarray) -> np.ndarray:
    """
    Estimate bias field from low-resolution data using N4 algorithm.

    This function uses ANTsPy's N4 bias field correction to estimate
    the bias field at low resolution.

    Args:
        lowres_array: Downsampled input image

    Returns:
        Estimated bias field at low resolution
    """
    if lowres_array.size == 0:
        raise ValueError("Input array is empty")

    # Handle different array dimensions
    if lowres_array.ndim < 2:
        raise ValueError("Input array must be at least 2D")

    # Store original shape
    original_shape = lowres_array.shape

    # For multi-dimensional arrays, work with the last 3 dimensions
    work_array = lowres_array
    if work_array.ndim > 3:
        # Flatten leading dimensions and work with spatial dimensions
        spatial_shape = work_array.shape[-3:]
        work_array = work_array.reshape(-1, *spatial_shape)

    # Ensure we're working with float data
    if work_array.dtype.kind in ["i", "u"]:  # integer types
        work_array = work_array.astype(np.float32)

    # Apply N4 bias field correction
    if work_array.ndim == 2:
        # For 2D data
        ants_img = ants.from_numpy(work_array)
        self.spline_param = [2, 2]
        # Use return_bias_field=True to get the bias field
        bias_result = ants.n4_bias_field_correction(
            ants_img,
            return_bias_field=True,
            spline_param=self.spline_param,
            convergence=self.convergence,
            shrink_factor=self.shrink_factor,
        )
        # Extract the bias field
        bias_field = bias_result.numpy()
    else:
        # Apply N4 to each volume if we have multiple or single 3D volume
        if work_array.ndim == 4:  # batched 3D volumes
            bias_field = np.zeros_like(work_array)
            for i in range(work_array.shape[0]):
                ants_img = ants.from_numpy(work_array[i])
                bias_result = ants.n4_bias_field_correction(
                    ants_img,
                    return_bias_field=True,
                    spline_param=self.spline_param,
                    convergence=self.convergence,
                    shrink_factor=self.shrink_factor,
                )
                bias_field[i] = bias_result.numpy()
        else:  # single 3D volume
            ants_img = ants.from_numpy(work_array)
            bias_result = ants.n4_bias_field_correction(
                ants_img,
                return_bias_field=True,
                spline_param=self.spline_param,
                convergence=self.convergence,
                shrink_factor=self.shrink_factor,
            )
            bias_field = bias_result.numpy()

    # Reshape back to original shape if needed
    if original_shape != bias_field.shape:
        bias_field = bias_field.reshape(original_shape)

    # Avoid division by zero by adding small epsilon
    bias_field = np.maximum(bias_field, np.finfo(bias_field.dtype).eps)

    return bias_field
zarrnii.plugins.N4BiasFieldCorrection.highres_func(fullres_array, upsampled_output)

Apply bias field correction to full-resolution data.

This function takes the upsampled bias field (same size as fullres_array) and applies it to the full-resolution data by division.

Works with both dask arrays ("default" method) and plain NumPy arrays ("map_blocks" method) because only NumPy-compatible operations are used.

Parameters:

  • fullres_array

    Full-resolution array (dask or NumPy)

  • upsampled_output

    Upsampled bias field (same shape as fullres; dask or NumPy)

Returns:

  • Bias-corrected full-resolution array (same type as inputs)

Source code in zarrnii/plugins/scaled_processing/n4_biasfield.py
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
@hookimpl
def highres_func(self, fullres_array, upsampled_output):
    """
    Apply bias field correction to full-resolution data.

    This function takes the upsampled bias field (same size as
    fullres_array) and applies it to the full-resolution data by division.

    Works with both dask arrays (``"default"`` method) and plain NumPy arrays
    (``"map_blocks"`` method) because only NumPy-compatible operations are used.

    Args:
        fullres_array: Full-resolution array (dask or NumPy)
        upsampled_output: Upsampled bias field (same shape as fullres;
            dask or NumPy)

    Returns:
        Bias-corrected full-resolution array (same type as inputs)
    """
    # Avoid division by zero by clamping the bias field to a small epsilon.
    # np.maximum works for both dask arrays (via __array_ufunc__) and NumPy.
    epsilon = np.finfo(np.float32).eps
    corrected_array = fullres_array / np.maximum(upsampled_output, epsilon)

    return corrected_array
zarrnii.plugins.N4BiasFieldCorrection.scaled_processing_plugin_name()

Return the name of the algorithm.

Source code in zarrnii/plugins/scaled_processing/n4_biasfield.py
180
181
182
183
@hookimpl
def scaled_processing_plugin_name(self) -> str:
    """Return the name of the algorithm."""
    return "N4 Bias Field Correction"
zarrnii.plugins.N4BiasFieldCorrection.scaled_processing_plugin_description()

Return a description of the algorithm.

Source code in zarrnii/plugins/scaled_processing/n4_biasfield.py
185
186
187
188
189
190
191
192
@hookimpl
def scaled_processing_plugin_description(self) -> str:
    """Return a description of the algorithm."""
    return (
        "Multi-resolution N4 bias field correction. Estimates smooth bias "
        "field at low resolution using ANTsPy N4 algorithm and applies "
        "correction to full resolution data by division."
    )

zarrnii.plugins.SegmentationCleaner(mask_threshold=50, max_extent=0.15, exclusion_threshold=50)

Segmentation cleaning plugin using multi-resolution processing.

This plugin removes artifactual objects from segmentations by: 1. At low resolution: performing connected components analysis on a thresholded mask and identifying objects with extent below a threshold 2. At high resolution: applying an exclusion mask to remove these objects from the full-resolution segmentation

The extent of a region is defined as the ratio of pixels in the region to pixels in the total bounding box. Objects with low extent (e.g., < 0.15) are typically large artifactual objects with sparse coverage.

Parameters:

  • mask_threshold (float, default: 50 ) –

    Threshold for creating initial binary mask (default: 50)

  • max_extent (float, default: 0.15 ) –

    Maximum extent threshold for exclusion (default: 0.15)

  • exclusion_threshold (float, default: 50 ) –

    Threshold for upsampled exclusion mask (default: 50)

Initialize segmentation cleaner plugin.

Parameters:

  • mask_threshold (float, default: 50 ) –

    Threshold for creating initial binary mask from input

  • max_extent (float, default: 0.15 ) –

    Maximum extent to include in exclusion mask (objects with extent < max_extent are considered artifactual)

  • exclusion_threshold (float, default: 50 ) –

    Threshold for applying upsampled exclusion mask

Source code in zarrnii/plugins/scaled_processing/segmentation_cleaner.py
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
def __init__(
    self,
    mask_threshold: float = 50,
    max_extent: float = 0.15,
    exclusion_threshold: float = 50,
):
    """
    Initialize segmentation cleaner plugin.

    Args:
        mask_threshold: Threshold for creating initial binary mask from input
        max_extent: Maximum extent to include in exclusion mask (objects with
                   extent < max_extent are considered artifactual)
        exclusion_threshold: Threshold for applying upsampled exclusion mask
    """
    self.mask_threshold = mask_threshold
    self.max_extent = max_extent
    self.exclusion_threshold = exclusion_threshold

Functions

zarrnii.plugins.SegmentationCleaner.lowres_func(lowres_array)

Create exclusion mask from low-resolution segmentation data.

This function performs connected components analysis on thresholded low-resolution data and creates an exclusion mask containing objects with extent below the threshold.

Parameters:

  • lowres_array (ndarray) –

    Downsampled segmentation image

Returns:

  • ndarray

    Exclusion mask at low resolution (uint8, values 0 or 100)

Source code in zarrnii/plugins/scaled_processing/segmentation_cleaner.py
 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
@hookimpl
def lowres_func(self, lowres_array: np.ndarray) -> np.ndarray:
    """
    Create exclusion mask from low-resolution segmentation data.

    This function performs connected components analysis on thresholded
    low-resolution data and creates an exclusion mask containing objects
    with extent below the threshold.

    Args:
        lowres_array: Downsampled segmentation image

    Returns:
        Exclusion mask at low resolution (uint8, values 0 or 100)
    """
    if lowres_array.size == 0:
        raise ValueError("Input array is empty")

    if lowres_array.ndim < 2:
        raise ValueError("Input array must be at least 2D")

    # Store original shape
    original_shape = lowres_array.shape

    # For multi-dimensional arrays, work with the last 3 dimensions
    work_array = lowres_array
    if work_array.ndim > 3:
        # Flatten leading dimensions and work with spatial dimensions
        spatial_shape = work_array.shape[-3:]
        work_array = work_array.reshape(-1, *spatial_shape)

    # Process connected components and create exclusion mask
    if work_array.ndim == 2:
        # 2D case
        mask_img = work_array > self.mask_threshold
        conncomp, nlabels = label(mask_img, return_num=True)
        props = regionprops(conncomp)
        keep_labels = {r.label for r in props if r.extent < self.max_extent}
        exclude_mask = np.isin(conncomp, list(keep_labels))
        exclude_mask = exclude_mask.astype("uint8") * 100
    else:
        # 3D or batched 3D case
        if work_array.ndim == 4:
            # Batched 3D volumes - process each separately
            exclude_mask = np.zeros_like(work_array, dtype="uint8")
            for i in range(work_array.shape[0]):
                mask_img = work_array[i] > self.mask_threshold
                conncomp, nlabels = label(mask_img, return_num=True)
                props = regionprops(conncomp)
                keep_labels = {r.label for r in props if r.extent < self.max_extent}
                batch_exclude = np.isin(conncomp, list(keep_labels))
                exclude_mask[i] = batch_exclude.astype("uint8") * 100
        else:
            # Single 3D volume
            mask_img = work_array > self.mask_threshold
            conncomp, nlabels = label(mask_img, return_num=True)
            props = regionprops(conncomp)
            keep_labels = {r.label for r in props if r.extent < self.max_extent}
            exclude_mask = np.isin(conncomp, list(keep_labels))
            exclude_mask = exclude_mask.astype("uint8") * 100

    # Reshape back to original shape if needed
    if original_shape != exclude_mask.shape:
        exclude_mask = exclude_mask.reshape(original_shape)

    return exclude_mask
zarrnii.plugins.SegmentationCleaner.highres_func(fullres_array, upsampled_output)

Apply exclusion mask to full-resolution segmentation data.

This function takes the upsampled exclusion mask and applies it to the full-resolution segmentation by zeroing out the excluded regions.

Works with both dask arrays ("default" method) and plain NumPy arrays ("map_blocks" method) because only NumPy-compatible operations are used.

Parameters:

  • fullres_array

    Full-resolution segmentation array (dask or NumPy)

  • upsampled_output

    Upsampled exclusion mask (same shape as fullres; dask or NumPy)

Returns:

  • Cleaned full-resolution segmentation array (same type as inputs)

Source code in zarrnii/plugins/scaled_processing/segmentation_cleaner.py
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
@hookimpl
def highres_func(self, fullres_array, upsampled_output):
    """
    Apply exclusion mask to full-resolution segmentation data.

    This function takes the upsampled exclusion mask and applies it to
    the full-resolution segmentation by zeroing out the excluded regions.

    Works with both dask arrays (``"default"`` method) and plain NumPy arrays
    (``"map_blocks"`` method) because only NumPy-compatible operations are used.

    Args:
        fullres_array: Full-resolution segmentation array (dask or NumPy)
        upsampled_output: Upsampled exclusion mask (same shape as fullres;
            dask or NumPy)

    Returns:
        Cleaned full-resolution segmentation array (same type as inputs)
    """
    # Threshold the upsampled exclusion mask.
    # Values >= exclusion_threshold (e.g., 50) indicate regions to exclude.
    exclusion_mask = upsampled_output >= self.exclusion_threshold

    # Apply mask: set excluded regions to zero.
    # np.where works for both dask arrays (via __array_ufunc__) and NumPy.
    cleaned_array = np.where(exclusion_mask, 0, fullres_array)

    return cleaned_array
zarrnii.plugins.SegmentationCleaner.scaled_processing_plugin_name()

Return the name of the algorithm.

Source code in zarrnii/plugins/scaled_processing/segmentation_cleaner.py
153
154
155
156
@hookimpl
def scaled_processing_plugin_name(self) -> str:
    """Return the name of the algorithm."""
    return "Segmentation Cleaner"
zarrnii.plugins.SegmentationCleaner.scaled_processing_plugin_description()

Return a description of the algorithm.

Source code in zarrnii/plugins/scaled_processing/segmentation_cleaner.py
158
159
160
161
162
163
164
165
166
@hookimpl
def scaled_processing_plugin_description(self) -> str:
    """Return a description of the algorithm."""
    return (
        "Multi-resolution segmentation cleaning. Identifies and removes "
        "artifactual objects by performing connected components analysis at "
        "low resolution, filtering by extent, and applying exclusion mask to "
        "full resolution data."
    )

zarrnii.plugins.LocalOtsuSegmentation(nbins=256)

Local Otsu thresholding segmentation plugin.

This plugin uses Otsu's method to automatically determine an optimal threshold for binary image segmentation. The method assumes a bimodal histogram and finds the threshold that minimizes intra-class variance. The threshold is computed locally for each processing block, making it suitable for images with varying illumination or contrast.

Parameters:

  • nbins (int, default: 256 ) –

    Number of bins for histogram computation (default: 256)

Initialize local Otsu segmentation plugin.

Parameters:

  • nbins (int, default: 256 ) –

    Number of bins for histogram computation

Source code in zarrnii/plugins/segmentation/local_otsu.py
32
33
34
35
36
37
38
39
def __init__(self, nbins: int = 256):
    """
    Initialize local Otsu segmentation plugin.

    Args:
        nbins: Number of bins for histogram computation
    """
    self.nbins = nbins

Functions

zarrnii.plugins.LocalOtsuSegmentation.segment(image, metadata=None)

Segment image using local Otsu thresholding.

Parameters:

  • image (ndarray) –

    Input image as numpy array

  • metadata (Optional[Dict[str, Any]], default: None ) –

    Optional metadata (unused in Otsu method)

Returns:

  • ndarray

    Binary segmentation mask as numpy array with same shape as input.

  • ndarray

    Values are 0 (background) and 1 (foreground).

Raises:

  • ValueError

    If input image is empty or has invalid dimensions

Source code in zarrnii/plugins/segmentation/local_otsu.py
 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
@hookimpl
def segment(
    self, image: np.ndarray, metadata: Optional[Dict[str, Any]] = None
) -> np.ndarray:
    """
    Segment image using local Otsu thresholding.

    Args:
        image: Input image as numpy array
        metadata: Optional metadata (unused in Otsu method)

    Returns:
        Binary segmentation mask as numpy array with same shape as input.
        Values are 0 (background) and 1 (foreground).

    Raises:
        ValueError: If input image is empty or has invalid dimensions
    """
    if image.size == 0:
        raise ValueError("Input image is empty")

    if image.ndim < 2:
        raise ValueError("Input image must be at least 2D")

    # Store original shape for output
    original_shape = image.shape

    # Ensure image is in a suitable format for Otsu thresholding
    if image.dtype == bool:
        # Already binary, return as-is
        return image.astype(np.uint8)

    # Handle multi-dimensional images
    work_image = image

    # For images with more than 3 dimensions, flatten extra dimensions
    if work_image.ndim > 3:
        # Reshape to 3D by flattening leading dimensions
        leading_dims = work_image.shape[:-2]  # All dimensions except last 2
        work_image = work_image.reshape(-1, *work_image.shape[-2:])

    if work_image.ndim == 3:
        # For 3D images, process the first slice/channel
        if work_image.shape[0] <= 4:  # Likely channels-first format
            work_image = work_image[0]
        else:
            # If first dimension is large, likely depth dimension, take middle slice
            mid_slice = work_image.shape[0] // 2
            work_image = work_image[mid_slice]

    # Now work_image should be 2D
    try:
        threshold = threshold_otsu(work_image, nbins=self.nbins)
    except ValueError as e:
        # Handle edge cases where Otsu fails (e.g., constant image)
        if "all values are identical" in str(e).lower() or np.all(
            work_image == work_image.flat[0]
        ):
            # Return all zeros for constant images with original shape
            return np.zeros(original_shape, dtype=np.uint8)
        else:
            raise e

    # Apply threshold to original image
    binary_mask = image > threshold

    return binary_mask.astype(np.uint8)
zarrnii.plugins.LocalOtsuSegmentation.segmentation_plugin_name()

Return the name of the segmentation algorithm.

Source code in zarrnii/plugins/segmentation/local_otsu.py
109
110
111
112
@hookimpl
def segmentation_plugin_name(self) -> str:
    """Return the name of the segmentation algorithm."""
    return "Local Otsu Thresholding"
zarrnii.plugins.LocalOtsuSegmentation.segmentation_plugin_description()

Return a description of the segmentation algorithm.

Source code in zarrnii/plugins/segmentation/local_otsu.py
114
115
116
117
118
119
120
121
122
@hookimpl
def segmentation_plugin_description(self) -> str:
    """Return a description of the segmentation algorithm."""
    return (
        "Local Otsu's automatic threshold selection method for binary segmentation. "
        "Finds the threshold that minimizes intra-class variance assuming a "
        "bimodal intensity distribution. Threshold is computed locally for each "
        "processing block, suitable for images with varying illumination."
    )
zarrnii.plugins.LocalOtsuSegmentation.get_threshold(image)

Get the Otsu threshold value without applying segmentation.

Parameters:

  • image (ndarray) –

    Input image as numpy array

Returns:

  • float

    Computed Otsu threshold value

Source code in zarrnii/plugins/segmentation/local_otsu.py
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
def get_threshold(self, image: np.ndarray) -> float:
    """
    Get the Otsu threshold value without applying segmentation.

    Args:
        image: Input image as numpy array

    Returns:
        Computed Otsu threshold value
    """
    if image.size == 0:
        raise ValueError("Input image is empty")

    # Handle multi-channel images by taking the first channel if needed
    if image.ndim > 3:
        while image.ndim > 3:
            image = image[0]

    if image.ndim == 3 and image.shape[0] <= 4:
        image = image[0]

    return threshold_otsu(image, nbins=self.nbins)

zarrnii.plugins.ThresholdSegmentation(thresholds, inclusive=True)

Threshold-based segmentation plugin.

This plugin applies threshold-based segmentation using either a single threshold value or multiple threshold values to create labeled regions. It can use manually specified thresholds or thresholds computed from analysis functions like Otsu multi-thresholding.

Parameters:

  • thresholds (Union[float, List[float]]) –

    Single threshold value or list of threshold values. For single threshold, creates binary segmentation (0/1). For multiple thresholds, creates multi-class segmentation (0/1/2/...).

  • inclusive (bool, default: True ) –

    Whether thresholds are inclusive (default: True). If True, pixels >= threshold are labeled as foreground. If False, pixels > threshold are labeled as foreground.

Initialize threshold segmentation plugin.

Parameters:

  • thresholds (Union[float, List[float]]) –

    Single threshold or list of thresholds

  • inclusive (bool, default: True ) –

    Whether thresholds are inclusive (>= vs >)

Source code in zarrnii/plugins/segmentation/threshold.py
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
def __init__(self, thresholds: Union[float, List[float]], inclusive: bool = True):
    """
    Initialize threshold segmentation plugin.

    Args:
        thresholds: Single threshold or list of thresholds
        inclusive: Whether thresholds are inclusive (>= vs >)
    """
    # Normalize thresholds to always be a list
    if isinstance(thresholds, (int, float)):
        self.thresholds = [float(thresholds)]
    else:
        self.thresholds = [float(t) for t in thresholds]

    # Sort thresholds to ensure proper ordering
    self.thresholds = sorted(self.thresholds)
    self.inclusive = inclusive

Functions

zarrnii.plugins.ThresholdSegmentation.segment(image, metadata=None)

Segment image using threshold values.

Parameters:

  • image (ndarray) –

    Input image as numpy array

  • metadata (Optional[Dict[str, Any]], default: None ) –

    Optional metadata (unused in threshold method)

Returns:

  • ndarray

    Labeled segmentation mask as numpy array with same shape as input.

  • ndarray

    Values are 0 (background), 1, 2, ... up to len(thresholds) classes.

Raises:

  • ValueError

    If input image is empty

Source code in zarrnii/plugins/segmentation/threshold.py
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
@hookimpl
def segment(
    self, image: np.ndarray, metadata: Optional[Dict[str, Any]] = None
) -> np.ndarray:
    """
    Segment image using threshold values.

    Args:
        image: Input image as numpy array
        metadata: Optional metadata (unused in threshold method)

    Returns:
        Labeled segmentation mask as numpy array with same shape as input.
        Values are 0 (background), 1, 2, ... up to len(thresholds) classes.

    Raises:
        ValueError: If input image is empty
    """
    if image.size == 0:
        raise ValueError("Input image is empty")

    # Initialize result with zeros (background class)
    result = np.zeros(image.shape, dtype=np.uint8)

    # Apply thresholds to create labeled regions
    for i, threshold in enumerate(self.thresholds):
        if self.inclusive:
            mask = image >= threshold
        else:
            mask = image > threshold

        # Assign class label (i+1) to pixels above threshold
        result[mask] = i + 1

    return result
zarrnii.plugins.ThresholdSegmentation.segmentation_plugin_name()

Return the name of the segmentation algorithm.

Source code in zarrnii/plugins/segmentation/threshold.py
89
90
91
92
93
94
95
@hookimpl
def segmentation_plugin_name(self) -> str:
    """Return the name of the segmentation algorithm."""
    if len(self.thresholds) == 1:
        return "Binary Threshold"
    else:
        return "Multi-level Threshold"
zarrnii.plugins.ThresholdSegmentation.segmentation_plugin_description()

Return a description of the segmentation algorithm.

Source code in zarrnii/plugins/segmentation/threshold.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
@hookimpl
def segmentation_plugin_description(self) -> str:
    """Return a description of the segmentation algorithm."""
    if len(self.thresholds) == 1:
        op = ">=" if self.inclusive else ">"
        return (
            f"Binary threshold segmentation using threshold = {self.thresholds[0]}. "
            f"Pixels {op} threshold are labeled as foreground (1), others as background (0)."
        )
    else:
        op = ">=" if self.inclusive else ">"
        return (
            f"Multi-level threshold segmentation using {len(self.thresholds)} thresholds: "
            f"{self.thresholds}. Creates {len(self.thresholds) + 1} labeled regions based on "
            f"which thresholds each pixel exceeds (using {op} comparison)."
        )
zarrnii.plugins.ThresholdSegmentation.get_thresholds()

Get the threshold values used by this plugin.

Returns:

  • List[float]

    List of threshold values

Source code in zarrnii/plugins/segmentation/threshold.py
118
119
120
121
122
123
124
125
def get_thresholds(self) -> List[float]:
    """
    Get the threshold values used by this plugin.

    Returns:
        List of threshold values
    """
    return self.thresholds.copy()

Functions

zarrnii.plugins.get_global_plugin_manager()

Get the global plugin manager instance.

Returns:

  • PluginManager

    Global PluginManager instance

Source code in zarrnii/plugins/plugin_manager.py
34
35
36
37
38
39
40
41
42
43
44
def get_global_plugin_manager() -> pluggy.PluginManager:
    """
    Get the global plugin manager instance.

    Returns:
        Global PluginManager instance
    """
    global _plugin_manager
    if _plugin_manager is None:
        _plugin_manager = get_plugin_manager()
    return _plugin_manager

zarrnii.plugins.get_plugin_manager()

Get or create the ZarrNii plugin manager.

Returns:

  • PluginManager

    PluginManager instance configured for ZarrNii plugins

Source code in zarrnii/plugins/plugin_manager.py
15
16
17
18
19
20
21
22
23
24
25
26
27
def get_plugin_manager() -> pluggy.PluginManager:
    """
    Get or create the ZarrNii plugin manager.

    Returns:
        PluginManager instance configured for ZarrNii plugins
    """
    pm = pluggy.PluginManager("zarrnii")
    pm.add_hookspecs(ZarrNiiSpec)
    # Discover and register external plugins declared via setuptools entry points
    # under the "zarrnii" group (matching the pluggy project name).
    pm.load_setuptools_entrypoints("zarrnii")
    return pm

Hook specifications

Plugin hook specifications for ZarrNii plugins.

This module defines the hook specifications that plugins must implement.

Classes

zarrnii.plugins.hookspecs.ZarrNiiSpec

Hook specifications for ZarrNii plugins.

Plugin authors should implement any subset of these hooks as plain methods decorated with @hookimpl from :mod:zarrnii.plugins.

Example::

from zarrnii.plugins import hookimpl

class MyPlugin:
    @hookimpl
    def segment(self, image, metadata=None):
        ...

Functions

zarrnii.plugins.hookspecs.ZarrNiiSpec.segment(image, metadata=None)

Segment an image and return a binary or labeled mask.

Parameters:

  • image

    Input image as numpy array.

  • metadata

    Optional metadata dictionary containing image information.

Returns:

  • Segmented image as numpy array.

Source code in zarrnii/plugins/hookspecs.py
28
29
30
31
32
33
34
35
36
37
38
@hookspec
def segment(self, image, metadata=None):
    """Segment an image and return a binary or labeled mask.

    Args:
        image: Input image as numpy array.
        metadata: Optional metadata dictionary containing image information.

    Returns:
        Segmented image as numpy array.
    """
zarrnii.plugins.hookspecs.ZarrNiiSpec.segmentation_plugin_name()

Return the name of the segmentation algorithm.

Returns:

  • str

    String name of the algorithm.

Source code in zarrnii/plugins/hookspecs.py
40
41
42
43
44
45
46
@hookspec
def segmentation_plugin_name(self) -> str:
    """Return the name of the segmentation algorithm.

    Returns:
        String name of the algorithm.
    """
zarrnii.plugins.hookspecs.ZarrNiiSpec.segmentation_plugin_description()

Return a description of the segmentation algorithm.

Returns:

  • str

    String description of the algorithm.

Source code in zarrnii/plugins/hookspecs.py
48
49
50
51
52
53
54
@hookspec
def segmentation_plugin_description(self) -> str:
    """Return a description of the segmentation algorithm.

    Returns:
        String description of the algorithm.
    """
zarrnii.plugins.hookspecs.ZarrNiiSpec.scaled_processing_plugin_name()

Return the name of the scaled processing algorithm.

Returns:

  • str

    String name of the algorithm.

Source code in zarrnii/plugins/hookspecs.py
56
57
58
59
60
61
62
@hookspec
def scaled_processing_plugin_name(self) -> str:
    """Return the name of the scaled processing algorithm.

    Returns:
        String name of the algorithm.
    """
zarrnii.plugins.hookspecs.ZarrNiiSpec.scaled_processing_plugin_description()

Return a description of the scaled processing algorithm.

Returns:

  • str

    String description of the algorithm.

Source code in zarrnii/plugins/hookspecs.py
64
65
66
67
68
69
70
@hookspec
def scaled_processing_plugin_description(self) -> str:
    """Return a description of the scaled processing algorithm.

    Returns:
        String description of the algorithm.
    """
zarrnii.plugins.hookspecs.ZarrNiiSpec.lowres_func(lowres_array)

Process low-resolution data and return the result.

This function operates on a downsampled numpy array and computes the algorithm output that will be upsampled and applied to the full-resolution data.

Parameters:

  • lowres_array

    Downsampled input image as numpy array.

Returns:

  • Low-resolution output array (e.g., bias field, correction map).

Source code in zarrnii/plugins/hookspecs.py
72
73
74
75
76
77
78
79
80
81
82
83
84
85
@hookspec
def lowres_func(self, lowres_array):
    """Process low-resolution data and return the result.

    This function operates on a downsampled numpy array and computes
    the algorithm output that will be upsampled and applied to the
    full-resolution data.

    Args:
        lowres_array: Downsampled input image as numpy array.

    Returns:
        Low-resolution output array (e.g., bias field, correction map).
    """
zarrnii.plugins.hookspecs.ZarrNiiSpec.highres_func(fullres_array, upsampled_output)

Apply upsampled output to full-resolution data blockwise.

Parameters:

  • fullres_array

    Full-resolution dask array.

  • upsampled_output

    Upsampled output (same shape as fullres_array).

Returns:

  • Processed full-resolution dask array.

Source code in zarrnii/plugins/hookspecs.py
87
88
89
90
91
92
93
94
95
96
97
@hookspec
def highres_func(self, fullres_array, upsampled_output):
    """Apply upsampled output to full-resolution data blockwise.

    Args:
        fullres_array: Full-resolution dask array.
        upsampled_output: Upsampled output (same shape as fullres_array).

    Returns:
        Processed full-resolution dask array.
    """

Plugin manager

Plugin manager for ZarrNii plugins.

This module provides the plugin manager that discovers and manages plugins using the pluggy framework.

Classes

Functions

zarrnii.plugins.plugin_manager.get_plugin_manager()

Get or create the ZarrNii plugin manager.

Returns:

  • PluginManager

    PluginManager instance configured for ZarrNii plugins

Source code in zarrnii/plugins/plugin_manager.py
15
16
17
18
19
20
21
22
23
24
25
26
27
def get_plugin_manager() -> pluggy.PluginManager:
    """
    Get or create the ZarrNii plugin manager.

    Returns:
        PluginManager instance configured for ZarrNii plugins
    """
    pm = pluggy.PluginManager("zarrnii")
    pm.add_hookspecs(ZarrNiiSpec)
    # Discover and register external plugins declared via setuptools entry points
    # under the "zarrnii" group (matching the pluggy project name).
    pm.load_setuptools_entrypoints("zarrnii")
    return pm

zarrnii.plugins.plugin_manager.get_global_plugin_manager()

Get the global plugin manager instance.

Returns:

  • PluginManager

    Global PluginManager instance

Source code in zarrnii/plugins/plugin_manager.py
34
35
36
37
38
39
40
41
42
43
44
def get_global_plugin_manager() -> pluggy.PluginManager:
    """
    Get the global plugin manager instance.

    Returns:
        Global PluginManager instance
    """
    global _plugin_manager
    if _plugin_manager is None:
        _plugin_manager = get_plugin_manager()
    return _plugin_manager

Built-in segmentation plugins

Local Otsu thresholding segmentation plugin.

This module implements Otsu's automatic threshold selection method for binary image segmentation, applied locally to each processing block.

Classes

zarrnii.plugins.segmentation.local_otsu.LocalOtsuSegmentation(nbins=256)

Local Otsu thresholding segmentation plugin.

This plugin uses Otsu's method to automatically determine an optimal threshold for binary image segmentation. The method assumes a bimodal histogram and finds the threshold that minimizes intra-class variance. The threshold is computed locally for each processing block, making it suitable for images with varying illumination or contrast.

Parameters:

  • nbins (int, default: 256 ) –

    Number of bins for histogram computation (default: 256)

Initialize local Otsu segmentation plugin.

Parameters:

  • nbins (int, default: 256 ) –

    Number of bins for histogram computation

Source code in zarrnii/plugins/segmentation/local_otsu.py
32
33
34
35
36
37
38
39
def __init__(self, nbins: int = 256):
    """
    Initialize local Otsu segmentation plugin.

    Args:
        nbins: Number of bins for histogram computation
    """
    self.nbins = nbins

Functions

zarrnii.plugins.segmentation.local_otsu.LocalOtsuSegmentation.segment(image, metadata=None)

Segment image using local Otsu thresholding.

Parameters:

  • image (ndarray) –

    Input image as numpy array

  • metadata (Optional[Dict[str, Any]], default: None ) –

    Optional metadata (unused in Otsu method)

Returns:

  • ndarray

    Binary segmentation mask as numpy array with same shape as input.

  • ndarray

    Values are 0 (background) and 1 (foreground).

Raises:

  • ValueError

    If input image is empty or has invalid dimensions

Source code in zarrnii/plugins/segmentation/local_otsu.py
 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
@hookimpl
def segment(
    self, image: np.ndarray, metadata: Optional[Dict[str, Any]] = None
) -> np.ndarray:
    """
    Segment image using local Otsu thresholding.

    Args:
        image: Input image as numpy array
        metadata: Optional metadata (unused in Otsu method)

    Returns:
        Binary segmentation mask as numpy array with same shape as input.
        Values are 0 (background) and 1 (foreground).

    Raises:
        ValueError: If input image is empty or has invalid dimensions
    """
    if image.size == 0:
        raise ValueError("Input image is empty")

    if image.ndim < 2:
        raise ValueError("Input image must be at least 2D")

    # Store original shape for output
    original_shape = image.shape

    # Ensure image is in a suitable format for Otsu thresholding
    if image.dtype == bool:
        # Already binary, return as-is
        return image.astype(np.uint8)

    # Handle multi-dimensional images
    work_image = image

    # For images with more than 3 dimensions, flatten extra dimensions
    if work_image.ndim > 3:
        # Reshape to 3D by flattening leading dimensions
        leading_dims = work_image.shape[:-2]  # All dimensions except last 2
        work_image = work_image.reshape(-1, *work_image.shape[-2:])

    if work_image.ndim == 3:
        # For 3D images, process the first slice/channel
        if work_image.shape[0] <= 4:  # Likely channels-first format
            work_image = work_image[0]
        else:
            # If first dimension is large, likely depth dimension, take middle slice
            mid_slice = work_image.shape[0] // 2
            work_image = work_image[mid_slice]

    # Now work_image should be 2D
    try:
        threshold = threshold_otsu(work_image, nbins=self.nbins)
    except ValueError as e:
        # Handle edge cases where Otsu fails (e.g., constant image)
        if "all values are identical" in str(e).lower() or np.all(
            work_image == work_image.flat[0]
        ):
            # Return all zeros for constant images with original shape
            return np.zeros(original_shape, dtype=np.uint8)
        else:
            raise e

    # Apply threshold to original image
    binary_mask = image > threshold

    return binary_mask.astype(np.uint8)
zarrnii.plugins.segmentation.local_otsu.LocalOtsuSegmentation.segmentation_plugin_name()

Return the name of the segmentation algorithm.

Source code in zarrnii/plugins/segmentation/local_otsu.py
109
110
111
112
@hookimpl
def segmentation_plugin_name(self) -> str:
    """Return the name of the segmentation algorithm."""
    return "Local Otsu Thresholding"
zarrnii.plugins.segmentation.local_otsu.LocalOtsuSegmentation.segmentation_plugin_description()

Return a description of the segmentation algorithm.

Source code in zarrnii/plugins/segmentation/local_otsu.py
114
115
116
117
118
119
120
121
122
@hookimpl
def segmentation_plugin_description(self) -> str:
    """Return a description of the segmentation algorithm."""
    return (
        "Local Otsu's automatic threshold selection method for binary segmentation. "
        "Finds the threshold that minimizes intra-class variance assuming a "
        "bimodal intensity distribution. Threshold is computed locally for each "
        "processing block, suitable for images with varying illumination."
    )
zarrnii.plugins.segmentation.local_otsu.LocalOtsuSegmentation.get_threshold(image)

Get the Otsu threshold value without applying segmentation.

Parameters:

  • image (ndarray) –

    Input image as numpy array

Returns:

  • float

    Computed Otsu threshold value

Source code in zarrnii/plugins/segmentation/local_otsu.py
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
def get_threshold(self, image: np.ndarray) -> float:
    """
    Get the Otsu threshold value without applying segmentation.

    Args:
        image: Input image as numpy array

    Returns:
        Computed Otsu threshold value
    """
    if image.size == 0:
        raise ValueError("Input image is empty")

    # Handle multi-channel images by taking the first channel if needed
    if image.ndim > 3:
        while image.ndim > 3:
            image = image[0]

    if image.ndim == 3 and image.shape[0] <= 4:
        image = image[0]

    return threshold_otsu(image, nbins=self.nbins)

Threshold segmentation plugin.

This module implements threshold-based segmentation that can use either manual threshold values or computed thresholds (e.g., from Otsu analysis).

Classes

zarrnii.plugins.segmentation.threshold.ThresholdSegmentation(thresholds, inclusive=True)

Threshold-based segmentation plugin.

This plugin applies threshold-based segmentation using either a single threshold value or multiple threshold values to create labeled regions. It can use manually specified thresholds or thresholds computed from analysis functions like Otsu multi-thresholding.

Parameters:

  • thresholds (Union[float, List[float]]) –

    Single threshold value or list of threshold values. For single threshold, creates binary segmentation (0/1). For multiple thresholds, creates multi-class segmentation (0/1/2/...).

  • inclusive (bool, default: True ) –

    Whether thresholds are inclusive (default: True). If True, pixels >= threshold are labeled as foreground. If False, pixels > threshold are labeled as foreground.

Initialize threshold segmentation plugin.

Parameters:

  • thresholds (Union[float, List[float]]) –

    Single threshold or list of thresholds

  • inclusive (bool, default: True ) –

    Whether thresholds are inclusive (>= vs >)

Source code in zarrnii/plugins/segmentation/threshold.py
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
def __init__(self, thresholds: Union[float, List[float]], inclusive: bool = True):
    """
    Initialize threshold segmentation plugin.

    Args:
        thresholds: Single threshold or list of thresholds
        inclusive: Whether thresholds are inclusive (>= vs >)
    """
    # Normalize thresholds to always be a list
    if isinstance(thresholds, (int, float)):
        self.thresholds = [float(thresholds)]
    else:
        self.thresholds = [float(t) for t in thresholds]

    # Sort thresholds to ensure proper ordering
    self.thresholds = sorted(self.thresholds)
    self.inclusive = inclusive

Functions

zarrnii.plugins.segmentation.threshold.ThresholdSegmentation.segment(image, metadata=None)

Segment image using threshold values.

Parameters:

  • image (ndarray) –

    Input image as numpy array

  • metadata (Optional[Dict[str, Any]], default: None ) –

    Optional metadata (unused in threshold method)

Returns:

  • ndarray

    Labeled segmentation mask as numpy array with same shape as input.

  • ndarray

    Values are 0 (background), 1, 2, ... up to len(thresholds) classes.

Raises:

  • ValueError

    If input image is empty

Source code in zarrnii/plugins/segmentation/threshold.py
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
@hookimpl
def segment(
    self, image: np.ndarray, metadata: Optional[Dict[str, Any]] = None
) -> np.ndarray:
    """
    Segment image using threshold values.

    Args:
        image: Input image as numpy array
        metadata: Optional metadata (unused in threshold method)

    Returns:
        Labeled segmentation mask as numpy array with same shape as input.
        Values are 0 (background), 1, 2, ... up to len(thresholds) classes.

    Raises:
        ValueError: If input image is empty
    """
    if image.size == 0:
        raise ValueError("Input image is empty")

    # Initialize result with zeros (background class)
    result = np.zeros(image.shape, dtype=np.uint8)

    # Apply thresholds to create labeled regions
    for i, threshold in enumerate(self.thresholds):
        if self.inclusive:
            mask = image >= threshold
        else:
            mask = image > threshold

        # Assign class label (i+1) to pixels above threshold
        result[mask] = i + 1

    return result
zarrnii.plugins.segmentation.threshold.ThresholdSegmentation.segmentation_plugin_name()

Return the name of the segmentation algorithm.

Source code in zarrnii/plugins/segmentation/threshold.py
89
90
91
92
93
94
95
@hookimpl
def segmentation_plugin_name(self) -> str:
    """Return the name of the segmentation algorithm."""
    if len(self.thresholds) == 1:
        return "Binary Threshold"
    else:
        return "Multi-level Threshold"
zarrnii.plugins.segmentation.threshold.ThresholdSegmentation.segmentation_plugin_description()

Return a description of the segmentation algorithm.

Source code in zarrnii/plugins/segmentation/threshold.py
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
@hookimpl
def segmentation_plugin_description(self) -> str:
    """Return a description of the segmentation algorithm."""
    if len(self.thresholds) == 1:
        op = ">=" if self.inclusive else ">"
        return (
            f"Binary threshold segmentation using threshold = {self.thresholds[0]}. "
            f"Pixels {op} threshold are labeled as foreground (1), others as background (0)."
        )
    else:
        op = ">=" if self.inclusive else ">"
        return (
            f"Multi-level threshold segmentation using {len(self.thresholds)} thresholds: "
            f"{self.thresholds}. Creates {len(self.thresholds) + 1} labeled regions based on "
            f"which thresholds each pixel exceeds (using {op} comparison)."
        )
zarrnii.plugins.segmentation.threshold.ThresholdSegmentation.get_thresholds()

Get the threshold values used by this plugin.

Returns:

  • List[float]

    List of threshold values

Source code in zarrnii/plugins/segmentation/threshold.py
118
119
120
121
122
123
124
125
def get_thresholds(self) -> List[float]:
    """
    Get the threshold values used by this plugin.

    Returns:
        List of threshold values
    """
    return self.thresholds.copy()

Built-in scaled-processing plugins

Gaussian Bias field correction plugin using multi-resolution processing.

This module implements a bias field correction plugin that estimates the bias field at low resolution and applies it to full resolution data.

Classes

zarrnii.plugins.scaled_processing.gaussian_biasfield.GaussianBiasFieldCorrection(sigma=5.0, mode='reflect')

Bias field correction plugin using multi-resolution processing.

This plugin estimates a smooth bias field at low resolution using simple smoothing (as a placeholder for more sophisticated methods like N4) and applies the correction to full resolution data by division.

Parameters:

  • sigma (float, default: 5.0 ) –

    Standard deviation for Gaussian smoothing (default: 5.0)

  • mode (str, default: 'reflect' ) –

    Boundary condition for smoothing (default: 'reflect')

Initialize bias field correction plugin.

Parameters:

  • sigma (float, default: 5.0 ) –

    Standard deviation for Gaussian smoothing

  • mode (str, default: 'reflect' ) –

    Boundary condition for smoothing

Source code in zarrnii/plugins/scaled_processing/gaussian_biasfield.py
29
30
31
32
33
34
35
36
37
38
def __init__(self, sigma: float = 5.0, mode: str = "reflect"):
    """
    Initialize bias field correction plugin.

    Args:
        sigma: Standard deviation for Gaussian smoothing
        mode: Boundary condition for smoothing
    """
    self.sigma = sigma
    self.mode = mode

Functions

zarrnii.plugins.scaled_processing.gaussian_biasfield.GaussianBiasFieldCorrection.lowres_func(lowres_array)

Estimate bias field from low-resolution data.

This is a simplified bias field estimation using Gaussian smoothing. In practice, this could be replaced with more sophisticated methods like N4 bias field correction.

Parameters:

  • lowres_array (ndarray) –

    Downsampled input image

Returns:

  • ndarray

    Estimated bias field at low resolution

Source code in zarrnii/plugins/scaled_processing/gaussian_biasfield.py
 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
@hookimpl
def lowres_func(self, lowres_array: np.ndarray) -> np.ndarray:
    """
    Estimate bias field from low-resolution data.

    This is a simplified bias field estimation using Gaussian smoothing.
    In practice, this could be replaced with more sophisticated methods
    like N4 bias field correction.

    Args:
        lowres_array: Downsampled input image

    Returns:
        Estimated bias field at low resolution
    """
    if lowres_array.size == 0:
        raise ValueError("Input array is empty")

    # Handle different array dimensions
    if lowres_array.ndim < 2:
        raise ValueError("Input array must be at least 2D")

    # Store original shape
    original_shape = lowres_array.shape

    # For multi-dimensional arrays, work with the last 3 dimensions
    work_array = lowres_array
    if work_array.ndim > 3:
        # Flatten leading dimensions and work with spatial dimensions
        leading_shape = work_array.shape[:-3]
        spatial_shape = work_array.shape[-3:]
        work_array = work_array.reshape(-1, *spatial_shape)

    # Ensure we're working with float data
    if work_array.dtype.kind in ["i", "u"]:  # integer types
        work_array = work_array.astype(np.float32)

    # Simple bias field estimation: smooth the image to get low-frequency components
    if work_array.ndim == 2:
        smoothed = ndimage.gaussian_filter(
            work_array, sigma=self.sigma, mode=self.mode
        )
    else:
        # Apply smoothing to each volume if we have multiple
        if work_array.ndim == 4:  # batched 3D volumes
            smoothed = np.zeros_like(work_array)
            for i in range(work_array.shape[0]):
                smoothed[i] = ndimage.gaussian_filter(
                    work_array[i], sigma=self.sigma, mode=self.mode
                )
        else:  # single 3D volume
            smoothed = ndimage.gaussian_filter(
                work_array, sigma=self.sigma, mode=self.mode
            )

    # Reshape back to original shape if needed
    if original_shape != smoothed.shape:
        smoothed = smoothed.reshape(original_shape)

    # Avoid division by zero by adding small epsilon
    smoothed = np.maximum(smoothed, np.finfo(smoothed.dtype).eps)

    return smoothed
zarrnii.plugins.scaled_processing.gaussian_biasfield.GaussianBiasFieldCorrection.highres_func(fullres_array, upsampled_output)

Apply bias field correction to full-resolution data.

This function takes the upsampled bias field (same size as fullres_array) and applies it to the full-resolution data by division.

Works with both dask arrays ("default" method) and plain NumPy arrays ("map_blocks" method) because only NumPy-compatible operations are used.

Parameters:

  • fullres_array

    Full-resolution array (dask or NumPy)

  • upsampled_output

    Upsampled bias field (same shape as fullres_array; dask or NumPy)

Returns:

  • Bias-corrected full-resolution array (same type as inputs)

Source code in zarrnii/plugins/scaled_processing/gaussian_biasfield.py
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
@hookimpl
def highres_func(self, fullres_array, upsampled_output):
    """
    Apply bias field correction to full-resolution data.

    This function takes the upsampled bias field (same size as fullres_array)
    and applies it to the full-resolution data by division.

    Works with both dask arrays (``"default"`` method) and plain NumPy arrays
    (``"map_blocks"`` method) because only NumPy-compatible operations are used.

    Args:
        fullres_array: Full-resolution array (dask or NumPy)
        upsampled_output: Upsampled bias field (same shape as fullres_array;
            dask or NumPy)

    Returns:
        Bias-corrected full-resolution array (same type as inputs)
    """
    # Avoid division by zero by clamping the bias field to a small epsilon.
    # np.maximum works for both dask arrays (via __array_ufunc__) and NumPy.
    epsilon = np.finfo(np.float32).eps
    corrected_array = fullres_array / np.maximum(upsampled_output, epsilon)

    return corrected_array
zarrnii.plugins.scaled_processing.gaussian_biasfield.GaussianBiasFieldCorrection.scaled_processing_plugin_name()

Return the name of the algorithm.

Source code in zarrnii/plugins/scaled_processing/gaussian_biasfield.py
130
131
132
133
@hookimpl
def scaled_processing_plugin_name(self) -> str:
    """Return the name of the algorithm."""
    return "Gaussian Bias Field Correction"
zarrnii.plugins.scaled_processing.gaussian_biasfield.GaussianBiasFieldCorrection.scaled_processing_plugin_description()

Return a description of the algorithm.

Source code in zarrnii/plugins/scaled_processing/gaussian_biasfield.py
135
136
137
138
139
140
141
142
@hookimpl
def scaled_processing_plugin_description(self) -> str:
    """Return a description of the algorithm."""
    return (
        "Multi-resolution bias field correction. Estimates smooth bias field "
        "at low resolution using Gaussian smoothing and applies correction "
        "to full resolution data by division."
    )

N4 Bias field correction plugin using multi-resolution processing.

This module implements a bias field correction plugin that estimates the bias field at low resolution using N4 algorithm from ANTsPy and applies it to full resolution data.

Classes

zarrnii.plugins.scaled_processing.n4_biasfield.N4BiasFieldCorrection(spline_param=[2, 2, 2], convergence={'iters': [50, 50, 50, 50], 'tol': 1e-07}, shrink_factor=1)

N4 bias field correction plugin using multi-resolution processing.

This plugin estimates a smooth bias field at low resolution using the N4 bias field correction algorithm from ANTsPy and applies the correction to full resolution data by division.

Parameters:

  • spline_param (tuple[int, int, int], default: [2, 2, 2] ) –

    Spacing between knots for spline fitting (default: 200)

  • convergence (Optional[Dict[str, Any]], default: {'iters': [50, 50, 50, 50], 'tol': 1e-07} ) –

    Convergence criteria [iters, tol] (default: [50, 0.001])

  • shrink_factor (int, default: 1 ) –

    Shrink factor for processing (default: 1)

Initialize N4 bias field correction plugin.

Parameters:

  • spline_param (tuple[int, int, int], default: [2, 2, 2] ) –

    Spacing between knots for spline fitting

  • convergence (Optional[Dict[str, Any]], default: {'iters': [50, 50, 50, 50], 'tol': 1e-07} ) –

    Convergence criteria dict with 'iters' (list), 'tol'

  • shrink_factor (int, default: 1 ) –

    Shrink factor for processing

Raises:

  • ImportError

    If antspyx is not installed

Source code in zarrnii/plugins/scaled_processing/n4_biasfield.py
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
def __init__(
    self,
    spline_param: tuple[int, int, int] = [2, 2, 2],
    convergence: Optional[Dict[str, Any]] = {
        "iters": [50, 50, 50, 50],
        "tol": 1e-07,
    },
    shrink_factor: int = 1,
):
    """
    Initialize N4 bias field correction plugin.

    Args:
        spline_param: Spacing between knots for spline fitting
        convergence: Convergence criteria dict with 'iters' (list), 'tol'
        shrink_factor: Shrink factor for processing

    Raises:
        ImportError: If antspyx is not installed
    """
    if not HAS_ANTSPYX:
        raise ImportError(
            "antspyx is required for N4BiasFieldCorrection. "
            "Install it with: pip install 'zarrnii[n4]' "
            "or pip install antspyx"
        )

    self.spline_param = spline_param
    self.convergence = convergence
    self.shrink_factor = shrink_factor

Functions

zarrnii.plugins.scaled_processing.n4_biasfield.N4BiasFieldCorrection.lowres_func(lowres_array)

Estimate bias field from low-resolution data using N4 algorithm.

This function uses ANTsPy's N4 bias field correction to estimate the bias field at low resolution.

Parameters:

  • lowres_array (ndarray) –

    Downsampled input image

Returns:

  • ndarray

    Estimated bias field at low resolution

Source code in zarrnii/plugins/scaled_processing/n4_biasfield.py
 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
@hookimpl
def lowres_func(self, lowres_array: np.ndarray) -> np.ndarray:
    """
    Estimate bias field from low-resolution data using N4 algorithm.

    This function uses ANTsPy's N4 bias field correction to estimate
    the bias field at low resolution.

    Args:
        lowres_array: Downsampled input image

    Returns:
        Estimated bias field at low resolution
    """
    if lowres_array.size == 0:
        raise ValueError("Input array is empty")

    # Handle different array dimensions
    if lowres_array.ndim < 2:
        raise ValueError("Input array must be at least 2D")

    # Store original shape
    original_shape = lowres_array.shape

    # For multi-dimensional arrays, work with the last 3 dimensions
    work_array = lowres_array
    if work_array.ndim > 3:
        # Flatten leading dimensions and work with spatial dimensions
        spatial_shape = work_array.shape[-3:]
        work_array = work_array.reshape(-1, *spatial_shape)

    # Ensure we're working with float data
    if work_array.dtype.kind in ["i", "u"]:  # integer types
        work_array = work_array.astype(np.float32)

    # Apply N4 bias field correction
    if work_array.ndim == 2:
        # For 2D data
        ants_img = ants.from_numpy(work_array)
        self.spline_param = [2, 2]
        # Use return_bias_field=True to get the bias field
        bias_result = ants.n4_bias_field_correction(
            ants_img,
            return_bias_field=True,
            spline_param=self.spline_param,
            convergence=self.convergence,
            shrink_factor=self.shrink_factor,
        )
        # Extract the bias field
        bias_field = bias_result.numpy()
    else:
        # Apply N4 to each volume if we have multiple or single 3D volume
        if work_array.ndim == 4:  # batched 3D volumes
            bias_field = np.zeros_like(work_array)
            for i in range(work_array.shape[0]):
                ants_img = ants.from_numpy(work_array[i])
                bias_result = ants.n4_bias_field_correction(
                    ants_img,
                    return_bias_field=True,
                    spline_param=self.spline_param,
                    convergence=self.convergence,
                    shrink_factor=self.shrink_factor,
                )
                bias_field[i] = bias_result.numpy()
        else:  # single 3D volume
            ants_img = ants.from_numpy(work_array)
            bias_result = ants.n4_bias_field_correction(
                ants_img,
                return_bias_field=True,
                spline_param=self.spline_param,
                convergence=self.convergence,
                shrink_factor=self.shrink_factor,
            )
            bias_field = bias_result.numpy()

    # Reshape back to original shape if needed
    if original_shape != bias_field.shape:
        bias_field = bias_field.reshape(original_shape)

    # Avoid division by zero by adding small epsilon
    bias_field = np.maximum(bias_field, np.finfo(bias_field.dtype).eps)

    return bias_field
zarrnii.plugins.scaled_processing.n4_biasfield.N4BiasFieldCorrection.highres_func(fullres_array, upsampled_output)

Apply bias field correction to full-resolution data.

This function takes the upsampled bias field (same size as fullres_array) and applies it to the full-resolution data by division.

Works with both dask arrays ("default" method) and plain NumPy arrays ("map_blocks" method) because only NumPy-compatible operations are used.

Parameters:

  • fullres_array

    Full-resolution array (dask or NumPy)

  • upsampled_output

    Upsampled bias field (same shape as fullres; dask or NumPy)

Returns:

  • Bias-corrected full-resolution array (same type as inputs)

Source code in zarrnii/plugins/scaled_processing/n4_biasfield.py
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
@hookimpl
def highres_func(self, fullres_array, upsampled_output):
    """
    Apply bias field correction to full-resolution data.

    This function takes the upsampled bias field (same size as
    fullres_array) and applies it to the full-resolution data by division.

    Works with both dask arrays (``"default"`` method) and plain NumPy arrays
    (``"map_blocks"`` method) because only NumPy-compatible operations are used.

    Args:
        fullres_array: Full-resolution array (dask or NumPy)
        upsampled_output: Upsampled bias field (same shape as fullres;
            dask or NumPy)

    Returns:
        Bias-corrected full-resolution array (same type as inputs)
    """
    # Avoid division by zero by clamping the bias field to a small epsilon.
    # np.maximum works for both dask arrays (via __array_ufunc__) and NumPy.
    epsilon = np.finfo(np.float32).eps
    corrected_array = fullres_array / np.maximum(upsampled_output, epsilon)

    return corrected_array
zarrnii.plugins.scaled_processing.n4_biasfield.N4BiasFieldCorrection.scaled_processing_plugin_name()

Return the name of the algorithm.

Source code in zarrnii/plugins/scaled_processing/n4_biasfield.py
180
181
182
183
@hookimpl
def scaled_processing_plugin_name(self) -> str:
    """Return the name of the algorithm."""
    return "N4 Bias Field Correction"
zarrnii.plugins.scaled_processing.n4_biasfield.N4BiasFieldCorrection.scaled_processing_plugin_description()

Return a description of the algorithm.

Source code in zarrnii/plugins/scaled_processing/n4_biasfield.py
185
186
187
188
189
190
191
192
@hookimpl
def scaled_processing_plugin_description(self) -> str:
    """Return a description of the algorithm."""
    return (
        "Multi-resolution N4 bias field correction. Estimates smooth bias "
        "field at low resolution using ANTsPy N4 algorithm and applies "
        "correction to full resolution data by division."
    )

Segmentation cleaning plugin using multi-resolution processing.

This module implements a segmentation cleaning plugin that removes artifactual objects from a segmentation by performing connected components analysis at low resolution and filtering by extent, then applying the exclusion mask to full resolution data.

Classes

zarrnii.plugins.scaled_processing.segmentation_cleaner.SegmentationCleaner(mask_threshold=50, max_extent=0.15, exclusion_threshold=50)

Segmentation cleaning plugin using multi-resolution processing.

This plugin removes artifactual objects from segmentations by: 1. At low resolution: performing connected components analysis on a thresholded mask and identifying objects with extent below a threshold 2. At high resolution: applying an exclusion mask to remove these objects from the full-resolution segmentation

The extent of a region is defined as the ratio of pixels in the region to pixels in the total bounding box. Objects with low extent (e.g., < 0.15) are typically large artifactual objects with sparse coverage.

Parameters:

  • mask_threshold (float, default: 50 ) –

    Threshold for creating initial binary mask (default: 50)

  • max_extent (float, default: 0.15 ) –

    Maximum extent threshold for exclusion (default: 0.15)

  • exclusion_threshold (float, default: 50 ) –

    Threshold for upsampled exclusion mask (default: 50)

Initialize segmentation cleaner plugin.

Parameters:

  • mask_threshold (float, default: 50 ) –

    Threshold for creating initial binary mask from input

  • max_extent (float, default: 0.15 ) –

    Maximum extent to include in exclusion mask (objects with extent < max_extent are considered artifactual)

  • exclusion_threshold (float, default: 50 ) –

    Threshold for applying upsampled exclusion mask

Source code in zarrnii/plugins/scaled_processing/segmentation_cleaner.py
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
def __init__(
    self,
    mask_threshold: float = 50,
    max_extent: float = 0.15,
    exclusion_threshold: float = 50,
):
    """
    Initialize segmentation cleaner plugin.

    Args:
        mask_threshold: Threshold for creating initial binary mask from input
        max_extent: Maximum extent to include in exclusion mask (objects with
                   extent < max_extent are considered artifactual)
        exclusion_threshold: Threshold for applying upsampled exclusion mask
    """
    self.mask_threshold = mask_threshold
    self.max_extent = max_extent
    self.exclusion_threshold = exclusion_threshold

Functions

zarrnii.plugins.scaled_processing.segmentation_cleaner.SegmentationCleaner.lowres_func(lowres_array)

Create exclusion mask from low-resolution segmentation data.

This function performs connected components analysis on thresholded low-resolution data and creates an exclusion mask containing objects with extent below the threshold.

Parameters:

  • lowres_array (ndarray) –

    Downsampled segmentation image

Returns:

  • ndarray

    Exclusion mask at low resolution (uint8, values 0 or 100)

Source code in zarrnii/plugins/scaled_processing/segmentation_cleaner.py
 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
@hookimpl
def lowres_func(self, lowres_array: np.ndarray) -> np.ndarray:
    """
    Create exclusion mask from low-resolution segmentation data.

    This function performs connected components analysis on thresholded
    low-resolution data and creates an exclusion mask containing objects
    with extent below the threshold.

    Args:
        lowres_array: Downsampled segmentation image

    Returns:
        Exclusion mask at low resolution (uint8, values 0 or 100)
    """
    if lowres_array.size == 0:
        raise ValueError("Input array is empty")

    if lowres_array.ndim < 2:
        raise ValueError("Input array must be at least 2D")

    # Store original shape
    original_shape = lowres_array.shape

    # For multi-dimensional arrays, work with the last 3 dimensions
    work_array = lowres_array
    if work_array.ndim > 3:
        # Flatten leading dimensions and work with spatial dimensions
        spatial_shape = work_array.shape[-3:]
        work_array = work_array.reshape(-1, *spatial_shape)

    # Process connected components and create exclusion mask
    if work_array.ndim == 2:
        # 2D case
        mask_img = work_array > self.mask_threshold
        conncomp, nlabels = label(mask_img, return_num=True)
        props = regionprops(conncomp)
        keep_labels = {r.label for r in props if r.extent < self.max_extent}
        exclude_mask = np.isin(conncomp, list(keep_labels))
        exclude_mask = exclude_mask.astype("uint8") * 100
    else:
        # 3D or batched 3D case
        if work_array.ndim == 4:
            # Batched 3D volumes - process each separately
            exclude_mask = np.zeros_like(work_array, dtype="uint8")
            for i in range(work_array.shape[0]):
                mask_img = work_array[i] > self.mask_threshold
                conncomp, nlabels = label(mask_img, return_num=True)
                props = regionprops(conncomp)
                keep_labels = {r.label for r in props if r.extent < self.max_extent}
                batch_exclude = np.isin(conncomp, list(keep_labels))
                exclude_mask[i] = batch_exclude.astype("uint8") * 100
        else:
            # Single 3D volume
            mask_img = work_array > self.mask_threshold
            conncomp, nlabels = label(mask_img, return_num=True)
            props = regionprops(conncomp)
            keep_labels = {r.label for r in props if r.extent < self.max_extent}
            exclude_mask = np.isin(conncomp, list(keep_labels))
            exclude_mask = exclude_mask.astype("uint8") * 100

    # Reshape back to original shape if needed
    if original_shape != exclude_mask.shape:
        exclude_mask = exclude_mask.reshape(original_shape)

    return exclude_mask
zarrnii.plugins.scaled_processing.segmentation_cleaner.SegmentationCleaner.highres_func(fullres_array, upsampled_output)

Apply exclusion mask to full-resolution segmentation data.

This function takes the upsampled exclusion mask and applies it to the full-resolution segmentation by zeroing out the excluded regions.

Works with both dask arrays ("default" method) and plain NumPy arrays ("map_blocks" method) because only NumPy-compatible operations are used.

Parameters:

  • fullres_array

    Full-resolution segmentation array (dask or NumPy)

  • upsampled_output

    Upsampled exclusion mask (same shape as fullres; dask or NumPy)

Returns:

  • Cleaned full-resolution segmentation array (same type as inputs)

Source code in zarrnii/plugins/scaled_processing/segmentation_cleaner.py
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
@hookimpl
def highres_func(self, fullres_array, upsampled_output):
    """
    Apply exclusion mask to full-resolution segmentation data.

    This function takes the upsampled exclusion mask and applies it to
    the full-resolution segmentation by zeroing out the excluded regions.

    Works with both dask arrays (``"default"`` method) and plain NumPy arrays
    (``"map_blocks"`` method) because only NumPy-compatible operations are used.

    Args:
        fullres_array: Full-resolution segmentation array (dask or NumPy)
        upsampled_output: Upsampled exclusion mask (same shape as fullres;
            dask or NumPy)

    Returns:
        Cleaned full-resolution segmentation array (same type as inputs)
    """
    # Threshold the upsampled exclusion mask.
    # Values >= exclusion_threshold (e.g., 50) indicate regions to exclude.
    exclusion_mask = upsampled_output >= self.exclusion_threshold

    # Apply mask: set excluded regions to zero.
    # np.where works for both dask arrays (via __array_ufunc__) and NumPy.
    cleaned_array = np.where(exclusion_mask, 0, fullres_array)

    return cleaned_array
zarrnii.plugins.scaled_processing.segmentation_cleaner.SegmentationCleaner.scaled_processing_plugin_name()

Return the name of the algorithm.

Source code in zarrnii/plugins/scaled_processing/segmentation_cleaner.py
153
154
155
156
@hookimpl
def scaled_processing_plugin_name(self) -> str:
    """Return the name of the algorithm."""
    return "Segmentation Cleaner"
zarrnii.plugins.scaled_processing.segmentation_cleaner.SegmentationCleaner.scaled_processing_plugin_description()

Return a description of the algorithm.

Source code in zarrnii/plugins/scaled_processing/segmentation_cleaner.py
158
159
160
161
162
163
164
165
166
@hookimpl
def scaled_processing_plugin_description(self) -> str:
    """Return a description of the algorithm."""
    return (
        "Multi-resolution segmentation cleaning. Identifies and removes "
        "artifactual objects by performing connected components analysis at "
        "low resolution, filtering by extent, and applying exclusion mask to "
        "full resolution data."
    )