NonOverlapping#

class deeptrack.features.NonOverlapping(feature: Feature, min_distance: float = 1, max_attempts: int = 5, max_iters: int = 100, **kwargs: dict[str, Any])#

Bases: Feature

Ensure volumes are placed non-overlapping in a 3D space.

This feature ensures that a list of 3D volumes are positioned such that their non-zero voxels do not overlap. If volumes overlap, their positions are resampled until they are non-overlapping. If the maximum number of attempts is exceeded, the feature regenerates the list of volumes and raises a warning if non-overlapping placement cannot be achieved.

Note: min_distance refers to the distance between the edges of volumes, not their centers. Due to the way volumes are calculated, slight rounding errors may affect the final distance.

This feature is incompatible with non-volumetric scatterers such as MieScatterers.

Parameters#

feature: Feature

The feature that generates the list of volumes to place non-overlapping.

min_distance: float, optional

The minimum distance between volumes in pixels. Defaults to 1. It can be negative to allow for partial overlap.

max_attempts: int, optional

The maximum number of attempts to place volumes without overlap. Defaults to 5.

max_iters: int, optional
The maximum number of resamplings. If this number is exceeded, a

new list of volumes is generated. Defaults to 100.

Attributes#

__distributed__: bool

Indicates whether this feature distributes computation across inputs. Always False for NonOverlapping.

Methods#

get(_: Any, min_distance: float, max_attempts: int, **kwargs: dict[str, Any]) -> list[np.ndarray]

Generate a list of non-overlapping 3D volumes.

_check_non_overlapping(list_of_volumes: list[np.ndarray]) -> bool

Check if all volumes in the list are non-overlapping.

_check_bounding_cubes_non_overlapping(bounding_cube_1: list[int], bounding_cube_2: list[int], min_distance: float) -> bool

Check if two bounding cubes are non-overlapping.

_get_overlapping_cube(bounding_cube_1: list[int], bounding_cube_2: list[int]) -> list[int]

Get the overlapping cube between two bounding cubes.

_get_overlapping_volume(volume: np.ndarray, bounding_cube: tuple[float, float, float, float, float, float], overlapping_cube: tuple[float, float, float, float, float, float]) -> np.ndarray

Get the overlapping volume between a volume and a bounding cube.

_check_volumes_non_overlapping(volume_1: np.ndarray, volume_2: np.ndarray, min_distance: float) -> bool

Check if two volumes are non-overlapping.

_resample_volume_position(volume: np.ndarray | Image) -> Image

Resample the position of a volume to avoid overlap.

Notes#

  • This feature performs bounding cube checks first to quickly reject obvious overlaps before voxel-level checks.

  • If the bounding cubes overlap, precise voxel-based checks are performed.

Examples#

>>> import deeptrack as dt
>>> import numpy as np
>>> import matplotlib.pyplot as plt

Define an ellipse scatterer with randomly positioned objects: >>> scatterer = dt.Ellipse( >>> radius= 13 * dt.units.pixels, >>> position=lambda: np.random.uniform(5, 115, size=2)* dt.units.pixels, >>> )

Create multiple scatterers: >>> scatterers = (scatterer ^ 8)

Define the optics and create the image with possible overlap: >>> optics = dt.Fluorescence() >>> im_with_overlap = optics(scatterers) >>> im_with_overlap.store_properties() >>> im_with_overlap_resolved = image_with_overlap()

Gather position from image: >>> pos_with_overlap = np.array( >>> im_with_overlap_resolved.get_property( >>> “position”, >>> get_one=False >>> ) >>> )

Enforce non-overlapping and create the image without overlap: >>> non_overlapping_scatterers = dt.NonOverlapping(scatterers, min_distance=4) >>> im_without_overlap = optics(non_overlapping_scatterers) >>> im_without_overlap.store_properties() >>> im_without_overlap_resolved = im_without_overlap()

Gather position from image: >>> pos_without_overlap = np.array( >>> im_without_overlap_resolved.get_property( >>> “position”, >>> get_one=False >>> ) >>> )

Create a figure with two subplots to visualize the difference: >>> fig, axes = plt.subplots(1, 2, figsize=(10, 5))

>>> axes[0].imshow(im_with_overlap_resolved, cmap="gray")
>>> axes[0].scatter(pos_with_overlap[:,1],pos_with_overlap[:,0])
>>> axes[0].set_title("Overlapping Objects")
>>> axes[0].axis("off")
>>> axes[1].imshow(im_without_overlap_resolved, cmap="gray")
>>> axes[1].scatter(pos_without_overlap[:,1],pos_without_overlap[:,0])
>>> axes[1].set_title("Non-Overlapping Objects")
>>> axes[1].axis("off")
>>> plt.tight_layout()
>>> plt.show()

Define function to calculate minimum distance: >>> def calculate_min_distance(positions): >>> distances = [ >>> np.linalg.norm(positions[i] - positions[j]) >>> for i in range(len(positions)) >>> for j in range(i + 1, len(positions)) >>> ] >>> return min(distances)

Print minimum distances with and without overlap: >>> print(calculate_min_distance(pos_with_overlap)) 10.768742383382174 >>> print(calculate_min_distance(pos_without_overlap)) 30.82531120942446

Methods Summary

get(_, min_distance, max_attempts, ...)

Generates a list of non-overlapping 3D volumes within a defined field of view (FOV).

Methods Documentation

get(_: Any, min_distance: float, max_attempts: int, max_iters: int, **kwargs: dict[str, Any]) list[ndarray]#

Generates a list of non-overlapping 3D volumes within a defined field of view (FOV).

This method iteratively attempts to place volumes while ensuring they maintain at least min_distance separation. If non-overlapping placement is not achieved within max_attempts, a warning is issued, and the best available configuration is returned.

Parameters#

_: Any

Placeholder parameter, typically for an input image.

min_distance: float

The minimum required separation distance between volumes, in pixels.

max_attempts: int

The maximum number of attempts to generate a valid non-overlapping configuration.

max_iters: int

The maximum number of resampling iterations per attempt.

**kwargs: dict[str, Any]

Additional parameters that may be used by subclasses.

Returns#

list[np.ndarray]

A list of 3D volumes represented as NumPy arrays. If non-overlapping placement is unsuccessful, the best available configuration is returned.

Warns#

UserWarning

If non-overlapping placement is not achieved within max_attempts, suggesting parameter adjustments such as increasing the FOV or reducing min_distance.

Notes#

  • The placement process prioritizes bounding cube checks for efficiency.

  • If bounding cubes overlap, voxel-based overlap checks are performed.