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, 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 guide_image.dtype == bool: guide_image = guide_image.astype(int) if numpy.any(guide_image != numpy.min(guide_image)): guide_image = (guide_image - numpy.min(guide_image)) / ( numpy.max(guide_image) - numpy.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 = numpy.unique(numpy.array(filtered_labels)) unique_labels = unique_labels[unique_labels != 0] object_count = len(unique_labels) if self.renumber_choice == R_RENUMBER: mapping = numpy.zeros( 1 if len(unique_labels) == 0 else numpy.max(unique_labels) + 1, int) mapping[unique_labels] = numpy.arange(1, object_count + 1) filtered_labels = [mapping[l] for l in filtered_labels] # # Make the objects out of the labels # filtered_objects = Objects() i, j = numpy.mgrid[0:filtered_labels[0].shape[0], 0:filtered_labels[0].shape[1]] ijv = numpy.zeros((0, 3), filtered_labels[0].dtype) for l in filtered_labels: ijv = numpy.vstack( (ijv, numpy.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, FF_PARENT % orig_objects_name, parents, ) m.add_measurement( orig_objects_name, FF_CHILDREN_COUNT % filtered_objects_name, child_count, ) # # The object count # add_object_count_measurements(m, filtered_objects_name, object_count) # # The object locations # add_object_location_measurements_ijv(m, filtered_objects_name, ijv) 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): 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.x_name.value) if img.shape != objects.shape: raise ValueError( "This module requires that the input image and object sets are the same size.\n" "The %s image and %s objects are not (%s vs %s).\n" "If they are paired correctly you may want to use the Resize, ResizeObjects or " "Crop module(s) to make them the same size." % ( image_name, self.x_name.value, img.shape, objects.shape, )) global_threshold = None if self.method == M_DISTANCE_N: has_threshold = False else: thresholded_image, global_threshold, sigma = self._threshold_image( image_name, workspace) workspace.display_data.global_threshold = global_threshold workspace.display_data.threshold_sigma = sigma 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 = numpy.hstack( (labels_in[0, :], labels_in[-1, :], labels_in[:, 0], labels_in[:, -1])) labels_touching_edge = numpy.unique(labels_touching_edge) is_touching = numpy.zeros(numpy.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 = numpy.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) = scipy.ndimage.distance_transform_edt( labels_in == 0, return_indices=True) labels_out = numpy.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 = centrosome.propagate.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: label_mask = labels_out == 0 small_removed_segmented_out = centrosome.cpmorphology.fill_labeled_holes( labels_out, mask=label_mask) 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 = centrosome.propagate.propagate( img, labels_in, thresholded_image, self.regularization_factor.value) if self.fill_holes: label_mask = labels_out == 0 small_removed_segmented_out = centrosome.cpmorphology.fill_labeled_holes( labels_out, mask=label_mask) 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 = numpy.abs(scipy.ndimage.sobel(img)) # # Combine the image mask and threshold to mask the watershed # watershed_mask = numpy.logical_or(thresholded_image, labels_in > 0) watershed_mask = numpy.logical_and(watershed_mask, mask) # # Perform the first watershed # labels_out = skimage.segmentation.watershed( connectivity=numpy.ones((3, 3), bool), image=sobel_image, markers=labels_in, mask=watershed_mask, ) if self.fill_holes: label_mask = labels_out == 0 small_removed_segmented_out = centrosome.cpmorphology.fill_labeled_holes( labels_out, mask=label_mask) 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 = numpy.logical_or(thresholded_image, labels_in > 0) watershed_mask = numpy.logical_and(watershed_mask, mask) # # Perform the watershed # labels_out = skimage.segmentation.watershed( connectivity=numpy.ones((3, 3), bool), image=inverted_img, markers=labels_in, mask=watershed_mask, ) if self.fill_holes: label_mask = labels_out == 0 small_removed_segmented_out = centrosome.cpmorphology.fill_labeled_holes( labels_out, mask=label_mask) 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 = scipy.ndimage.maximum( segmented_out, objects.segmented, list(range(numpy.max(objects.segmented) + 1)), ) lookup = centrosome.cpmorphology.fixup_scipy_ndimage_result(lookup) lookup[0] = 0 lookup[lookup != 0] = numpy.arange(numpy.sum(lookup != 0)) + 1 segmented_labels = lookup[objects.segmented] segmented_out = lookup[segmented_out] new_objects = 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 # # Add the objects to the object set # objects_out = 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.y_name.value workspace.object_set.add_objects(objects_out, objname) object_count = numpy.max(segmented_out) # # Add measurements # measurements = workspace.measurements super(IdentifySecondaryObjects, self).add_measurements(workspace) # # 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.x_name.value, FF_CHILDREN_COUNT % objname, children_per_parent, ) measurements.add_measurement( objname, FF_PARENT % self.x_name.value, parents_of_children, ) image_numbers = (numpy.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.x_name.value, self.y_name.value, image_numbers[mask], parents_of_children[mask], image_numbers[mask], numpy.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) super(IdentifySecondaryObjects, self).add_measurements( workspace, input_object_name=self.x_name.value, output_object_name=self.new_primary_objects_name.value, ) children_per_parent, parents_of_children = new_objects.relate_children( objects_out) measurements.add_measurement( self.new_primary_objects_name.value, FF_CHILDREN_COUNT % objname, children_per_parent, ) measurements.add_measurement( objname, FF_PARENT % self.new_primary_objects_name.value, parents_of_children, ) if self.show_window: object_area = numpy.sum(segmented_out > 0) workspace.display_data.object_pct = ( 100 * object_area / numpy.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 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 = numpy.max(labels) # # Resize the mask to cover the objects # mask, m1 = 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 = fixup_scipy_ndimage_result( scipy.ndimage.sum( mask, labels, numpy.arange(1, nobjects + 1, dtype=numpy.int32) ) ) if self.overlap_choice == P_KEEP: keep = pixel_counts > 0 else: total_pixels = fixup_scipy_ndimage_result( scipy.ndimage.sum( numpy.ones(labels.shape), labels, numpy.arange(1, nobjects + 1, dtype=numpy.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 = numpy.hstack(([False], keep)) labels[~keep[labels]] = 0 # # Renumber the labels matrix if requested # if self.retain_or_renumber == R_RENUMBER: unique_labels = numpy.unique(labels[labels != 0]) indexer = numpy.zeros(nobjects + 1, int) indexer[unique_labels] = numpy.arange(1, len(unique_labels) + 1) labels = indexer[labels] parent_objects = unique_labels else: parent_objects = numpy.arange(1, nobjects + 1) # # Add the objects # remaining_objects = 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, FF_PARENT % object_name, parent_objects, ) if numpy.max(original_objects.segmented) == 0: child_count = numpy.array([], int) else: child_count = fixup_scipy_ndimage_result( scipy.ndimage.sum( labels, original_objects.segmented, numpy.arange(1, nobjects + 1, dtype=numpy.int32), ) ) child_count = (child_count > 0).astype(int) m.add_measurement( object_name, 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) add_object_count_measurements(m, remaining_object_name, remaining_object_count) add_object_location_measurements(m, remaining_object_name, labels) # # 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_name = self.objects_name.value objects = workspace.object_set.get_objects(objects_name) assert isinstance(objects, Objects) labels = objects.segmented if self.relabel_option == OPTION_SPLIT: output_labels, count = scipy.ndimage.label( labels > 0, numpy.ones((3, 3), bool)) else: if self.merge_option == UNIFY_DISTANCE: mask = labels > 0 if self.distance_threshold.value > 0: # # Take the distance transform of the reverse of the mask # and figure out what points are less than 1/2 of the # distance from an object. # d = scipy.ndimage.distance_transform_edt(~mask) mask = d < self.distance_threshold.value / 2 + 1 output_labels, count = scipy.ndimage.label( mask, numpy.ones((3, 3), bool)) output_labels[labels == 0] = 0 if self.wants_image: output_labels = self.filter_using_image(workspace, mask) elif self.merge_option == UNIFY_PARENT: parents_name = self.parent_object.value parents_of = workspace.measurements[objects_name, "_".join( (C_PARENT, parents_name))] output_labels = labels.copy().astype(numpy.uint32) output_labels[labels > 0] = parents_of[labels[labels > 0] - 1] if self.merging_method == UM_CONVEX_HULL: ch_pts, n_pts = centrosome.cpmorphology.convex_hull( output_labels) ijv = centrosome.cpmorphology.fill_convex_hulls( ch_pts, n_pts) output_labels[ijv[:, 0], ijv[:, 1]] = ijv[:, 2] output_objects = Objects() output_objects.segmented = output_labels if objects.has_small_removed_segmented: output_objects.small_removed_segmented = copy_labels( objects.small_removed_segmented, output_labels) if objects.has_unedited_segmented: output_objects.unedited_segmented = copy_labels( objects.unedited_segmented, output_labels) output_objects.parent_image = objects.parent_image workspace.object_set.add_objects(output_objects, self.output_objects_name.value) measurements = workspace.measurements add_object_count_measurements( measurements, self.output_objects_name.value, numpy.max(output_objects.segmented), ) add_object_location_measurements(measurements, self.output_objects_name.value, output_objects.segmented) # # Relate the output objects to the input ones and record # the relationship. # children_per_parent, parents_of_children = objects.relate_children( output_objects) measurements.add_measurement( self.objects_name.value, FF_CHILDREN_COUNT % self.output_objects_name.value, children_per_parent, ) measurements.add_measurement( self.output_objects_name.value, FF_PARENT % self.objects_name.value, parents_of_children, ) if self.show_window: workspace.display_data.orig_labels = objects.segmented workspace.display_data.output_labels = output_objects.segmented if self.merge_option == UNIFY_PARENT: workspace.display_data.parent_labels = workspace.object_set.get_objects( self.parent_object.value).segmented