-
Notifications
You must be signed in to change notification settings - Fork 68
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
Pipeline Components: LearnTransform and ApplyTransform #1083
Conversation
Codecov Report
@@ Coverage Diff @@
## master #1083 +/- ##
==========================================
+ Coverage 89.89% 90.23% +0.33%
==========================================
Files 120 125 +5
Lines 4414 4637 +223
==========================================
+ Hits 3968 4184 +216
- Misses 446 453 +7
Continue to review full report at Codecov.
|
"""Class that applies a list of arbitrary skimage GeometricTransforms to an ImageStack | ||
using skimage.transform.warp""" | ||
|
||
def __init__(self, transforms_list: Union[str, TransformsList]): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
things that can be loaded from a file should be passed into run(..)
. Furthermore, the loading from a file should happen in the _cli
method.
if not in_place: | ||
# create a copy of the ImageStack, call apply on that stack with in_place=True | ||
image_stack = deepcopy(stack) | ||
return self.run( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: you could probably unwrap
Parameters | ||
---------- | ||
transforms_list: TransformsList | ||
A list of tuples that describe a subset of an ImageStack axis and a GeometricTransform |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A list of tuples that describe a subset of an ImageStack axis and a GeometricTransform | |
A list of tuples. Each tuple consists of an ImageStack selector and a GeometricTransform |
---------- | ||
transforms_list: TransformsList | ||
A list of tuples that describe a subset of an ImageStack axis and a GeometricTransform | ||
to apply to it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to apply to it. | |
to apply to the portion of the image identified by the selector. |
from starfish.types import Axes | ||
|
||
|
||
class TransformsList: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this to be the canonical file format for transforms? Is it generic enough?
It may be worth asking whether this will need to be versioned.
def run(self, stack: ImageStack) -> TransformsList: | ||
""" | ||
Iterate over the given axis of an ImageStack and learn the Similarity transform | ||
based off the instantiated reference_image. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
based off the instantiated reference_image. | |
based off the instantiated reference_image. |
if len(target_image.shape) != 2: | ||
raise ValueError( | ||
"Only axes: " + self.axis.value + " can have a length > 1, " | ||
"please max project." |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can this be a pointer to the max projection filter?
upsample_factor=self.upsampling) | ||
print(f"For {self.axis}: {a}, Shift: {shift}, Error: {error}") | ||
selectors = {self.axis: a} | ||
# reverse shift because SimilarityTransform stores in y,x format |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
shift, error, phasediff = register_translation(src_image=target_image, | ||
target_image=reference_image, | ||
upsample_factor=self.upsampling) | ||
print(f"For {self.axis}: {a}, Shift: {shift}, Error: {error}") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we want to gate this on verbose?
…ase (#1095) Rather than each specific PipelineComponent have a link to the AlgorithmBase that describes the contract that the algorithm must implement, each AlgorithmBase implementation points to the PipelineComponent that it serves. The current arrangement forces all the algorithms to be defined and evaluated in the interpreter before the pipeline component is defined and evaluated. If we want to create an algorithm that's only used in tests, this is not possible. The new arrangement ensures that any implementation of a specific AlgorithmBase is registered with its corresponding PipelineComponent. This adds explicit abstract classes to the AlgorithmBase class hierarchy to make it easier to detect what's an actual algorithm implementation. Depends on #1094 Test plan: `make -j fast`
Makefile
Outdated
@@ -29,7 +29,7 @@ all: fast | |||
|
|||
### UNIT ##################################################### | |||
# | |||
fast: lint mypy fast-test docs-html | |||
fast: lint mypy fast-test clean-docs docs-html |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is from #1100?
docs/source/api/image/index.rst
Outdated
@@ -10,8 +10,5 @@ Image Manipulation | |||
.. toctree:: | |||
filtering/index.rst | |||
|
|||
.. toctree:: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We probably need a new doc sections for LearnTransform and ApplyTransform.
transformed.export(output) | ||
|
||
@staticmethod | ||
@click.group("apply_transform") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@click.group("apply_transform") | |
@click.group(COMPONENT_NAME) |
if True, process ImageStack in-place, otherwise return a new stack | ||
verbose : bool | ||
if True, report on filtering progress (default = False) | ||
transforms_list: TransformsList |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you reorder this to before in_place
?
|
||
|
||
COMPONENT_NAME = "registration" | ||
COMPONENT_NAME = "lean_transform" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
COMPONENT_NAME = "lean_transform" | |
COMPONENT_NAME = "learn_transform" |
transform_object: GeometricTransform | ||
) -> None: | ||
""" | ||
Adds a new TransformationObject to the list |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TransformationObject
is not a thing, is it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just meant it to refer to the GeometricTransform class and subclasses, I'll make it more clear though
reference_stack: ImageStack | ||
The target image used in skimage.feature.register_translation | ||
upsampling: int | ||
upsampling factor, defualt 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
upsampling factor, defualt 1 | |
upsampling factor (default=1). See http://scikit-image.org/docs/dev/api/skimage.feature.html#skimage.feature.register_translation for an explanation of this parameter. |
def run(self, stack: ImageStack) -> TransformsList: | ||
""" | ||
Iterate over the given axes of an ImageStack and learn the Similarity transform | ||
based off the instantiated reference_image. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
reference_stack?
how about:
based off the instantiated reference_image. | |
based off the reference_stack passed into :py:class:`Translation`'s constructor. |
target_image = np.squeeze(stack.sel({self.axes: a}).xarray) | ||
if len(target_image.shape) != 2: | ||
raise ValueError( | ||
"Only axes: " + self.axes.value + " can have a length > 1, " |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use f-strings:
"Only axes: " + self.axes.value + " can have a length > 1, " | |
f"Only axes: {self.axes.value} can have a length > 1, please use the MaxProj filter." |
shift, error, phasediff = register_translation(src_image=target_image, | ||
target_image=reference_image, | ||
upsample_factor=self.upsampling) | ||
print(f"For {self.axes}: {a}, Shift: {shift}, Error: {error}") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
intended? should this be gated on verbose?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was in the old fourier registration class so I kept it for continuity. @ambrosejcarr still needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be gated. Always hated this. 😆
Co-Authored-By: shanaxel42 <shannon.axelrod@chanzuckerberg.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is great work. Thank you for this! I made a few suggestions but should be fine to merge when resolved.
@@ -59,7 +59,6 @@ To see the code or report a bug, please visit the `github repository | |||
<div class="col-md-3"> | |||
<h2>Features</h2> | |||
|
|||
* Registration: :ref:`API <registration>` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it make sense to link to the newly created docs for learning and applying transformations?
in_place : bool | ||
if True, process ImageStack in-place, otherwise return a new stack | ||
verbose : bool | ||
if True, report on filtering progress (default = False) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if True, report on filtering progress (default = False) | |
if True, report on transformation progress (default = False) |
objects to apply to an Imagestack""" | ||
|
||
def __init__(self, | ||
transforms_list: List[Tuple[Mapping[Axes, int], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggest a top-level NamedTuple
for this:
TransformsList(NamedTuple):
axes: Mapping[Axes, int] # axes this transformation modifies
transform_type: TransformType # one of <relevant skimage docs>
transform: GeometricTransform # dunno what to write here
It would also simplify the append function.
def from_json(cls, url_or_path: str) -> "TransformsList": | ||
""" | ||
Load a TransformsList from a json file or a url pointing to such a file | ||
Loads configuration from :py:class:`starfish.config.StarfishConfig` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the relevance of configuration for this function? I don't think the user will know.
|
||
def run(self, stack: ImageStack, *args) -> TransformsList: | ||
""" | ||
Iterate over the given axes of an ImageStack and learn the Similarity transform |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should note that it only supports 2-d data at this time, somewhere.
shift, error, phasediff = register_translation(src_image=target_image, | ||
target_image=reference_image, | ||
upsample_factor=self.upsampling) | ||
print(f"For {self.axes}: {a}, Shift: {shift}, Error: {error}") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be gated. Always hated this. 😆
return transforms | ||
|
||
@staticmethod | ||
@click.command("Translation") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On theme with our crusade against string constants, does the following work?
@click.command("Translation") | |
@click.command(Translation.__name__) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, that doesn't, because you're still defining the class. :(
assert np.array_equal(genes, np.array(['ACTB', 'CD68', 'CTSL2', 'EPCAM', 'ETV4', 'GAPDH', | ||
'HER2', 'MET', 'RAC1', 'TFRC', 'TP53', 'VEGF'])) | ||
assert np.array_equal(gene_counts, [18, 1, 5, 2, 1, 12, 3, 1, 2, 1, 1, 2]) | ||
assert np.array_equal(genes, np.array(['ACTB', 'CD68', 'CTSL2', 'EPCAM', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lol. I guess I'm cool with this. 👍
This is probably our fault for using a fragile spot detector.
assert pipeline_log[1]['method'] == 'FourierShiftRegistration' | ||
assert pipeline_log[2]['method'] == 'BlobDetector' | ||
assert pipeline_log[1]['method'] == 'Warp' | ||
assert pipeline_log[3]['method'] == 'BlobDetector' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Um... what's step 2?
exp = data.ISS(use_test_data=True) | ||
stack = exp.fov().get_image('primary') | ||
reference_stack = exp.fov().get_image('dots') | ||
translation = Translation(reference_stack=reference_stack, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: unwrap
…to saxelrod-translation
starfish/test/full_pipelines/cli/test_iss.py changed in #1083, but docs/source/usage/iss/iss_cli.sh was never updated.
starfish/test/full_pipelines/cli/test_iss.py changed in #1083, but docs/source/usage/iss/iss_cli.sh was never updated.
This PR adds two new pipeline components LearnTransform, and ApplyTransform replacing our existing Registration component.