def measure_objects(self, operand, workspace): '''Performs the measurements on the requested objects''' objects = workspace.get_objects(operand.operand_objects.value) if objects.has_parent_image: area_occupied = np.sum(objects.segmented[objects.parent_image.mask]>0) perimeter = np.sum(outline(np.logical_and(objects.segmented != 0,objects.parent_image.mask))) total_area = np.sum(objects.parent_image.mask) else: area_occupied = np.sum(objects.segmented > 0) perimeter = np.sum(outline(objects.segmented) > 0) total_area = np.product(objects.segmented.shape) m = workspace.measurements m.add_image_measurement(F_AREA_OCCUPIED%(operand.operand_objects.value), np.array([area_occupied], dtype=float )) m.add_image_measurement(F_PERIMETER%(operand.operand_objects.value), np.array([perimeter], dtype=float )) m.add_image_measurement(F_TOTAL_AREA%(operand.operand_objects.value), np.array([total_area], dtype=float)) if operand.should_save_image.value: binary_pixels = objects.segmented > 0 output_image = cpi.Image(binary_pixels, parent_image = objects.parent_image) workspace.image_set.add(operand.image_name.value, output_image) return[[operand.operand_objects.value, str(area_occupied),str(perimeter),str(total_area)]]
def measure_objects(self, operand, workspace): '''Performs the measurements on the requested objects''' objects = workspace.get_objects(operand.operand_objects.value) if objects.has_parent_image: area_occupied = np.sum( objects.segmented[objects.parent_image.mask] > 0) perimeter = np.sum( outline( np.logical_and(objects.segmented != 0, objects.parent_image.mask))) total_area = np.sum(objects.parent_image.mask) else: area_occupied = np.sum(objects.segmented > 0) perimeter = np.sum(outline(objects.segmented) > 0) total_area = np.product(objects.segmented.shape) m = workspace.measurements m.add_image_measurement( F_AREA_OCCUPIED % (operand.operand_objects.value), np.array([area_occupied], dtype=float)) m.add_image_measurement(F_PERIMETER % (operand.operand_objects.value), np.array([perimeter], dtype=float)) m.add_image_measurement(F_TOTAL_AREA % (operand.operand_objects.value), np.array([total_area], dtype=float)) if operand.should_save_image.value: binary_pixels = objects.segmented > 0 output_image = cpi.Image(binary_pixels, parent_image=objects.parent_image) workspace.image_set.add(operand.image_name.value, output_image) return [[ operand.operand_objects.value, str(area_occupied), str(perimeter), str(total_area) ]]
def display(self, workspace, figure): """Create an informative display for the module""" import matplotlib original_labels = workspace.display_data.original_labels final_labels = workspace.display_data.final_labels mask = workspace.display_data.mask # # Create a composition of the final labels and mask # outlines = outline(original_labels) > 0 cm = figure.return_cmap() sm = matplotlib.cm.ScalarMappable(cmap=cm) # # Paint the labels in color # image = sm.to_rgba(final_labels, norm=False)[:, :, :3] image[final_labels == 0, :] = 0 # # Make the mask a dark gray # image[(final_labels == 0) & mask, :] = 0.25 # # Make the outlines of the kept objects the primary color # and the outlines of removed objects red. # final_outlines = outline(final_labels) > 0 original_color = ( np.array(cpprefs.get_secondary_outline_color()[0:3], float) / 255) final_color = np.array(cpprefs.get_primary_outline_color()[0:3], float) / 255 image[outlines, :] = original_color[np.newaxis, :] image[final_outlines, :] = final_color[np.newaxis, :] figure.set_subplots((2, 1)) figure.subplot_imshow_labels( 0, 0, original_labels, title=self.object_name.value, colormap=sm, ) figure.subplot_imshow_color( 1, 0, image, title=self.remaining_objects.value, sharexy=figure.subplot(0, 0), colormap=sm, )
def test_07_01_make_ijv_outlines(self): np.random.seed(70) x = cpo.Objects() ii, jj = np.mgrid[0:10, 0:20] masks = [(ii - ic) ** 2 + (jj - jc) ** 2 < r ** 2 for ic, jc, r in ((4, 5, 5), (4, 12, 5), (6, 8, 5))] i = np.hstack([ii[mask] for mask in masks]) j = np.hstack([jj[mask] for mask in masks]) v = np.hstack([[k + 1] * np.sum(mask) for k, mask in enumerate(masks)]) x.set_ijv(np.column_stack((i, j, v)), ii.shape) x.parent_image = cpi.Image(np.zeros((10, 20))) colors = np.random.uniform(size=(3, 3)).astype(np.float32) image = x.make_ijv_outlines(colors) i1 = [i for i, color in enumerate(colors) if np.all(color == image[0, 5, :])] self.assertEqual(len(i1), 1) i2 = [i for i, color in enumerate(colors) if np.all(color == image[0, 12, :])] self.assertEqual(len(i2), 1) i3 = [i for i, color in enumerate(colors) if np.all(color == image[-1, 8, :])] self.assertEqual(len(i3), 1) self.assertNotEqual(i1[0], i2[0]) self.assertNotEqual(i2[0], i3[0]) colors = colors[np.array([i1[0], i2[0], i3[0]])] outlines = np.zeros((10, 20, 3), np.float32) alpha = np.zeros((10, 20)) for i, (color, mask) in enumerate(zip(colors, masks)): my_outline = outline(mask) outlines[my_outline] += color alpha[my_outline] += 1 alpha[alpha == 0] = 1 outlines /= alpha[:, :, np.newaxis] np.testing.assert_almost_equal(outlines, image)
def test_01_02_object_with_cropping(self): labels = np.zeros((10, 10), int) labels[0:7, 3:8] = 1 mask = np.zeros((10, 10), bool) mask[1:9, 1:9] = True image = cpi.Image(np.zeros((10, 10)), mask=mask) area_occupied = np.sum(labels[mask]) perimeter = np.sum(outline(np.logical_and(labels, mask))) total_area = np.sum(mask) workspace = self.make_workspace(labels, image) module = workspace.module module.operands[0].operand_choice.value = "Objects" module.run(workspace) m = workspace.measurements def mn(x): return "AreaOccupied_%s_%s" % ( x, module.operands[0].operand_objects.value) self.assertEqual( m.get_current_measurement("Image", mn("AreaOccupied")), area_occupied) self.assertEqual(m.get_current_measurement("Image", mn("Perimeter")), perimeter) self.assertEqual(m.get_current_measurement("Image", mn("TotalArea")), total_area)
def test_04_02_masked_edge(self): # Regression test of issue #1115 labels = np.zeros((20, 50), int) labels[15:25, 15:25] = 1 image = np.random.uniform(size=labels.shape).astype(np.float32) # # Mask the edge of the object # mask = ~cpmo.outline(labels).astype(bool) m = cpmeas.Measurements() m.add(IMAGE_NAME, cpi.Image(image, mask=mask)) object_set = cpo.ObjectSet() o = cpo.Objects() o.segmented = labels object_set.add_objects(o, OBJECT_NAME) pipeline = P.Pipeline() def callback(caller, event): self.assertFalse(isinstance(event, P.RunExceptionEvent)) pipeline.add_listener(callback) module = MOI.MeasureObjectIntensity() module.module_num = 1 module.images[0].name.value = IMAGE_NAME module.objects[0].name.value = OBJECT_NAME pipeline.add_module(module) workspace = cpw.Workspace(pipeline, module, m, object_set, m, None) module.run(workspace)
def outlines(self): '''Get a mask of all the points on the border of objects''' if self._outlines is None: for i, labels in enumerate(self.labels): if i == 0: self._outlines = outline(labels) != 0 else: self._outlines = self._outlines | (outline(labels) != 0) if self.line_width > 1: hw = float(self.line_width) / 2 d = distance_transform_edt(~self._outlines) dti, dtj = np.where((d < hw + .5) & ~self._outlines) self._outlines = self._outlines.astype(np.float32) self._outlines[dti, dtj] = np.minimum(1, hw + .5 - d[dti, dtj]) return self._outlines.astype(np.float32)
def test_04_02_masked_edge(self): # Regression test of issue #1115 labels = np.zeros((20, 50), int) labels[15:25, 15:25] = 1 image = np.random.uniform(size=labels.shape).astype(np.float32) # # Mask the edge of the object # mask = ~ cpmo.outline(labels).astype(bool) m = cpmeas.Measurements() m.add(IMAGE_NAME, cpi.Image(image, mask=mask)) object_set = cpo.ObjectSet() o = cpo.Objects() o.segmented = labels object_set.add_objects(o, OBJECT_NAME) pipeline = P.Pipeline() def callback(caller, event): self.assertFalse(isinstance(event, P.RunExceptionEvent)) pipeline.add_listener(callback) module = MOI.MeasureObjectIntensity() module.module_num = 1 module.images[0].name.value = IMAGE_NAME module.objects[0].name.value = OBJECT_NAME pipeline.add_module(module) workspace = cpw.Workspace(pipeline, module, m, object_set, m, None) module.run(workspace)
def test_01_02_two_disjoint(self): x = numpy.array([ [0, 0, 0, 0, 0, 0, 0], [0, 0, 1, 1, 1, 0, 0], [0, 0, 1, 1, 1, 0, 0], [0, 0, 1, 1, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0], [0, 0, 2, 2, 2, 0, 0], [0, 0, 2, 2, 2, 0, 0], [0, 0, 2, 2, 2, 0, 0], [0, 0, 0, 0, 0, 0, 0], ]) e = numpy.array([ [0, 0, 0, 0, 0, 0, 0], [0, 0, 1, 1, 1, 0, 0], [0, 0, 1, 0, 1, 0, 0], [0, 0, 1, 1, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0], [0, 0, 2, 2, 2, 0, 0], [0, 0, 2, 0, 2, 0, 0], [0, 0, 2, 2, 2, 0, 0], [0, 0, 0, 0, 0, 0, 0], ]) result = OL.outline(x) self.assertTrue(numpy.all(result == e))
def outlines(self): '''Get a mask of all the points on the border of objects''' if self._outlines is None: for i, labels in enumerate(self.labels): if i == 0: self._outlines = outline(labels) != 0 else: self._outlines = self._outlines | (outline(labels) != 0) if self.line_width > 1: hw = float(self.line_width) / 2 d = distance_transform_edt(~ self._outlines) dti, dtj = np.where((d < hw+.5) & ~self._outlines) self._outlines = self._outlines.astype(np.float32) self._outlines[dti, dtj] = np.minimum(1, hw + .5 - d[dti, dtj]) return self._outlines.astype(np.float32)
def run(self, workspace): input_objects = workspace.object_set.get_objects(self.object_name.value) output_objects = cpo.Objects() output_objects.segmented = self.do_labels(input_objects.segmented) if (input_objects.has_small_removed_segmented and self.operation not in (O_EXPAND, O_EXPAND_INF, O_DIVIDE)): output_objects.small_removed_segmented = \ self.do_labels(input_objects.small_removed_segmented) if (input_objects.has_unedited_segmented and self.operation not in (O_EXPAND, O_EXPAND_INF, O_DIVIDE)): output_objects.unedited_segmented = \ self.do_labels(input_objects.unedited_segmented) workspace.object_set.add_objects(output_objects, self.output_object_name.value) add_object_count_measurements(workspace.measurements, self.output_object_name.value, np.max(output_objects.segmented)) add_object_location_measurements(workspace.measurements, self.output_object_name.value, output_objects.segmented) if self.wants_outlines.value: outline_image = cpi.Image(outline(output_objects.segmented) > 0, parent_image=input_objects.parent_image) workspace.image_set.add(self.outlines_name.value, outline_image) if self.show_window: workspace.display_data.input_objects_segmented = input_objects.segmented workspace.display_data.output_objects_segmented = output_objects.segmented
def test_02_04_edge(self): x = numpy.array([[0, 0, 1, 1, 1, 0, 0], [0, 0, 1, 1, 1, 0, 0], [0, 0, 1, 1, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0]]) e = numpy.array([[0, 0, 1, 1, 1, 0, 0], [0, 0, 1, 0, 1, 0, 0], [0, 0, 1, 1, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0]]) result = OL.outline(x) self.assertTrue(numpy.all(result == e))
def display(self, workspace, figure): '''Create an informative display for the module''' import matplotlib from cellprofiler.gui.tools import renumber_labels_for_display original_labels = workspace.display_data.original_labels final_labels = workspace.display_data.final_labels mask = workspace.display_data.mask # # Create a composition of the final labels and mask # final_labels = renumber_labels_for_display(final_labels) outlines = outline(original_labels) > 0 cm = matplotlib.cm.get_cmap(cpprefs.get_default_colormap()) sm = matplotlib.cm.ScalarMappable(cmap=cm) # # Paint the labels in color # image = sm.to_rgba(final_labels)[:, :, :3] image[final_labels == 0, :] = 0 # # Make the mask a dark gray # image[(final_labels == 0) & mask, :] = .25 # # Make the outlines of the kept objects the primary color # and the outlines of removed objects red. # final_outlines = outline(final_labels) > 0 original_color = np.array(cpprefs.get_secondary_outline_color(), float) / 255 final_color = np.array(cpprefs.get_primary_outline_color(), float) / 255 image[outlines, :] = original_color[np.newaxis, :] image[final_outlines, :] = final_color[np.newaxis, :] figure.set_subplots((2, 1)) figure.subplot_imshow_labels(0, 0, original_labels, title=self.object_name.value) figure.subplot_imshow_color(1, 0, image, title=self.remaining_objects.value, sharexy=figure.subplot(0, 0))
def test_02_04_edge(self): x = numpy.array([[ 0,0,1,1,1,0,0], [ 0,0,1,1,1,0,0], [ 0,0,1,1,1,0,0], [ 0,0,0,0,0,0,0]]) e = numpy.array([[ 0,0,1,1,1,0,0], [ 0,0,1,0,1,0,0], [ 0,0,1,1,1,0,0], [ 0,0,0,0,0,0,0]]) result = OL.outline(x) self.assertTrue(numpy.all(result==e))
def test_01_03_touching(self): x = numpy.array([[0, 0, 0, 0, 0, 0, 0], [0, 0, 1, 1, 1, 0, 0], [0, 0, 1, 1, 1, 0, 0], [0, 0, 1, 1, 1, 0, 0], [0, 0, 2, 2, 2, 0, 0], [0, 0, 2, 2, 2, 0, 0], [0, 0, 2, 2, 2, 0, 0], [0, 0, 0, 0, 0, 0, 0]]) e = numpy.array([[0, 0, 0, 0, 0, 0, 0], [0, 0, 1, 1, 1, 0, 0], [0, 0, 1, 0, 1, 0, 0], [0, 0, 1, 1, 1, 0, 0], [0, 0, 2, 2, 2, 0, 0], [0, 0, 2, 0, 2, 0, 0], [0, 0, 2, 2, 2, 0, 0], [0, 0, 0, 0, 0, 0, 0]]) result = OL.outline(x) self.assertTrue(numpy.all(result == e))
def display(self, workspace, figure): '''Create an informative display for the module''' import matplotlib from cellprofiler.gui.cpfigure import renumber_labels_for_display original_labels = workspace.display_data.original_labels final_labels = workspace.display_data.final_labels mask = workspace.display_data.mask # # Create a composition of the final labels and mask # final_labels = renumber_labels_for_display(final_labels) outlines = outline(original_labels) > 0 cm = matplotlib.cm.get_cmap(cpprefs.get_default_colormap()) sm = matplotlib.cm.ScalarMappable(cmap = cm) # # Paint the labels in color # image = sm.to_rgba(final_labels)[:,:,:3] image[final_labels == 0,:] = 0 # # Make the mask a dark gray # image[(final_labels == 0) & mask,:] = .25 # # Make the outlines of the kept objects the primary color # and the outlines of removed objects red. # final_outlines = outline(final_labels) > 0 original_color = np.array(cpprefs.get_secondary_outline_color(), float) / 255 final_color = np.array(cpprefs.get_primary_outline_color(), float) / 255 image[outlines, :] = original_color[np.newaxis, :] image[final_outlines, :] = final_color[np.newaxis, :] figure.set_subplots((2, 1)) figure.subplot_imshow_labels(0, 0, original_labels, title = self.object_name.value) figure.subplot_imshow_color(1, 0, image, title = self.remaining_objects.value, sharexy = figure.subplot(0,0))
def measure_images(self, operand, workspace): '''Performs measurements on the requested images''' image = workspace.image_set.get_image(operand.binary_name.value, must_be_binary=True) area_occupied = np.sum(image.pixel_data > 0) perimeter = np.sum(outline(image.pixel_data) > 0) total_area = np.prod(np.shape(image.pixel_data)) m = workspace.measurements m.add_image_measurement(F_AREA_OCCUPIED % operand.binary_name.value, np.array([area_occupied], dtype=float)) m.add_image_measurement(F_PERIMETER % operand.binary_name.value, np.array([perimeter], dtype=float)) m.add_image_measurement(F_TOTAL_AREA % operand.binary_name.value, np.array([total_area], dtype=float)) return [[operand.binary_name.value, str(area_occupied), str(perimeter), str(total_area)]]
def test_07_01_outlines(self): """Create an outline of the resulting objects""" labels = np.zeros((10, 10), int) labels[4, 4] = 1 i, j = np.mgrid[0:10, 0:10] - 4 expected = (i ** 2 + j ** 2 <= 4).astype(int) expected_outlines = outline(expected) workspace, module = self.make_workspace(labels, E.O_EXPAND, 2, wants_outlines=True) module.run(workspace) objects = workspace.object_set.get_objects(OUTPUT_NAME) self.assertTrue(np.all(objects.segmented == expected)) self.assertTrue(OUTLINES_NAME in workspace.image_set.names) outlines = workspace.image_set.get_image(OUTLINES_NAME).pixel_data self.assertTrue(np.all(outlines == expected_outlines))
def measure_images(self,operand,workspace): '''Performs measurements on the requested images''' image = workspace.image_set.get_image(operand.binary_name.value, must_be_binary = True) area_occupied = np.sum(image.pixel_data > 0) perimeter = np.sum(outline(image.pixel_data) > 0) total_area = np.prod(np.shape(image.pixel_data)) m = workspace.measurements m.add_image_measurement(F_AREA_OCCUPIED%(operand.binary_name.value), np.array([area_occupied], dtype=float)) m.add_image_measurement(F_PERIMETER%(operand.binary_name.value), np.array([perimeter], dtype=float)) m.add_image_measurement(F_TOTAL_AREA%(operand.binary_name.value), np.array([total_area], dtype=float)) return [[operand.binary_name.value, str(area_occupied), str(perimeter), str(total_area)]]
def test_07_01_outlines(self): '''Create an outline of the resulting objects''' labels = np.zeros((10,10), int) labels[4,4] = 1 i,j = np.mgrid[0:10,0:10]-4 expected = (i**2 + j**2 <=4).astype(int) expected_outlines = outline(expected) workspace, module = self.make_workspace(labels, E.O_EXPAND, 2, wants_outlines = True) module.run(workspace) objects = workspace.object_set.get_objects(OUTPUT_NAME) self.assertTrue(np.all(objects.segmented == expected)) self.assertTrue(OUTLINES_NAME in workspace.image_set.get_names()) outlines = workspace.image_set.get_image(OUTLINES_NAME).pixel_data self.assertTrue(np.all(outlines == expected_outlines))
def test_01_01_single(self): x = numpy.array([ [0, 0, 0, 0, 0, 0, 0], [0, 0, 1, 1, 1, 0, 0], [0, 0, 1, 1, 1, 0, 0], [0, 0, 1, 1, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0], ]) e = numpy.array([ [0, 0, 0, 0, 0, 0, 0], [0, 0, 1, 1, 1, 0, 0], [0, 0, 1, 0, 1, 0, 0], [0, 0, 1, 1, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0], ]) result = OL.outline(x) self.assertTrue(numpy.all(result == e))
def make_ijv_outlines(self, colors): '''Make ijv-style color outlines Make outlines, coloring each object differently to distinguish between objects that might overlap. colors: a N x 3 color map to be used to color the outlines ''' # # Get planes of non-overlapping objects. The idea here is to use # the most similar colors in the color space for objects that # don't overlap. # all_labels = [(outline(label), indexes) for label, indexes in self.get_labels()] image = np.zeros(list(all_labels[0][0].shape) + [3], np.float32) # # Find out how many unique labels in each # counts = [np.sum(np.unique(l) != 0) for l, _ in all_labels] if len(counts) == 1 and counts[0] == 0: return image if len(colors) < len(all_labels): # Have to color 2 planes using the same color! # There's some chance that overlapping objects will get # the same color. Give me more colors to work with please. colors = np.vstack([colors] * (1 + len(all_labels) / len(colors))) r = np.random.mtrand.RandomState() alpha = np.zeros(all_labels[0][0].shape, np.float32) order = np.lexsort([counts]) label_colors = [] for idx, i in enumerate(order): max_available = len(colors) / (len(all_labels) - idx) ncolors = min(counts[i], max_available) my_colors = colors[:ncolors] colors = colors[ncolors:] my_colors = my_colors[r.permutation(np.arange(ncolors))] my_labels, indexes = all_labels[i] color_idx = np.zeros(np.max(indexes) + 1, int) color_idx[indexes] = np.arange(len(indexes)) % ncolors image[my_labels != 0,:] += \ my_colors[color_idx[my_labels[my_labels != 0]],:] alpha[my_labels != 0] += 1 image[alpha > 0, :] /= alpha[alpha > 0][:, np.newaxis] return image
def test_02_05_diagonal(self): x = numpy.array([ [0, 0, 0, 0, 0, 0, 0], [0, 0, 1, 1, 1, 0, 0], [0, 1, 1, 1, 1, 0, 0], [0, 1, 1, 1, 1, 0, 0], [0, 0, 1, 1, 0, 0, 0], ]) e = numpy.array([ [0, 0, 0, 0, 0, 0, 0], [0, 0, 1, 1, 1, 0, 0], [0, 1, 1, 0, 1, 0, 0], [0, 1, 1, 1, 1, 0, 0], [0, 0, 1, 1, 0, 0, 0], ]) result = OL.outline(x) self.assertTrue(numpy.all(result == e))
def make_ijv_outlines(self, colors): '''Make ijv-style color outlines Make outlines, coloring each object differently to distinguish between objects that might overlap. colors: a N x 3 color map to be used to color the outlines ''' # # Get planes of non-overlapping objects. The idea here is to use # the most similar colors in the color space for objects that # don't overlap. # all_labels = [(outline(label), indexes) for label, indexes in self.get_labels()] image = np.zeros(list(all_labels[0][0].shape) + [3], np.float32) # # Find out how many unique labels in each # counts = [np.sum(np.unique(l) != 0) for l, _ in all_labels] if len(counts) == 1 and counts[0] == 0: return image if len(colors) < len(all_labels): # Have to color 2 planes using the same color! # There's some chance that overlapping objects will get # the same color. Give me more colors to work with please. colors = np.vstack([colors] * (1 + len(all_labels) / len(colors))) r = np.random.mtrand.RandomState() alpha = np.zeros(all_labels[0][0].shape, np.float32) order = np.lexsort([counts]) label_colors = [] for idx,i in enumerate(order): max_available = len(colors) / (len(all_labels) - idx) ncolors = min(counts[i], max_available) my_colors = colors[:ncolors] colors = colors[ncolors:] my_colors = my_colors[r.permutation(np.arange(ncolors))] my_labels, indexes = all_labels[i] color_idx = np.zeros(np.max(indexes) + 1, int) color_idx[indexes] = np.arange(len(indexes)) % ncolors image[my_labels != 0,:] += \ my_colors[color_idx[my_labels[my_labels != 0]],:] alpha[my_labels != 0] += 1 image[alpha > 0, :] /= alpha[alpha > 0][:, np.newaxis] return image
def test_01_03_touching(self): x = numpy.array([[ 0,0,0,0,0,0,0], [ 0,0,1,1,1,0,0], [ 0,0,1,1,1,0,0], [ 0,0,1,1,1,0,0], [ 0,0,2,2,2,0,0], [ 0,0,2,2,2,0,0], [ 0,0,2,2,2,0,0], [ 0,0,0,0,0,0,0]]) e = numpy.array([[ 0,0,0,0,0,0,0], [ 0,0,1,1,1,0,0], [ 0,0,1,0,1,0,0], [ 0,0,1,1,1,0,0], [ 0,0,2,2,2,0,0], [ 0,0,2,0,2,0,0], [ 0,0,2,2,2,0,0], [ 0,0,0,0,0,0,0]]) result = OL.outline(x) self.assertTrue(numpy.all(result==e))
def run(self, workspace): image_name = self.image_name.value objects_name = self.objects_name.value outlines_name = self.outlines_name.value image = workspace.image_set.get_image(image_name) pixel_data = image.pixel_data labels = workspace.interaction_request( self, pixel_data, workspace.measurements.image_set_number) if labels is None: # User cancelled. Soldier on as best we can. workspace.cancel_request() labels = np.zeros(pixel_data.shape[:2], int) objects = cpo.Objects() objects.segmented = labels workspace.object_set.add_objects(objects, objects_name) ################## # # Add measurements # m = workspace.measurements # # The object count # object_count = np.max(labels) I.add_object_count_measurements(m, objects_name, object_count) # # The object locations # I.add_object_location_measurements(m, objects_name, labels) # # Outlines if we want them # if self.wants_outlines: outlines_name = self.outlines_name.value outlines = outline(labels) outlines_image = cpi.Image(outlines.astype(bool)) workspace.image_set.add(outlines_name, outlines_image) workspace.display_data.labels = labels workspace.display_data.pixel_data = pixel_data
def test_01_02_object_with_cropping(self): labels = np.zeros((10,10),int) labels[0:7,3:8] = 1 mask = np.zeros((10,10),bool) mask[1:9,1:9] = True image = cpi.Image(np.zeros((10,10)),mask=mask) area_occupied = np.sum(labels[mask]) perimeter = np.sum(outline(np.logical_and(labels,mask))) total_area = np.sum(mask) workspace = self.make_workspace(labels, image) module = workspace.module module.operands[0].operand_choice.value = "Objects" module.run(workspace) m = workspace.measurements def mn(x): return "AreaOccupied_%s_%s"%(x, module.operands[0].operand_objects.value) self.assertEqual(m.get_current_measurement("Image",mn("AreaOccupied")), area_occupied) self.assertEqual(m.get_current_measurement("Image",mn("Perimeter")), perimeter) self.assertEqual(m.get_current_measurement("Image",mn("TotalArea")), total_area)
def test_01_02_two_disjoint(self): x = numpy.array([[ 0,0,0,0,0,0,0], [ 0,0,1,1,1,0,0], [ 0,0,1,1,1,0,0], [ 0,0,1,1,1,0,0], [ 0,0,0,0,0,0,0], [ 0,0,2,2,2,0,0], [ 0,0,2,2,2,0,0], [ 0,0,2,2,2,0,0], [ 0,0,0,0,0,0,0]]) e = numpy.array([[ 0,0,0,0,0,0,0], [ 0,0,1,1,1,0,0], [ 0,0,1,0,1,0,0], [ 0,0,1,1,1,0,0], [ 0,0,0,0,0,0,0], [ 0,0,2,2,2,0,0], [ 0,0,2,0,2,0,0], [ 0,0,2,2,2,0,0], [ 0,0,0,0,0,0,0]]) result = OL.outline(x) self.assertTrue(numpy.all(result==e))
def run(self, workspace): '''Find the outlines on the current image set workspace - The workspace contains pipeline - instance of cpp for this run image_set - the images in the image set being processed object_set - the objects (labeled masks) in this image set measurements - the measurements for this run frame - the parent frame to whatever frame is created. None means don't draw. ''' gridding = workspace.get_grid(self.grid_name.value) if self.shape_choice == SHAPE_RECTANGLE: labels = self.run_rectangle(workspace, gridding) elif self.shape_choice == SHAPE_CIRCLE_FORCED: labels = self.run_forced_circle(workspace, gridding) elif self.shape_choice == SHAPE_CIRCLE_NATURAL: labels = self.run_natural_circle(workspace, gridding) elif self.shape_choice == SHAPE_NATURAL: labels = self.run_natural(workspace, gridding) objects = cpo.Objects() objects.segmented = labels object_count = gridding.rows * gridding.columns workspace.object_set.add_objects(objects, self.output_objects_name.value) add_object_location_measurements(workspace.measurements, self.output_objects_name.value, labels, object_count) add_object_count_measurements(workspace.measurements, self.output_objects_name.value, object_count) if self.wants_outlines: outlines = outline(labels != 0) outline_image = cpi.Image(outlines) workspace.image_set.add(self.outlines_name.value, outline_image) if self.show_window: workspace.display_data.gridding = gridding workspace.display_data.labels = labels
def draw_outlines(self, pixel_data, labels): '''Draw a color image that shows the objects pixel_data - image, either b & w or color labels - labels for image returns - color image of same size as pixel_data ''' from cellprofiler.gui.cpfigure import renumber_labels_for_display import matplotlib labels = renumber_labels_for_display(labels) outlines = outline(labels) if pixel_data.ndim == 3: image = pixel_data.copy() else: image = np.dstack([pixel_data] * 3) # # make labeled pixels a grayscale times the label color # cm = matplotlib.cm.get_cmap(cpprefs.get_default_colormap()) sm = matplotlib.cm.ScalarMappable(cmap=cm) labels_image = sm.to_rgba(labels)[:, :, :3] lmask = labels > 0 gray = (image[lmask, 0] + image[lmask, 1] + image[lmask, 2]) / 3 for i in range(3): image[lmask, i] = gray * labels_image[lmask, i] # # Make the outline pixels a solid color # outlines_image = sm.to_rgba(outlines)[:, :, :3] image[outlines > 0, :] = outlines_image[outlines > 0, :] return image
def draw_outlines(self, pixel_data, labels): '''Draw a color image that shows the objects pixel_data - image, either b & w or color labels - labels for image returns - color image of same size as pixel_data ''' from cellprofiler.gui.tools import renumber_labels_for_display import matplotlib labels = renumber_labels_for_display(labels) outlines = outline(labels) if pixel_data.ndim == 3: image = pixel_data.copy() else: image = np.dstack([pixel_data] * 3) # # make labeled pixels a grayscale times the label color # cm = matplotlib.cm.get_cmap(cpprefs.get_default_colormap()) sm = matplotlib.cm.ScalarMappable(cmap=cm) labels_image = sm.to_rgba(labels)[:, :, :3] lmask = labels > 0 gray = (image[lmask, 0] + image[lmask, 1] + image[lmask, 2]) / 3 for i in range(3): image[lmask, i] = gray * labels_image[lmask, i] # # Make the outline pixels a solid color # outlines_image = sm.to_rgba(outlines)[:, :, :3] image[outlines > 0, :] = outlines_image[outlines > 0, :] return image
def test_00_00_zeros(self): x = numpy.zeros((10, 10), int) result = OL.outline(x) self.assertTrue(numpy.all(x == result))
def run(self, workspace): '''Run the module on an image set''' object_name = self.object_name.value remaining_object_name = self.remaining_objects.value original_objects = workspace.object_set.get_objects(object_name) if self.mask_choice == MC_IMAGE: mask = workspace.image_set.get_image(self.masking_image.value, must_be_binary = True) mask = mask.pixel_data else: masking_objects = workspace.object_set.get_objects( self.masking_objects.value) mask = masking_objects.segmented > 0 if self.wants_inverted_mask: mask = ~mask # # Load the labels # labels = original_objects.segmented.copy() nobjects = np.max(labels) # # Resize the mask to cover the objects # mask, m1 = cpo.size_similarly(labels, mask) mask[~m1] = False # # Apply the mask according to the overlap choice. # if nobjects == 0: pass elif self.overlap_choice == P_MASK: labels = labels * mask else: pixel_counts = fix(scind.sum(mask, labels, np.arange(1, nobjects+1,dtype=np.int32))) if self.overlap_choice == P_KEEP: keep = pixel_counts > 0 else: total_pixels = fix(scind.sum(np.ones(labels.shape), labels, np.arange(1, nobjects+1,dtype=np.int32))) if self.overlap_choice == P_REMOVE: keep = pixel_counts == total_pixels elif self.overlap_choice == P_REMOVE_PERCENTAGE: fraction = self.overlap_fraction.value keep = pixel_counts / total_pixels >= fraction else: raise NotImplementedError("Unknown overlap-handling choice: %s", self.overlap_choice.value) keep = np.hstack(([False], keep)) labels[~ keep[labels]] = 0 # # Renumber the labels matrix if requested # if self.retain_or_renumber == R_RENUMBER: unique_labels = np.unique(labels[labels!=0]) indexer = np.zeros(nobjects+1, int) indexer[unique_labels] = np.arange(1, len(unique_labels)+1) labels = indexer[labels] parent_objects = unique_labels else: parent_objects = np.arange(1, nobjects+1) # # Add the objects # remaining_objects = cpo.Objects() remaining_objects.segmented = labels remaining_objects.unedited_segmented = original_objects.unedited_segmented workspace.object_set.add_objects(remaining_objects, remaining_object_name) # # Add measurements # m = workspace.measurements m.add_measurement(remaining_object_name, I.FF_PARENT % object_name, parent_objects) if np.max(original_objects.segmented) == 0: child_count = np.array([],int) else: child_count = fix(scind.sum(labels, original_objects.segmented, np.arange(1, nobjects+1,dtype=np.int32))) child_count = (child_count > 0).astype(int) m.add_measurement(object_name, I.FF_CHILDREN_COUNT % remaining_object_name, child_count) if self.retain_or_renumber == R_RETAIN: remaining_object_count = nobjects else: remaining_object_count = len(unique_labels) I.add_object_count_measurements(m, remaining_object_name, remaining_object_count) I.add_object_location_measurements(m, remaining_object_name, labels) # # Add an outline if asked to do so # if self.wants_outlines.value: outline_image = cpi.Image(outline(labels) > 0, parent_image = original_objects.parent_image) workspace.image_set.add(self.outlines_name.value, outline_image) # # Save the input, mask and output images for display # if self.show_window: workspace.display_data.original_labels = original_objects.segmented workspace.display_data.final_labels = labels workspace.display_data.mask = mask
def run(self, workspace): if self.show_window: workspace.display_data.col_labels = ("Image", "Object", "Feature", "Mean", "Median", "STD") workspace.display_data.statistics = statistics = [] for image_name in [img.name for img in self.images]: image = workspace.image_set.get_image(image_name.value, must_be_grayscale=True) for object_name in [obj.name for obj in self.objects]: # Need to refresh image after each iteration... img = image.pixel_data if image.has_mask: masked_image = img.copy() masked_image[~image.mask] = 0 else: masked_image = img objects = workspace.object_set.get_objects(object_name.value) nobjects = objects.count integrated_intensity = np.zeros((nobjects,)) integrated_intensity_edge = np.zeros((nobjects,)) mean_intensity = np.zeros((nobjects,)) mean_intensity_edge = np.zeros((nobjects,)) std_intensity = np.zeros((nobjects,)) std_intensity_edge = np.zeros((nobjects,)) min_intensity = np.zeros((nobjects,)) min_intensity_edge = np.zeros((nobjects,)) max_intensity = np.zeros((nobjects,)) max_intensity_edge = np.zeros((nobjects,)) mass_displacement = np.zeros((nobjects,)) lower_quartile_intensity = np.zeros((nobjects,)) median_intensity = np.zeros((nobjects,)) mad_intensity = np.zeros((nobjects,)) upper_quartile_intensity = np.zeros((nobjects,)) cmi_x = np.zeros((nobjects,)) cmi_y = np.zeros((nobjects,)) max_x = np.zeros((nobjects,)) max_y = np.zeros((nobjects,)) for labels, lindexes in objects.get_labels(): lindexes = lindexes[lindexes != 0] labels, img = cpo.crop_labels_and_image(labels, img) _, masked_image = cpo.crop_labels_and_image(labels, masked_image) outlines = cpmo.outline(labels) if image.has_mask: _, mask = cpo.crop_labels_and_image(labels, image.mask) masked_labels = labels.copy() masked_labels[~mask] = 0 masked_outlines = outlines.copy() masked_outlines[~mask] = 0 else: masked_labels = labels masked_outlines = outlines lmask = masked_labels > 0 & np.isfinite(img) # Ignore NaNs, Infs has_objects = np.any(lmask) if has_objects: limg = img[lmask] llabels = labels[lmask] mesh_y, mesh_x = np.mgrid[0 : masked_image.shape[0], 0 : masked_image.shape[1]] mesh_x = mesh_x[lmask] mesh_y = mesh_y[lmask] lcount = fix(nd.sum(np.ones(len(limg)), llabels, lindexes)) integrated_intensity[lindexes - 1] = fix(nd.sum(limg, llabels, lindexes)) mean_intensity[lindexes - 1] = integrated_intensity[lindexes - 1] / lcount std_intensity[lindexes - 1] = np.sqrt( fix(nd.mean((limg - mean_intensity[llabels - 1]) ** 2, llabels, lindexes)) ) min_intensity[lindexes - 1] = fix(nd.minimum(limg, llabels, lindexes)) max_intensity[lindexes - 1] = fix(nd.maximum(limg, llabels, lindexes)) # Compute the position of the intensity maximum max_position = np.array(fix(nd.maximum_position(limg, llabels, lindexes)), dtype=int) max_position = np.reshape(max_position, (max_position.shape[0],)) max_x[lindexes - 1] = mesh_x[max_position] max_y[lindexes - 1] = mesh_y[max_position] # The mass displacement is the distance between the center # of mass of the binary image and of the intensity image. The # center of mass is the average X or Y for the binary image # and the sum of X or Y * intensity / integrated intensity cm_x = fix(nd.mean(mesh_x, llabels, lindexes)) cm_y = fix(nd.mean(mesh_y, llabels, lindexes)) i_x = fix(nd.sum(mesh_x * limg, llabels, lindexes)) i_y = fix(nd.sum(mesh_y * limg, llabels, lindexes)) cmi_x[lindexes - 1] = i_x / integrated_intensity[lindexes - 1] cmi_y[lindexes - 1] = i_y / integrated_intensity[lindexes - 1] diff_x = cm_x - cmi_x[lindexes - 1] diff_y = cm_y - cmi_y[lindexes - 1] mass_displacement[lindexes - 1] = np.sqrt(diff_x * diff_x + diff_y * diff_y) # # Sort the intensities by label, then intensity. # For each label, find the index above and below # the 25%, 50% and 75% mark and take the weighted # average. # order = np.lexsort((limg, llabels)) areas = lcount.astype(int) indices = np.cumsum(areas) - areas for dest, fraction in ( (lower_quartile_intensity, 1.0 / 4.0), (median_intensity, 1.0 / 2.0), (upper_quartile_intensity, 3.0 / 4.0), ): qindex = indices.astype(float) + areas * fraction qfraction = qindex - np.floor(qindex) qindex = qindex.astype(int) qmask = qindex < indices + areas - 1 qi = qindex[qmask] qf = qfraction[qmask] dest[lindexes[qmask] - 1] = limg[order[qi]] * (1 - qf) + limg[order[qi + 1]] * qf # # In some situations (e.g. only 3 points), there may # not be an upper bound. # qmask = (~qmask) & (areas > 0) dest[lindexes[qmask] - 1] = limg[order[qindex[qmask]]] # # Once again, for the MAD # madimg = np.abs(limg - median_intensity[llabels - 1]) order = np.lexsort((madimg, llabels)) qindex = indices.astype(float) + areas / 2.0 qfraction = qindex - np.floor(qindex) qindex = qindex.astype(int) qmask = qindex < indices + areas - 1 qi = qindex[qmask] qf = qfraction[qmask] mad_intensity[lindexes[qmask] - 1] = madimg[order[qi]] * (1 - qf) + madimg[order[qi + 1]] * qf qmask = (~qmask) & (areas > 0) mad_intensity[lindexes[qmask] - 1] = madimg[order[qindex[qmask]]] emask = masked_outlines > 0 eimg = img[emask] elabels = labels[emask] has_edge = len(eimg) > 0 if has_edge: ecount = fix(nd.sum(np.ones(len(eimg)), elabels, lindexes)) integrated_intensity_edge[lindexes - 1] = fix(nd.sum(eimg, elabels, lindexes)) mean_intensity_edge[lindexes - 1] = integrated_intensity_edge[lindexes - 1] / ecount std_intensity_edge[lindexes - 1] = np.sqrt( fix(nd.mean((eimg - mean_intensity_edge[elabels - 1]) ** 2, elabels, lindexes)) ) min_intensity_edge[lindexes - 1] = fix(nd.minimum(eimg, elabels, lindexes)) max_intensity_edge[lindexes - 1] = fix(nd.maximum(eimg, elabels, lindexes)) m = workspace.measurements for category, feature_name, measurement in ( (INTENSITY, INTEGRATED_INTENSITY, integrated_intensity), (INTENSITY, MEAN_INTENSITY, mean_intensity), (INTENSITY, STD_INTENSITY, std_intensity), (INTENSITY, MIN_INTENSITY, min_intensity), (INTENSITY, MAX_INTENSITY, max_intensity), (INTENSITY, INTEGRATED_INTENSITY_EDGE, integrated_intensity_edge), (INTENSITY, MEAN_INTENSITY_EDGE, mean_intensity_edge), (INTENSITY, STD_INTENSITY_EDGE, std_intensity_edge), (INTENSITY, MIN_INTENSITY_EDGE, min_intensity_edge), (INTENSITY, MAX_INTENSITY_EDGE, max_intensity_edge), (INTENSITY, MASS_DISPLACEMENT, mass_displacement), (INTENSITY, LOWER_QUARTILE_INTENSITY, lower_quartile_intensity), (INTENSITY, MEDIAN_INTENSITY, median_intensity), (INTENSITY, MAD_INTENSITY, mad_intensity), (INTENSITY, UPPER_QUARTILE_INTENSITY, upper_quartile_intensity), (C_LOCATION, LOC_CMI_X, cmi_x), (C_LOCATION, LOC_CMI_Y, cmi_y), (C_LOCATION, LOC_MAX_X, max_x), (C_LOCATION, LOC_MAX_Y, max_y), ): measurement_name = "%s_%s_%s" % (category, feature_name, image_name.value) m.add_measurement(object_name.value, measurement_name, measurement) if self.show_window and len(measurement) > 0: statistics.append( ( image_name.value, object_name.value, feature_name, np.round(np.mean(measurement), 3), np.round(np.median(measurement), 3), np.round(np.std(measurement), 3), ) )
def test_02_01_forced_location(self): d = D.DefineGrid() d.ordering.value = D.NUM_BY_COLUMNS # # Grid with x spacing = 10, y spacing = 20 # diameter = 6 gridding =d.build_grid_info(15,25,1,1,25,45,2,2) expected = self.make_rectangular_grid(gridding) i,j = np.mgrid[0:expected.shape[0],0:expected.shape[1]] ispot, jspot = np.mgrid[0:gridding.rows, 0:gridding.columns] y_locations = np.zeros(np.max(gridding.spot_table)+1,int) y_locations[gridding.spot_table.flatten()] = \ gridding.y_locations[ispot.flatten()] x_locations = np.zeros(np.max(gridding.spot_table)+1,int) x_locations[gridding.spot_table.flatten()] =\ gridding.x_locations[jspot.flatten()] idist = (i - y_locations[expected]) jdist = (j - x_locations[expected]) expected[idist**2 + jdist**2 > (float(diameter + 1)/2)**2] = 0 workspace, module = self.make_workspace(gridding) self.assertTrue(isinstance(module, I.IdentifyObjectsInGrid)) module.diameter_choice.value = I.AM_MANUAL module.diameter.value = diameter module.shape_choice.value = I.SHAPE_CIRCLE_FORCED module.wants_outlines.value = True module.run(workspace) labels = workspace.object_set.get_objects(OUTPUT_OBJECTS_NAME).segmented self.assertTrue(np.all(labels == expected[0:labels.shape[0],0:labels.shape[1]])) # # Check measurements # m = workspace.measurements self.assertTrue(isinstance(m, cpmeas.Measurements)) xm = m.get_current_measurement(OUTPUT_OBJECTS_NAME, 'Location_Center_X') self.assertTrue(np.all(xm == x_locations[1:])) ym = m.get_current_measurement(OUTPUT_OBJECTS_NAME, 'Location_Center_Y') self.assertTrue(np.all(ym == y_locations[1:])) count = m.get_current_image_measurement('Count_%s'%OUTPUT_OBJECTS_NAME) self.assertEqual(count, gridding.rows * gridding.columns) columns = module.get_measurement_columns(workspace.pipeline) self.assertEqual(len(columns), 4) count_feature = 'Count_%s'%OUTPUT_OBJECTS_NAME self.assertTrue(all([column[0] == ("Image" if column[1] == count_feature else OUTPUT_OBJECTS_NAME) for column in columns])) self.assertTrue(all([column[1] in ('Location_Center_X','Location_Center_Y', count_feature,'Number_Object_Number') for column in columns])) # # Check the outlines # outlines = workspace.image_set.get_image(OUTLINES_NAME) outlines = outlines.pixel_data expected_outlines = outline(expected) self.assertTrue(np.all(outlines == (expected_outlines[0:outlines.shape[0],0:outlines.shape[1]]>0))) # # Check the measurements # categories = list(module.get_categories(None, OUTPUT_OBJECTS_NAME)) self.assertEqual(len(categories), 2) categories.sort() self.assertEqual(categories[0], "Location") self.assertEqual(categories[1], "Number") categories = module.get_categories(None, cpmeas.IMAGE) self.assertEqual(len(categories), 1) self.assertEqual(categories[0], "Count") measurements = module.get_measurements(None, cpmeas.IMAGE, "Count") self.assertEqual(len(measurements), 1) self.assertEqual(measurements[0], OUTPUT_OBJECTS_NAME) measurements = module.get_measurements(None, OUTPUT_OBJECTS_NAME, "Location") self.assertEqual(len(measurements), 2) self.assertTrue(all(m in ('Center_X','Center_Y') for m in measurements)) self.assertTrue('Center_X' in measurements) self.assertTrue('Center_Y' in measurements) measurements = module.get_measurements(None, OUTPUT_OBJECTS_NAME, "Number") self.assertEqual(len(measurements),1) self.assertEqual(measurements[0], "Object_Number")
def calculate_minimum_distances(self, workspace, parent_name): '''Calculate the distance from child center to parent perimeter''' meas = workspace.measurements assert isinstance(meas,cpmeas.Measurements) sub_object_name = self.sub_object_name.value parents = workspace.object_set.get_objects(parent_name) children = workspace.object_set.get_objects(sub_object_name) parents_of = self.get_parents_of(workspace, parent_name) if len(parents_of) == 0: dist = np.zeros((0,)) elif np.all(parents_of == 0): dist = np.array([np.NaN] * len(parents_of)) else: mask = parents_of > 0 ccenters = centers_of_labels(children.segmented).transpose() ccenters = ccenters[mask,:] parents_of_masked = parents_of[mask] - 1 pperim = outline(parents.segmented) # # Get a list of all points on the perimeter # perim_loc = np.argwhere(pperim != 0) # # Get the label # for each point # perim_idx = pperim[perim_loc[:,0],perim_loc[:,1]] # # Sort the points by label # # idx = np.lexsort((perim_loc[:,1],perim_loc[:,0],perim_idx)) perim_loc = perim_loc[idx,:] perim_idx = perim_idx[idx] # # Get counts and indexes to each run of perimeter points # counts = fix(scind.sum(np.ones(len(perim_idx)),perim_idx, np.arange(1,perim_idx[-1]+1))).astype(np.int32) indexes = np.cumsum(counts) - counts # # For the children, get the index and count of the parent # ccounts = counts[parents_of_masked] cindexes = indexes[parents_of_masked] # # Now make an array that has an element for each of that child's # perimeter points # clabel = np.zeros(np.sum(ccounts), int) # # cfirst is the eventual first index of each child in the # clabel array # cfirst = np.cumsum(ccounts) - ccounts clabel[cfirst[1:]] += 1 clabel = np.cumsum(clabel) # # Make an index that runs from 0 to ccounts for each # child label. # cp_index = np.arange(len(clabel)) - cfirst[clabel] # # then add cindexes to get an index to the perimeter point # cp_index += cindexes[clabel] # # Now, calculate the distance from the centroid of each label # to each perimeter point in the parent. # dist = np.sqrt(np.sum((perim_loc[cp_index,:] - ccenters[clabel,:])**2,1)) # # Finally, find the minimum distance per child # min_dist = fix(scind.minimum(dist, clabel, np.arange(len(ccounts)))) # # Account for unparented children # dist = np.array([np.NaN] * len(mask)) dist[mask] = min_dist meas.add_measurement(sub_object_name, FF_MINIMUM % parent_name, dist)
def run(self, workspace): """Run the module on the current data set workspace - has the current image set, object set, measurements and the parent frame for the application if the module is allowed to display. If the module should not display, workspace.frame is None. """ # # The object set holds "objects". Each of these is a container # for holding up to three kinds of image labels. # object_set = workspace.object_set # # Get the primary objects (the centers to be removed). # Get the string value out of primary_object_name. # primary_objects = object_set.get_objects(self.primary_objects_name.value) # # Get the cleaned-up labels image # primary_labels = primary_objects.segmented # # Do the same with the secondary object secondary_objects = object_set.get_objects(self.secondary_objects_name.value) secondary_labels = secondary_objects.segmented # # If one of the two label images is smaller than the other, we # try to find the cropping mask and we apply that mask to the larger # try: if any([p_size < s_size for p_size,s_size in zip(primary_labels.shape, secondary_labels.shape)]): # # Look for a cropping mask associated with the primary_labels # and apply that mask to resize the secondary labels # secondary_labels = primary_objects.crop_image_similarly(secondary_labels) tertiary_image = primary_objects.parent_image elif any([p_size > s_size for p_size,s_size in zip(primary_labels.shape, secondary_labels.shape)]): primary_labels = secondary_objects.crop_image_similarly(primary_labels) tertiary_image = secondary_objects.parent_image elif secondary_objects.parent_image is not None: tertiary_image = secondary_objects.parent_image else: tertiary_image = primary_objects.parent_image except ValueError: # No suitable cropping - resize all to fit the secondary # labels which are the most critical. # primary_labels, _ = cpo.size_similarly(secondary_labels, primary_labels) if secondary_objects.parent_image is not None: tertiary_image = secondary_objects.parent_image else: tertiary_image = primary_objects.parent_image if tertiary_image is not None: tertiary_image, _ = cpo.size_similarly(secondary_labels, tertiary_image) # # Find the outlines of the primary image and use this to shrink the # primary image by one. This guarantees that there is something left # of the secondary image after subtraction # primary_outline = outline(primary_labels) tertiary_labels = secondary_labels.copy() if self.shrink_primary: primary_mask = np.logical_or(primary_labels == 0, primary_outline) else: primary_mask = primary_labels == 0 tertiary_labels[primary_mask == False] = 0 # # Get the outlines of the tertiary image # tertiary_outlines = outline(tertiary_labels)!=0 # # Make the tertiary objects container # tertiary_objects = cpo.Objects() tertiary_objects.segmented = tertiary_labels tertiary_objects.parent_image = tertiary_image # # Relate tertiary objects to their parents & record # child_count_of_secondary, secondary_parents = \ secondary_objects.relate_children(tertiary_objects) if self.shrink_primary: child_count_of_primary, primary_parents = \ primary_objects.relate_children(tertiary_objects) else: # Primary and tertiary don't overlap. # Establish overlap between primary and secondary and commute _, secondary_of_primary = \ secondary_objects.relate_children(primary_objects) mask = secondary_of_primary != 0 child_count_of_primary = np.zeros(mask.shape, int) child_count_of_primary[mask] = child_count_of_secondary[ secondary_of_primary[mask] - 1] primary_parents = np.zeros(secondary_parents.shape, secondary_parents.dtype) primary_of_secondary = np.zeros(secondary_objects.count+1, int) primary_of_secondary[secondary_of_primary] = \ np.arange(1, len(secondary_of_primary)+1) primary_of_secondary[0] = 0 primary_parents = primary_of_secondary[secondary_parents] # # Write out the objects # workspace.object_set.add_objects(tertiary_objects, self.subregion_objects_name.value) # # Write out the measurements # m = workspace.measurements # # The parent/child associations # for parent_objects_name, parents_of, child_count, relationship in ( (self.primary_objects_name, primary_parents, child_count_of_primary, R_REMOVED), (self.secondary_objects_name, secondary_parents, child_count_of_secondary, R_PARENT)): m.add_measurement(self.subregion_objects_name.value, cpmi.FF_PARENT%(parent_objects_name.value), parents_of) m.add_measurement(parent_objects_name.value, cpmi.FF_CHILDREN_COUNT%(self.subregion_objects_name.value), child_count) mask = parents_of != 0 image_number = np.ones(np.sum(mask), int) * m.image_set_number child_object_number = np.argwhere(mask).flatten() + 1 parent_object_number = parents_of[mask] m.add_relate_measurement( self.module_num, relationship, parent_objects_name.value, self.subregion_objects_name.value, image_number, parent_object_number, image_number, child_object_number) object_count = tertiary_objects.count # # The object count # cpmi.add_object_count_measurements(workspace.measurements, self.subregion_objects_name.value, object_count) # # The object locations # cpmi.add_object_location_measurements(workspace.measurements, self.subregion_objects_name.value, tertiary_labels) # # The outlines # if self.use_outlines.value: out_img = cpi.Image(tertiary_outlines.astype(bool), parent_image = tertiary_image) workspace.image_set.add(self.outlines_name.value, out_img) if self.show_window: workspace.display_data.primary_labels = primary_labels workspace.display_data.secondary_labels = secondary_labels workspace.display_data.tertiary_labels = tertiary_labels workspace.display_data.tertiary_outlines = tertiary_outlines
def calculate_minimum_distances(self, workspace, parent_name): '''Calculate the distance from child center to parent perimeter''' meas = workspace.measurements assert isinstance(meas, cpmeas.Measurements) sub_object_name = self.sub_object_name.value parents = workspace.object_set.get_objects(parent_name) children = workspace.object_set.get_objects(sub_object_name) parents_of = self.get_parents_of(workspace, parent_name) if len(parents_of) == 0: dist = np.zeros((0, )) elif np.all(parents_of == 0): dist = np.array([np.NaN] * len(parents_of)) else: mask = parents_of > 0 ccenters = centers_of_labels(children.segmented).transpose() ccenters = ccenters[mask, :] parents_of_masked = parents_of[mask] - 1 pperim = outline(parents.segmented) # # Get a list of all points on the perimeter # perim_loc = np.argwhere(pperim != 0) # # Get the label # for each point # perim_idx = pperim[perim_loc[:, 0], perim_loc[:, 1]] # # Sort the points by label # # idx = np.lexsort((perim_loc[:, 1], perim_loc[:, 0], perim_idx)) perim_loc = perim_loc[idx, :] perim_idx = perim_idx[idx] # # Get counts and indexes to each run of perimeter points # counts = fix( scind.sum(np.ones(len(perim_idx)), perim_idx, np.arange(1, perim_idx[-1] + 1))).astype(np.int32) indexes = np.cumsum(counts) - counts # # For the children, get the index and count of the parent # ccounts = counts[parents_of_masked] cindexes = indexes[parents_of_masked] # # Now make an array that has an element for each of that child's # perimeter points # clabel = np.zeros(np.sum(ccounts), int) # # cfirst is the eventual first index of each child in the # clabel array # cfirst = np.cumsum(ccounts) - ccounts clabel[cfirst[1:]] += 1 clabel = np.cumsum(clabel) # # Make an index that runs from 0 to ccounts for each # child label. # cp_index = np.arange(len(clabel)) - cfirst[clabel] # # then add cindexes to get an index to the perimeter point # cp_index += cindexes[clabel] # # Now, calculate the distance from the centroid of each label # to each perimeter point in the parent. # dist = np.sqrt( np.sum((perim_loc[cp_index, :] - ccenters[clabel, :])**2, 1)) # # Finally, find the minimum distance per child # min_dist = fix(scind.minimum(dist, clabel, np.arange(len(ccounts)))) # # Account for unparented children # dist = np.array([np.NaN] * len(mask)) dist[mask] = min_dist meas.add_measurement(sub_object_name, FF_MINIMUM % parent_name, dist)
def test_02_01_forced_location(self): d = D.DefineGrid() d.ordering.value = D.NUM_BY_COLUMNS # # Grid with x spacing = 10, y spacing = 20 # diameter = 6 gridding = d.build_grid_info(15, 25, 1, 1, 25, 45, 2, 2) expected = self.make_rectangular_grid(gridding) i, j = np.mgrid[0:expected.shape[0], 0:expected.shape[1]] ispot, jspot = np.mgrid[0:gridding.rows, 0:gridding.columns] y_locations = np.zeros(np.max(gridding.spot_table) + 1, int) y_locations[gridding.spot_table.flatten()] = \ gridding.y_locations[ispot.flatten()] x_locations = np.zeros(np.max(gridding.spot_table) + 1, int) x_locations[gridding.spot_table.flatten()] = \ gridding.x_locations[jspot.flatten()] idist = (i - y_locations[expected]) jdist = (j - x_locations[expected]) expected[idist**2 + jdist**2 > (float(diameter + 1) / 2)**2] = 0 workspace, module = self.make_workspace(gridding) self.assertTrue(isinstance(module, I.IdentifyObjectsInGrid)) module.diameter_choice.value = I.AM_MANUAL module.diameter.value = diameter module.shape_choice.value = I.SHAPE_CIRCLE_FORCED module.wants_outlines.value = True module.run(workspace) labels = workspace.object_set.get_objects( OUTPUT_OBJECTS_NAME).segmented self.assertTrue( np.all(labels == expected[0:labels.shape[0], 0:labels.shape[1]])) # # Check measurements # m = workspace.measurements self.assertTrue(isinstance(m, cpmeas.Measurements)) xm = m.get_current_measurement(OUTPUT_OBJECTS_NAME, 'Location_Center_X') self.assertTrue(np.all(xm == x_locations[1:])) ym = m.get_current_measurement(OUTPUT_OBJECTS_NAME, 'Location_Center_Y') self.assertTrue(np.all(ym == y_locations[1:])) count = m.get_current_image_measurement('Count_%s' % OUTPUT_OBJECTS_NAME) self.assertEqual(count, gridding.rows * gridding.columns) columns = module.get_measurement_columns(workspace.pipeline) self.assertEqual(len(columns), 4) count_feature = 'Count_%s' % OUTPUT_OBJECTS_NAME self.assertTrue( all([ column[0] == ("Image" if column[1] == count_feature else OUTPUT_OBJECTS_NAME) for column in columns ])) self.assertTrue( all([ column[1] in ('Location_Center_X', 'Location_Center_Y', count_feature, 'Number_Object_Number') for column in columns ])) # # Check the outlines # outlines = workspace.image_set.get_image(OUTLINES_NAME) outlines = outlines.pixel_data expected_outlines = outline(expected) self.assertTrue( np.all(outlines == (expected_outlines[0:outlines.shape[0], 0:outlines.shape[1]] > 0))) # # Check the measurements # categories = list(module.get_categories(None, OUTPUT_OBJECTS_NAME)) self.assertEqual(len(categories), 2) categories.sort() self.assertEqual(categories[0], "Location") self.assertEqual(categories[1], "Number") categories = module.get_categories(None, cpmeas.IMAGE) self.assertEqual(len(categories), 1) self.assertEqual(categories[0], "Count") measurements = module.get_measurements(None, cpmeas.IMAGE, "Count") self.assertEqual(len(measurements), 1) self.assertEqual(measurements[0], OUTPUT_OBJECTS_NAME) measurements = module.get_measurements(None, OUTPUT_OBJECTS_NAME, "Location") self.assertEqual(len(measurements), 2) self.assertTrue( all(m in ('Center_X', 'Center_Y') for m in measurements)) self.assertTrue('Center_X' in measurements) self.assertTrue('Center_Y' in measurements) measurements = module.get_measurements(None, OUTPUT_OBJECTS_NAME, "Number") self.assertEqual(len(measurements), 1) self.assertEqual(measurements[0], "Object_Number")
def run(self, workspace): """Run the module workspace - The workspace contains pipeline - instance of cpp for this run image_set - the images in the image set being processed object_set - the objects (labeled masks) in this image set measurements - the measurements for this run frame - the parent frame to whatever frame is created. None means don't draw. """ orig_objects_name = self.object_name.value filtered_objects_name = self.filtered_objects.value orig_objects = workspace.object_set.get_objects(orig_objects_name) assert isinstance(orig_objects, cpo.Objects) orig_labels = [l for l, c in orig_objects.get_labels()] if self.wants_image_display: guide_image = workspace.image_set.get_image(self.image_name.value) guide_image = guide_image.pixel_data if np.any(guide_image != np.min(guide_image)): guide_image = (guide_image - np.min(guide_image)) / (np.max(guide_image) - np.min(guide_image)) else: guide_image = None filtered_labels = workspace.interaction_request( self, orig_labels, guide_image, workspace.measurements.image_set_number) if filtered_labels is None: # Ask whoever is listening to stop doing stuff workspace.cancel_request() # Have to soldier on until the cancel takes effect... filtered_labels = orig_labels # # Renumber objects consecutively if asked to do so # unique_labels = np.unique(np.array(filtered_labels)) unique_labels = unique_labels[unique_labels != 0] object_count = len(unique_labels) if self.renumber_choice == R_RENUMBER: mapping = np.zeros(1 if len(unique_labels) == 0 else np.max(unique_labels)+1, int) mapping[unique_labels] = np.arange(1,object_count + 1) filtered_labels = [mapping[l] for l in filtered_labels] # # Make the objects out of the labels # filtered_objects = cpo.Objects() i, j = np.mgrid[0:filtered_labels[0].shape[0], 0:filtered_labels[0].shape[1]] ijv = np.zeros((0, 3), filtered_labels[0].dtype) for l in filtered_labels: ijv = np.vstack((ijv, np.column_stack((i[l != 0], j[l != 0], l[l != 0])))) filtered_objects.set_ijv(ijv, orig_labels[0].shape) if orig_objects.has_unedited_segmented(): filtered_objects.unedited_segmented = orig_objects.unedited_segmented if orig_objects.parent_image is not None: filtered_objects.parent_image = orig_objects.parent_image workspace.object_set.add_objects(filtered_objects, filtered_objects_name) # # Add parent/child & other measurements # m = workspace.measurements child_count, parents = orig_objects.relate_children(filtered_objects) m.add_measurement(filtered_objects_name, I.FF_PARENT%(orig_objects_name), parents) m.add_measurement(orig_objects_name, I.FF_CHILDREN_COUNT%(filtered_objects_name), child_count) # # The object count # I.add_object_count_measurements(m, filtered_objects_name, object_count) # # The object locations # I.add_object_location_measurements_ijv(m, filtered_objects_name, ijv) # # Outlines if we want them # if self.wants_outlines: outlines_name = self.outlines_name.value outlines = outline(filtered_labels[0]).astype(bool) outlines_image = cpi.Image(outlines) workspace.image_set.add(outlines_name, outlines_image) workspace.display_data.orig_ijv = orig_objects.ijv workspace.display_data.filtered_ijv = filtered_objects.ijv workspace.display_data.shape = orig_labels[0].shape
def run(self, workspace): objects = workspace.object_set.get_objects(self.object_name.value) assert isinstance(objects, cpo.Objects) has_pixels = objects.areas > 0 labels = objects.small_removed_segmented kept_labels = objects.segmented neighbor_objects = workspace.object_set.get_objects( self.neighbors_name.value) assert isinstance(neighbor_objects, cpo.Objects) neighbor_labels = neighbor_objects.small_removed_segmented # # Need to add in labels touching border. # unedited_segmented = neighbor_objects.unedited_segmented touching_border = np.zeros(np.max(unedited_segmented) + 1, bool) touching_border[unedited_segmented[0, :]] = True touching_border[unedited_segmented[-1, :]] = True touching_border[unedited_segmented[:, 0]] = True touching_border[unedited_segmented[:, -1]] = True touching_border[0] = False touching_border_mask = touching_border[unedited_segmented] nobjects = np.max(labels) nkept_objects = objects.count nneighbors = np.max(neighbor_labels) if np.any(touching_border) and \ np.all(~ touching_border_mask[neighbor_labels != 0]): # Add the border labels if any were excluded touching_border_object_number = np.cumsum(touching_border) + \ np.max(neighbor_labels) touching_border_mask = touching_border_mask & (neighbor_labels == 0) neighbor_labels = neighbor_labels.copy().astype(np.int32) neighbor_labels[ touching_border_mask] = touching_border_object_number[ unedited_segmented[touching_border_mask]] _, object_numbers = objects.relate_labels(labels, kept_labels) if self.neighbors_are_objects: neighbor_numbers = object_numbers neighbor_has_pixels = has_pixels else: _, neighbor_numbers = neighbor_objects.relate_labels( neighbor_labels, neighbor_objects.segmented) neighbor_has_pixels = np.bincount(neighbor_labels.ravel())[1:] > 0 neighbor_count = np.zeros((nobjects, )) pixel_count = np.zeros((nobjects, )) first_object_number = np.zeros((nobjects, ), int) second_object_number = np.zeros((nobjects, ), int) first_x_vector = np.zeros((nobjects, )) second_x_vector = np.zeros((nobjects, )) first_y_vector = np.zeros((nobjects, )) second_y_vector = np.zeros((nobjects, )) angle = np.zeros((nobjects, )) percent_touching = np.zeros((nobjects, )) expanded_labels = None if self.distance_method == D_EXPAND: # Find the i,j coordinates of the nearest foreground point # to every background point i, j = scind.distance_transform_edt(labels == 0, return_distances=False, return_indices=True) # Assign each background pixel to the label of its nearest # foreground pixel. Assign label to label for foreground. labels = labels[i, j] expanded_labels = labels # for display distance = 1 # dilate once to make touching edges overlap scale = S_EXPANDED if self.neighbors_are_objects: neighbor_labels = labels.copy() elif self.distance_method == D_WITHIN: distance = self.distance.value scale = str(distance) elif self.distance_method == D_ADJACENT: distance = 1 scale = S_ADJACENT else: raise ValueError("Unknown distance method: %s" % self.distance_method.value) if nneighbors > (1 if self.neighbors_are_objects else 0): first_objects = [] second_objects = [] object_indexes = np.arange(nobjects, dtype=np.int32) + 1 # # First, compute the first and second nearest neighbors, # and the angles between self and the first and second # nearest neighbors # ocenters = centers_of_labels( objects.small_removed_segmented).transpose() ncenters = centers_of_labels( neighbor_objects.small_removed_segmented).transpose() areas = fix( scind.sum(np.ones(labels.shape), labels, object_indexes)) perimeter_outlines = outline(labels) perimeters = fix( scind.sum(np.ones(labels.shape), perimeter_outlines, object_indexes)) i, j = np.mgrid[0:nobjects, 0:nneighbors] distance_matrix = np.sqrt((ocenters[i, 0] - ncenters[j, 0])**2 + (ocenters[i, 1] - ncenters[j, 1])**2) # # order[:,0] should be arange(nobjects) # order[:,1] should be the nearest neighbor # order[:,2] should be the next nearest neighbor # if distance_matrix.shape[1] == 1: # a little buggy, lexsort assumes that a 2-d array of # second dimension = 1 is a 1-d array order = np.zeros(distance_matrix.shape, int) else: order = np.lexsort([distance_matrix]) first_neighbor = 1 if self.neighbors_are_objects else 0 first_object_index = order[:, first_neighbor] first_x_vector = ncenters[first_object_index, 1] - ocenters[:, 1] first_y_vector = ncenters[first_object_index, 0] - ocenters[:, 0] if nneighbors > first_neighbor + 1: second_object_index = order[:, first_neighbor + 1] second_x_vector = ncenters[second_object_index, 1] - ocenters[:, 1] second_y_vector = ncenters[second_object_index, 0] - ocenters[:, 0] v1 = np.array((first_x_vector, first_y_vector)) v2 = np.array((second_x_vector, second_y_vector)) # # Project the unit vector v1 against the unit vector v2 # dot = (np.sum(v1 * v2, 0) / np.sqrt(np.sum(v1**2, 0) * np.sum(v2**2, 0))) angle = np.arccos(dot) * 180. / np.pi # Make the structuring element for dilation strel = strel_disk(distance) # # A little bigger one to enter into the border with a structure # that mimics the one used to create the outline # strel_touching = strel_disk(distance + .5) # # Get the extents for each object and calculate the patch # that excises the part of the image that is "distance" # away i, j = np.mgrid[0:labels.shape[0], 0:labels.shape[1]] min_i, max_i, min_i_pos, max_i_pos = \ scind.extrema(i, labels, object_indexes) min_j, max_j, min_j_pos, max_j_pos = \ scind.extrema(j, labels, object_indexes) min_i = np.maximum(fix(min_i) - distance, 0).astype(int) max_i = np.minimum(fix(max_i) + distance + 1, labels.shape[0]).astype(int) min_j = np.maximum(fix(min_j) - distance, 0).astype(int) max_j = np.minimum(fix(max_j) + distance + 1, labels.shape[1]).astype(int) # # Loop over all objects # Calculate which ones overlap "index" # Calculate how much overlap there is of others to "index" # for object_number in object_numbers: if object_number == 0: # # No corresponding object in small-removed. This means # that the object has no pixels, e.g. not renumbered. # continue index = object_number - 1 patch = labels[min_i[index]:max_i[index], min_j[index]:max_j[index]] npatch = neighbor_labels[min_i[index]:max_i[index], min_j[index]:max_j[index]] # # Find the neighbors # patch_mask = patch == (index + 1) extended = scind.binary_dilation(patch_mask, strel) neighbors = np.unique(npatch[extended]) neighbors = neighbors[neighbors != 0] if self.neighbors_are_objects: neighbors = neighbors[neighbors != object_number] nc = len(neighbors) neighbor_count[index] = nc if nc > 0: first_objects.append(np.ones(nc, int) * object_number) second_objects.append(neighbors) if self.neighbors_are_objects: # # Find the # of overlapping pixels. Dilate the neighbors # and see how many pixels overlap our image. Use a 3x3 # structuring element to expand the overlapping edge # into the perimeter. # outline_patch = perimeter_outlines[ min_i[index]:max_i[index], min_j[index]:max_j[index]] == object_number extended = scind.binary_dilation( (patch != 0) & (patch != object_number), strel_touching) overlap = np.sum(outline_patch & extended) pixel_count[index] = overlap if sum([len(x) for x in first_objects]) > 0: first_objects = np.hstack(first_objects) reverse_object_numbers = np.zeros( max(np.max(object_numbers), np.max(first_objects)) + 1, int) reverse_object_numbers[object_numbers] = np.arange( len(object_numbers)) + 1 first_objects = reverse_object_numbers[first_objects] second_objects = np.hstack(second_objects) reverse_neighbor_numbers = np.zeros( max(np.max(neighbor_numbers), np.max(second_objects)) + 1, int) reverse_neighbor_numbers[neighbor_numbers] = np.arange( len(neighbor_numbers)) + 1 second_objects = reverse_neighbor_numbers[second_objects] to_keep = (first_objects > 0) & (second_objects > 0) first_objects = first_objects[to_keep] second_objects = second_objects[to_keep] else: first_objects = np.zeros(0, int) second_objects = np.zeros(0, int) if self.neighbors_are_objects: percent_touching = pixel_count * 100 / perimeters else: percent_touching = pixel_count * 100.0 / areas object_indexes = object_numbers - 1 neighbor_indexes = neighbor_numbers - 1 # # Have to recompute nearest # first_object_number = np.zeros(nkept_objects, int) second_object_number = np.zeros(nkept_objects, int) if nkept_objects > (1 if self.neighbors_are_objects else 0): di = (ocenters[object_indexes[:, np.newaxis], 0] - ncenters[neighbor_indexes[np.newaxis, :], 0]) dj = (ocenters[object_indexes[:, np.newaxis], 1] - ncenters[neighbor_indexes[np.newaxis, :], 1]) distance_matrix = np.sqrt(di * di + dj * dj) distance_matrix[~has_pixels, :] = np.inf distance_matrix[:, ~neighbor_has_pixels] = np.inf # # order[:,0] should be arange(nobjects) # order[:,1] should be the nearest neighbor # order[:,2] should be the next nearest neighbor # order = np.lexsort([distance_matrix ]).astype(first_object_number.dtype) if self.neighbors_are_objects: first_object_number[has_pixels] = order[has_pixels, 1] + 1 if nkept_objects > 2: second_object_number[has_pixels] = order[has_pixels, 2] + 1 else: first_object_number[has_pixels] = order[has_pixels, 0] + 1 if order.shape[1] > 1: second_object_number[has_pixels] = order[has_pixels, 1] + 1 else: object_indexes = object_numbers - 1 neighbor_indexes = neighbor_numbers - 1 first_objects = np.zeros(0, int) second_objects = np.zeros(0, int) # # Now convert all measurements from the small-removed to # the final number set. # neighbor_count = neighbor_count[object_indexes] neighbor_count[~has_pixels] = 0 percent_touching = percent_touching[object_indexes] percent_touching[~has_pixels] = 0 first_x_vector = first_x_vector[object_indexes] second_x_vector = second_x_vector[object_indexes] first_y_vector = first_y_vector[object_indexes] second_y_vector = second_y_vector[object_indexes] angle = angle[object_indexes] # # Record the measurements # assert (isinstance(workspace, cpw.Workspace)) m = workspace.measurements assert (isinstance(m, cpmeas.Measurements)) image_set = workspace.image_set features_and_data = [ (M_NUMBER_OF_NEIGHBORS, neighbor_count), (M_FIRST_CLOSEST_OBJECT_NUMBER, first_object_number), (M_FIRST_CLOSEST_DISTANCE, np.sqrt(first_x_vector**2 + first_y_vector**2)), (M_SECOND_CLOSEST_OBJECT_NUMBER, second_object_number), (M_SECOND_CLOSEST_DISTANCE, np.sqrt(second_x_vector**2 + second_y_vector**2)), (M_ANGLE_BETWEEN_NEIGHBORS, angle) ] if self.neighbors_are_objects: features_and_data.append((M_PERCENT_TOUCHING, percent_touching)) for feature_name, data in features_and_data: m.add_measurement(self.object_name.value, self.get_measurement_name(feature_name), data) if len(first_objects) > 0: m.add_relate_measurement( self.module_num, cpmeas.NEIGHBORS, self.object_name.value, self.object_name.value if self.neighbors_are_objects else self.neighbors_name.value, m.image_set_number * np.ones(first_objects.shape, int), first_objects, m.image_set_number * np.ones(second_objects.shape, int), second_objects) labels = kept_labels neighbor_count_image = np.zeros(labels.shape, int) object_mask = objects.segmented != 0 object_indexes = objects.segmented[object_mask] - 1 neighbor_count_image[object_mask] = neighbor_count[object_indexes] workspace.display_data.neighbor_count_image = neighbor_count_image if self.neighbors_are_objects: percent_touching_image = np.zeros(labels.shape) percent_touching_image[object_mask] = percent_touching[ object_indexes] workspace.display_data.percent_touching_image = percent_touching_image image_set = workspace.image_set if self.wants_count_image.value: neighbor_cm_name = self.count_colormap.value neighbor_cm = get_colormap(neighbor_cm_name) sm = matplotlib.cm.ScalarMappable(cmap=neighbor_cm) img = sm.to_rgba(neighbor_count_image)[:, :, :3] img[:, :, 0][~object_mask] = 0 img[:, :, 1][~object_mask] = 0 img[:, :, 2][~object_mask] = 0 count_image = cpi.Image(img, masking_objects=objects) image_set.add(self.count_image_name.value, count_image) else: neighbor_cm_name = cpprefs.get_default_colormap() neighbor_cm = matplotlib.cm.get_cmap(neighbor_cm_name) if self.neighbors_are_objects and self.wants_percent_touching_image: percent_touching_cm_name = self.touching_colormap.value percent_touching_cm = get_colormap(percent_touching_cm_name) sm = matplotlib.cm.ScalarMappable(cmap=percent_touching_cm) img = sm.to_rgba(percent_touching_image)[:, :, :3] img[:, :, 0][~object_mask] = 0 img[:, :, 1][~object_mask] = 0 img[:, :, 2][~object_mask] = 0 touching_image = cpi.Image(img, masking_objects=objects) image_set.add(self.touching_image_name.value, touching_image) else: percent_touching_cm_name = cpprefs.get_default_colormap() percent_touching_cm = matplotlib.cm.get_cmap( percent_touching_cm_name) if self.show_window: workspace.display_data.neighbor_cm_name = neighbor_cm_name workspace.display_data.percent_touching_cm_name = percent_touching_cm_name workspace.display_data.orig_labels = objects.segmented workspace.display_data.expanded_labels = expanded_labels workspace.display_data.object_mask = object_mask
def run(self, workspace): assert isinstance(workspace, cpw.Workspace) image_name = self.image_name.value image = workspace.image_set.get_image(image_name, must_be_grayscale = True) workspace.display_data.statistics = [] img = image.pixel_data mask = image.mask objects = workspace.object_set.get_objects(self.primary_objects.value) global_threshold = None if self.method == M_DISTANCE_N: has_threshold = False else: thresholded_image = self.threshold_image(image_name, workspace) has_threshold = True # # Get the following labels: # * all edited labels # * labels touching the edge, including small removed # labels_in = objects.unedited_segmented.copy() labels_touching_edge = np.hstack( (labels_in[0,:], labels_in[-1,:], labels_in[:,0], labels_in[:,-1])) labels_touching_edge = np.unique(labels_touching_edge) is_touching = np.zeros(np.max(labels_in)+1, bool) is_touching[labels_touching_edge] = True is_touching = is_touching[labels_in] labels_in[(~ is_touching) & (objects.segmented == 0)] = 0 # # Stretch the input labels to match the image size. If there's no # label matrix, then there's no label in that area. # if tuple(labels_in.shape) != tuple(img.shape): tmp = np.zeros(img.shape, labels_in.dtype) i_max = min(img.shape[0], labels_in.shape[0]) j_max = min(img.shape[1], labels_in.shape[1]) tmp[:i_max, :j_max] = labels_in[:i_max, :j_max] labels_in = tmp if self.method in (M_DISTANCE_B, M_DISTANCE_N): if self.method == M_DISTANCE_N: distances,(i,j) = scind.distance_transform_edt(labels_in == 0, return_indices = True) labels_out = np.zeros(labels_in.shape,int) dilate_mask = distances <= self.distance_to_dilate.value labels_out[dilate_mask] =\ labels_in[i[dilate_mask],j[dilate_mask]] else: labels_out, distances = propagate(img, labels_in, thresholded_image, 1.0) labels_out[distances>self.distance_to_dilate.value] = 0 labels_out[labels_in > 0] = labels_in[labels_in>0] if self.fill_holes: small_removed_segmented_out = fill_labeled_holes(labels_out) else: small_removed_segmented_out = labels_out # # Create the final output labels by removing labels in the # output matrix that are missing from the segmented image # segmented_labels = objects.segmented segmented_out = self.filter_labels(small_removed_segmented_out, objects, workspace) elif self.method == M_PROPAGATION: labels_out, distance = propagate(img, labels_in, thresholded_image, self.regularization_factor.value) if self.fill_holes: small_removed_segmented_out = fill_labeled_holes(labels_out) else: small_removed_segmented_out = labels_out.copy() segmented_out = self.filter_labels(small_removed_segmented_out, objects, workspace) elif self.method == M_WATERSHED_G: # # First, apply the sobel filter to the image (both horizontal # and vertical). The filter measures gradient. # sobel_image = np.abs(scind.sobel(img)) # # Combine the image mask and threshold to mask the watershed # watershed_mask = np.logical_or(thresholded_image, labels_in > 0) watershed_mask = np.logical_and(watershed_mask, mask) # # Perform the first watershed # labels_out = watershed(sobel_image, labels_in, np.ones((3,3),bool), mask=watershed_mask) if self.fill_holes: small_removed_segmented_out = fill_labeled_holes(labels_out) else: small_removed_segmented_out = labels_out.copy() segmented_out = self.filter_labels(small_removed_segmented_out, objects, workspace) elif self.method == M_WATERSHED_I: # # invert the image so that the maxima are filled first # and the cells compete over what's close to the threshold # inverted_img = 1-img # # Same as above, but perform the watershed on the original image # watershed_mask = np.logical_or(thresholded_image, labels_in > 0) watershed_mask = np.logical_and(watershed_mask, mask) # # Perform the watershed # labels_out = watershed(inverted_img, labels_in, np.ones((3,3),bool), mask=watershed_mask) if self.fill_holes: small_removed_segmented_out = fill_labeled_holes(labels_out) else: small_removed_segmented_out = labels_out segmented_out = self.filter_labels(small_removed_segmented_out, objects, workspace) if self.wants_discard_edge and self.wants_discard_primary: # # Make a new primary object # lookup = scind.maximum(segmented_out, objects.segmented, range(np.max(objects.segmented)+1)) lookup = fix(lookup) lookup[0] = 0 lookup[lookup != 0] = np.arange(np.sum(lookup != 0)) + 1 segmented_labels = lookup[objects.segmented] segmented_out = lookup[segmented_out] new_objects = cpo.Objects() new_objects.segmented = segmented_labels if objects.has_unedited_segmented: new_objects.unedited_segmented = objects.unedited_segmented if objects.has_small_removed_segmented: new_objects.small_removed_segmented = objects.small_removed_segmented new_objects.parent_image = objects.parent_image primary_outline = outline(segmented_labels) if self.wants_primary_outlines: out_img = cpi.Image(primary_outline.astype(bool), parent_image = image) workspace.image_set.add(self.new_primary_outlines_name.value, out_img) else: primary_outline = outline(objects.segmented) secondary_outline = outline(segmented_out) # # Add the objects to the object set # objects_out = cpo.Objects() objects_out.unedited_segmented = small_removed_segmented_out objects_out.small_removed_segmented = small_removed_segmented_out objects_out.segmented = segmented_out objects_out.parent_image = image objname = self.objects_name.value workspace.object_set.add_objects(objects_out, objname) if self.use_outlines.value: out_img = cpi.Image(secondary_outline.astype(bool), parent_image = image) workspace.image_set.add(self.outlines_name.value, out_img) object_count = np.max(segmented_out) # # Add measurements # measurements = workspace.measurements cpmi.add_object_count_measurements(measurements, objname, object_count) cpmi.add_object_location_measurements(measurements, objname, segmented_out) # # Relate the secondary objects to the primary ones and record # the relationship. # children_per_parent, parents_of_children = \ objects.relate_children(objects_out) measurements.add_measurement(self.primary_objects.value, cpmi.FF_CHILDREN_COUNT%objname, children_per_parent) measurements.add_measurement(objname, cpmi.FF_PARENT%self.primary_objects.value, parents_of_children) image_numbers = np.ones(len(parents_of_children), int) *\ measurements.image_set_number mask = parents_of_children > 0 measurements.add_relate_measurement( self.module_num, R_PARENT, self.primary_objects.value, self.objects_name.value, image_numbers[mask], parents_of_children[mask], image_numbers[mask], np.arange(1, len(parents_of_children) + 1)[mask]) # # If primary objects were created, add them # if self.wants_discard_edge and self.wants_discard_primary: workspace.object_set.add_objects(new_objects, self.new_primary_objects_name.value) cpmi.add_object_count_measurements(measurements, self.new_primary_objects_name.value, np.max(new_objects.segmented)) cpmi.add_object_location_measurements(measurements, self.new_primary_objects_name.value, new_objects.segmented) for parent_objects, parent_name, child_objects, child_name in ( (objects, self.primary_objects.value, new_objects, self.new_primary_objects_name.value), (new_objects, self.new_primary_objects_name.value, objects_out, objname)): children_per_parent, parents_of_children = \ parent_objects.relate_children(child_objects) measurements.add_measurement(parent_name, cpmi.FF_CHILDREN_COUNT%child_name, children_per_parent) measurements.add_measurement(child_name, cpmi.FF_PARENT%parent_name, parents_of_children) if self.show_window: object_area = np.sum(segmented_out > 0) workspace.display_data.object_pct = \ 100 * object_area / np.product(segmented_out.shape) workspace.display_data.img = img workspace.display_data.segmented_out = segmented_out workspace.display_data.primary_labels = objects.segmented workspace.display_data.global_threshold = global_threshold workspace.display_data.object_count = object_count
def run(self, workspace): objects = workspace.object_set.get_objects(self.object_name.value) assert isinstance(objects, cpo.Objects) has_pixels = objects.areas > 0 labels = objects.small_removed_segmented kept_labels = objects.segmented neighbor_objects = workspace.object_set.get_objects( self.neighbors_name.value) assert isinstance(neighbor_objects, cpo.Objects) neighbor_labels = neighbor_objects.small_removed_segmented # # Need to add in labels touching border. # unedited_segmented = neighbor_objects.unedited_segmented touching_border = np.zeros(np.max(unedited_segmented) + 1, bool) touching_border[unedited_segmented[0, :]] = True touching_border[unedited_segmented[-1, :]] = True touching_border[unedited_segmented[:, 0]] = True touching_border[unedited_segmented[:, -1]] = True touching_border[0] = False touching_border_mask = touching_border[unedited_segmented] nobjects = np.max(labels) nkept_objects = objects.count nneighbors = np.max(neighbor_labels) if np.any(touching_border) and \ np.all(~ touching_border_mask[neighbor_labels!=0]): # Add the border labels if any were excluded touching_border_object_number = np.cumsum(touching_border) + \ np.max(neighbor_labels) touching_border_mask = touching_border_mask & (neighbor_labels == 0) neighbor_labels = neighbor_labels.copy().astype(np.int32) neighbor_labels[touching_border_mask] = touching_border_object_number[ unedited_segmented[touching_border_mask]] neighbor_has_pixels = np.bincount(neighbor_labels.ravel())[1:] > 0 _, object_numbers = objects.relate_labels(labels, kept_labels) if self.neighbors_are_objects: neighbor_numbers = object_numbers else: _, neighbor_numbers = neighbor_objects.relate_labels( neighbor_labels, neighbor_objects.segmented) neighbor_count = np.zeros((nobjects,)) pixel_count = np.zeros((nobjects,)) first_object_number = np.zeros((nobjects,),int) second_object_number = np.zeros((nobjects,),int) first_x_vector = np.zeros((nobjects,)) second_x_vector = np.zeros((nobjects,)) first_y_vector = np.zeros((nobjects,)) second_y_vector = np.zeros((nobjects,)) angle = np.zeros((nobjects,)) percent_touching = np.zeros((nobjects,)) expanded_labels = None if self.distance_method == D_EXPAND: # Find the i,j coordinates of the nearest foreground point # to every background point i,j = scind.distance_transform_edt(labels==0, return_distances=False, return_indices=True) # Assign each background pixel to the label of its nearest # foreground pixel. Assign label to label for foreground. labels = labels[i,j] expanded_labels = labels # for display distance = 1 # dilate once to make touching edges overlap scale = S_EXPANDED if self.neighbors_are_objects: neighbor_labels = labels.copy() elif self.distance_method == D_WITHIN: distance = self.distance.value scale = str(distance) elif self.distance_method == D_ADJACENT: distance = 1 scale = S_ADJACENT else: raise ValueError("Unknown distance method: %s" % self.distance_method.value) if nneighbors > (1 if self.neighbors_are_objects else 0): first_objects = [] second_objects = [] object_indexes = np.arange(nobjects, dtype=np.int32)+1 # # First, compute the first and second nearest neighbors, # and the angles between self and the first and second # nearest neighbors # ocenters = centers_of_labels( objects.small_removed_segmented).transpose() ncenters = centers_of_labels( neighbor_objects.small_removed_segmented).transpose() areas = fix(scind.sum(np.ones(labels.shape),labels, object_indexes)) perimeter_outlines = outline(labels) perimeters = fix(scind.sum( np.ones(labels.shape), perimeter_outlines, object_indexes)) i,j = np.mgrid[0:nobjects,0:nneighbors] distance_matrix = np.sqrt((ocenters[i,0] - ncenters[j,0])**2 + (ocenters[i,1] - ncenters[j,1])**2) # # order[:,0] should be arange(nobjects) # order[:,1] should be the nearest neighbor # order[:,2] should be the next nearest neighbor # if distance_matrix.shape[1] == 1: # a little buggy, lexsort assumes that a 2-d array of # second dimension = 1 is a 1-d array order = np.zeros(distance_matrix.shape, int) else: order = np.lexsort([distance_matrix]) first_neighbor = 1 if self.neighbors_are_objects else 0 first_object_index = order[:, first_neighbor] first_x_vector = ncenters[first_object_index,1] - ocenters[:,1] first_y_vector = ncenters[first_object_index,0] - ocenters[:,0] if nneighbors > first_neighbor+1: second_object_index = order[:, first_neighbor + 1] second_x_vector = ncenters[second_object_index,1] - ocenters[:,1] second_y_vector = ncenters[second_object_index,0] - ocenters[:,0] v1 = np.array((first_x_vector,first_y_vector)) v2 = np.array((second_x_vector,second_y_vector)) # # Project the unit vector v1 against the unit vector v2 # dot = (np.sum(v1*v2,0) / np.sqrt(np.sum(v1**2,0)*np.sum(v2**2,0))) angle = np.arccos(dot) * 180. / np.pi # Make the structuring element for dilation strel = strel_disk(distance) # # A little bigger one to enter into the border with a structure # that mimics the one used to create the outline # strel_touching = strel_disk(distance + .5) # # Get the extents for each object and calculate the patch # that excises the part of the image that is "distance" # away i,j = np.mgrid[0:labels.shape[0],0:labels.shape[1]] min_i, max_i, min_i_pos, max_i_pos =\ scind.extrema(i,labels,object_indexes) min_j, max_j, min_j_pos, max_j_pos =\ scind.extrema(j,labels,object_indexes) min_i = np.maximum(fix(min_i)-distance,0).astype(int) max_i = np.minimum(fix(max_i)+distance+1,labels.shape[0]).astype(int) min_j = np.maximum(fix(min_j)-distance,0).astype(int) max_j = np.minimum(fix(max_j)+distance+1,labels.shape[1]).astype(int) # # Loop over all objects # Calculate which ones overlap "index" # Calculate how much overlap there is of others to "index" # for object_number in object_numbers: if object_number == 0: # # No corresponding object in small-removed. This means # that the object has no pixels, e.g. not renumbered. # continue index = object_number - 1 patch = labels[min_i[index]:max_i[index], min_j[index]:max_j[index]] npatch = neighbor_labels[min_i[index]:max_i[index], min_j[index]:max_j[index]] # # Find the neighbors # patch_mask = patch==(index+1) extended = scind.binary_dilation(patch_mask,strel) neighbors = np.unique(npatch[extended]) neighbors = neighbors[neighbors != 0] if self.neighbors_are_objects: neighbors = neighbors[neighbors != object_number] nc = len(neighbors) neighbor_count[index] = nc if nc > 0: first_objects.append(np.ones(nc,int) * object_number) second_objects.append(neighbors) if self.neighbors_are_objects: # # Find the # of overlapping pixels. Dilate the neighbors # and see how many pixels overlap our image. Use a 3x3 # structuring element to expand the overlapping edge # into the perimeter. # outline_patch = perimeter_outlines[ min_i[index]:max_i[index], min_j[index]:max_j[index]] == object_number extended = scind.binary_dilation( (patch != 0) & (patch != object_number), strel_touching) overlap = np.sum(outline_patch & extended) pixel_count[index] = overlap if sum([len(x) for x in first_objects]) > 0: first_objects = np.hstack(first_objects) reverse_object_numbers = np.zeros( max(np.max(object_numbers), np.max(first_objects)) + 1, int) reverse_object_numbers[object_numbers] = np.arange(len(object_numbers)) + 1 first_objects = reverse_object_numbers[first_objects] second_objects = np.hstack(second_objects) reverse_neighbor_numbers = np.zeros( max(np.max(neighbor_numbers), np.max(second_objects)) + 1, int) reverse_neighbor_numbers[neighbor_numbers] = np.arange(len(neighbor_numbers)) + 1 second_objects= reverse_neighbor_numbers[second_objects] to_keep = (first_objects > 0) & (second_objects > 0) first_objects = first_objects[to_keep] second_objects = second_objects[to_keep] else: first_objects = np.zeros(0, int) second_objects = np.zeros(0, int) if self.neighbors_are_objects: percent_touching = pixel_count * 100 / perimeters else: percent_touching = pixel_count * 100.0 / areas object_indexes = object_numbers - 1 neighbor_indexes = neighbor_numbers - 1 # # Have to recompute nearest # first_object_number = np.zeros(nkept_objects, int) second_object_number = np.zeros(nkept_objects, int) if nkept_objects > (1 if self.neighbors_are_objects else 0): di = (ocenters[object_indexes[:, np.newaxis], 0] - ncenters[neighbor_indexes[np.newaxis, :], 0]) dj = (ocenters[object_indexes[:, np.newaxis], 1] - ncenters[neighbor_indexes[np.newaxis, :], 1]) distance_matrix = np.sqrt(di*di + dj*dj) distance_matrix[~ has_pixels, :] = np.inf distance_matrix[:, ~neighbor_has_pixels] = np.inf # # order[:,0] should be arange(nobjects) # order[:,1] should be the nearest neighbor # order[:,2] should be the next nearest neighbor # order = np.lexsort([distance_matrix]).astype( first_object_number.dtype) if self.neighbors_are_objects: first_object_number[has_pixels] = order[has_pixels,1] + 1 if nkept_objects > 2: second_object_number[has_pixels] = order[has_pixels,2] + 1 else: first_object_number[has_pixels] = order[has_pixels,0] + 1 if order.shape[1] > 1: second_object_number[has_pixels] = order[has_pixels,1] + 1 else: object_indexes = object_numbers - 1 neighbor_indexes = neighbor_numbers - 1 first_objects = np.zeros(0, int) second_objects = np.zeros(0, int) # # Now convert all measurements from the small-removed to # the final number set. # neighbor_count = neighbor_count[object_indexes] neighbor_count[~ has_pixels] = 0 percent_touching = percent_touching[object_indexes] percent_touching[~ has_pixels] = 0 first_x_vector = first_x_vector[object_indexes] second_x_vector = second_x_vector[object_indexes] first_y_vector = first_y_vector[object_indexes] second_y_vector = second_y_vector[object_indexes] angle = angle[object_indexes] # # Record the measurements # assert(isinstance(workspace, cpw.Workspace)) m = workspace.measurements assert(isinstance(m, cpmeas.Measurements)) image_set = workspace.image_set features_and_data = [ (M_NUMBER_OF_NEIGHBORS, neighbor_count), (M_FIRST_CLOSEST_OBJECT_NUMBER, first_object_number), (M_FIRST_CLOSEST_DISTANCE, np.sqrt(first_x_vector**2+first_y_vector**2)), (M_SECOND_CLOSEST_OBJECT_NUMBER, second_object_number), (M_SECOND_CLOSEST_DISTANCE, np.sqrt(second_x_vector**2+second_y_vector**2)), (M_ANGLE_BETWEEN_NEIGHBORS, angle)] if self.neighbors_are_objects: features_and_data.append((M_PERCENT_TOUCHING, percent_touching)) for feature_name, data in features_and_data: m.add_measurement(self.object_name.value, self.get_measurement_name(feature_name), data) if len(first_objects) > 0: m.add_relate_measurement( self.module_num, cpmeas.NEIGHBORS, self.object_name.value, self.object_name.value if self.neighbors_are_objects else self.neighbors_name.value, m.image_set_number * np.ones(first_objects.shape, int), first_objects, m.image_set_number * np.ones(second_objects.shape, int), second_objects) labels = kept_labels neighbor_count_image = np.zeros(labels.shape,int) object_mask = objects.segmented != 0 object_indexes = objects.segmented[object_mask]-1 neighbor_count_image[object_mask] = neighbor_count[object_indexes] workspace.display_data.neighbor_count_image = neighbor_count_image if self.neighbors_are_objects: percent_touching_image = np.zeros(labels.shape) percent_touching_image[object_mask] = percent_touching[object_indexes] workspace.display_data.percent_touching_image = percent_touching_image image_set = workspace.image_set if self.wants_count_image.value: neighbor_cm_name = self.count_colormap.value neighbor_cm = get_colormap(neighbor_cm_name) sm = matplotlib.cm.ScalarMappable(cmap = neighbor_cm) img = sm.to_rgba(neighbor_count_image)[:,:,:3] img[:,:,0][~ object_mask] = 0 img[:,:,1][~ object_mask] = 0 img[:,:,2][~ object_mask] = 0 count_image = cpi.Image(img, masking_objects = objects) image_set.add(self.count_image_name.value, count_image) else: neighbor_cm_name = cpprefs.get_default_colormap() neighbor_cm = matplotlib.cm.get_cmap(neighbor_cm_name) if self.neighbors_are_objects and self.wants_percent_touching_image: percent_touching_cm_name = self.touching_colormap.value percent_touching_cm = get_colormap(percent_touching_cm_name) sm = matplotlib.cm.ScalarMappable(cmap = percent_touching_cm) img = sm.to_rgba(percent_touching_image)[:,:,:3] img[:,:,0][~ object_mask] = 0 img[:,:,1][~ object_mask] = 0 img[:,:,2][~ object_mask] = 0 touching_image = cpi.Image(img, masking_objects = objects) image_set.add(self.touching_image_name.value, touching_image) else: percent_touching_cm_name = cpprefs.get_default_colormap() percent_touching_cm = matplotlib.cm.get_cmap(percent_touching_cm_name) if self.show_window: workspace.display_data.neighbor_cm_name = neighbor_cm_name workspace.display_data.percent_touching_cm_name = percent_touching_cm_name workspace.display_data.orig_labels = objects.segmented workspace.display_data.expanded_labels = expanded_labels workspace.display_data.object_mask = object_mask
def run(self, workspace): assert isinstance(workspace, cpw.Workspace) image_name = self.image_name.value image = workspace.image_set.get_image(image_name, must_be_grayscale=True) workspace.display_data.statistics = [] img = image.pixel_data mask = image.mask objects = workspace.object_set.get_objects(self.primary_objects.value) global_threshold = None if self.method == M_DISTANCE_N: has_threshold = False else: thresholded_image = self.threshold_image(image_name, workspace) has_threshold = True # # Get the following labels: # * all edited labels # * labels touching the edge, including small removed # labels_in = objects.unedited_segmented.copy() labels_touching_edge = np.hstack( (labels_in[0, :], labels_in[-1, :], labels_in[:, 0], labels_in[:, -1])) labels_touching_edge = np.unique(labels_touching_edge) is_touching = np.zeros(np.max(labels_in) + 1, bool) is_touching[labels_touching_edge] = True is_touching = is_touching[labels_in] labels_in[(~is_touching) & (objects.segmented == 0)] = 0 # # Stretch the input labels to match the image size. If there's no # label matrix, then there's no label in that area. # if tuple(labels_in.shape) != tuple(img.shape): tmp = np.zeros(img.shape, labels_in.dtype) i_max = min(img.shape[0], labels_in.shape[0]) j_max = min(img.shape[1], labels_in.shape[1]) tmp[:i_max, :j_max] = labels_in[:i_max, :j_max] labels_in = tmp if self.method in (M_DISTANCE_B, M_DISTANCE_N): if self.method == M_DISTANCE_N: distances, (i, j) = scind.distance_transform_edt( labels_in == 0, return_indices=True) labels_out = np.zeros(labels_in.shape, int) dilate_mask = distances <= self.distance_to_dilate.value labels_out[dilate_mask] =\ labels_in[i[dilate_mask],j[dilate_mask]] else: labels_out, distances = propagate(img, labels_in, thresholded_image, 1.0) labels_out[distances > self.distance_to_dilate.value] = 0 labels_out[labels_in > 0] = labels_in[labels_in > 0] if self.fill_holes: small_removed_segmented_out = fill_labeled_holes(labels_out) else: small_removed_segmented_out = labels_out # # Create the final output labels by removing labels in the # output matrix that are missing from the segmented image # segmented_labels = objects.segmented segmented_out = self.filter_labels(small_removed_segmented_out, objects, workspace) elif self.method == M_PROPAGATION: labels_out, distance = propagate(img, labels_in, thresholded_image, self.regularization_factor.value) if self.fill_holes: small_removed_segmented_out = fill_labeled_holes(labels_out) else: small_removed_segmented_out = labels_out.copy() segmented_out = self.filter_labels(small_removed_segmented_out, objects, workspace) elif self.method == M_WATERSHED_G: # # First, apply the sobel filter to the image (both horizontal # and vertical). The filter measures gradient. # sobel_image = np.abs(scind.sobel(img)) # # Combine the image mask and threshold to mask the watershed # watershed_mask = np.logical_or(thresholded_image, labels_in > 0) watershed_mask = np.logical_and(watershed_mask, mask) # # Perform the first watershed # labels_out = watershed(sobel_image, labels_in, np.ones((3, 3), bool), mask=watershed_mask) if self.fill_holes: small_removed_segmented_out = fill_labeled_holes(labels_out) else: small_removed_segmented_out = labels_out.copy() segmented_out = self.filter_labels(small_removed_segmented_out, objects, workspace) elif self.method == M_WATERSHED_I: # # invert the image so that the maxima are filled first # and the cells compete over what's close to the threshold # inverted_img = 1 - img # # Same as above, but perform the watershed on the original image # watershed_mask = np.logical_or(thresholded_image, labels_in > 0) watershed_mask = np.logical_and(watershed_mask, mask) # # Perform the watershed # labels_out = watershed(inverted_img, labels_in, np.ones((3, 3), bool), mask=watershed_mask) if self.fill_holes: small_removed_segmented_out = fill_labeled_holes(labels_out) else: small_removed_segmented_out = labels_out segmented_out = self.filter_labels(small_removed_segmented_out, objects, workspace) if self.wants_discard_edge and self.wants_discard_primary: # # Make a new primary object # lookup = scind.maximum(segmented_out, objects.segmented, range(np.max(objects.segmented) + 1)) lookup = fix(lookup) lookup[0] = 0 lookup[lookup != 0] = np.arange(np.sum(lookup != 0)) + 1 segmented_labels = lookup[objects.segmented] segmented_out = lookup[segmented_out] new_objects = cpo.Objects() new_objects.segmented = segmented_labels if objects.has_unedited_segmented: new_objects.unedited_segmented = objects.unedited_segmented if objects.has_small_removed_segmented: new_objects.small_removed_segmented = objects.small_removed_segmented new_objects.parent_image = objects.parent_image primary_outline = outline(segmented_labels) if self.wants_primary_outlines: out_img = cpi.Image(primary_outline.astype(bool), parent_image=image) workspace.image_set.add(self.new_primary_outlines_name.value, out_img) else: primary_outline = outline(objects.segmented) secondary_outline = outline(segmented_out) # # Add the objects to the object set # objects_out = cpo.Objects() objects_out.unedited_segmented = small_removed_segmented_out objects_out.small_removed_segmented = small_removed_segmented_out objects_out.segmented = segmented_out objects_out.parent_image = image objname = self.objects_name.value workspace.object_set.add_objects(objects_out, objname) if self.use_outlines.value: out_img = cpi.Image(secondary_outline.astype(bool), parent_image=image) workspace.image_set.add(self.outlines_name.value, out_img) object_count = np.max(segmented_out) # # Add measurements # measurements = workspace.measurements cpmi.add_object_count_measurements(measurements, objname, object_count) cpmi.add_object_location_measurements(measurements, objname, segmented_out) # # Relate the secondary objects to the primary ones and record # the relationship. # children_per_parent, parents_of_children = \ objects.relate_children(objects_out) measurements.add_measurement(self.primary_objects.value, cpmi.FF_CHILDREN_COUNT % objname, children_per_parent) measurements.add_measurement( objname, cpmi.FF_PARENT % self.primary_objects.value, parents_of_children) image_numbers = np.ones(len(parents_of_children), int) *\ measurements.image_set_number mask = parents_of_children > 0 measurements.add_relate_measurement( self.module_num, R_PARENT, self.primary_objects.value, self.objects_name.value, image_numbers[mask], parents_of_children[mask], image_numbers[mask], np.arange(1, len(parents_of_children) + 1)[mask]) # # If primary objects were created, add them # if self.wants_discard_edge and self.wants_discard_primary: workspace.object_set.add_objects( new_objects, self.new_primary_objects_name.value) cpmi.add_object_count_measurements( measurements, self.new_primary_objects_name.value, np.max(new_objects.segmented)) cpmi.add_object_location_measurements( measurements, self.new_primary_objects_name.value, new_objects.segmented) for parent_objects, parent_name, child_objects, child_name in ( (objects, self.primary_objects.value, new_objects, self.new_primary_objects_name.value), (new_objects, self.new_primary_objects_name.value, objects_out, objname)): children_per_parent, parents_of_children = \ parent_objects.relate_children(child_objects) measurements.add_measurement( parent_name, cpmi.FF_CHILDREN_COUNT % child_name, children_per_parent) measurements.add_measurement(child_name, cpmi.FF_PARENT % parent_name, parents_of_children) if self.show_window: object_area = np.sum(segmented_out > 0) workspace.display_data.object_pct = \ 100 * object_area / np.product(segmented_out.shape) workspace.display_data.img = img workspace.display_data.segmented_out = segmented_out workspace.display_data.primary_labels = objects.segmented workspace.display_data.global_threshold = global_threshold workspace.display_data.object_count = object_count
def run(self, workspace): """Run the module workspace - The workspace contains pipeline - instance of cpp for this run image_set - the images in the image set being processed object_set - the objects (labeled masks) in this image set measurements - the measurements for this run frame - the parent frame to whatever frame is created. None means don't draw. """ orig_objects_name = self.object_name.value filtered_objects_name = self.filtered_objects.value orig_objects = workspace.object_set.get_objects(orig_objects_name) assert isinstance(orig_objects, cpo.Objects) orig_labels = [l for l, c in orig_objects.get_labels()] if self.wants_image_display: guide_image = workspace.image_set.get_image(self.image_name.value) guide_image = guide_image.pixel_data if np.any(guide_image != np.min(guide_image)): guide_image = (guide_image - np.min(guide_image)) / ( np.max(guide_image) - np.min(guide_image)) else: guide_image = None filtered_labels = workspace.interaction_request( self, orig_labels, guide_image, workspace.measurements.image_set_number) if filtered_labels is None: # Ask whoever is listening to stop doing stuff workspace.cancel_request() # Have to soldier on until the cancel takes effect... filtered_labels = orig_labels # # Renumber objects consecutively if asked to do so # unique_labels = np.unique(np.array(filtered_labels)) unique_labels = unique_labels[unique_labels != 0] object_count = len(unique_labels) if self.renumber_choice == R_RENUMBER: mapping = np.zeros( 1 if len(unique_labels) == 0 else np.max(unique_labels) + 1, int) mapping[unique_labels] = np.arange(1, object_count + 1) filtered_labels = [mapping[l] for l in filtered_labels] # # Make the objects out of the labels # filtered_objects = cpo.Objects() i, j = np.mgrid[0:filtered_labels[0].shape[0], 0:filtered_labels[0].shape[1]] ijv = np.zeros((0, 3), filtered_labels[0].dtype) for l in filtered_labels: ijv = np.vstack( (ijv, np.column_stack((i[l != 0], j[l != 0], l[l != 0])))) filtered_objects.set_ijv(ijv, orig_labels[0].shape) if orig_objects.has_unedited_segmented(): filtered_objects.unedited_segmented = orig_objects.unedited_segmented if orig_objects.parent_image is not None: filtered_objects.parent_image = orig_objects.parent_image workspace.object_set.add_objects(filtered_objects, filtered_objects_name) # # Add parent/child & other measurements # m = workspace.measurements child_count, parents = orig_objects.relate_children(filtered_objects) m.add_measurement(filtered_objects_name, I.FF_PARENT % orig_objects_name, parents) m.add_measurement(orig_objects_name, I.FF_CHILDREN_COUNT % filtered_objects_name, child_count) # # The object count # I.add_object_count_measurements(m, filtered_objects_name, object_count) # # The object locations # I.add_object_location_measurements_ijv(m, filtered_objects_name, ijv) # # Outlines if we want them # if self.wants_outlines: outlines_name = self.outlines_name.value outlines = outline(filtered_labels[0]).astype(bool) outlines_image = cpi.Image(outlines) workspace.image_set.add(outlines_name, outlines_image) workspace.display_data.orig_ijv = orig_objects.ijv workspace.display_data.filtered_ijv = filtered_objects.ijv workspace.display_data.shape = orig_labels[0].shape
def run(self, workspace): if self.show_window: workspace.display_data.col_labels = ("Image", "channel", "Object", "Feature", "Mean", "Median", "STD") workspace.display_data.statistics = statistics = [] for im in self.images: image_name = im.name image = workspace.image_set.get_image(image_name.value, must_be_grayscale=False) nchan = im.nchannels.value for channel in range(nchan): for object_name in [obj.name for obj in self.objects]: # Need to refresh image after each iteration... if nchan == 1: img = image.pixel_data.copy() else: img = image.pixel_data[:, :, channel].squeeze().copy() if image.has_mask: masked_image = img.copy() masked_image[~image.mask] = 0 else: masked_image = img objects = workspace.object_set.get_objects( object_name.value) nobjects = objects.count integrated_intensity = np.zeros((nobjects, )) integrated_intensity_edge = np.zeros((nobjects, )) mean_intensity = np.zeros((nobjects, )) mean_intensity_edge = np.zeros((nobjects, )) std_intensity = np.zeros((nobjects, )) std_intensity_edge = np.zeros((nobjects, )) min_intensity = np.zeros((nobjects, )) min_intensity_edge = np.zeros((nobjects, )) max_intensity = np.zeros((nobjects, )) max_intensity_edge = np.zeros((nobjects, )) mass_displacement = np.zeros((nobjects, )) lower_quartile_intensity = np.zeros((nobjects, )) median_intensity = np.zeros((nobjects, )) mad_intensity = np.zeros((nobjects, )) upper_quartile_intensity = np.zeros((nobjects, )) cmi_x = np.zeros((nobjects, )) cmi_y = np.zeros((nobjects, )) max_x = np.zeros((nobjects, )) max_y = np.zeros((nobjects, )) for labels, lindexes in objects.get_labels(): lindexes = lindexes[lindexes != 0] labels, img = cpo.crop_labels_and_image(labels, img) _, masked_image = cpo.crop_labels_and_image( labels, masked_image) outlines = cpmo.outline(labels) if image.has_mask: _, mask = cpo.crop_labels_and_image( labels, image.mask) masked_labels = labels.copy() masked_labels[~mask] = 0 masked_outlines = outlines.copy() masked_outlines[~mask] = 0 else: masked_labels = labels masked_outlines = outlines lmask = masked_labels > 0 & np.isfinite( img) # Ignore NaNs, Infs has_objects = np.any(lmask) if has_objects: limg = img[lmask] llabels = labels[lmask] mesh_y, mesh_x = np.mgrid[0:masked_image.shape[0], 0:masked_image.shape[1]] mesh_x = mesh_x[lmask] mesh_y = mesh_y[lmask] lcount = fix( nd.sum(np.ones(len(limg)), llabels, lindexes)) integrated_intensity[lindexes - 1] = \ fix(nd.sum(limg, llabels, lindexes)) mean_intensity[lindexes - 1] = \ integrated_intensity[lindexes - 1] / lcount std_intensity[lindexes - 1] = np.sqrt( fix( nd.mean((limg - mean_intensity[llabels - 1])**2, llabels, lindexes))) min_intensity[lindexes - 1] = fix( nd.minimum(limg, llabels, lindexes)) max_intensity[lindexes - 1] = fix( nd.maximum(limg, llabels, lindexes)) # Compute the position of the intensity maximum max_position = np.array(fix( nd.maximum_position(limg, llabels, lindexes)), dtype=int) max_position = np.reshape( max_position, (max_position.shape[0], )) max_x[lindexes - 1] = mesh_x[max_position] max_y[lindexes - 1] = mesh_y[max_position] # The mass displacement is the distance between the center # of mass of the binary image and of the intensity image. The # center of mass is the average X or Y for the binary image # and the sum of X or Y * intensity / integrated intensity cm_x = fix(nd.mean(mesh_x, llabels, lindexes)) cm_y = fix(nd.mean(mesh_y, llabels, lindexes)) i_x = fix(nd.sum(mesh_x * limg, llabels, lindexes)) i_y = fix(nd.sum(mesh_y * limg, llabels, lindexes)) cmi_x[lindexes - 1] = i_x / integrated_intensity[lindexes - 1] cmi_y[lindexes - 1] = i_y / integrated_intensity[lindexes - 1] diff_x = cm_x - cmi_x[lindexes - 1] diff_y = cm_y - cmi_y[lindexes - 1] mass_displacement[lindexes - 1] = \ np.sqrt(diff_x * diff_x + diff_y * diff_y) # # Sort the intensities by label, then intensity. # For each label, find the index above and below # the 25%, 50% and 75% mark and take the weighted # average. # order = np.lexsort((limg, llabels)) areas = lcount.astype(int) indices = np.cumsum(areas) - areas for dest, fraction in ((lower_quartile_intensity, 1.0 / 4.0), (median_intensity, 1.0 / 2.0), (upper_quartile_intensity, 3.0 / 4.0)): qindex = indices.astype( float) + areas * fraction qfraction = qindex - np.floor(qindex) qindex = qindex.astype(int) qmask = qindex < indices + areas - 1 qi = qindex[qmask] qf = qfraction[qmask] dest[lindexes[qmask] - 1] = (limg[order[qi]] * (1 - qf) + limg[order[qi + 1]] * qf) # # In some situations (e.g. only 3 points), there may # not be an upper bound. # qmask = (~qmask) & (areas > 0) dest[lindexes[qmask] - 1] = limg[order[qindex[qmask]]] # # Once again, for the MAD # madimg = np.abs(limg - median_intensity[llabels - 1]) order = np.lexsort((madimg, llabels)) qindex = indices.astype(float) + areas / 2.0 qfraction = qindex - np.floor(qindex) qindex = qindex.astype(int) qmask = qindex < indices + areas - 1 qi = qindex[qmask] qf = qfraction[qmask] mad_intensity[lindexes[qmask] - 1] = (madimg[order[qi]] * (1 - qf) + madimg[order[qi + 1]] * qf) qmask = (~qmask) & (areas > 0) mad_intensity[lindexes[qmask] - 1] = madimg[order[qindex[qmask]]] emask = masked_outlines > 0 eimg = img[emask] elabels = labels[emask] has_edge = len(eimg) > 0 if has_edge: ecount = fix( nd.sum(np.ones(len(eimg)), elabels, lindexes)) integrated_intensity_edge[lindexes - 1] = \ fix(nd.sum(eimg, elabels, lindexes)) mean_intensity_edge[lindexes - 1] = \ integrated_intensity_edge[lindexes - 1] / ecount std_intensity_edge[lindexes - 1] = \ np.sqrt(fix(nd.mean( (eimg - mean_intensity_edge[elabels - 1]) ** 2, elabels, lindexes))) min_intensity_edge[lindexes - 1] = fix( nd.minimum(eimg, elabels, lindexes)) max_intensity_edge[lindexes - 1] = fix( nd.maximum(eimg, elabels, lindexes)) m = workspace.measurements for category, feature_name, measurement in \ ((INTENSITY, INTEGRATED_INTENSITY, integrated_intensity), (INTENSITY, MEAN_INTENSITY, mean_intensity), (INTENSITY, STD_INTENSITY, std_intensity), (INTENSITY, MIN_INTENSITY, min_intensity), (INTENSITY, MAX_INTENSITY, max_intensity), (INTENSITY, INTEGRATED_INTENSITY_EDGE, integrated_intensity_edge), (INTENSITY, MEAN_INTENSITY_EDGE, mean_intensity_edge), (INTENSITY, STD_INTENSITY_EDGE, std_intensity_edge), (INTENSITY, MIN_INTENSITY_EDGE, min_intensity_edge), (INTENSITY, MAX_INTENSITY_EDGE, max_intensity_edge), (INTENSITY, MASS_DISPLACEMENT, mass_displacement), (INTENSITY, LOWER_QUARTILE_INTENSITY, lower_quartile_intensity), (INTENSITY, MEDIAN_INTENSITY, median_intensity), (INTENSITY, MAD_INTENSITY, mad_intensity), (INTENSITY, UPPER_QUARTILE_INTENSITY, upper_quartile_intensity), (C_LOCATION, LOC_CMI_X, cmi_x), (C_LOCATION, LOC_CMI_Y, cmi_y), (C_LOCATION, LOC_MAX_X, max_x), (C_LOCATION, LOC_MAX_Y, max_y)): measurement_name = "%s_%s_%s_c%s" % ( category, feature_name, image_name.value, str(channel + 1)) m.add_measurement(object_name.value, measurement_name, measurement) if self.show_window and len(measurement) > 0: statistics.append( (image_name.value, 'c' + str(channel + 1), object_name.value, feature_name, np.round(np.mean(measurement), 3), np.round(np.median(measurement), 3), np.round(np.std(measurement), 3)))
def run(self, workspace): """Run the module on the current data set workspace - has the current image set, object set, measurements and the parent frame for the application if the module is allowed to display. If the module should not display, workspace.frame is None. """ # # The object set holds "objects". Each of these is a container # for holding up to three kinds of image labels. # object_set = workspace.object_set # # Get the primary objects (the centers to be removed). # Get the string value out of primary_object_name. # primary_objects = object_set.get_objects( self.primary_objects_name.value) # # Get the cleaned-up labels image # primary_labels = primary_objects.segmented # # Do the same with the secondary object secondary_objects = object_set.get_objects( self.secondary_objects_name.value) secondary_labels = secondary_objects.segmented # # If one of the two label images is smaller than the other, we # try to find the cropping mask and we apply that mask to the larger # try: if any([ p_size < s_size for p_size, s_size in zip( primary_labels.shape, secondary_labels.shape) ]): # # Look for a cropping mask associated with the primary_labels # and apply that mask to resize the secondary labels # secondary_labels = primary_objects.crop_image_similarly( secondary_labels) tertiary_image = primary_objects.parent_image elif any([ p_size > s_size for p_size, s_size in zip( primary_labels.shape, secondary_labels.shape) ]): primary_labels = secondary_objects.crop_image_similarly( primary_labels) tertiary_image = secondary_objects.parent_image elif secondary_objects.parent_image is not None: tertiary_image = secondary_objects.parent_image else: tertiary_image = primary_objects.parent_image except ValueError: # No suitable cropping - resize all to fit the secondary # labels which are the most critical. # primary_labels, _ = cpo.size_similarly(secondary_labels, primary_labels) if secondary_objects.parent_image is not None: tertiary_image = secondary_objects.parent_image else: tertiary_image = primary_objects.parent_image if tertiary_image is not None: tertiary_image, _ = cpo.size_similarly( secondary_labels, tertiary_image) # # Find the outlines of the primary image and use this to shrink the # primary image by one. This guarantees that there is something left # of the secondary image after subtraction # primary_outline = outline(primary_labels) tertiary_labels = secondary_labels.copy() if self.shrink_primary: primary_mask = np.logical_or(primary_labels == 0, primary_outline) else: primary_mask = primary_labels == 0 tertiary_labels[primary_mask == False] = 0 # # Get the outlines of the tertiary image # tertiary_outlines = outline(tertiary_labels) != 0 # # Make the tertiary objects container # tertiary_objects = cpo.Objects() tertiary_objects.segmented = tertiary_labels tertiary_objects.parent_image = tertiary_image # # Relate tertiary objects to their parents & record # child_count_of_secondary, secondary_parents = \ secondary_objects.relate_children(tertiary_objects) if self.shrink_primary: child_count_of_primary, primary_parents = \ primary_objects.relate_children(tertiary_objects) else: # Primary and tertiary don't overlap. # Establish overlap between primary and secondary and commute _, secondary_of_primary = \ secondary_objects.relate_children(primary_objects) mask = secondary_of_primary != 0 child_count_of_primary = np.zeros(mask.shape, int) child_count_of_primary[mask] = child_count_of_secondary[ secondary_of_primary[mask] - 1] primary_parents = np.zeros(secondary_parents.shape, secondary_parents.dtype) primary_of_secondary = np.zeros(secondary_objects.count + 1, int) primary_of_secondary[secondary_of_primary] = \ np.arange(1, len(secondary_of_primary) + 1) primary_of_secondary[0] = 0 primary_parents = primary_of_secondary[secondary_parents] # # Write out the objects # workspace.object_set.add_objects(tertiary_objects, self.subregion_objects_name.value) # # Write out the measurements # m = workspace.measurements # # The parent/child associations # for parent_objects_name, parents_of, child_count, relationship in ( (self.primary_objects_name, primary_parents, child_count_of_primary, R_REMOVED), (self.secondary_objects_name, secondary_parents, child_count_of_secondary, R_PARENT)): m.add_measurement( self.subregion_objects_name.value, cellprofiler.measurement.FF_PARENT % parent_objects_name.value, parents_of) m.add_measurement( parent_objects_name.value, cellprofiler.measurement.FF_CHILDREN_COUNT % self.subregion_objects_name.value, child_count) mask = parents_of != 0 image_number = np.ones(np.sum(mask), int) * m.image_set_number child_object_number = np.argwhere(mask).flatten() + 1 parent_object_number = parents_of[mask] m.add_relate_measurement(self.module_num, relationship, parent_objects_name.value, self.subregion_objects_name.value, image_number, parent_object_number, image_number, child_object_number) object_count = tertiary_objects.count # # The object count # cpmi.add_object_count_measurements(workspace.measurements, self.subregion_objects_name.value, object_count) # # The object locations # cpmi.add_object_location_measurements( workspace.measurements, self.subregion_objects_name.value, tertiary_labels) if self.show_window: workspace.display_data.primary_labels = primary_labels workspace.display_data.secondary_labels = secondary_labels workspace.display_data.tertiary_labels = tertiary_labels workspace.display_data.tertiary_outlines = tertiary_outlines
def test_00_00_zeros(self): x = numpy.zeros((10,10),int) result = OL.outline(x) self.assertTrue(numpy.all(x==result))
def run(self, workspace): '''Run the module on an image set''' object_name = self.object_name.value remaining_object_name = self.remaining_objects.value original_objects = workspace.object_set.get_objects(object_name) if self.mask_choice == MC_IMAGE: mask = workspace.image_set.get_image(self.masking_image.value, must_be_binary=True) mask = mask.pixel_data else: masking_objects = workspace.object_set.get_objects( self.masking_objects.value) mask = masking_objects.segmented > 0 if self.wants_inverted_mask: mask = ~mask # # Load the labels # labels = original_objects.segmented.copy() nobjects = np.max(labels) # # Resize the mask to cover the objects # mask, m1 = cpo.size_similarly(labels, mask) mask[~m1] = False # # Apply the mask according to the overlap choice. # if nobjects == 0: pass elif self.overlap_choice == P_MASK: labels = labels * mask else: pixel_counts = fix( scind.sum(mask, labels, np.arange(1, nobjects + 1, dtype=np.int32))) if self.overlap_choice == P_KEEP: keep = pixel_counts > 0 else: total_pixels = fix( scind.sum(np.ones(labels.shape), labels, np.arange(1, nobjects + 1, dtype=np.int32))) if self.overlap_choice == P_REMOVE: keep = pixel_counts == total_pixels elif self.overlap_choice == P_REMOVE_PERCENTAGE: fraction = self.overlap_fraction.value keep = pixel_counts / total_pixels >= fraction else: raise NotImplementedError( "Unknown overlap-handling choice: %s", self.overlap_choice.value) keep = np.hstack(([False], keep)) labels[~keep[labels]] = 0 # # Renumber the labels matrix if requested # if self.retain_or_renumber == R_RENUMBER: unique_labels = np.unique(labels[labels != 0]) indexer = np.zeros(nobjects + 1, int) indexer[unique_labels] = np.arange(1, len(unique_labels) + 1) labels = indexer[labels] parent_objects = unique_labels else: parent_objects = np.arange(1, nobjects + 1) # # Add the objects # remaining_objects = cpo.Objects() remaining_objects.segmented = labels remaining_objects.unedited_segmented = original_objects.unedited_segmented workspace.object_set.add_objects(remaining_objects, remaining_object_name) # # Add measurements # m = workspace.measurements m.add_measurement(remaining_object_name, I.FF_PARENT % object_name, parent_objects) if np.max(original_objects.segmented) == 0: child_count = np.array([], int) else: child_count = fix( scind.sum(labels, original_objects.segmented, np.arange(1, nobjects + 1, dtype=np.int32))) child_count = (child_count > 0).astype(int) m.add_measurement(object_name, I.FF_CHILDREN_COUNT % remaining_object_name, child_count) if self.retain_or_renumber == R_RETAIN: remaining_object_count = nobjects else: remaining_object_count = len(unique_labels) I.add_object_count_measurements(m, remaining_object_name, remaining_object_count) I.add_object_location_measurements(m, remaining_object_name, labels) # # Add an outline if asked to do so # if self.wants_outlines.value: outline_image = cpi.Image( outline(labels) > 0, parent_image=original_objects.parent_image) workspace.image_set.add(self.outlines_name.value, outline_image) # # Save the input, mask and output images for display # if self.show_window: workspace.display_data.original_labels = original_objects.segmented workspace.display_data.final_labels = labels workspace.display_data.mask = mask
def run(self, workspace): objects = workspace.object_set.get_objects(self.object_name.value) dimensions = len(objects.shape) assert isinstance(objects, Objects) has_pixels = objects.areas > 0 labels = objects.small_removed_segmented kept_labels = objects.segmented neighbor_objects = workspace.object_set.get_objects( self.neighbors_name.value) neighbor_labels = neighbor_objects.small_removed_segmented neighbor_kept_labels = neighbor_objects.segmented assert isinstance(neighbor_objects, Objects) if not self.wants_excluded_objects.value: # Remove labels not present in kept segmentation while preserving object IDs. mask = neighbor_kept_labels > 0 neighbor_labels[~mask] = 0 nobjects = numpy.max(labels) nkept_objects = len(objects.indices) nneighbors = numpy.max(neighbor_labels) _, object_numbers = objects.relate_labels(labels, kept_labels) if self.neighbors_are_objects: neighbor_numbers = object_numbers neighbor_has_pixels = has_pixels else: _, neighbor_numbers = neighbor_objects.relate_labels( neighbor_labels, neighbor_objects.small_removed_segmented) neighbor_has_pixels = numpy.bincount( neighbor_labels.ravel())[1:] > 0 neighbor_count = numpy.zeros((nobjects, )) pixel_count = numpy.zeros((nobjects, )) first_object_number = numpy.zeros((nobjects, ), int) second_object_number = numpy.zeros((nobjects, ), int) first_x_vector = numpy.zeros((nobjects, )) second_x_vector = numpy.zeros((nobjects, )) first_y_vector = numpy.zeros((nobjects, )) second_y_vector = numpy.zeros((nobjects, )) angle = numpy.zeros((nobjects, )) percent_touching = numpy.zeros((nobjects, )) expanded_labels = None if self.distance_method == D_EXPAND: # Find the i,j coordinates of the nearest foreground point # to every background point if dimensions == 2: i, j = scipy.ndimage.distance_transform_edt( labels == 0, return_distances=False, return_indices=True) # Assign each background pixel to the label of its nearest # foreground pixel. Assign label to label for foreground. labels = labels[i, j] else: k, i, j = scipy.ndimage.distance_transform_edt( labels == 0, return_distances=False, return_indices=True) labels = labels[k, i, j] expanded_labels = labels # for display distance = 1 # dilate once to make touching edges overlap scale = S_EXPANDED if self.neighbors_are_objects: neighbor_labels = labels.copy() elif self.distance_method == D_WITHIN: distance = self.distance.value scale = str(distance) elif self.distance_method == D_ADJACENT: distance = 1 scale = S_ADJACENT else: raise ValueError("Unknown distance method: %s" % self.distance_method.value) if nneighbors > (1 if self.neighbors_are_objects else 0): first_objects = [] second_objects = [] object_indexes = numpy.arange(nobjects, dtype=numpy.int32) + 1 # # First, compute the first and second nearest neighbors, # and the angles between self and the first and second # nearest neighbors # ocenters = centers_of_labels( objects.small_removed_segmented).transpose() ncenters = centers_of_labels( neighbor_objects.small_removed_segmented).transpose() areas = fix( scipy.ndimage.sum(numpy.ones(labels.shape), labels, object_indexes)) perimeter_outlines = outline(labels) perimeters = fix( scipy.ndimage.sum(numpy.ones(labels.shape), perimeter_outlines, object_indexes)) # # order[:,0] should be arange(nobjects) # order[:,1] should be the nearest neighbor # order[:,2] should be the next nearest neighbor # order = numpy.zeros((nobjects, min(nneighbors, 3)), dtype=numpy.uint32) j = numpy.arange(nneighbors) # (0, 1, 2) unless there are less than 3 neighbors partition_keys = tuple(range(min(nneighbors, 3))) for i in range(nobjects): dr = numpy.sqrt((ocenters[i, 0] - ncenters[j, 0])**2 + (ocenters[i, 1] - ncenters[j, 1])**2) order[i, :] = numpy.argpartition(dr, partition_keys)[:3] first_neighbor = 1 if self.neighbors_are_objects else 0 first_object_index = order[:, first_neighbor] first_x_vector = ncenters[first_object_index, 1] - ocenters[:, 1] first_y_vector = ncenters[first_object_index, 0] - ocenters[:, 0] if nneighbors > first_neighbor + 1: second_object_index = order[:, first_neighbor + 1] second_x_vector = ncenters[second_object_index, 1] - ocenters[:, 1] second_y_vector = ncenters[second_object_index, 0] - ocenters[:, 0] v1 = numpy.array((first_x_vector, first_y_vector)) v2 = numpy.array((second_x_vector, second_y_vector)) # # Project the unit vector v1 against the unit vector v2 # dot = numpy.sum(v1 * v2, 0) / numpy.sqrt( numpy.sum(v1**2, 0) * numpy.sum(v2**2, 0)) angle = numpy.arccos(dot) * 180.0 / numpy.pi # Make the structuring element for dilation if dimensions == 2: strel = strel_disk(distance) else: strel = skimage.morphology.ball(distance) # # A little bigger one to enter into the border with a structure # that mimics the one used to create the outline # if dimensions == 2: strel_touching = strel_disk(distance + 0.5) else: strel_touching = skimage.morphology.ball(distance + 0.5) # # Get the extents for each object and calculate the patch # that excises the part of the image that is "distance" # away if dimensions == 2: i, j = numpy.mgrid[0:labels.shape[0], 0:labels.shape[1]] minimums_i, maximums_i, _, _ = scipy.ndimage.extrema( i, labels, object_indexes) minimums_j, maximums_j, _, _ = scipy.ndimage.extrema( j, labels, object_indexes) minimums_i = numpy.maximum(fix(minimums_i) - distance, 0).astype(int) maximums_i = numpy.minimum( fix(maximums_i) + distance + 1, labels.shape[0]).astype(int) minimums_j = numpy.maximum(fix(minimums_j) - distance, 0).astype(int) maximums_j = numpy.minimum( fix(maximums_j) + distance + 1, labels.shape[1]).astype(int) else: k, i, j = numpy.mgrid[0:labels.shape[0], 0:labels.shape[1], 0:labels.shape[2]] minimums_k, maximums_k, _, _ = scipy.ndimage.extrema( k, labels, object_indexes) minimums_i, maximums_i, _, _ = scipy.ndimage.extrema( i, labels, object_indexes) minimums_j, maximums_j, _, _ = scipy.ndimage.extrema( j, labels, object_indexes) minimums_k = numpy.maximum(fix(minimums_k) - distance, 0).astype(int) maximums_k = numpy.minimum( fix(maximums_k) + distance + 1, labels.shape[0]).astype(int) minimums_i = numpy.maximum(fix(minimums_i) - distance, 0).astype(int) maximums_i = numpy.minimum( fix(maximums_i) + distance + 1, labels.shape[1]).astype(int) minimums_j = numpy.maximum(fix(minimums_j) - distance, 0).astype(int) maximums_j = numpy.minimum( fix(maximums_j) + distance + 1, labels.shape[2]).astype(int) # # Loop over all objects # Calculate which ones overlap "index" # Calculate how much overlap there is of others to "index" # for object_number in object_numbers: if object_number == 0: # # No corresponding object in small-removed. This means # that the object has no pixels, e.g., not renumbered. # continue index = object_number - 1 if dimensions == 2: patch = labels[minimums_i[index]:maximums_i[index], minimums_j[index]:maximums_j[index], ] npatch = neighbor_labels[ minimums_i[index]:maximums_i[index], minimums_j[index]:maximums_j[index], ] else: patch = labels[minimums_k[index]:maximums_k[index], minimums_i[index]:maximums_i[index], minimums_j[index]:maximums_j[index], ] npatch = neighbor_labels[ minimums_k[index]:maximums_k[index], minimums_i[index]:maximums_i[index], minimums_j[index]:maximums_j[index], ] # # Find the neighbors # patch_mask = patch == (index + 1) if distance <= 5: extended = scipy.ndimage.binary_dilation(patch_mask, strel) else: extended = (scipy.signal.fftconvolve( patch_mask, strel, mode="same") > 0.5) neighbors = numpy.unique(npatch[extended]) neighbors = neighbors[neighbors != 0] if self.neighbors_are_objects: neighbors = neighbors[neighbors != object_number] nc = len(neighbors) neighbor_count[index] = nc if nc > 0: first_objects.append(numpy.ones(nc, int) * object_number) second_objects.append(neighbors) # # Find the # of overlapping pixels. Dilate the neighbors # and see how many pixels overlap our image. Use a 3x3 # structuring element to expand the overlapping edge # into the perimeter. # if dimensions == 2: outline_patch = (perimeter_outlines[ minimums_i[index]:maximums_i[index], minimums_j[index]:maximums_j[index], ] == object_number ) else: outline_patch = (perimeter_outlines[ minimums_k[index]:maximums_k[index], minimums_i[index]:maximums_i[index], minimums_j[index]:maximums_j[index], ] == object_number ) if self.neighbors_are_objects: extendme = (patch != 0) & (patch != object_number) if distance <= 5: extended = scipy.ndimage.binary_dilation( extendme, strel_touching) else: extended = (scipy.signal.fftconvolve( extendme, strel_touching, mode="same") > 0.5) else: if distance <= 5: extended = scipy.ndimage.binary_dilation( (npatch != 0), strel_touching) else: extended = (scipy.signal.fftconvolve( (npatch != 0), strel_touching, mode="same") > 0.5) overlap = numpy.sum(outline_patch & extended) pixel_count[index] = overlap if sum([len(x) for x in first_objects]) > 0: first_objects = numpy.hstack(first_objects) reverse_object_numbers = numpy.zeros( max(numpy.max(object_numbers), numpy.max(first_objects)) + 1, int) reverse_object_numbers[object_numbers] = ( numpy.arange(len(object_numbers)) + 1) first_objects = reverse_object_numbers[first_objects] second_objects = numpy.hstack(second_objects) reverse_neighbor_numbers = numpy.zeros( max(numpy.max(neighbor_numbers), numpy.max(second_objects)) + 1, int) reverse_neighbor_numbers[neighbor_numbers] = ( numpy.arange(len(neighbor_numbers)) + 1) second_objects = reverse_neighbor_numbers[second_objects] to_keep = (first_objects > 0) & (second_objects > 0) first_objects = first_objects[to_keep] second_objects = second_objects[to_keep] else: first_objects = numpy.zeros(0, int) second_objects = numpy.zeros(0, int) percent_touching = pixel_count * 100 / perimeters object_indexes = object_numbers - 1 neighbor_indexes = neighbor_numbers - 1 # # Have to recompute nearest # first_object_number = numpy.zeros(nkept_objects, int) second_object_number = numpy.zeros(nkept_objects, int) if nkept_objects > (1 if self.neighbors_are_objects else 0): di = (ocenters[object_indexes[:, numpy.newaxis], 0] - ncenters[neighbor_indexes[numpy.newaxis, :], 0]) dj = (ocenters[object_indexes[:, numpy.newaxis], 1] - ncenters[neighbor_indexes[numpy.newaxis, :], 1]) distance_matrix = numpy.sqrt(di * di + dj * dj) distance_matrix[~has_pixels, :] = numpy.inf distance_matrix[:, ~neighbor_has_pixels] = numpy.inf # # order[:,0] should be arange(nobjects) # order[:,1] should be the nearest neighbor # order[:,2] should be the next nearest neighbor # order = numpy.lexsort([distance_matrix ]).astype(first_object_number.dtype) if self.neighbors_are_objects: first_object_number[has_pixels] = order[has_pixels, 1] + 1 if nkept_objects > 2: second_object_number[has_pixels] = order[has_pixels, 2] + 1 else: first_object_number[has_pixels] = order[has_pixels, 0] + 1 if order.shape[1] > 1: second_object_number[has_pixels] = order[has_pixels, 1] + 1 else: object_indexes = object_numbers - 1 neighbor_indexes = neighbor_numbers - 1 first_objects = numpy.zeros(0, int) second_objects = numpy.zeros(0, int) # # Now convert all measurements from the small-removed to # the final number set. # neighbor_count = neighbor_count[object_indexes] neighbor_count[~has_pixels] = 0 percent_touching = percent_touching[object_indexes] percent_touching[~has_pixels] = 0 first_x_vector = first_x_vector[object_indexes] second_x_vector = second_x_vector[object_indexes] first_y_vector = first_y_vector[object_indexes] second_y_vector = second_y_vector[object_indexes] angle = angle[object_indexes] # # Record the measurements # assert isinstance(workspace, Workspace) m = workspace.measurements assert isinstance(m, Measurements) image_set = workspace.image_set features_and_data = [ (M_NUMBER_OF_NEIGHBORS, neighbor_count), (M_FIRST_CLOSEST_OBJECT_NUMBER, first_object_number), ( M_FIRST_CLOSEST_DISTANCE, numpy.sqrt(first_x_vector**2 + first_y_vector**2), ), (M_SECOND_CLOSEST_OBJECT_NUMBER, second_object_number), ( M_SECOND_CLOSEST_DISTANCE, numpy.sqrt(second_x_vector**2 + second_y_vector**2), ), (M_ANGLE_BETWEEN_NEIGHBORS, angle), (M_PERCENT_TOUCHING, percent_touching), ] for feature_name, data in features_and_data: m.add_measurement(self.object_name.value, self.get_measurement_name(feature_name), data) if len(first_objects) > 0: m.add_relate_measurement( self.module_num, NEIGHBORS, self.object_name.value, self.object_name.value if self.neighbors_are_objects else self.neighbors_name.value, m.image_set_number * numpy.ones(first_objects.shape, int), first_objects, m.image_set_number * numpy.ones(second_objects.shape, int), second_objects, ) labels = kept_labels neighbor_count_image = numpy.zeros(labels.shape, int) object_mask = objects.segmented != 0 object_indexes = objects.segmented[object_mask] - 1 neighbor_count_image[object_mask] = neighbor_count[object_indexes] workspace.display_data.neighbor_count_image = neighbor_count_image percent_touching_image = numpy.zeros(labels.shape) percent_touching_image[object_mask] = percent_touching[object_indexes] workspace.display_data.percent_touching_image = percent_touching_image image_set = workspace.image_set if self.wants_count_image.value: neighbor_cm_name = self.count_colormap.value neighbor_cm = get_colormap(neighbor_cm_name) sm = matplotlib.cm.ScalarMappable(cmap=neighbor_cm) img = sm.to_rgba(neighbor_count_image)[:, :, :3] img[:, :, 0][~object_mask] = 0 img[:, :, 1][~object_mask] = 0 img[:, :, 2][~object_mask] = 0 count_image = Image(img, masking_objects=objects) image_set.add(self.count_image_name.value, count_image) else: neighbor_cm_name = "Blues" neighbor_cm = matplotlib.cm.get_cmap(neighbor_cm_name) if self.wants_percent_touching_image: percent_touching_cm_name = self.touching_colormap.value percent_touching_cm = get_colormap(percent_touching_cm_name) sm = matplotlib.cm.ScalarMappable(cmap=percent_touching_cm) img = sm.to_rgba(percent_touching_image)[:, :, :3] img[:, :, 0][~object_mask] = 0 img[:, :, 1][~object_mask] = 0 img[:, :, 2][~object_mask] = 0 touching_image = Image(img, masking_objects=objects) image_set.add(self.touching_image_name.value, touching_image) else: percent_touching_cm_name = "Oranges" percent_touching_cm = matplotlib.cm.get_cmap( percent_touching_cm_name) if self.show_window: workspace.display_data.neighbor_cm_name = neighbor_cm_name workspace.display_data.percent_touching_cm_name = percent_touching_cm_name workspace.display_data.orig_labels = objects.segmented workspace.display_data.neighbor_labels = neighbor_labels workspace.display_data.expanded_labels = expanded_labels workspace.display_data.object_mask = object_mask workspace.display_data.dimensions = dimensions