Skip to content

Commit

Permalink
Use the new levels module.
Browse files Browse the repository at this point in the history
Adjusts all the filters to use levels instead of clip/preserve_float_range.  `clip_method` is still supported though deprecated.  If both are provided, an exception is thrown.  If `clip_method` by itself is provided, then a deprecation warning is printed and the `clip_method` is translated to the appropriate `levels_method`.

Depends on #1666, #1668

Test plan: this is an -alltest branch since it affects so much code.  if goes green, we should presumably be good. :)
  • Loading branch information
Tony Tung committed Dec 3, 2019
1 parent e1375df commit 6e2a100
Show file tree
Hide file tree
Showing 13 changed files with 504 additions and 210 deletions.
54 changes: 39 additions & 15 deletions starfish/core/image/Filter/bandpass.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
import xarray as xr
from trackpy import bandpass

from starfish.core.imagestack.imagestack import ImageStack
from starfish.core.types import Clip, Number
from starfish.core.imagestack.imagestack import _reconcile_clip_and_level, ImageStack
from starfish.core.types import Clip, Levels, Number
from ._base import FilterAlgorithm
from .util import determine_axes_to_group_by

Expand Down Expand Up @@ -33,20 +33,44 @@ class Bandpass(FilterAlgorithm):
deviations (default 4)
is_volume : bool
If True, 3d (z, y, x) volumes will be filtered. By default, filter 2-d (y, x) planes
clip_method : Union[str, Clip]
(Default Clip.CLIP) Controls the way that data are scaled to retain skimage dtype
requirements that float data fall in [0, 1].
Clip.CLIP: data above 1 are set to 1, and below 0 are set to 0
Clip.SCALE_BY_IMAGE: data above 1 are scaled by the maximum value, with the maximum
value calculated over the entire ImageStack
Clip.SCALE_BY_CHUNK: data above 1 are scaled by the maximum value, with the maximum
value calculated over each slice, where slice shapes are determined by the group_by
parameters
clip_method : Optional[Union[str, :py:class:`~starfish.types.Clip`]]
Deprecated method to control the way that data are scaled to retain skimage dtype
requirements that float data fall in [0, 1]. In all modes, data below 0 are set to 0.
- Clip.CLIP: data above 1 are set to 1. This has been replaced with
level_method=Levels.CLIP.
- Clip.SCALE_BY_IMAGE: when any data in the entire ImageStack is greater than 1, the entire
ImageStack is scaled by the maximum value in the ImageStack. This has been replaced with
level_method=Levels.SCALE_SATURATED_BY_IMAGE.
- Clip.SCALE_BY_CHUNK: when any data in any slice is greater than 1, each slice is scaled by
the maximum value found in that slice. The slice shapes are determined by the
``group_by`` parameters. This has been replaced with
level_method=Levels.SCALE_SATURATED_BY_CHUNK.
level_method : :py:class:`~starfish.types.Levels`
Controls the way that data are scaled to retain skimage dtype requirements that float data
fall in [0, 1]. In all modes, data below 0 are set to 0.
- Levels.CLIP (default): data above 1 are set to 1.
- Levels.SCALE_SATURATED_BY_IMAGE: when any data in the entire ImageStack is greater
than 1, the entire ImageStack is scaled by the maximum value in the ImageStack.
- Levels.SCALE_SATURATED_BY_CHUNK: when any data in any slice is greater than 1, each
slice is scaled by the maximum value found in that slice. The slice shapes are
determined by the ``group_by`` parameters.
- Levels.SCALE_BY_IMAGE: scale the entire ImageStack by the maximum value in the
ImageStack.
- Levels.SCALE_BY_CHUNK: scale each slice by the maximum value found in that slice. The
slice shapes are determined by the ``group_by`` parameters.
"""

def __init__(
self, lshort: Number, llong: int, threshold: Number = 0, truncate: Number = 4,
is_volume: bool = False, clip_method: Union[str, Clip] = Clip.CLIP
self,
lshort: Number,
llong: int,
threshold: Number = 0,
truncate: Number = 4,
is_volume: bool = False,
clip_method: Optional[Union[str, Clip]] = None,
level_method: Optional[Levels] = None
) -> None:
self.lshort = lshort
self.llong = llong
Expand All @@ -57,7 +81,7 @@ def __init__(
self.threshold = threshold
self.truncate = truncate
self.is_volume = is_volume
self.clip_method = clip_method
self.level_method = _reconcile_clip_and_level(clip_method, level_method)

_DEFAULT_TESTING_PARAMETERS = {"lshort": 1, "llong": 3, "threshold": 0.01}

Expand Down Expand Up @@ -134,7 +158,7 @@ def run(
group_by=group_by,
in_place=in_place,
n_processes=n_processes,
clip_method=self.clip_method,
level_method=self.level_method,
verbose=verbose,
)
return result
51 changes: 36 additions & 15 deletions starfish/core/image/Filter/element_wise_mult.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
import numpy as np
import xarray as xr

from starfish.core.imagestack.imagestack import ImageStack
from starfish.core.types import Clip
from starfish.core.util.levels import preserve_float_range
from starfish.core.imagestack.imagestack import _reconcile_clip_and_level, ImageStack
from starfish.core.types import Clip, Levels
from starfish.core.util.levels import levels
from ._base import FilterAlgorithm


Expand All @@ -19,22 +19,37 @@ class ElementWiseMultiply(FilterAlgorithm):
----------
mult_mat : xr.DataArray
the image is element-wise multiplied with this array
clip_method : Union[str, Clip]
(Default Clip.CLIP) Controls the way that data are scaled to retain skimage dtype
requirements that float data fall in [0, 1].
Clip.CLIP: data above 1 are set to 1, and below 0 are set to 0
Clip.SCALE_BY_IMAGE: data above 1 are scaled by the maximum value, with the maximum
value calculated over the entire ImageStack
clip_method : Optional[Union[str, :py:class:`~starfish.types.Clip`]]
Deprecated method to control the way that data are scaled to retain skimage dtype
requirements that float data fall in [0, 1]. In all modes, data below 0 are set to 0.
- Clip.CLIP: data above 1 are set to 1. This has been replaced with
level_method=Levels.CLIP.
- Clip.SCALE_BY_IMAGE: when any data in the entire ImageStack is greater than 1, the entire
ImageStack is scaled by the maximum value in the ImageStack. This has been replaced with
level_method=Levels.SCALE_SATURATED_BY_IMAGE.
level_method : :py:class:`~starfish.types.Levels`
Controls the way that data are scaled to retain skimage dtype requirements that float data
fall in [0, 1]. In all modes, data below 0 are set to 0.
- Levels.CLIP (default): data above 1 are set to 1.
- Levels.SCALE_SATURATED_BY_IMAGE: when any data in the entire ImageStack is greater
than 1, the entire ImageStack is scaled by the maximum value in the ImageStack.
- Levels.SCALE_BY_IMAGE: scale the entire ImageStack by the maximum value in the
ImageStack.
"""

