Skip to content

Commit

Permalink
refactor bigfish.detection - 'psf' concept removed
Browse files Browse the repository at this point in the history
  • Loading branch information
Henley13 committed Jan 21, 2022
1 parent 2b26fcb commit 609d332
Show file tree
Hide file tree
Showing 11 changed files with 1,519 additions and 1,245 deletions.
26 changes: 13 additions & 13 deletions bigfish/detection/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
and 3-d.
"""

# TODO adapt to float64 coordinates

from .spot_detection import detect_spots
from .spot_detection import local_maximum_detection
from .spot_detection import spots_thresholding
Expand All @@ -19,21 +17,21 @@
from .dense_decomposition import get_dense_region
from .dense_decomposition import simulate_gaussian_mixture

from .spot_modeling import fit_subpixel
from .spot_modeling import build_reference_spot
from .spot_modeling import modelize_spot
from .spot_modeling import precompute_erf
from .spot_modeling import initialize_grid
from .spot_modeling import gaussian_2d
from .spot_modeling import gaussian_3d
from .spot_modeling import precompute_erf
from .spot_modeling import fit_subpixel

from .cluster_detection import detect_clusters

from .snr import compute_snr_spots

from .utils import convert_spot_coordinates
from .utils import get_object_radius_pixel
from .utils import get_object_radius_nm
from .utils import build_reference_spot
from .utils import compute_snr_spots
from .utils import get_breaking_point


_spots = [
Expand All @@ -49,13 +47,12 @@
"simulate_gaussian_mixture"]

_model = [
"fit_subpixel",
"build_reference_spot",
"modelize_spot",
"precompute_erf",
"initialize_grid",
"gaussian_2d",
"gaussian_3d"]
"gaussian_3d",
"precompute_erf",
"fit_subpixel"]

_clusters = [
"detect_clusters"]
Expand All @@ -66,6 +63,9 @@
_utils = [
"convert_spot_coordinates",
"get_object_radius_pixel",
"get_object_radius_nm"]
"get_object_radius_nm",
"build_reference_spot",
"compute_snr_spots",
"get_breaking_point"]

__all__ = (_spots + _dense + _model + _clusters + _snr + _utils)
__all__ = _spots + _dense + _model + _clusters + _utils
80 changes: 42 additions & 38 deletions bigfish/detection/cluster_detection.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,17 @@
"""

import numpy as np

import bigfish.stack as stack
from sklearn.cluster import DBSCAN

from .utils import convert_spot_coordinates

from sklearn.cluster import DBSCAN


# ### Detect clusters ###

def detect_clusters(spots, voxel_size_z=None, voxel_size_yx=100, radius=350,
nb_min_spots=4):
def detect_clusters(spots, voxel_size, radius=350, nb_min_spots=4):
"""Cluster spots and detect relevant aggregated structures.
#. If two spots are distant within a specific radius, we consider they are
Expand All @@ -25,14 +27,13 @@ def detect_clusters(spots, voxel_size_z=None, voxel_size_yx=100, radius=350,
Parameters
----------
spots : np.ndarray, np.int64
spots : np.ndarray
Coordinates of the detected spots with shape (nb_spots, 3) or
(nb_spots, 2).
voxel_size_z : int or float or None
Height of a voxel, along the z axis, in nanometer. If None, spots are
considered in 2-d.
voxel_size_yx : int or float
Size of a voxel on the yx plan, in nanometer.
voxel_size : int, float, Tuple(int, float) or List(int, float)
Size of a voxel, in nanometer. One value per spatial dimension (zyx or
yx dimensions). If it's a scalar, the same value is applied to every
dimensions.
radius : int
The maximum distance between two samples for one to be considered as
in the neighborhood of the other. Radius expressed in nanometer.
Expand All @@ -43,63 +44,67 @@ def detect_clusters(spots, voxel_size_z=None, voxel_size_yx=100, radius=350,
Returns
-------
clustered_spots : np.ndarray, np.int64
clustered_spots : np.ndarray
Coordinates of the detected spots with shape (nb_spots, 4) or
(nb_spots, 3). One coordinate per dimension (zyx or yx coordinates)
plus the index of the cluster assigned to the spot. If no cluster was
assigned, value is -1.
clusters : np.ndarray, np.int64
clusters : np.ndarray
Array with shape (nb_clusters, 5) or (nb_clusters, 4). One coordinate
per dimension for the clusters centroid (zyx or yx coordinates), the
number of spots detected in the clusters and its index.
"""
# TODO check that the behavior is the same with float64 and int64
# coordinates
# check parameters
stack.check_array(spots, ndim=2, dtype=np.int64)
stack.check_parameter(voxel_size_z=(int, float, type(None)),
voxel_size_yx=(int, float),
radius=int,
nb_min_spots=int)

