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

Add optional ability to make parent-child objects in RunCellpose #238

Closed
wants to merge 6 commits into from
Closed
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
102 changes: 98 additions & 4 deletions active_plugins/runcellpose.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,15 @@
from cellprofiler_core.setting import Binary, ValidationError
from cellprofiler_core.setting.choice import Choice
from cellprofiler_core.setting.do_something import DoSomething
from cellprofiler_core.setting.subscriber import ImageSubscriber
from cellprofiler_core.setting.subscriber import ImageSubscriber, LabelSubscriber
from cellprofiler_core.preferences import get_default_output_directory
from cellprofiler_core.setting.text import (
Integer,
ImageName,
Directory,
Filename,
Float,
LabelName,
)

CUDA_LINK = "https://pytorch.org/get-started/locally/"
Expand Down Expand Up @@ -95,7 +96,7 @@ class RunCellpose(ImageSegmentation):

module_name = "RunCellpose"

variable_revision_number = 4
variable_revision_number = 5

doi = {
"Please cite the following when using RunCellPose:": "https://doi.org/10.1038/s41592-020-01018-x",
Expand Down Expand Up @@ -337,6 +338,28 @@ def set_directory_fn(path):
""",
)

self.make_primary_secondary = Binary(
text="Bring in a previous object as a 'primary' object and make this a 'secondary' object?",
value=False,
doc="""
TODO
""",
)

self.primary_object_type = LabelSubscriber(
"Primary objects",
doc="""\
TODO.
""",
)

self.filtered_primary_object_name = LabelName(
"Name to call the filtred primary objects",
"FilteredPrimaryObjects",
doc="""\
TODO. """,
)

def settings(self):
return [
self.x_name,
Expand All @@ -362,6 +385,9 @@ def settings(self):
self.omni,
self.invert,
self.remove_edge_masks,
self.make_primary_secondary,
self.primary_object_type,
self.filtered_primary_object_name,
]

def visible_settings(self):
Expand Down Expand Up @@ -403,7 +429,12 @@ def visible_settings(self):
if self.save_probabilities.value:
vis_settings += [self.probabilities_name]

vis_settings += [self.use_averaging, self.use_gpu]
vis_settings += [self.use_averaging, self.make_primary_secondary]

if self.make_primary_secondary.value:
vis_settings+=[self.primary_object_type,self.filtered_primary_object_name]

vis_settings += [self.use_gpu]

if self.docker_or_python.value == 'Python':
if self.use_gpu.value:
Expand Down Expand Up @@ -601,10 +632,71 @@ def run(self, workspace):
LOGGER.error("Unable to delete temporary directory, files may be in use by another program.")
LOGGER.error("Temp folder is subfolder {tempdir} in your Default Output Folder.\nYou may need to remove it manually.")


#instantiate the cell objects, give them some properties
y = Objects()
y.segmented = y_data
y.parent_image = x.parent_image

# Do primary/secondary mapping if it's asked for
if self.make_primary_secondary.value:

# pull the primary object segementations
pre_primary_objects = workspace.object_set.get_objects(self.primary_object_type.value)
pre_primary_labels = pre_primary_objects.segmented

# for each primary object, find the secondary object it most overlaps
child_count, parents_of = y.relate_children(pre_primary_objects)
# this gives us for each cell, how many nuclei are in it, and for each nucleus, what is the cell it most overlaps
"""Returns two 1-d arrays. The first gives the number of children within
each parent. The second gives the mapping of each child to its parent's
object number.
"""
for each_child_count in child_count: # for each cell, see if it has >1 nucleus (we'll handle 0 at the end)
if each_child_count > 1:
#use parents_of to get the object numbers of the nuclei in question
#to get percent overlap, for each nucleus, get the counts of the cell array values in it using unique, calculate fraction for this cell : unique, counts = numpy.unique(a, return_counts=True)
#if there is a winner, get the index
#else, we go to the second tiebreaker; for each cell, get the array values of the nuclei in it (using unique as above), pick a winner
#set the nuclear segmentation array to 0s in the non-winner nuclei, in the areas where they overlap the cell



#How to do it in CellProfiler modules
# MaskObjects, mode "Keep overlapping region", masking Nuclei with Cells (MaskedNuclei)
# MeasureObjectSizeAndShape on MaskedNuclei
# RelateObjects, Cells as parents, MaskedNuclei as children
# FilterObjects on MaskedNuclei (FilteredMaskedNuclei)->use MaximalPerObject filtering, with Cells as the parent object and AreaShape_Area as the measurement -> FilteredMaskedNuclei will now be the largest nucleus-inside-a-given-cell's-boundary (so now all cells contain only one nucelus, but we have yet to do the inverse)
# MaskObjects masking Cells with FilteredMaskedNuclei, mode "Keep" -> CellsWithNuclei
# MaskObjects masking Nuclei with FilteredMaskedNuclei, mode "Keep" -> NucleiPassingFilter
# RelateObjects, parent NucleiPassingFilter, child CellsWithNuclei
# FilterObjects on NucleiPassingFilter, keep only NucleiPassingFilter that have between 0.9 and 1.1 Child_CellsWithNuclei -> FinalNuclei
# MaskObjects, mode "Keep", CellsWithNuclei with FinalNuclei -> FinalCells



# tiebreaker step - if multiple primary objects pick the same secondary object, pick the one with the most % overlap with the secondary object and throw the other(s) away
# tiebreaker step part 2 - if above still results in a tie, pick the one with the most area inside the secondary object and throw the other(s) away
#parent_array = numpy.where(y_data==each_child_count,pre_primary_labels,0)
# we could write a fancier heuristic to not throw things out above but instead try to rematch them but Beth doesn't wanna

# for each primary object left, if it doesn't have a secondary object, throw it away

# for each secondary object, see if it has a primary object, if not, throw it away

# for the two object sets now, renumber them to make the numbers sequential

# add a parent child relationship as if it were 'real' 1* and 2*
# TODO - right variable names, but this is from IDSecondary
# children_per_parent, parents_of_children = objects.relate_children(objects_out)

# add the filtered primary object to the workspace - we'll add the new secondary in the code below
primary_objects = Objects()
primary_objects.segmented = some_var #TODO -figure out what this is
primary_objects.parent_image = pre_primary_objects.parent_image
objects = workspace.object_set
objects.add_objects(primary_objects, self.filtered_primary_object_name.value)
pass

objects = workspace.object_set
objects.add_objects(y, y_name)

Expand Down Expand Up @@ -699,4 +791,6 @@ def upgrade_settings(self, setting_values, variable_revision_number, module_name
if variable_revision_number == 3:
setting_values = [setting_values[0]] + ["Python",CELLPOSE_DOCKER_IMAGE_WITH_PRETRAINED] + setting_values[1:]
variable_revision_number = 4
if variable_revision_number == 4:
setting_values += [False,"None","FilteredPrimaryObject"]
return setting_values, variable_revision_number
Loading