forked from CellProfiler/CellProfiler-plugins
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathconstrainobjects.py
199 lines (153 loc) · 6.15 KB
/
constrainobjects.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# coding=utf-8
"""
ConstrainObjects
================
**ConstrainObjects** removes portions of an object that exist beyond the boundaries
of a parent object. This assumes that the objects are related, i.e. have the
same object number. In order to achieve this, use a module like **RelateObjects**.
|
============ ============ ===============
Supports 2D? Supports 3D? Respects masks?
============ ============ ===============
NO YES NO
============ ============ ===============
"""
import numpy
import logging
import cellprofiler.image
import cellprofiler.object
import cellprofiler.module
import cellprofiler.setting
log = logging.getLogger(__name__)
METHOD_IGNORE = "Ignore"
METHOD_REMOVE = "Remove protruding pieces"
class ConstrainObjects(cellprofiler.module.ObjectProcessing):
category = "Advanced"
module_name = "ConstrainObjects"
variable_revision_number = 1
def create_settings(self):
super(ConstrainObjects, self).create_settings()
self.reference_name = cellprofiler.setting.ObjectNameSubscriber(
text="Constraining Objects",
doc="Objects to use as reference for the constraint"
)
self.coersion_method = cellprofiler.setting.Choice(
text="Handle protruding objects",
choices=[METHOD_IGNORE, METHOD_REMOVE],
value=METHOD_IGNORE,
doc="""\
Assuming the objects are related, there may be some "child" objects
that protrude into the space of a "parent" object with a different label.
E.g. a nuclei from one cell may protrude into the membrane segmentation
of a difference cell. This method sets how to handle these cases
**{METHOD_IGNORE}**: Ignore these protrusions, only constrain the real child
**{METHOD_REMOVE}**: Remove the portion of the child that protrudes into the wrong parent
""".format(**{
"METHOD_IGNORE": METHOD_IGNORE,
"METHOD_REMOVE": METHOD_REMOVE
}
)
)
self.remove_orphans = cellprofiler.setting.Binary(
text="Remove children without a corresponding parent",
value=False,
doc="""
Some objects may be "parent-less" orphans, e.g. nuclei segmentations that have no
corresponding, surrounding membrane segmentations. This specifies how to handle these
objects.
**{NO}**: Ignore them
**{YES}**: Remove the entire object from set
""".format(**{
"YES": cellprofiler.setting.YES,
"NO": cellprofiler.setting.NO
})
)
def settings(self):
__settings__ = super(ConstrainObjects, self).settings()
return __settings__ + [
self.reference_name,
self.coersion_method,
self.remove_orphans
]
def visible_settings(self):
__settings__ = super(ConstrainObjects, self).visible_settings()
__settings__ += [
self.reference_name,
self.coersion_method,
self.remove_orphans
]
return __settings__
def run(self, workspace):
x_name = self.x_name.value
y_name = self.y_name.value
object_set = workspace.object_set
x = object_set.get_objects(x_name)
x_data = x.segmented
dimensions = x.dimensions
y_data = x.segmented.copy()
reference_name = self.reference_name.value
reference = object_set.get_objects(reference_name)
reference_data = reference.segmented
# Get the parent object labels
outer_labels = numpy.unique(reference_data)
if self.remove_orphans.value:
# Get the child object labels
inner_labels = numpy.unique(x_data)
# Find the discrepancies between child and parent
orphans = numpy.setdiff1d(inner_labels, outer_labels)
# Remove them from the original array
orphan_mask = numpy.in1d(x_data, orphans)
# orphan_mask here is a 1D array, but it has the same number of elements
# as y_data. Since we know that, we can reshape it to the original array
# shape and use it as a boolean mask to take out the orphaned objects
y_data[orphan_mask.reshape(x_data.shape)] = 0
for obj in outer_labels:
# Ignore the background
if obj == 0:
continue
# Find where in the array the child object is outside of the
# parent object (i.e. where the original array is *not* that
# object *and* where the child array is that object)
constrain_mask = (reference_data != obj) & (x_data == obj)
# Remove those parts outside the parent
y_data[constrain_mask] = 0
# Only remove intruding pieces if the user has requested it
if self.coersion_method.value == METHOD_REMOVE:
intrude_mask = (reference_data == obj) & (x_data != obj) & (x_data != 0)
y_data[intrude_mask] = 0
objects = cellprofiler.object.Objects()
objects.segmented = y_data
objects.parent_image = x.parent_image
workspace.object_set.add_objects(objects, y_name)
self.add_measurements(workspace)
if self.show_window:
workspace.display_data.x_data = x_data
workspace.display_data.y_data = y_data
workspace.display_data.reference = reference_data
workspace.display_data.dimensions = dimensions
def display(self, workspace, figure):
layout = (2, 2)
figure.set_subplots(
dimensions=workspace.display_data.dimensions,
subplots=layout
)
figure.subplot_imshow_labels(
image=workspace.display_data.x_data,
title=self.x_name.value,
x=0,
y=0
)
figure.subplot_imshow_labels(
image=workspace.display_data.y_data,
sharexy=figure.subplot(0, 0),
title=self.y_name.value,
x=1,
y=0
)
figure.subplot_imshow_grayscale(
image=workspace.display_data.reference,
sharexy=figure.subplot(0, 0),
title=self.reference_name.value,
x=0,
y=1
)