Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clean up watershed (again) #1693

Merged
merged 1 commit into from
Dec 16, 2019
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 17 additions & 21 deletions starfish/core/image/Segment/watershed.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
from skimage.feature import peak_local_max
from skimage.morphology import disk, watershed

from starfish.core.image.Filter import Reduce
from starfish.core.image.Filter.util import bin_open
from starfish.core.imagestack.imagestack import ImageStack
from starfish.core.morphology import Filter
Expand Down Expand Up @@ -77,21 +76,12 @@ def run(
masks : BinaryMaskCollection
binary masks segmenting each cell
"""

# create a 'stain' for segmentation
mp = primary_images.reduce({Axes.CH, Axes.ZPLANE}, func="max")
mean = Reduce(
dims=(Axes.ROUND,),
func="mean",
level_method=Levels.SCALE_BY_IMAGE).run(mp)
stain = mean._squeezed_numpy(Axes.ROUND, Axes.CH, Axes.ZPLANE)

# TODO make these parameterizable or determine whether they are useful or not
size_lim = (10, 10000)
disk_size_markers = None
disk_size_mask = None

self._segmentation_instance = _WatershedSegmenter(nuclei, stain)
self._segmentation_instance = _WatershedSegmenter(primary_images, nuclei)
label_image_array = self._segmentation_instance.segment(
self.nuclei_threshold, self.input_threshold, size_lim, disk_size_markers,
disk_size_mask, self.min_distance
Expand All @@ -118,28 +108,32 @@ def show(self, figsize: Tuple[int, int]=(10, 10)) -> None:


class _WatershedSegmenter:
def __init__(self, nuclei: ImageStack, stain_img: np.ndarray) -> None:
def __init__(self, primary_images: ImageStack, nuclei: ImageStack) -> None:
"""Implements watershed segmentation of cells seeded from a nuclei image

Algorithm is seeded by a nuclei image. Binary segmentation mask is computed from a maximum
projection of spots across C and R, which is subsequently thresholded.

Parameters
----------
primary_images : ImageStack
primary hybridization images
nuclei : ImageStack
nuclei image
stain_img : np.ndarray[np.float32]
stain image
"""
max_project_and_scale = Reduce(
# create a 'stain' for segmentation
mp = primary_images.reduce({Axes.CH, Axes.ZPLANE}, func="max")
self.stain = mp.reduce({
Axes.ROUND},
func="mean",
level_method=Levels.SCALE_BY_IMAGE)

self.nuclei_mp_scaled = nuclei.reduce(
{Axes.ROUND, Axes.CH, Axes.ZPLANE},
func="max",
level_method=Levels.SCALE_BY_IMAGE,
)
self.nuclei_mp_scaled = max_project_and_scale.run(nuclei)
self.stain = stain_img / stain_img.max()

self.nuclei_thresholded: Optional[np.ndarray] = None # dtype: bool
self.markers = None
self.num_cells: Optional[int] = None
self.mask = None
Expand Down Expand Up @@ -320,7 +314,7 @@ def watershed_mask(self, stain_thresh: Number, markers: np.ndarray, disk_size: O
thresholded stain image

"""
st = self.stain >= stain_thresh
st = self.stain._squeezed_numpy(Axes.ROUND, Axes.CH, Axes.ZPLANE) >= stain_thresh
watershed_mask: np.ndarray = np.logical_or(st, markers > 0) # dtype bool
if disk_size is not None:
watershed_mask = bin_open(watershed_mask, disk_size)
Expand All @@ -342,7 +336,7 @@ def watershed(self, markers: np.ndarray, watershed_mask: np.ndarray) -> np.ndarr
np.ndarray[np.int32] :
labeled image, each segment has a unique integer value
"""
img = 1 - self.stain
img = 1 - self.stain._squeezed_numpy(Axes.ROUND, Axes.CH, Axes.ZPLANE)

res = watershed(image=img,
markers=markers,
Expand Down Expand Up @@ -384,7 +378,9 @@ def show(self, figsize=(10, 10)):
plt.title('Nuclei')

plt.subplot(322)
image(self.stain, ax=plt.gca(), size=20, bar=True)
image(
self.stain._squeezed_numpy(Axes.ROUND, Axes.CH, Axes.ZPLANE),
ax=plt.gca(), size=20, bar=True)
plt.title('Stain')

plt.subplot(323)
Expand Down