Skip to content

Commit 1f847d9

Browse files
authored
add FilterObjects_StringMatch (#254)
Uses FilterObjects as template for filtering objects that match a given string.
1 parent b68e0d0 commit 1f847d9

File tree

1 file changed

+179
-0
lines changed

1 file changed

+179
-0
lines changed
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
from cellprofiler_core.module.image_segmentation import ObjectProcessing
2+
from cellprofiler_core.setting import (
3+
Divider,
4+
)
5+
from cellprofiler_core.setting.text import Alphanumeric
6+
7+
__doc__ = ""
8+
9+
import logging
10+
import os
11+
12+
import numpy
13+
import scipy
14+
import scipy.ndimage
15+
import scipy.sparse
16+
17+
import cellprofiler_core.object
18+
from cellprofiler.utilities.rules import Rules
19+
20+
LOGGER = logging.getLogger(__name__)
21+
22+
23+
class FilterObjects_StringMatch(ObjectProcessing):
24+
module_name = "FilterObjects_StringMatch"
25+
26+
variable_revision_number = 1
27+
28+
def __init__(self):
29+
self.rules = Rules()
30+
31+
super(FilterObjects_StringMatch, self).__init__()
32+
33+
def create_settings(self):
34+
super(FilterObjects_StringMatch, self).create_settings()
35+
36+
self.x_name.text = """Select the objects to filter"""
37+
38+
self.x_name.doc = ""
39+
40+
self.y_name.text = """Name the output objects"""
41+
42+
self.y_name.doc = "Enter a name for the collection of objects that are retained after applying the filter(s)."
43+
44+
self.spacer_1 = Divider(line=False)
45+
46+
self.filter_out = Alphanumeric(
47+
"What string to filter out",
48+
"AAAA",
49+
doc="""Enter a name for the measurement calculated by this module.""",
50+
)
51+
52+
self.rules.create_settings()
53+
54+
def settings(self):
55+
settings = super(FilterObjects_StringMatch, self).settings()
56+
settings += [self.filter_out]
57+
return settings
58+
59+
def visible_settings(self):
60+
visible_settings = super(FilterObjects_StringMatch, self).visible_settings()
61+
visible_settings += [
62+
self.filter_out
63+
]
64+
return visible_settings
65+
66+
def run(self, workspace):
67+
"""Filter objects for this image set, display results"""
68+
src_objects = workspace.get_objects(self.x_name.value)
69+
70+
indexes = self.keep_by_string(workspace, src_objects)
71+
72+
#
73+
# Create an array that maps label indexes to their new values
74+
# All labels to be deleted have a value in this array of zero
75+
#
76+
new_object_count = len(indexes)
77+
max_label = numpy.max(src_objects.segmented)
78+
label_indexes = numpy.zeros((max_label + 1,), int)
79+
label_indexes[indexes] = numpy.arange(1, new_object_count + 1)
80+
#
81+
# Loop over both the primary and additional objects
82+
#
83+
object_list = [(self.x_name.value, self.y_name.value)]
84+
m = workspace.measurements
85+
first_set = True
86+
for src_name, target_name in object_list:
87+
src_objects = workspace.get_objects(src_name)
88+
target_labels = src_objects.segmented.copy()
89+
#
90+
# Reindex the labels of the old source image
91+
#
92+
target_labels[target_labels > max_label] = 0
93+
target_labels = label_indexes[target_labels]
94+
#
95+
# Make a new set of objects - retain the old set's unedited
96+
# segmentation for the new and generally try to copy stuff
97+
# from the old to the new.
98+
#
99+
target_objects = cellprofiler_core.object.Objects()
100+
target_objects.segmented = target_labels
101+
target_objects.unedited_segmented = src_objects.unedited_segmented
102+
#
103+
# Remove the filtered objects from the small_removed_segmented
104+
# if present. "small_removed_segmented" should really be
105+
# "filtered_removed_segmented".
106+
#
107+
small_removed = src_objects.small_removed_segmented.copy()
108+
small_removed[(target_labels == 0) & (src_objects.segmented != 0)] = 0
109+
target_objects.small_removed_segmented = small_removed
110+
if src_objects.has_parent_image:
111+
target_objects.parent_image = src_objects.parent_image
112+
workspace.object_set.add_objects(target_objects, target_name)
113+
114+
self.add_measurements(workspace, src_name, target_name)
115+
if self.show_window and first_set:
116+
workspace.display_data.src_objects_segmented = src_objects.segmented
117+
workspace.display_data.target_objects_segmented = target_objects.segmented
118+
workspace.display_data.dimensions = src_objects.dimensions
119+
first_set = False
120+
121+
def display(self, workspace, figure):
122+
"""Display what was filtered"""
123+
src_name = self.x_name.value
124+
src_objects_segmented = workspace.display_data.src_objects_segmented
125+
target_objects_segmented = workspace.display_data.target_objects_segmented
126+
dimensions = workspace.display_data.dimensions
127+
128+
target_name = self.y_name.value
129+
130+
figure.set_subplots((2, 2), dimensions=dimensions)
131+
132+
figure.subplot_imshow_labels(
133+
0, 0, src_objects_segmented, title="Original: %s" % src_name
134+
)
135+
136+
figure.subplot_imshow_labels(
137+
1,
138+
0,
139+
target_objects_segmented,
140+
title="Filtered: %s" % target_name,
141+
sharexy=figure.subplot(0, 0),
142+
)
143+
144+
pre = numpy.max(src_objects_segmented)
145+
post = numpy.max(target_objects_segmented)
146+
147+
statistics = [[pre], [post], [pre - post]]
148+
149+
figure.subplot_table(
150+
0,
151+
1,
152+
statistics,
153+
row_labels=(
154+
"Number of objects pre-filtering",
155+
"Number of objects post-filtering",
156+
"Number of objects removed",
157+
),
158+
)
159+
160+
def keep_by_string(self, workspace, src_objects):
161+
"""
162+
workspace - workspace passed into Run
163+
src_objects - the Objects instance to be filtered
164+
"""
165+
src_name = self.x_name.value
166+
m = workspace.measurements
167+
values = m.get_current_measurement(src_name, "Barcode_BarcodeCalled")
168+
# Is this structure still necessary or is it an artifact?
169+
# Could be just values == self.filter_out.value
170+
# Make an array of True
171+
hits = numpy.ones(len(values), bool)
172+
# Fill with False for those where we want to filter out
173+
hits[values == self.filter_out.value] = False
174+
# Get object numbers for things that are True
175+
indexes = numpy.argwhere(hits)[:, 0]
176+
# Objects are 1 counted, Python is 0 counted
177+
indexes = indexes + 1
178+
179+
return indexes

0 commit comments

Comments
 (0)