Skip to content

Commit

Permalink
refactor 'get_sigma'
Browse files Browse the repository at this point in the history
  • Loading branch information
Henley13 committed Jan 20, 2022
1 parent 8d688a6 commit 2b26fcb
Show file tree
Hide file tree
Showing 8 changed files with 162 additions and 64 deletions.
7 changes: 6 additions & 1 deletion bigfish/detection/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@
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


_spots = [
"detect_spots",
Expand Down Expand Up @@ -61,6 +64,8 @@
"compute_snr_spots"]

_utils = [
"convert_spot_coordinates"]
"convert_spot_coordinates",
"get_object_radius_pixel",
"get_object_radius_nm"]

__all__ = (_spots + _dense + _model + _clusters + _snr + _utils)
11 changes: 11 additions & 0 deletions bigfish/detection/tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# -*- coding: utf-8 -*-
# Author: Arthur Imbert <[email protected]>
# License: BSD 3 clause

"""
Unitary tests for bigfish.detection.utils module.
"""

# TODO test bigfish.detection.convert_spot_coordinates
# TODO add test for bigfish.stack.get_object_radius_pixel
# TODO add test for bigfish.stack.get_object_radius_nm
145 changes: 136 additions & 9 deletions bigfish/detection/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,22 @@
Utility functions for bigfish.detection subpackage.
"""

import numpy as np
import bigfish.stack as stack

def convert_spot_coordinates(spots, voxel_size_z, voxel_size_yx):

def convert_spot_coordinates(spots, voxel_size):
"""Convert spots coordinates from pixel to nanometer.
Parameters
----------
spots : np.ndarray, np.int64
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 : 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.
Returns
-------
Expand All @@ -27,13 +30,137 @@ def convert_spot_coordinates(spots, voxel_size_z, voxel_size_yx):
(nb_spots, 3), in nanometer.
"""
# check parameters
stack.check_parameter(
voxel_size=(int, float, tuple, list))
stack.check_array(spots, ndim=2, dtype=[np.float64, np.int64])

# check consistency between parameters
ndim = spots.shape[1]
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

# convert spots coordinates in nanometer
spots_nanometer = spots.copy()
if spots.shape[1] == 3:
spots_nanometer[:, 0] *= voxel_size_z
spots_nanometer[:, 1:] *= voxel_size_yx
if ndim == 3:
spots_nanometer[:, 0] *= voxel_size[0]
spots_nanometer[:, 1:] *= voxel_size[-1]

else:
spots_nanometer *= voxel_size_yx
spots_nanometer *= voxel_size[-1]

return spots_nanometer


def get_object_radius_pixel(voxel_size_nm, object_radius_nm, ndim):
"""Convert the object radius in pixel.
When the object considered is a spot this value can be interpreted as the
standard deviation of the spot PSF, in pixel. For any object modelled with
a gaussian signal, this value can be interpreted as the standard deviation
of the gaussian.
Parameters
----------
voxel_size_nm : 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.
object_radius_nm : int, float, Tuple(int, float) or List(int, float)
Radius of the object, in nanometer. One value per spatial dimension
(zyx or yx dimensions). If it's a scalar, the same radius is applied to
every dimensions.
ndim : int
Number of spatial dimension to consider.
Returns
-------
object_radius_px : Tuple[float]
Radius of the object in pixel, one element per dimension (zyx or yx
dimensions).
"""
# check parameters
stack.check_parameter(
voxel_size_nm=(int, float, tuple, list),
object_radius_nm=(int, float, tuple, list),
ndim=int)

# check consistency between parameters
if isinstance(voxel_size_nm, (tuple, list)):
if len(voxel_size_nm) != ndim:
raise ValueError("'voxel_size_nm' must be a scalar or a sequence "
"with {0} elements.".format(ndim))
else:
voxel_size_nm = (voxel_size_nm,) * ndim
if isinstance(object_radius_nm, (tuple, list)):
if len(object_radius_nm) != ndim:
raise ValueError("'object_radius_nm' must be a scalar or a "
"sequence with {0} elements.".format(ndim))
else:
object_radius_nm = (object_radius_nm,) * ndim

# get radius in pixel
object_radius_px = [b / a for a, b in zip(voxel_size_nm, object_radius_nm)]
object_radius_px = tuple(object_radius_px)

return object_radius_px


def get_object_radius_nm(voxel_size_nm, object_radius_px, ndim):
"""Convert the object radius in nanometer.
When the object considered is a spot this value can be interpreted as the
standard deviation of the spot PSF, in nanometer. For any object modelled
with a gaussian signal, this value can be interpreted as the standard
deviation of the gaussian.
Parameters
----------
voxel_size_nm : 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.
object_radius_px : int, float, Tuple(int, float) or List(int, float)
Radius of the object, in pixel. One value per spatial dimension
(zyx or yx dimensions). If it's a scalar, the same radius is applied to
every dimensions.
ndim : int
Number of spatial dimension to consider.
Returns
-------
object_radius_nm : Tuple[float]
Radius of the object in nanometer, one element per dimension (zyx or yx
dimensions).
"""
# check parameters
stack.check_parameter(
voxel_size_nm=(int, float, tuple, list),
object_radius_px=(int, float, tuple, list),
ndim=int)

# check consistency between parameters
if isinstance(voxel_size_nm, (tuple, list)):
if len(voxel_size_nm) != ndim:
raise ValueError("'voxel_size_nm' must be a scalar or a sequence "
"with {0} elements.".format(ndim))
else:
voxel_size_nm = (voxel_size_nm,) * ndim
if isinstance(object_radius_px, (tuple, list)):
if len(object_radius_px) != ndim:
raise ValueError("'object_radius_px' must be a scalar or a "
"sequence with {0} elements.".format(ndim))
else:
object_radius_px = (object_radius_px,) * ndim

# get radius in pixel
object_radius_nm = [a * b for a, b in zip(voxel_size_nm, object_radius_px)]
object_radius_nm = tuple(object_radius_nm)

return object_radius_nm
4 changes: 1 addition & 3 deletions bigfish/stack/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
from .utils import check_input_data
from .utils import moving_average
from .utils import centered_moving_average
from .utils import get_sigma

from .io import read_image
from .io import read_dv
Expand Down Expand Up @@ -77,8 +76,7 @@
"compute_hash",
"check_input_data",
"moving_average",
"centered_moving_average",
"get_sigma"]
"centered_moving_average"]

_io = [
"read_image",
Expand Down
10 changes: 8 additions & 2 deletions bigfish/stack/filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,8 +264,8 @@ def log_filter(image, sigma):
# check sigma
if isinstance(sigma, (tuple, list)):
if len(sigma) != image.ndim:
raise ValueError("'sigma' must be a scalar or a sequence with the "
"same length as 'image.ndim'.")
raise ValueError("'sigma' must be a scalar or a sequence with {0} "
"elements.".format(image.ndim))

# we apply LoG filter
image_filtered = gaussian_laplace(image_float, sigma=sigma)
Expand Down Expand Up @@ -313,9 +313,15 @@ def gaussian_filter(image, sigma, allow_negative=False):
check_parameter(sigma=(float, int, tuple, list),
allow_negative=bool)

# check parameters consistency
if image.dtype in [np.uint8, np.uint16] and allow_negative:
raise ValueError("Negative values are impossible with unsigned "
"integer image.")
# check sigma
if isinstance(sigma, (tuple, list)):
if len(sigma) != image.ndim:
raise ValueError("'sigma' must be a scalar or a sequence with {0} "
"elements.".format(image.ndim))

# we cast the data in np.float to allow negative values
if image.dtype == np.uint8:
Expand Down
1 change: 0 additions & 1 deletion bigfish/stack/tests/test_projection.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,4 +183,3 @@ def test_one_hot_3d(dtype):
expected_one_hot = expected_one_hot.astype(bool)
assert_array_equal(one_hot, expected_one_hot)
assert one_hot.dtype == bool

1 change: 0 additions & 1 deletion bigfish/stack/tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
# TODO add test for bigfish.stack.check_input_data
# TODO add test for bigfish.stack.moving_average
# TODO add test for bigfish.stack.centered_moving_average
# TODO add test for bigfish.stack.get_sigma

# ### Test sanity check functions ###

Expand Down
47 changes: 0 additions & 47 deletions bigfish/stack/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -611,50 +611,3 @@ def centered_moving_average(array, n):
results = moving_average(array_padded, n)

return results


# ### Spot utilities ###

def get_sigma(voxel_size_z=None, voxel_size_yx=100, object_radius_z=None,
object_radius_yx=200):
"""Convert the object radius in pixel.
When the object considered is a spot this value can be interpreted as the
standard deviation of the spot PSF, in pixel. For any object modelled with
a gaussian signal, this value can be interpreted as the standard deviation
of the gaussian (hence the name `sigma`).
Parameters
----------
voxel_size_z : int or float or None
Size of a voxel along the z axis, in nanometer. If None, we consider
a pixel.
voxel_size_yx : int or float
Size of a voxel in the yx plan, in nanometer.
object_radius_z : int or float or None
Radius of the object along the z axis, in nanometer. If None, we
consider a 2-d object.
object_radius_yx : int or float
Radius of the object in the yx plan, in nanometer.
Returns
-------
sigma : Tuple[float]
Radius of the object in pixel, one element per dimension.
"""
# check parameters
check_parameter(voxel_size_z=(int, float, type(None)),
voxel_size_yx=(int, float),
object_radius_z=(int, float, type(None)),
object_radius_yx=(int, float))

# compute sigma
sigma_yx = object_radius_yx / voxel_size_yx

if voxel_size_z is None or object_radius_z is None:
return sigma_yx, sigma_yx

else:
sigma_z = object_radius_z / voxel_size_z
return sigma_z, sigma_yx, sigma_yx

0 comments on commit 2b26fcb

Please sign in to comment.