-
Notifications
You must be signed in to change notification settings - Fork 69
/
Copy pathfilterobjects_stringmatch.py
267 lines (225 loc) · 10.5 KB
/
filterobjects_stringmatch.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
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
from cellprofiler_core.module.image_segmentation import ObjectProcessing
from cellprofiler_core.setting import Divider
from cellprofiler_core.setting.text import Alphanumeric
from cellprofiler_core.setting.choice import Choice
from cellprofiler_core.setting import Measurement, HiddenCount, SettingsGroup
from cellprofiler_core.setting.do_something import DoSomething, RemoveSettingButton
__doc__ = ""
import logging
import os
import numpy
import scipy
import scipy.ndimage
import scipy.sparse
import cellprofiler_core.object
from cellprofiler.utilities.rules import Rules
LOGGER = logging.getLogger(__name__)
METHOD_EXACT = "Filter out strings matching"
METHOD_CONTAINS = "Filter out strings containing"
METHOD_KEEP_EXACT = "Keep only strings matching"
METHOD_KEEP_CONTAINS = "Keep only strings containing"
class FilterObjects_StringMatch(ObjectProcessing):
module_name = "FilterObjects_StringMatch"
variable_revision_number = 2
def __init__(self):
self.rules = Rules()
super(FilterObjects_StringMatch, self).__init__()
def create_settings(self):
super(FilterObjects_StringMatch, self).create_settings()
self.x_name.text = """Select the objects to filter"""
self.x_name.doc = ""
self.y_name.text = """Name the output objects"""
self.y_name.doc = "Enter a name for the collection of objects that are retained after applying the filter(s)."
self.spacer_1 = Divider(line=False)
self.filter_out = Alphanumeric(
"String to use for filter",
"AAAA",
doc="""Enter the string that should be used to filter objects.""",
)
self.filter_method = Choice(
"Filter method",
[METHOD_EXACT, METHOD_CONTAINS, METHOD_KEEP_EXACT, METHOD_KEEP_CONTAINS],
doc="""Select whether to filter out objects that are an exact match for the string entered
(e.g. Object 'AAAAB' will NOT be filtered by string 'AAAA')
to filter any object that contains the string entered
(e.g. Object 'AAAAB' will be filtered by string 'AAAA'), to keep only objects that
are an exact match for the string entered (e.g. Only 'AAAA' objects will be kept by string
'AAAA'), or keep only objects that contain the string entered (e.g. 'AAAAB' and 'AAAAA' objects
but not 'AAAB' objects will be kept by string 'AAAA').""",
)
self.filter_column = Measurement("Measurement",
self.x_name.get_value,
"Barcode_BarcodeCalled",
doc="""Select the measurement column that will be used for filtering.""",
)
self.additional_strings = []
self.additional_string_count = HiddenCount(
self.additional_strings, "Additional string count"
)
self.spacer_2 = Divider(line=True)
self.additional_string_button = DoSomething(
"Add an additional string to use to filter objects?",
"Add an additional string",
self.add_additional_string,
doc="""\
Click this button to add an additional string to apply to the objects with the same rules.""",
)
self.rules.create_settings()
def add_additional_string(self):
group = SettingsGroup()
group.append(
"additional_string",
Alphanumeric(
"String to use for additional filter",
"AAAA",
doc="""Enter the string that should be used to filter objects.""",
),
)
group.append(
"remover",
RemoveSettingButton(
"", "Remove this additional string", self.additional_strings, group
),
)
group.append("divider", Divider(line=False))
self.additional_strings.append(group)
def settings(self):
settings = super(FilterObjects_StringMatch, self).settings()
settings += [self.filter_out,self.filter_method, self.filter_column]
for x in self.additional_strings:
settings += [self.filter_out]
return settings
def visible_settings(self):
visible_settings = super(FilterObjects_StringMatch, self).visible_settings()
visible_settings += [
self.filter_out,
self.filter_method,
self.filter_column
]
if self.filter_method != METHOD_KEEP_EXACT:
for x in self.additional_strings:
visible_settings += x.visible_settings()
visible_settings += [self.additional_string_button]
return visible_settings
def run(self, workspace):
"""Filter objects for this image set, display results"""
src_objects = workspace.get_objects(self.x_name.value)
indexes = self.keep_by_string(workspace, src_objects)
#
# Create an array that maps label indexes to their new values
# All labels to be deleted have a value in this array of zero
#
new_object_count = len(indexes)
max_label = numpy.max(src_objects.segmented)
label_indexes = numpy.zeros((max_label + 1,), int)
label_indexes[indexes] = numpy.arange(1, new_object_count + 1)
#
# Loop over both the primary and additional objects
#
object_list = [(self.x_name.value, self.y_name.value)]
m = workspace.measurements
first_set = True
for src_name, target_name in object_list:
src_objects = workspace.get_objects(src_name)
target_labels = src_objects.segmented.copy()
#
# Reindex the labels of the old source image
#
target_labels[target_labels > max_label] = 0
target_labels = label_indexes[target_labels]
#
# Make a new set of objects - retain the old set's unedited
# segmentation for the new and generally try to copy stuff
# from the old to the new.
#
target_objects = cellprofiler_core.object.Objects()
target_objects.segmented = target_labels
target_objects.unedited_segmented = src_objects.unedited_segmented
#
# Remove the filtered objects from the small_removed_segmented
# if present. "small_removed_segmented" should really be
# "filtered_removed_segmented".
#
small_removed = src_objects.small_removed_segmented.copy()
small_removed[(target_labels == 0) & (src_objects.segmented != 0)] = 0
target_objects.small_removed_segmented = small_removed
if src_objects.has_parent_image:
target_objects.parent_image = src_objects.parent_image
workspace.object_set.add_objects(target_objects, target_name)
self.add_measurements(workspace, src_name, target_name)
if self.show_window and first_set:
workspace.display_data.src_objects_segmented = src_objects.segmented
workspace.display_data.target_objects_segmented = target_objects.segmented
workspace.display_data.dimensions = src_objects.dimensions
first_set = False
def display(self, workspace, figure):
"""Display what was filtered"""
src_name = self.x_name.value
src_objects_segmented = workspace.display_data.src_objects_segmented
target_objects_segmented = workspace.display_data.target_objects_segmented
dimensions = workspace.display_data.dimensions
target_name = self.y_name.value
figure.set_subplots((2, 2), dimensions=dimensions)
figure.subplot_imshow_labels(
0, 0, src_objects_segmented, title="Original: %s" % src_name
)
figure.subplot_imshow_labels(
1,
0,
target_objects_segmented,
title="Filtered: %s" % target_name,
sharexy=figure.subplot(0, 0),
)
pre = numpy.max(src_objects_segmented)
post = numpy.max(target_objects_segmented)
statistics = [[pre], [post], [pre - post]]
figure.subplot_table(
0,
1,
statistics,
row_labels=(
"Number of objects pre-filtering",
"Number of objects post-filtering",
"Number of objects removed",
),
)
def keep_by_string(self, workspace, src_objects):
"""
workspace - workspace passed into Run
src_objects - the Objects instance to be filtered
"""
src_name = self.x_name.value
m = workspace.measurements
values = m.get_current_measurement(src_name, self.filter_column.value)
# keep hits
if self.filter_method == METHOD_EXACT:
hits = [self.filter_out.value != x for x in values]
if self.additional_strings:
for group in self.additional_strings:
more_hits = [group.additional_string.value != x for x in values]
hits = [a and b for a, b in zip(hits, more_hits)]
elif self.filter_method == METHOD_KEEP_EXACT:
hits = [self.filter_out.value == x for x in values]
elif self.filter_method == METHOD_CONTAINS:
hits = [self.filter_out.value not in x for x in values]
if self.additional_strings:
for group in self.additional_strings:
more_hits = [group.additional_string.value not in x for x in values]
hits = [a and b for a, b in zip(hits, more_hits)]
elif self.filter_method == METHOD_KEEP_CONTAINS:
hits = [self.filter_out.value in x for x in values]
if self.additional_strings:
for group in self.additional_strings:
more_hits = [group.additional_string.value in x for x in values]
hits = [a and b for a, b in zip(hits, more_hits)]
# Get object numbers for things that are True
indexes = numpy.argwhere(hits)[:, 0]
# Objects are 1 counted, Python is 0 counted
indexes = indexes + 1
return indexes
def upgrade_settings(self, setting_values, variable_revision_number, module_name):
if variable_revision_number == 1:
setting_values = [value.replace('Exact match','Filter out strings matching') for value in setting_values]
setting_values = [value.replace('String contains','Filter out strings containing') for value in setting_values]
variable_revision_number = 2
return setting_values, variable_revision_number