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

Pipeline Components: LearnTransform and ApplyTransform #1083

Merged
merged 26 commits into from
Apr 2, 2019

Conversation

shanaxel42
Copy link
Collaborator

@shanaxel42 shanaxel42 commented Mar 14, 2019

This PR adds two new pipeline components LearnTransform, and ApplyTransform replacing our existing Registration component.

  • LearnTransform currently only provides one method, Translation that computes translation shifts on an imagestack given a reference image and stores them as a new object, TransformsList.
  • ApplyTransform also has one method Warp; that takes an imagestack and a TransformsList, applies the transforms to each axes subset described, and returns a transformed Imagstack.
  • In the ISS pipeline Fourier Registration was replaced with this Learn/Apply series in the notebook and cli test.
  • Fourier Registration was deleted.

@codecov-io
Copy link

codecov-io commented Mar 14, 2019

Codecov Report

Merging #1083 into master will increase coverage by 0.33%.
The diff coverage is 98.61%.

Impacted file tree graph

@@            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
Impacted Files Coverage Δ
starfish/pipeline/algorithmbase.py 97.72% <ø> (ø) ⬆️
starfish/test/full_pipelines/cli/test_iss.py 100% <ø> (ø) ⬆️
starfish/types/__init__.py 100% <ø> (ø) ⬆️
starfish/types/_constants.py 97.91% <100%> (+0.09%) ⬆️
starfish/test/full_pipelines/api/test_iss_api.py 100% <100%> (ø) ⬆️
starfish/image/_learn_transform/transforms_list.py 100% <100%> (ø)
...rfish/test/image/registration/test_registration.py 100% <100%> (ø)
starfish/image/_apply_transform/__init__.py 100% <100%> (ø)
starfish/image/_learn_transform/__init__.py 100% <100%> (ø)
starfish/image/__init__.py 100% <100%> (ø) ⬆️
... and 15 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update c8b6801...fb0657a. Read the comment docs.

"""Class that applies a list of arbitrary skimage GeometricTransforms to an ImageStack
using skimage.transform.warp"""

def __init__(self, transforms_list: Union[str, TransformsList]):
Copy link
Collaborator

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(
Copy link
Collaborator

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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
to apply to it.
to apply to the portion of the image identified by the selector.

from starfish.types import Axes


class TransformsList:
Copy link
Collaborator

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.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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."
Copy link
Collaborator

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
Copy link
Collaborator

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}")
Copy link
Collaborator

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?

starfish/pipeline/algorithmbase.py Outdated Show resolved Hide resolved
ttung and others added 4 commits March 22, 2019 09:54
…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`
@shanaxel42 shanaxel42 requested a review from ttung March 22, 2019 21:24
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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is from #1100?

@@ -10,8 +10,5 @@ Image Manipulation
.. toctree::
filtering/index.rst

.. toctree::
Copy link
Collaborator

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")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@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
Copy link
Collaborator

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"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
COMPONENT_NAME = "lean_transform"
COMPONENT_NAME = "learn_transform"

transform_object: GeometricTransform
) -> None:
"""
Adds a new TransformationObject to the list
Copy link
Collaborator

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?

Copy link
Collaborator Author

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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reference_stack?

how about:

Suggested change
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, "
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use f-strings:

Suggested change
"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}")
Copy link
Collaborator

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?

Copy link
Collaborator Author

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?

Copy link
Member

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. 😆

Shannon Axelrod and others added 3 commits April 1, 2019 09:56
Co-Authored-By: shanaxel42 <shannon.axelrod@chanzuckerberg.com>
@shanaxel42 shanaxel42 requested a review from ttung April 1, 2019 17:20
Copy link
Member

@ambrosejcarr ambrosejcarr left a 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>`
Copy link
Member

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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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],
Copy link
Member

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`
Copy link
Member

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
Copy link
Member

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}")
Copy link
Member

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")
Copy link
Member

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?

Suggested change
@click.command("Translation")
@click.command(Translation.__name__)

Copy link
Collaborator

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',
Copy link
Member

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'
Copy link
Collaborator

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,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: unwrap

@shanaxel42 shanaxel42 merged commit b168903 into master Apr 2, 2019
@shanaxel42 shanaxel42 deleted the saxelrod-translation branch April 2, 2019 18:44
ttung pushed a commit that referenced this pull request Apr 8, 2019
starfish/test/full_pipelines/cli/test_iss.py changed in #1083, but docs/source/usage/iss/iss_cli.sh was never updated.
ttung added a commit that referenced this pull request Apr 8, 2019
starfish/test/full_pipelines/cli/test_iss.py changed in #1083, but docs/source/usage/iss/iss_cli.sh was never updated.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants