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

Restructure the relationship between PipelineComponent and AlgorithmBase #1095

Merged
merged 1 commit into from
Mar 22, 2019
Merged
Show file tree
Hide file tree
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
43 changes: 2 additions & 41 deletions starfish/image/_filter/__init__.py
Original file line number Diff line number Diff line change
@@ -1,42 +1,3 @@
from typing import Type

from starfish.imagestack.imagestack import ImageStack
from starfish.pipeline import AlgorithmBase, import_all_submodules, PipelineComponent
from starfish.util import click
from . import _base
from starfish.pipeline import import_all_submodules
from ._base import Filter
import_all_submodules(__file__, __package__)


COMPONENT_NAME = "filter"


class Filter(PipelineComponent):
@classmethod
def pipeline_component_type_name(cls) -> str:
return COMPONENT_NAME

@classmethod
def _get_algorithm_base_class(cls) -> Type[AlgorithmBase]:
return _base.FilterAlgorithmBase

@classmethod
def _cli_run(cls, ctx, instance):
output = ctx.obj["output"]
stack = ctx.obj["stack"]
filtered = instance.run(stack)
filtered.export(output)

@staticmethod
@click.group(COMPONENT_NAME)
@click.option("-i", "--input", type=click.Path(exists=True))
@click.option("-o", "--output", required=True)
@click.pass_context
def _cli(ctx, input, output):
"""smooth, sharpen, denoise, etc"""
print("Filtering images...")
ctx.obj = dict(
component=Filter,
input=input,
output=output,
stack=ImageStack.from_path_or_url(input),
)
41 changes: 41 additions & 0 deletions starfish/image/_filter/_base.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,49 @@
from abc import abstractmethod
from typing import Type

from starfish.imagestack.imagestack import ImageStack
from starfish.pipeline.algorithmbase import AlgorithmBase
from starfish.pipeline.pipelinecomponent import PipelineComponent
from starfish.util import click


COMPONENT_NAME = "filter"


class Filter(PipelineComponent):
@classmethod
def pipeline_component_type_name(cls) -> str:
return COMPONENT_NAME

@classmethod
def _cli_run(cls, ctx, instance):
output = ctx.obj["output"]
stack = ctx.obj["stack"]
filtered = instance.run(stack)
filtered.export(output)

@staticmethod
@click.group(COMPONENT_NAME)
@click.option("-i", "--input", type=click.Path(exists=True))
@click.option("-o", "--output", required=True)
@click.pass_context
def _cli(ctx, input, output):
"""smooth, sharpen, denoise, etc"""
print("Filtering images...")
ctx.obj = dict(
component=Filter,
input=input,
output=output,
stack=ImageStack.from_path_or_url(input),
)


class FilterAlgorithmBase(AlgorithmBase):
@classmethod
def get_pipeline_component_class(cls) -> Type[PipelineComponent]:
return Filter

@abstractmethod
def run(self, stack: ImageStack) -> ImageStack:
"""Performs filtering on an ImageStack."""
raise NotImplementedError()
44 changes: 2 additions & 42 deletions starfish/image/_registration/__init__.py
Original file line number Diff line number Diff line change
@@ -1,43 +1,3 @@
from typing import Type

from starfish.imagestack.imagestack import ImageStack
from starfish.pipeline import AlgorithmBase, import_all_submodules, PipelineComponent
from starfish.util import click
from . import _base
from starfish.pipeline import import_all_submodules
from ._base import Registration
import_all_submodules(__file__, __package__)


COMPONENT_NAME = "registration"


class Registration(PipelineComponent):

@classmethod
def pipeline_component_type_name(cls) -> str:
return COMPONENT_NAME

@classmethod
def _get_algorithm_base_class(cls) -> Type[AlgorithmBase]:
return _base.RegistrationAlgorithmBase

@classmethod
def _cli_run(cls, ctx, instance):
output = ctx.obj["output"]
stack = ctx.obj["stack"]
instance.run(stack)
stack.export(output)

@staticmethod
@click.group(COMPONENT_NAME)
@click.option("-i", "--input", type=click.Path(exists=True))
@click.option("-o", "--output", required=True)
@click.pass_context
def _cli(ctx, input, output):
"""translation correction of image stacks"""
print("Registering...")
ctx.obj = dict(
component=Registration,
input=input,
output=output,
stack=ImageStack.from_path_or_url(input),
)
43 changes: 42 additions & 1 deletion starfish/image/_registration/_base.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,51 @@
from typing import Optional
from abc import abstractmethod
from typing import Optional, Type

import click

from starfish.imagestack.imagestack import ImageStack
from starfish.pipeline import PipelineComponent
from starfish.pipeline.algorithmbase import AlgorithmBase


COMPONENT_NAME = "registration"


class Registration(PipelineComponent):

@classmethod
def pipeline_component_type_name(cls) -> str:
return COMPONENT_NAME

@classmethod
def _cli_run(cls, ctx, instance):
output = ctx.obj["output"]
stack = ctx.obj["stack"]
instance.run(stack)
stack.export(output)

@staticmethod
@click.group(COMPONENT_NAME)
@click.option("-i", "--input", type=click.Path(exists=True))
@click.option("-o", "--output", required=True)
@click.pass_context
def _cli(ctx, input, output):
"""translation correction of image stacks"""
print("Registering...")
ctx.obj = dict(
component=Registration,
input=input,
output=output,
stack=ImageStack.from_path_or_url(input),
)


class RegistrationAlgorithmBase(AlgorithmBase):
@classmethod
def get_pipeline_component_class(cls) -> Type[PipelineComponent]:
return Registration

@abstractmethod
def run(self, stack) -> Optional[ImageStack]:
"""Performs registration on the stack provided."""
raise NotImplementedError()
51 changes: 2 additions & 49 deletions starfish/image/_segmentation/__init__.py
Original file line number Diff line number Diff line change
@@ -1,50 +1,3 @@
from typing import Type

from skimage.io import imsave

from starfish.imagestack.imagestack import ImageStack
from starfish.pipeline import AlgorithmBase, import_all_submodules, PipelineComponent
from starfish.util import click
from . import _base
from starfish.pipeline import import_all_submodules
from ._base import Segmentation
import_all_submodules(__file__, __package__)


COMPONENT_NAME = "segment"


class Segmentation(PipelineComponent):

@classmethod
def pipeline_component_type_name(cls) -> str:
return COMPONENT_NAME

@classmethod
def _get_algorithm_base_class(cls) -> Type[AlgorithmBase]:
return _base.SegmentationAlgorithmBase

@classmethod
def _cli_run(cls, ctx, instance):
output = ctx.obj["output"]
pri_stack = ctx.obj["primary_images"]
nuc_stack = ctx.obj["nuclei"]

label_image = instance.run(pri_stack, nuc_stack)

print(f"Writing label image to {output}")
imsave(output, label_image)

@staticmethod
@click.group(COMPONENT_NAME)
@click.option("--primary-images", required=True, type=click.Path(exists=True))
@click.option("--nuclei", required=True, type=click.Path(exists=True))
@click.option("-o", "--output", required=True)
@click.pass_context
def _cli(ctx, primary_images, nuclei, output):
"""define polygons for cell boundaries and assign spots"""
print('Segmenting ...')
ctx.obj = dict(
component=Segmentation,
output=output,
primary_images=ImageStack.from_path_or_url(primary_images),
nuclei=ImageStack.from_path_or_url(nuclei),
)
49 changes: 49 additions & 0 deletions starfish/image/_segmentation/_base.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,57 @@
from abc import abstractmethod
from typing import Type

import click
from skimage.io import imsave

from starfish.imagestack.imagestack import ImageStack
from starfish.pipeline import PipelineComponent
from starfish.pipeline.algorithmbase import AlgorithmBase


COMPONENT_NAME = "segment"


class Segmentation(PipelineComponent):

