-
Notifications
You must be signed in to change notification settings - Fork 68
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Pipeline component and implementation for merging BinaryMaskCollections
This does not handle the case where the pixel/physical ticks do not line up. This is for cases where we derive the data from the exact same set of images, and inherit their pixel/physical ticks from the same source. Test plan: Added one positive test case, and tested the cases where the merge should fail.
- Loading branch information
Tony Tung
committed
Dec 16, 2019
1 parent
715b762
commit 65ac815
Showing
7 changed files
with
175 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
"""Algorithms in this module merge multiple BinaryMaskCollections together.""" | ||
from ._base import MergeAlgorithm | ||
from .simple import SimpleMerge | ||
|
||
# autodoc's automodule directive only captures the modules explicitly listed in __all__. | ||
__all__ = list(set( | ||
implementation_name | ||
for implementation_name, implementation_cls in locals().items() | ||
if isinstance(implementation_cls, type) and issubclass(implementation_cls, MergeAlgorithm) | ||
)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
from abc import abstractmethod | ||
from typing import Sequence | ||
|
||
from starfish.core.morphology.binary_mask import BinaryMaskCollection | ||
from starfish.core.pipeline.algorithmbase import AlgorithmBase | ||
|
||
|
||
class MergeAlgorithm(metaclass=AlgorithmBase): | ||
|
||
@abstractmethod | ||
def run( | ||
self, | ||
binary_mask_collections: Sequence[BinaryMaskCollection], | ||
*args, | ||
**kwargs | ||
) -> BinaryMaskCollection: | ||
"""Merge multiple binary mask collections together.""" | ||
raise NotImplementedError() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
from typing import Mapping, Optional, Sequence | ||
|
||
import numpy as np | ||
|
||
from starfish.core.morphology.binary_mask import BinaryMaskCollection | ||
from starfish.core.morphology.util import _ticks_equal | ||
from starfish.core.types import ArrayLike, Axes, Coordinates, Number | ||
from ._base import MergeAlgorithm | ||
|
||
|
||
class SimpleMerge(MergeAlgorithm): | ||
def run( | ||
self, | ||
binary_mask_collections: Sequence[BinaryMaskCollection], | ||
*args, | ||
**kwargs | ||
) -> BinaryMaskCollection: | ||
"""Merge multiple binary mask collections together. This implementation requires that all | ||
the binary mask collections have the same pixel and physical ticks.""" | ||
pixel_ticks: Optional[Mapping[Axes, ArrayLike[int]]] = None | ||
physical_ticks: Optional[Mapping[Coordinates, ArrayLike[Number]]] = None | ||
|
||
# validate that they have the same pixel/physical ticks. | ||
for binary_mask_collection in binary_mask_collections: | ||
pixel_ticks = pixel_ticks or binary_mask_collection._pixel_ticks | ||
physical_ticks = physical_ticks or binary_mask_collection._physical_ticks | ||
|
||
if not _ticks_equal(pixel_ticks, binary_mask_collection._pixel_ticks): | ||
raise ValueError("not all masks have the same pixel ticks") | ||
if not _ticks_equal(physical_ticks, binary_mask_collection._physical_ticks): | ||
raise ValueError("not all masks have the same physical ticks") | ||
|
||
# gather up all the uncropped masks. | ||
all_uncropped_masks = [ | ||
np.asarray(binary_mask_collection.uncropped_mask(ix)) | ||
for binary_mask_collection in binary_mask_collections | ||
for ix in range(len(binary_mask_collection)) | ||
] | ||
|
||
assert pixel_ticks is not None | ||
assert physical_ticks is not None | ||
|
||
return BinaryMaskCollection.from_binary_arrays_and_ticks( | ||
all_uncropped_masks, | ||
pixel_ticks, | ||
physical_ticks, | ||
None, | ||
) |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import numpy as np | ||
import pytest | ||
|
||
from starfish.core.morphology.binary_mask import BinaryMaskCollection | ||
from starfish.core.morphology.binary_mask.test.factories import ( | ||
binary_arrays_2d, | ||
binary_mask_collection_2d, | ||
binary_mask_collection_3d, | ||
) | ||
from starfish.core.morphology.util import _ticks_equal | ||
from starfish.core.types import Axes, Coordinates | ||
from ..simple import SimpleMerge | ||
|
||
|
||
def test_success(): | ||
mask_collection_0 = binary_mask_collection_2d() | ||
binary_arrays, physical_ticks = binary_arrays_2d() | ||
binary_arrays_negated = [ | ||
np.bitwise_not(binary_array) | ||
for binary_array in binary_arrays | ||
] | ||
mask_collection_1 = BinaryMaskCollection.from_binary_arrays_and_ticks( | ||
binary_arrays_negated, None, physical_ticks, None) | ||
|
||
merged = SimpleMerge().run([mask_collection_0, mask_collection_1]) | ||
|
||
assert _ticks_equal(merged._pixel_ticks, mask_collection_0._pixel_ticks) | ||
assert _ticks_equal(merged._physical_ticks, mask_collection_0._physical_ticks) | ||
assert len(mask_collection_0) + len(mask_collection_1) == len(merged) | ||
|
||
# go through all the original uncroppped masks, and verify that they are somewhere in the merged | ||
# set. | ||
for mask_collection in (mask_collection_0, mask_collection_1): | ||
for ix in range(len(mask_collection)): | ||
uncropped_original_mask = mask_collection.uncropped_mask(ix) | ||
for jx in range(len(merged)): | ||
uncropped_copy_mask = merged.uncropped_mask(jx) | ||
|
||
if uncropped_original_mask.equals(uncropped_copy_mask): | ||
# found the copy, break | ||
break | ||
else: | ||
pytest.fail("could not find mask in merged set.") | ||
|
||
def test_pixel_tick_mismatch(): | ||
mask_collection_0 = binary_mask_collection_2d() | ||
mask_collection_0._pixel_ticks[Axes.X.value] = np.asarray( | ||
mask_collection_0._pixel_ticks[Axes.X.value]) + 1 | ||
binary_arrays, physical_ticks = binary_arrays_2d() | ||
binary_arrays_negated = [ | ||
np.bitwise_not(binary_array) | ||
for binary_array in binary_arrays | ||
] | ||
mask_collection_1 = BinaryMaskCollection.from_binary_arrays_and_ticks( | ||
binary_arrays_negated, None, physical_ticks, None) | ||
|
||
with pytest.raises(ValueError): | ||
SimpleMerge().run([mask_collection_0, mask_collection_1]) | ||
|
||
|
||
def test_physical_tick_mismatch(): | ||
mask_collection_0 = binary_mask_collection_2d() | ||
mask_collection_0._physical_ticks[Coordinates.X] = np.asarray( | ||
mask_collection_0._physical_ticks[Coordinates.X]) + 1 | ||
binary_arrays, physical_ticks = binary_arrays_2d() | ||
binary_arrays_negated = [ | ||
np.bitwise_not(binary_array) | ||
for binary_array in binary_arrays | ||
] | ||
mask_collection_1 = BinaryMaskCollection.from_binary_arrays_and_ticks( | ||
binary_arrays_negated, None, physical_ticks, None) | ||
|
||
with pytest.raises(ValueError): | ||
SimpleMerge().run([mask_collection_0, mask_collection_1]) | ||
|
||
|
||
def test_shape_mismatch(): | ||
mask_collection_0 = binary_mask_collection_2d() | ||
mask_collection_1 = binary_mask_collection_3d() | ||
|
||
with pytest.raises(ValueError): | ||
SimpleMerge().run([mask_collection_0, mask_collection_1]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
from starfish.core.morphology import ( # noqa: F401 | ||
Binarize, | ||
Filter, | ||
Merge, | ||
) | ||
from starfish.core.morphology.binary_mask import BinaryMaskCollection # noqa: F401 | ||
from starfish.core.morphology.label_image import LabelImage # noqa: F401 |