# check number of dimensions
stack.check_array(spots, ndim=2, dtype=[np.float64, np.int64])
stack.check_parameter(
voxel_size=(int, float, tuple, list),
radius=int,
nb_min_spots=int)

# check consistency between parameters
dtype = spots.dtype
ndim = spots.shape[1]
if ndim not in [2, 3]:
raise ValueError("Spot coordinates should be in 2 or 3 dimensions, "
"not {0}.".format(ndim))
if ndim == 3 and voxel_size_z is None:
raise ValueError("Provided spot coordinates has {0} dimensions but "
"'voxel_size_z' parameter is missing.".format(ndim))
if ndim == 2:
voxel_size_z = None
if isinstance(voxel_size, (tuple, list)):
if len(voxel_size) != ndim:
raise ValueError(
"'voxel_size' must be a scalar or a sequence with {0} "
"elements.".format(ndim))
else:
voxel_size = (voxel_size,) * ndim

# case where no spot were detected
if spots.size == 0:
clustered_spots = np.array([], dtype=np.int64).reshape((0, ndim + 1))
clusters = np.array([], dtype=np.int64).reshape((0, ndim + 2))
clustered_spots = np.array([], dtype=dtype).reshape((0, ndim + 1))
clusters = np.array([], dtype=dtype).reshape((0, ndim + 2))
return clustered_spots, clusters

# cluster spots
clustered_spots = _cluster_spots(
spots, voxel_size_z, voxel_size_yx, radius, nb_min_spots)
spots, voxel_size, radius, nb_min_spots)

# extract and shape clusters information
clusters = _extract_information(clustered_spots)

return clustered_spots, clusters


def _cluster_spots(spots, voxel_size_z, voxel_size_yx, radius, nb_min_spots):
def _cluster_spots(spots, voxel_size, radius, nb_min_spots):
"""Assign a cluster to each spot.
Parameters
----------
spots : np.ndarray, np.int64
spots : np.ndarray
Coordinates of the detected spots with shape (nb_spots, 3) or
(nb_spots, 2).
voxel_size_z : int or float
Height of a voxel, along the z axis, in nanometer.
voxel_size_yx : int or float
Size of a voxel on the yx plan, in nanometer.
voxel_size : Tuple(int, float) or List(int, float)
Size of a voxel, in nanometer. One value per spatial dimension (zyx or
yx dimensions).
radius : int
The maximum distance between two samples for one to be considered as
in the neighborhood of the other. Radius expressed in nanometer.
Expand All @@ -110,17 +115,16 @@ def _cluster_spots(spots, voxel_size_z, voxel_size_yx, radius, nb_min_spots):
Returns
-------
clustered_spots : np.ndarray, np.int64
clustered_spots : np.ndarray
Coordinates of the detected spots with shape (nb_spots, 4) or
(nb_spots, 3). One coordinate per dimension (zyx or yx coordinates)
plus the index of the cluster assigned to the spot. If no cluster was
assigned, value is -1.
"""
# convert spots coordinates in nanometer
spots_nanometer = convert_spot_coordinates(spots=spots,
voxel_size_z=voxel_size_z,
voxel_size_yx=voxel_size_yx)
spots_nanometer = convert_spot_coordinates(
spots=spots, voxel_size=voxel_size)

# fit a DBSCAN clustering algorithm with a specific radius
dbscan = DBSCAN(eps=radius, min_samples=nb_min_spots)
Expand All @@ -140,15 +144,15 @@ def _extract_information(clustered_spots):
Parameters
----------
clustered_spots : np.ndarray, np.int64
clustered_spots : np.ndarray
Coordinates of the detected spots with shape (nb_spots, 4) or
(nb_spots, 3). One coordinate per dimension (zyx or yx coordinates)
plus the index of the cluster assigned to the spot. If no cluster was
assigned, value is -1.
Returns
-------
clusters : np.ndarray, np.int64
clusters : np.ndarray
Array with shape (nb_clusters, 5) or (nb_clusters, 4). One coordinate
per dimension for the cluster centroid (zyx or yx coordinates), the
number of spots detected in the cluster and its index.
Expand Down
Loading

0 comments on commit 609d332

Please sign in to comment.