@classmethod
def pipeline_component_type_name(cls) -> str:
return COMPONENT_NAME

@classmethod
def _cli_run(cls, ctx, instance):
output = ctx.obj["output"]
pri_stack = ctx.obj["primary_images"]
nuc_stack = ctx.obj["nuclei"]

label_image = instance.run(pri_stack, nuc_stack)

print(f"Writing label image to {output}")
imsave(output, label_image)

@staticmethod
@click.group(COMPONENT_NAME)
@click.option("--primary-images", required=True, type=click.Path(exists=True))
@click.option("--nuclei", required=True, type=click.Path(exists=True))
@click.option("-o", "--output", required=True)
@click.pass_context
def _cli(ctx, primary_images, nuclei, output):
"""define polygons for cell boundaries and assign spots"""
print('Segmenting ...')
ctx.obj = dict(
component=Segmentation,
output=output,
primary_images=ImageStack.from_path_or_url(primary_images),
nuclei=ImageStack.from_path_or_url(nuclei),
)


class SegmentationAlgorithmBase(AlgorithmBase):
@classmethod
def get_pipeline_component_class(cls) -> Type[PipelineComponent]:
return Segmentation

@abstractmethod
def run(self, primary_image_stack: ImageStack, nuclei_stack: ImageStack):
"""Performs registration on the stack provided."""
raise NotImplementedError()
38 changes: 30 additions & 8 deletions starfish/pipeline/algorithmbase.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,43 @@
import inspect
from abc import ABCMeta, abstractmethod
from typing import Type

from starfish.imagestack.imagestack import ImageStack
from starfish.intensity_table.intensity_table import IntensityTable
from starfish.types import LOG
from starfish.types._constants import STARFISH_EXTRAS_KEY
from starfish.util.logging import LogEncoder
from .pipelinecomponent import PipelineComponent


class AlgorithmBaseType(type):

class AlgorithmBaseType(ABCMeta):
def __init__(cls, name, bases, namespace):
super().__init__(name, bases, namespace)
if len(bases) != 0:
# this is _not_ AlgorithmBase. Instead, it's a subclass of AlgorithmBase.
if not inspect.isabstract(cls):
AlgorithmBaseType.register_with_pipeline_component(cls)
cls.run = AlgorithmBaseType.run_with_logging(cls.run)

@staticmethod
def register_with_pipeline_component(algorithm_cls):
pipeline_component_cls = algorithm_cls.get_pipeline_component_class()
if pipeline_component_cls._algorithm_to_class_map_int is None:
pipeline_component_cls._algorithm_to_class_map_int = {}
pipeline_component_cls._algorithm_to_class_map_int[algorithm_cls.__name__] = algorithm_cls
setattr(pipeline_component_cls, algorithm_cls._get_algorithm_name(), algorithm_cls)

pipeline_component_cls._cli.add_command(algorithm_cls._cli)

@staticmethod
def run_with_logging(func):
"""
This method extends each pipeline component.run() method to also log itself and
runtime parameters to the IntensityTable and Imagestack objects. There are two
runtime parameters to the IntensityTable and ImageStack objects. There are two
scenarios for this method:
1.) Filtering:
Imagestack -> Imagestack
ImageStack -> ImageStack
2.) Spot Detection:
Imagestack -> IntensityTable
Imagestack -> [IntenistyTable, ConnectedComponentDecodingResult]
ImageStack -> IntensityTable
ImageStack -> [IntensityTable, ConnectedComponentDecodingResult]
TODO segmentation and decoding
"""
def helper(*args, **kwargs):
Expand Down Expand Up @@ -91,3 +105,11 @@ def _get_algorithm_name(cls):
https://docs.python.org/3/reference/lexical_analysis.html#identifiers
"""
return cls.__name__

@classmethod
@abstractmethod
def get_pipeline_component_class(cls) -> Type[PipelineComponent]:
"""
Returns the class of PipelineComponent this algorithm implements.
"""
raise NotImplementedError()
Loading