def __init__(
self, mult_array: xr.DataArray, clip_method: Union[str, Clip] = Clip.CLIP
self,
mult_array: xr.DataArray,
clip_method: Optional[Union[str, Clip]] = None,
level_method: Optional[Levels] = None,
) -> None:

self.mult_array = mult_array
if clip_method == Clip.SCALE_BY_CHUNK:
self.level_method = _reconcile_clip_and_level(clip_method, level_method)
if self.level_method in (Levels.SCALE_BY_CHUNK, Levels.SCALE_SATURATED_BY_CHUNK):
raise ValueError("`scale_by_chunk` is not a valid clip_method for ElementWiseMultiply")
self.clip_method = clip_method

_DEFAULT_TESTING_PARAMETERS = {
"mult_array": xr.DataArray(
Expand Down Expand Up @@ -80,8 +95,14 @@ def run(
return stack

stack.xarray.values *= mult_array_aligned
if self.clip_method == Clip.CLIP:
stack.xarray.values = preserve_float_range(stack.xarray.values, rescale=False)
if self.level_method == Levels.CLIP:
stack.xarray.values = levels(stack.xarray.values)
elif self.level_method == Levels.SCALE_BY_IMAGE:
stack.xarray.values = levels(stack.xarray.values, rescale=True)
elif self.level_method == Levels.SCALE_SATURATED_BY_IMAGE:
stack.xarray.values = levels(stack.xarray.values, rescale_saturated=True)
else:
stack.xarray.values = preserve_float_range(stack.xarray.values, rescale=True)
raise ValueError(
f"Unknown level method {self.level_method}. See starfish.types.Levels for valid "
f"options")
return None
56 changes: 38 additions & 18 deletions starfish/core/image/Filter/gaussian_high_pass.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
import xarray as xr

from starfish.core.image.Filter.gaussian_low_pass import GaussianLowPass
from starfish.core.imagestack.imagestack import ImageStack
from starfish.core.types import Clip, Number
from starfish.core.util.levels import preserve_float_range
from starfish.core.imagestack.imagestack import _reconcile_clip_and_level, ImageStack
from starfish.core.types import Clip, Levels, Number
from starfish.core.util.levels import levels
from ._base import FilterAlgorithm
from .util import (
determine_axes_to_group_by,
Expand All @@ -30,33 +30,53 @@ class GaussianHighPass(FilterAlgorithm):
is_volume : bool
If True, 3d (z, y, x) volumes will be filtered, otherwise, filter 2d tiles
independently.
clip_method : Union[str, Clip]
(Default Clip.CLIP) Controls the way that data are scaled to retain skimage dtype
requirements that float data fall in [0, 1].
Clip.CLIP: data above 1 are set to 1, and below 0 are set to 0
Clip.SCALE_BY_IMAGE: data above 1 are scaled by the maximum value, with the maximum
value calculated over the entire ImageStack
Clip.SCALE_BY_CHUNK: data above 1 are scaled by the maximum value, with the maximum
value calculated over each slice, where slice shapes are determined by the group_by
parameters
clip_method : Optional[Union[str, :py:class:`~starfish.types.Clip`]]
Deprecated method to control the way that data are scaled to retain skimage dtype
requirements that float data fall in [0, 1]. In all modes, data below 0 are set to 0.
- Clip.CLIP: data above 1 are set to 1. This has been replaced with
level_method=Levels.CLIP.
- Clip.SCALE_BY_IMAGE: when any data in the entire ImageStack is greater than 1, the entire
ImageStack is scaled by the maximum value in the ImageStack. This has been replaced with
level_method=Levels.SCALE_SATURATED_BY_IMAGE.
- Clip.SCALE_BY_CHUNK: when any data in any slice is greater than 1, each slice is scaled by
the maximum value found in that slice. The slice shapes are determined by the
``group_by`` parameters. This has been replaced with
level_method=Levels.SCALE_SATURATED_BY_CHUNK.
level_method : :py:class:`~starfish.types.Levels`
Controls the way that data are scaled to retain skimage dtype requirements that float data
fall in [0, 1]. In all modes, data below 0 are set to 0.
- Levels.CLIP (default): data above 1 are set to 1.
- Levels.SCALE_SATURATED_BY_IMAGE: when any data in the entire ImageStack is greater
than 1, the entire ImageStack is scaled by the maximum value in the ImageStack.
- Levels.SCALE_SATURATED_BY_CHUNK: when any data in any slice is greater than 1, each
slice is scaled by the maximum value found in that slice. The slice shapes are
determined by the ``group_by`` parameters.
- Levels.SCALE_BY_IMAGE: scale the entire ImageStack by the maximum value in the
ImageStack.
- Levels.SCALE_BY_CHUNK: scale each slice by the maximum value found in that slice. The
slice shapes are determined by the ``group_by`` parameters.
"""

def __init__(
self, sigma: Union[Number, Tuple[Number]], is_volume: bool = False,
clip_method: Union[str, Clip] = Clip.CLIP
self,
sigma: Union[Number, Tuple[Number]],
is_volume: bool = False,
clip_method: Optional[Union[str, Clip]] = None,
level_method: Optional[Levels] = None
) -> None:

self.sigma = validate_and_broadcast_kernel_size(sigma, is_volume)
self.is_volume = is_volume
self.clip_method = clip_method
self.level_method = _reconcile_clip_and_level(clip_method, level_method)

_DEFAULT_TESTING_PARAMETERS = {"sigma": 3}

@staticmethod
def _high_pass(
image: xr.DataArray,
sigma: Union[Number, Tuple[Number]],
rescale: bool = False
) -> xr.DataArray:
"""
Applies a gaussian high pass filter to an image
Expand All @@ -77,8 +97,8 @@ def _high_pass(
"""

blurred = GaussianLowPass._low_pass(image, sigma)
blurred = levels(blurred) # clip negative values to 0.
filtered = image - blurred
filtered = preserve_float_range(filtered, rescale)

return filtered

Expand Down Expand Up @@ -116,6 +136,6 @@ def run(
result = stack.apply(
high_pass,
group_by=group_by, verbose=verbose, in_place=in_place, n_processes=n_processes,
clip_method=self.clip_method
level_method=self.level_method
)
return result
55 changes: 36 additions & 19 deletions starfish/core/image/Filter/gaussian_low_pass.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@
import xarray as xr
from skimage.filters import gaussian

from starfish.core.imagestack.imagestack import ImageStack
from starfish.core.types import Clip, Number
from starfish.core.util.levels import preserve_float_range
from starfish.core.imagestack.imagestack import _reconcile_clip_and_level, ImageStack
from starfish.core.types import Clip, Levels, Number
from ._base import FilterAlgorithm
from .util import (
determine_axes_to_group_by,
Expand All @@ -29,33 +28,53 @@ class GaussianLowPass(FilterAlgorithm):
is_volume : bool
If True, 3d (z, y, x) volumes will be filtered, otherwise, filter 2d tiles
independently.
clip_method : Union[str, Clip]
(Default Clip.CLIP) Controls the way that data are scaled to retain skimage dtype
requirements that float data fall in [0, 1].
Clip.CLIP: data above 1 are set to 1, and below 0 are set to 0
Clip.SCALE_BY_IMAGE: data above 1 are scaled by the maximum value, with the maximum
value calculated over the entire ImageStack
Clip.SCALE_BY_CHUNK: data above 1 are scaled by the maximum value, with the maximum
value calculated over each slice, where slice shapes are determined by the group_by
parameters
clip_method : Optional[Union[str, :py:class:`~starfish.types.Clip`]]
Deprecated method to control the way that data are scaled to retain skimage dtype
requirements that float data fall in [0, 1]. In all modes, data below 0 are set to 0.
- Clip.CLIP: data above 1 are set to 1. This has been replaced with
level_method=Levels.CLIP.
- Clip.SCALE_BY_IMAGE: when any data in the entire ImageStack is greater than 1, the entire
ImageStack is scaled by the maximum value in the ImageStack. This has been replaced with
level_method=Levels.SCALE_SATURATED_BY_IMAGE.
- Clip.SCALE_BY_CHUNK: when any data in any slice is greater than 1, each slice is scaled by
the maximum value found in that slice. The slice shapes are determined by the
``group_by`` parameters. This has been replaced with
level_method=Levels.SCALE_SATURATED_BY_CHUNK.
level_method : :py:class:`~starfish.types.Levels`
Controls the way that data are scaled to retain skimage dtype requirements that float data
fall in [0, 1]. In all modes, data below 0 are set to 0.
- Levels.CLIP (default): data above 1 are set to 1.
- Levels.SCALE_SATURATED_BY_IMAGE: when any data in the entire ImageStack is greater
than 1, the entire ImageStack is scaled by the maximum value in the ImageStack.
- Levels.SCALE_SATURATED_BY_CHUNK: when any data in any slice is greater than 1, each
slice is scaled by the maximum value found in that slice. The slice shapes are
determined by the ``group_by`` parameters.
- Levels.SCALE_BY_IMAGE: scale the entire ImageStack by the maximum value in the
ImageStack.
- Levels.SCALE_BY_CHUNK: scale each slice by the maximum value found in that slice. The
slice shapes are determined by the ``group_by`` parameters.
"""

def __init__(
self, sigma: Union[Number, Tuple[Number]], is_volume: bool = False,
clip_method: Union[str, Clip] = Clip.CLIP
self,
sigma: Union[Number, Tuple[Number]],
is_volume: bool = False,
clip_method: Optional[Union[str, Clip]] = None,
level_method: Optional[Levels] = None
) -> None:

self.sigma = validate_and_broadcast_kernel_size(sigma, is_volume)
self.is_volume = is_volume
self.clip_method = clip_method
self.level_method = _reconcile_clip_and_level(clip_method, level_method)

_DEFAULT_TESTING_PARAMETERS = {"sigma": 1}

@staticmethod
def _low_pass(
image: xr.DataArray,
sigma: Union[Number, Tuple[Number]],
rescale: bool = False
) -> xr.DataArray:
"""
Apply a Gaussian blur operation over a multi-dimensional image.
Expand All @@ -82,8 +101,6 @@ def _low_pass(
sigma=sigma, output=None, cval=0, multichannel=False, preserve_range=True, truncate=4.0
)

filtered = preserve_float_range(filtered, rescale)

return filtered

def run(
Expand Down Expand Up @@ -120,6 +137,6 @@ def run(
result = stack.apply(
low_pass,
group_by=group_by, verbose=verbose, in_place=in_place, n_processes=n_processes,
clip_method=self.clip_method
level_method=self.level_method
)
return result
Loading

0 comments on commit 6e2a100

Please sign in to comment.