def run(self, workspace): """Run the module workspace - the workspace contains: pipeline - instance of CellProfiler.Pipeline 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 - display within this frame (or None to not display) """ input = workspace.image_set.get_image(self.image_name.value, must_be_grayscale=True) pixels = input.pixel_data.copy() if self.threshold_modifier == TM_PER_OBJECT: objects = workspace.object_set.get_objects( self.enclosing_objects_name.value) labels = objects.segmented else: labels = None local_thresh, global_thresh = self.get_threshold( pixels, input.mask, labels, workspace) if self.binary != 'Grayscale': pixels = (pixels > local_thresh) & input.mask else: if self.low_or_high == TH_BELOW_THRESHOLD: thresholded_pixels = pixels < local_thresh pixels[input.mask & thresholded_pixels] = 0 if self.shift.value: pixels[ input.mask & ~thresholded_pixels] -= local_thresh if self.threshold_modifier == TM_GLOBAL else local_thresh[ input.mask & ~thresholded_pixels] elif self.low_or_high == TH_ABOVE_THRESHOLD: undilated = input.mask & (pixels >= local_thresh) dilated = binary_dilation(undilated, strel_disk(self.dilation.value), mask=input.mask) pixels[dilated] = 0 else: raise NotImplementedError( """Threshold setting, "%s" is not "%s" or "%s".""" % (self.low_or_high.value, TH_BELOW_THRESHOLD, TH_ABOVE_THRESHOLD)) output = cpimage.Image(pixels, parent_image=input) workspace.image_set.add(self.thresholded_image_name.value, output) self.add_threshold_measurements(workspace.measurements, input.pixel_data, input.mask, local_thresh, global_thresh, self.thresholded_image_name.value) if workspace.frame is not None: workspace.display_data.input_pixel_data = input.pixel_data workspace.display_data.output_pixel_data = output.pixel_data statistics = workspace.display_data.statistics = [("Feature", "Value")] for column in self.get_measurement_columns(workspace.pipeline): value = workspace.measurements.get_current_image_measurement( column[1]) statistics += [(column[1].split('_')[1], str(value))]
def test_04_03_strel_disk(self): r = np.random.RandomState() r.seed(43) module = morph.Morph() module.functions[0].structuring_element.value = morph.SE_DISK strel = cpmorph.strel_disk(3.5) pixel_data = r.uniform(size=(20, 30)) > .5 expected = scind.binary_dilation(pixel_data, strel) result = self.execute(pixel_data, morph.F_DILATE, scale = 7, module = module) np.testing.assert_array_equal(expected, result)
def run(self,workspace): """Run the module workspace - the workspace contains: pipeline - instance of CellProfiler.Pipeline 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 - display within this frame (or None to not display) """ input = workspace.image_set.get_image(self.image_name.value, must_be_grayscale=True) pixels = input.pixel_data.copy() if self.threshold_modifier == TM_PER_OBJECT: objects = workspace.object_set.get_objects(self.enclosing_objects_name.value) labels = objects.segmented else: labels = None local_thresh,global_thresh = self.get_threshold(pixels,input.mask,labels, workspace) if self.binary != 'Grayscale': pixels = (pixels > local_thresh) & input.mask else: if self.low_or_high == TH_BELOW_THRESHOLD: thresholded_pixels = pixels < local_thresh pixels[input.mask & thresholded_pixels] = 0 if self.shift.value: pixels[input.mask & ~ thresholded_pixels] -= local_thresh if self.threshold_modifier == TM_GLOBAL else local_thresh[input.mask & ~ thresholded_pixels] elif self.low_or_high == TH_ABOVE_THRESHOLD: undilated = input.mask & (pixels >= local_thresh) dilated = binary_dilation(undilated, strel_disk(self.dilation.value), mask=input.mask) pixels[dilated] = 0 else: raise NotImplementedError( """Threshold setting, "%s" is not "%s" or "%s".""" % (self.low_or_high.value, TH_BELOW_THRESHOLD, TH_ABOVE_THRESHOLD)) output = cpimage.Image(pixels, parent_image=input) workspace.image_set.add(self.thresholded_image_name.value, output) self.add_threshold_measurements(workspace.measurements, input.pixel_data, input.mask, local_thresh, global_thresh, self.thresholded_image_name.value) if workspace.frame is not None: workspace.display_data.input_pixel_data = input.pixel_data workspace.display_data.output_pixel_data = output.pixel_data statistics = workspace.display_data.statistics = [ ("Feature","Value")] for column in self.get_measurement_columns(workspace.pipeline): value = workspace.measurements.get_current_image_measurement(column[1]) statistics += [(column[1].split('_')[1], str(value))]
def test_04_03_strel_disk(self): r = np.random.RandomState() r.seed(43) module = morph.Morph() module.functions[0].structuring_element.value = morph.SE_DISK strel = cpmorph.strel_disk(3.5) pixel_data = r.uniform(size=(20, 30)) > .5 expected = scind.binary_dilation(pixel_data, strel) result = self.execute(pixel_data, morph.F_DILATE, scale=7, module=module) np.testing.assert_array_equal(expected, result)
def binary_tteesstt(self, function_name, function, gray_out=False, scale=None, custom_repeats=None): np.random.seed(map(ord,function_name)) input = np.random.uniform(size=(20,20)) > .7 output = self.execute(input, function_name, scale=scale, custom_repeats=custom_repeats) if scale is None: expected = function(input) else: footprint = cpmorph.strel_disk(float(scale) / 2.0) expected = function(input, footprint = footprint) if not gray_out: expected = expected > 0 self.assertTrue(np.all(output==expected)) else: self.assertTrue(np.all(np.abs(output-expected) < np.finfo(np.float32).eps))
def run(self, workspace): """Run the module workspace - the workspace contains: pipeline - instance of CellProfiler.Pipeline 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 - display within this frame (or None to not display) """ input = workspace.image_set.get_image(self.image_name.value, must_be_grayscale=True) pixels = input.pixel_data.copy() binary_image, local_thresh = self.threshold_image( self.image_name.value, workspace, wants_local_threshold=True) if self.binary != 'Grayscale': pixels = binary_image & input.mask else: if self.low_or_high == TH_BELOW_THRESHOLD: pixels[input.mask & ~binary_image] = 0 if self.shift.value: pixels[input.mask & binary_image] -= \ local_thresh if self.threshold_modifier == TM_GLOBAL \ else local_thresh[input.mask & binary_image] elif self.low_or_high == TH_ABOVE_THRESHOLD: undilated = input.mask & binary_image dilated = binary_dilation(undilated, strel_disk(self.dilation.value), mask=input.mask) pixels[dilated] = 0 else: raise NotImplementedError( """Threshold setting, "%s" is not "%s" or "%s".""" % (self.low_or_high.value, TH_BELOW_THRESHOLD, TH_ABOVE_THRESHOLD)) output = cpimage.Image(pixels, parent_image=input) workspace.image_set.add(self.thresholded_image_name.value, output) if self.show_window: workspace.display_data.input_pixel_data = input.pixel_data workspace.display_data.output_pixel_data = output.pixel_data statistics = workspace.display_data.statistics = [] workspace.display_data.col_labels = ("Feature", "Value") for column in self.get_measurement_columns(workspace.pipeline): value = workspace.measurements.get_current_image_measurement( column[1]) statistics += [(column[1].split('_')[1], str(value))]
def run(self,workspace): """Run the module workspace - the workspace contains: pipeline - instance of CellProfiler.Pipeline 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 - display within this frame (or None to not display) """ input = workspace.image_set.get_image(self.image_name.value, must_be_grayscale=True) pixels = input.pixel_data.copy() binary_image, local_thresh = self.threshold_image( self.image_name.value, workspace, wants_local_threshold=True) if self.binary != 'Grayscale': pixels = binary_image & input.mask else: if self.low_or_high == TH_BELOW_THRESHOLD: pixels[input.mask & ~ binary_image] = 0 if self.shift.value: pixels[input.mask & binary_image] -= \ local_thresh if self.threshold_modifier == TM_GLOBAL \ else local_thresh[input.mask & binary_image] elif self.low_or_high == TH_ABOVE_THRESHOLD: undilated = input.mask & binary_image dilated = binary_dilation(undilated, strel_disk(self.dilation.value), mask=input.mask) pixels[dilated] = 0 else: raise NotImplementedError( """Threshold setting, "%s" is not "%s" or "%s".""" % (self.low_or_high.value, TH_BELOW_THRESHOLD, TH_ABOVE_THRESHOLD)) output = cpimage.Image(pixels, parent_image=input) workspace.image_set.add(self.thresholded_image_name.value, output) if self.show_window: workspace.display_data.input_pixel_data = input.pixel_data workspace.display_data.output_pixel_data = output.pixel_data statistics = workspace.display_data.statistics = [] workspace.display_data.col_labels = ("Feature", "Value") for column in self.get_measurement_columns(workspace.pipeline): value = workspace.measurements.get_current_image_measurement(column[1]) statistics += [(column[1].split('_')[1], str(value))]
def binary_tteesstt(self, function_name, function, gray_out=False, scale=None, custom_repeats=None): np.random.seed(map(ord, function_name)) input = np.random.uniform(size=(20, 20)) > .7 output = self.execute(input, function_name, scale=scale, custom_repeats=custom_repeats) if scale is None: expected = function(input) else: footprint = cpmorph.strel_disk(float(scale) / 2.0) expected = function(input, footprint=footprint) if not gray_out: expected = expected > 0 self.assertTrue(np.all(output == expected)) else: self.assertTrue( np.all(np.abs(output - expected) < np.finfo(np.float32).eps))
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] if np.any(touching_border) and \ np.all(~ touching_border_mask[neighbor_labels]): # 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[touching_border_mask] = touching_border_object_number[ unedited_segmented[touching_border_mask]] nobjects = np.max(labels) nneighbors = np.max(neighbor_labels) nkept_objects = objects.count _, 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[:, ~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 nneighbors > 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): '''Run the module on the image set''' seed_objects_name = self.seed_objects_name.value skeleton_name = self.image_name.value seed_objects = workspace.object_set.get_objects(seed_objects_name) labels = seed_objects.segmented labels_count = np.max(labels) label_range = np.arange(labels_count, dtype=np.int32) + 1 skeleton_image = workspace.image_set.get_image(skeleton_name, must_be_binary=True) skeleton = skeleton_image.pixel_data if skeleton_image.has_mask: skeleton = skeleton & skeleton_image.mask try: labels = skeleton_image.crop_image_similarly(labels) except: labels, m1 = cpo.size_similarly(skeleton, labels) labels[~m1] = 0 # # The following code makes a ring around the seed objects with # the skeleton trunks sticking out of it. # # Create a new skeleton with holes at the seed objects # First combine the seed objects with the skeleton so # that the skeleton trunks come out of the seed objects. # # Erode the labels once so that all of the trunk branchpoints # will be within the labels # # # Dilate the objects, then subtract them to make a ring # my_disk = morph.strel_disk(1.5).astype(int) dilated_labels = grey_dilation(labels, footprint=my_disk) seed_mask = dilated_labels > 0 combined_skel = skeleton | seed_mask closed_labels = grey_erosion(dilated_labels, footprint=my_disk) seed_center = closed_labels > 0 combined_skel = combined_skel & (~seed_center) # # Fill in single holes (but not a one-pixel hole made by # a one-pixel image) # if self.wants_to_fill_holes: def size_fn(area, is_object): return (~is_object) and (area <= self.maximum_hole_size.value) combined_skel = morph.fill_labeled_holes(combined_skel, ~seed_center, size_fn) # # Reskeletonize to make true branchpoints at the ring boundaries # combined_skel = morph.skeletonize(combined_skel) # # The skeleton outside of the labels # outside_skel = combined_skel & (dilated_labels == 0) # # Associate all skeleton points with seed objects # dlabels, distance_map = propagate.propagate(np.zeros(labels.shape), dilated_labels, combined_skel, 1) # # Get rid of any branchpoints not connected to seeds # combined_skel[dlabels == 0] = False # # Find the branchpoints # branch_points = morph.branchpoints(combined_skel) # # Odd case: when four branches meet like this, branchpoints are not # assigned because they are arbitrary. So assign them. # # . . # B. # .B # . . # odd_case = (combined_skel[:-1, :-1] & combined_skel[1:, :-1] & combined_skel[:-1, 1:] & combined_skel[1, 1]) branch_points[:-1, :-1][odd_case] = True branch_points[1:, 1:][odd_case] = True # # Find the branching counts for the trunks (# of extra branches # eminating from a point other than the line it might be on). # branching_counts = morph.branchings(combined_skel) branching_counts = np.array([0, 0, 0, 1, 2])[branching_counts] # # Only take branches within 1 of the outside skeleton # dilated_skel = scind.binary_dilation(outside_skel, morph.eight_connect) branching_counts[~dilated_skel] = 0 # # Find the endpoints # end_points = morph.endpoints(combined_skel) # # We use two ranges for classification here: # * anything within one pixel of the dilated image is a trunk # * anything outside of that range is a branch # nearby_labels = dlabels.copy() nearby_labels[distance_map > 1.5] = 0 outside_labels = dlabels.copy() outside_labels[nearby_labels > 0] = 0 # # The trunks are the branchpoints that lie within one pixel of # the dilated image. # if labels_count > 0: trunk_counts = fix( scind.sum(branching_counts, nearby_labels, label_range)).astype(int) else: trunk_counts = np.zeros((0, ), int) # # The branches are the branchpoints that lie outside the seed objects # if labels_count > 0: branch_counts = fix( scind.sum(branch_points, outside_labels, label_range)) else: branch_counts = np.zeros((0, ), int) # # Save the endpoints # if labels_count > 0: end_counts = fix(scind.sum(end_points, outside_labels, label_range)) else: end_counts = np.zeros((0, ), int) # # Save measurements # m = workspace.measurements assert isinstance(m, cpmeas.Measurements) feature = "_".join((C_NEURON, F_NUMBER_TRUNKS, skeleton_name)) m.add_measurement(seed_objects_name, feature, trunk_counts) feature = "_".join( (C_NEURON, F_NUMBER_NON_TRUNK_BRANCHES, skeleton_name)) m.add_measurement(seed_objects_name, feature, branch_counts) feature = "_".join((C_NEURON, F_NUMBER_BRANCH_ENDS, skeleton_name)) m.add_measurement(seed_objects_name, feature, end_counts) # # Collect the graph information # if self.wants_neuron_graph: trunk_mask = (branching_counts > 0) & (nearby_labels != 0) intensity_image = workspace.image_set.get_image( self.intensity_image_name.value) edge_graph, vertex_graph = self.make_neuron_graph( combined_skel, dlabels, trunk_mask, branch_points & ~trunk_mask, end_points, intensity_image.pixel_data) image_number = workspace.measurements.image_set_number edge_path, vertex_path = self.get_graph_file_paths( m, m.image_number) workspace.interaction_request(self, m.image_number, edge_path, edge_graph, vertex_path, vertex_graph, headless_ok=True) if self.show_window: workspace.display_data.edge_graph = edge_graph workspace.display_data.vertex_graph = vertex_graph workspace.display_data.intensity_image = intensity_image.pixel_data # # Make the display image # if self.show_window or self.wants_branchpoint_image: branchpoint_image = np.zeros( (skeleton.shape[0], skeleton.shape[1], 3)) trunk_mask = (branching_counts > 0) & (nearby_labels != 0) branch_mask = branch_points & (outside_labels != 0) end_mask = end_points & (outside_labels != 0) branchpoint_image[outside_skel, :] = 1 branchpoint_image[trunk_mask | branch_mask | end_mask, :] = 0 branchpoint_image[trunk_mask, 0] = 1 branchpoint_image[branch_mask, 1] = 1 branchpoint_image[end_mask, 2] = 1 branchpoint_image[dilated_labels != 0, :] *= .875 branchpoint_image[dilated_labels != 0, :] += .1 if self.show_window: workspace.display_data.branchpoint_image = branchpoint_image if self.wants_branchpoint_image: bi = cpi.Image(branchpoint_image, parent_image=skeleton_image) workspace.image_set.add(self.branchpoint_image_name.value, bi)
def run(self, workspace): '''Run the module on the image set''' seed_objects_name = self.seed_objects_name.value skeleton_name = self.image_name.value seed_objects = workspace.object_set.get_objects(seed_objects_name) labels = seed_objects.segmented labels_count = np.max(labels) label_range = np.arange(labels_count,dtype=np.int32)+1 skeleton_image = workspace.image_set.get_image( skeleton_name, must_be_binary = True) skeleton = skeleton_image.pixel_data if skeleton_image.has_mask: skeleton = skeleton & skeleton_image.mask try: labels = skeleton_image.crop_image_similarly(labels) except: labels, m1 = cpo.size_similarly(skeleton, labels) labels[~m1] = 0 # # The following code makes a ring around the seed objects with # the skeleton trunks sticking out of it. # # Create a new skeleton with holes at the seed objects # First combine the seed objects with the skeleton so # that the skeleton trunks come out of the seed objects. # # Erode the labels once so that all of the trunk branchpoints # will be within the labels # # # Dilate the objects, then subtract them to make a ring # my_disk = morph.strel_disk(1.5).astype(int) dilated_labels = grey_dilation(labels, footprint=my_disk) seed_mask = dilated_labels > 0 combined_skel = skeleton | seed_mask closed_labels = grey_erosion(dilated_labels, footprint = my_disk) seed_center = closed_labels > 0 combined_skel = combined_skel & (~seed_center) # # Fill in single holes (but not a one-pixel hole made by # a one-pixel image) # if self.wants_to_fill_holes: def size_fn(area, is_object): return (~ is_object) and (area <= self.maximum_hole_size.value) combined_skel = morph.fill_labeled_holes( combined_skel, ~seed_center, size_fn) # # Reskeletonize to make true branchpoints at the ring boundaries # combined_skel = morph.skeletonize(combined_skel) # # The skeleton outside of the labels # outside_skel = combined_skel & (dilated_labels == 0) # # Associate all skeleton points with seed objects # dlabels, distance_map = propagate.propagate(np.zeros(labels.shape), dilated_labels, combined_skel, 1) # # Get rid of any branchpoints not connected to seeds # combined_skel[dlabels == 0] = False # # Find the branchpoints # branch_points = morph.branchpoints(combined_skel) # # Odd case: when four branches meet like this, branchpoints are not # assigned because they are arbitrary. So assign them. # # . . # B. # .B # . . # odd_case = (combined_skel[:-1,:-1] & combined_skel[1:,:-1] & combined_skel[:-1,1:] & combined_skel[1,1]) branch_points[:-1,:-1][odd_case] = True branch_points[1:,1:][odd_case] = True # # Find the branching counts for the trunks (# of extra branches # eminating from a point other than the line it might be on). # branching_counts = morph.branchings(combined_skel) branching_counts = np.array([0,0,0,1,2])[branching_counts] # # Only take branches within 1 of the outside skeleton # dilated_skel = scind.binary_dilation(outside_skel, morph.eight_connect) branching_counts[~dilated_skel] = 0 # # Find the endpoints # end_points = morph.endpoints(combined_skel) # # We use two ranges for classification here: # * anything within one pixel of the dilated image is a trunk # * anything outside of that range is a branch # nearby_labels = dlabels.copy() nearby_labels[distance_map > 1.5] = 0 outside_labels = dlabels.copy() outside_labels[nearby_labels > 0] = 0 # # The trunks are the branchpoints that lie within one pixel of # the dilated image. # if labels_count > 0: trunk_counts = fix(scind.sum(branching_counts, nearby_labels, label_range)).astype(int) else: trunk_counts = np.zeros((0,),int) # # The branches are the branchpoints that lie outside the seed objects # if labels_count > 0: branch_counts = fix(scind.sum(branch_points, outside_labels, label_range)) else: branch_counts = np.zeros((0,),int) # # Save the endpoints # if labels_count > 0: end_counts = fix(scind.sum(end_points, outside_labels, label_range)) else: end_counts = np.zeros((0,), int) # # Save measurements # m = workspace.measurements assert isinstance(m, cpmeas.Measurements) feature = "_".join((C_NEURON, F_NUMBER_TRUNKS, skeleton_name)) m.add_measurement(seed_objects_name, feature, trunk_counts) feature = "_".join((C_NEURON, F_NUMBER_NON_TRUNK_BRANCHES, skeleton_name)) m.add_measurement(seed_objects_name, feature, branch_counts) feature = "_".join((C_NEURON, F_NUMBER_BRANCH_ENDS, skeleton_name)) m.add_measurement(seed_objects_name, feature, end_counts) # # Collect the graph information # if self.wants_neuron_graph: trunk_mask = (branching_counts > 0) & (nearby_labels != 0) intensity_image = workspace.image_set.get_image( self.intensity_image_name.value) edge_graph, vertex_graph = self.make_neuron_graph( combined_skel, dlabels, trunk_mask, branch_points & ~trunk_mask, end_points, intensity_image.pixel_data) image_number = workspace.measurements.image_set_number edge_path, vertex_path = self.get_graph_file_paths(m, m.image_number) workspace.interaction_request( self, m.image_number, edge_path, edge_graph, vertex_path, vertex_graph, headless_ok = True) if self.show_window: workspace.display_data.edge_graph = edge_graph workspace.display_data.vertex_graph = vertex_graph workspace.display_data.intensity_image = intensity_image.pixel_data # # Make the display image # if self.show_window or self.wants_branchpoint_image: branchpoint_image = np.zeros((skeleton.shape[0], skeleton.shape[1], 3)) trunk_mask = (branching_counts > 0) & (nearby_labels != 0) branch_mask = branch_points & (outside_labels != 0) end_mask = end_points & (outside_labels != 0) branchpoint_image[outside_skel,:] = 1 branchpoint_image[trunk_mask | branch_mask | end_mask,:] = 0 branchpoint_image[trunk_mask,0] = 1 branchpoint_image[branch_mask,1] = 1 branchpoint_image[end_mask, 2] = 1 branchpoint_image[dilated_labels != 0,:] *= .875 branchpoint_image[dilated_labels != 0,:] += .1 if self.show_window: workspace.display_data.branchpoint_image = branchpoint_image if self.wants_branchpoint_image: bi = cpi.Image(branchpoint_image, parent_image = skeleton_image) workspace.image_set.add(self.branchpoint_image_name.value, bi)
def run_function(self, function_name, pixel_data, mask, count, scale, custom_repeats): '''Apply the function once to the image, returning the result''' is_binary = pixel_data.dtype.kind == 'b' strel = morph.strel_disk(scale / 2.0) if (function_name in (F_BRANCHPOINTS, F_BRIDGE, F_CLEAN, F_DIAG, F_CONVEX_HULL, F_DISTANCE, F_ENDPOINTS, F_FILL, F_FILL_SMALL, F_HBREAK, F_LIFE, F_MAJORITY, F_REMOVE, F_SHRINK, F_SKEL, F_SPUR, F_THICKEN, F_THIN, F_VBREAK) and not is_binary): # Apply a very crude threshold to the image for binary algorithms logger.warning("Warning: converting image to binary for %s\n"% function_name) pixel_data = pixel_data != 0 if (function_name in (F_BRANCHPOINTS, F_BRIDGE, F_CLEAN, F_DIAG, F_CONVEX_HULL, F_DISTANCE, F_ENDPOINTS, F_FILL, F_FILL_SMALL, F_HBREAK, F_INVERT, F_LIFE, F_MAJORITY, F_REMOVE, F_SHRINK, F_SKEL, F_SPUR, F_THICKEN, F_THIN, F_VBREAK) or (is_binary and function_name in (F_CLOSE, F_DILATE, F_ERODE, F_OPEN))): # All of these have an iterations argument or it makes no # sense to iterate if function_name == F_BRANCHPOINTS: return morph.branchpoints(pixel_data, mask) elif function_name == F_BRIDGE: return morph.bridge(pixel_data, mask, count) elif function_name == F_CLEAN: return morph.clean(pixel_data, mask, count) elif function_name == F_CLOSE: if mask is None: return scind.binary_closing(pixel_data, strel, iterations = count) else: return (scind.binary_closing(pixel_data & mask, strel, iterations = count) | (pixel_data & ~ mask)) elif function_name == F_CONVEX_HULL: if mask is None: return morph.convex_hull_image(pixel_data) else: return morph.convex_hull_image(pixel_data & mask) elif function_name == F_DIAG: return morph.diag(pixel_data, mask, count) elif function_name == F_DILATE: return scind.binary_dilation(pixel_data, strel, iterations=count, mask=mask) elif function_name == F_DISTANCE: image = scind.distance_transform_edt(pixel_data) img_max = np.max(image) if img_max > 0: image = image / img_max return image elif function_name == F_ENDPOINTS: return morph.endpoints(pixel_data, mask) elif function_name == F_ERODE: return scind.binary_erosion(pixel_data, strel, iterations = count, mask = mask) elif function_name == F_FILL: return morph.fill(pixel_data, mask, count) elif function_name == F_FILL_SMALL: def small_fn(area, foreground): return (not foreground) and (area <= custom_repeats) return morph.fill_labeled_holes(pixel_data, mask, small_fn) elif function_name == F_HBREAK: return morph.hbreak(pixel_data, mask, count) elif function_name == F_INVERT: if is_binary: if mask is None: return ~ pixel_data result = pixel_data.copy() result[mask] = ~result[mask] return result elif mask is None: return 1-pixel_data else: result = pixel_data.copy() result[mask] = 1-result[mask] return result elif function_name == F_LIFE: return morph.life(pixel_data, count) elif function_name == F_MAJORITY: return morph.majority(pixel_data, mask, count) elif function_name == F_OPEN: if mask is None: return scind.binary_opening(pixel_data, strel, iterations = count) else: return (scind.binary_opening(pixel_data & mask, strel, iterations = count) | (pixel_data & ~ mask)) elif function_name == F_REMOVE: return morph.remove(pixel_data, mask, count) elif function_name == F_SHRINK: return morph.binary_shrink(pixel_data, count) elif function_name == F_SKEL: return morph.skeletonize(pixel_data, mask) elif function_name == F_SPUR: return morph.spur(pixel_data, mask, count) elif function_name == F_THICKEN: return morph.thicken(pixel_data, mask, count) elif function_name == F_THIN: return morph.thin(pixel_data, mask, count) elif function_name == F_VBREAK: return morph.vbreak(pixel_data, mask) else: raise NotImplementedError("Unimplemented morphological function: %s" % function_name) else: for i in range(count): if function_name == F_BOTHAT: new_pixel_data = morph.black_tophat(pixel_data, mask=mask, footprint=strel) elif function_name == F_CLOSE: new_pixel_data = morph.closing(pixel_data, mask=mask, footprint=strel) elif function_name == F_DILATE: new_pixel_data = morph.grey_dilation(pixel_data, mask=mask, footprint=strel) elif function_name == F_ERODE: new_pixel_data = morph.grey_erosion(pixel_data, mask=mask, footprint=strel) elif function_name == F_OPEN: new_pixel_data = morph.opening(pixel_data, mask=mask, footprint=strel) elif function_name == F_TOPHAT: new_pixel_data = morph.white_tophat(pixel_data, mask=mask, footprint=strel) else: raise NotImplementedError("Unimplemented morphological function: %s" % function_name) if np.all(new_pixel_data == pixel_data): break; pixel_data = new_pixel_data return pixel_data
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) nneighbors = np.max(neighbor_labels) nkept_objects = objects.count 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 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[:, ~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 nneighbors > 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_function(self, function, pixel_data, mask): '''Apply the function once to the image, returning the result''' count = function.repeat_count function_name = function.function.value scale = function.scale.value custom_repeats = function.custom_repeats.value is_binary = pixel_data.dtype.kind == 'b' if function.structuring_element == SE_ARBITRARY: strel = np.array(function.strel.get_matrix()) elif function.structuring_element == SE_DISK: strel = morph.strel_disk(scale / 2.0) elif function.structuring_element == SE_DIAMOND: strel = morph.strel_diamond(scale / 2.0) elif function.structuring_element == SE_LINE: strel = morph.strel_line(scale, function.angle.value) elif function.structuring_element == SE_OCTAGON: strel = morph.strel_octagon(scale / 2.0) elif function.structuring_element == SE_PAIR: strel = morph.strel_pair(function.x_offset.value, function.y_offset.value) elif function.structuring_element == SE_PERIODIC_LINE: xoff = function.x_offset.value yoff = function.y_offset.value n = max(scale / 2.0 / np.sqrt(float(xoff*xoff+yoff*yoff)), 1) strel = morph.strel_periodicline( xoff, yoff, n) elif function.structuring_element == SE_RECTANGLE: strel = morph.strel_rectangle( function.width.value, function.height.value) else: strel = morph.strel_square(scale) if (function_name in (F_BRANCHPOINTS, F_BRIDGE, F_CLEAN, F_DIAG, F_CONVEX_HULL, F_DISTANCE, F_ENDPOINTS, F_FILL, F_FILL_SMALL, F_HBREAK, F_LIFE, F_MAJORITY, F_REMOVE, F_SHRINK, F_SKEL, F_SKELPE, F_SPUR, F_THICKEN, F_THIN, F_VBREAK) and not is_binary): # Apply a very crude threshold to the image for binary algorithms logger.warning("Warning: converting image to binary for %s\n"% function_name) pixel_data = pixel_data != 0 if (function_name in (F_BRANCHPOINTS, F_BRIDGE, F_CLEAN, F_DIAG, F_CONVEX_HULL, F_DISTANCE, F_ENDPOINTS, F_FILL, F_FILL_SMALL, F_HBREAK, F_INVERT, F_LIFE, F_MAJORITY, F_REMOVE, F_SHRINK, F_SKEL, F_SKELPE, F_SPUR, F_THICKEN, F_THIN, F_VBREAK) or (is_binary and function_name in (F_CLOSE, F_DILATE, F_ERODE, F_OPEN))): # All of these have an iterations argument or it makes no # sense to iterate if function_name == F_BRANCHPOINTS: return morph.branchpoints(pixel_data, mask) elif function_name == F_BRIDGE: return morph.bridge(pixel_data, mask, count) elif function_name == F_CLEAN: return morph.clean(pixel_data, mask, count) elif function_name == F_CLOSE: if mask is None: return scind.binary_closing(pixel_data, strel, iterations = count) else: return (scind.binary_closing(pixel_data & mask, strel, iterations = count) | (pixel_data & ~ mask)) elif function_name == F_CONVEX_HULL: if mask is None: return morph.convex_hull_image(pixel_data) else: return morph.convex_hull_image(pixel_data & mask) elif function_name == F_DIAG: return morph.diag(pixel_data, mask, count) elif function_name == F_DILATE: return scind.binary_dilation(pixel_data, strel, iterations=count, mask=mask) elif function_name == F_DISTANCE: image = scind.distance_transform_edt(pixel_data) if function.rescale_values.value: image = image / np.max(image) return image elif function_name == F_ENDPOINTS: return morph.endpoints(pixel_data, mask) elif function_name == F_ERODE: return scind.binary_erosion(pixel_data, strel, iterations = count, mask = mask) elif function_name == F_FILL: return morph.fill(pixel_data, mask, count) elif function_name == F_FILL_SMALL: def small_fn(area, foreground): return (not foreground) and (area <= custom_repeats) return morph.fill_labeled_holes(pixel_data, mask, small_fn) elif function_name == F_HBREAK: return morph.hbreak(pixel_data, mask, count) elif function_name == F_INVERT: if is_binary: if mask is None: return ~ pixel_data result = pixel_data.copy() result[mask] = ~result[mask] return result elif mask is None: return 1-pixel_data else: result = pixel_data.copy() result[mask] = 1-result[mask] return result elif function_name == F_LIFE: return morph.life(pixel_data, count) elif function_name == F_MAJORITY: return morph.majority(pixel_data, mask, count) elif function_name == F_OPEN: if mask is None: return scind.binary_opening(pixel_data, strel, iterations = count) else: return (scind.binary_opening(pixel_data & mask, strel, iterations = count) | (pixel_data & ~ mask)) elif function_name == F_REMOVE: return morph.remove(pixel_data, mask, count) elif function_name == F_SHRINK: return morph.binary_shrink(pixel_data, count) elif function_name == F_SKEL: return morph.skeletonize(pixel_data, mask) elif function_name == F_SKELPE: return morph.skeletonize( pixel_data, mask, scind.distance_transform_edt(pixel_data) * poisson_equation(pixel_data)) elif function_name == F_SPUR: return morph.spur(pixel_data, mask, count) elif function_name == F_THICKEN: return morph.thicken(pixel_data, mask, count) elif function_name == F_THIN: return morph.thin(pixel_data, mask, count) elif function_name == F_VBREAK: return morph.vbreak(pixel_data, mask) else: raise NotImplementedError("Unimplemented morphological function: %s" % function_name) else: for i in range(count): if function_name == F_BOTHAT: new_pixel_data = morph.black_tophat(pixel_data, mask=mask, footprint=strel) elif function_name == F_CLOSE: new_pixel_data = morph.closing(pixel_data, mask=mask, footprint=strel) elif function_name == F_DILATE: new_pixel_data = morph.grey_dilation(pixel_data, mask=mask, footprint=strel) elif function_name == F_ERODE: new_pixel_data = morph.grey_erosion(pixel_data, mask=mask, footprint=strel) elif function_name == F_OPEN: new_pixel_data = morph.opening(pixel_data, mask=mask, footprint=strel) elif function_name == F_TOPHAT: new_pixel_data = morph.white_tophat(pixel_data, mask=mask, footprint=strel) else: raise NotImplementedError("Unimplemented morphological function: %s" % function_name) if np.all(new_pixel_data == pixel_data): break; pixel_data = new_pixel_data return pixel_data
def run(self, workspace): '''Run the module on the image set''' seed_objects_name = self.seed_objects_name.value skeleton_name = self.image_name.value seed_objects = workspace.object_set.get_objects(seed_objects_name) labels = seed_objects.segmented labels_count = np.max(labels) label_range = np.arange(labels_count,dtype=np.int32)+1 skeleton_image = workspace.image_set.get_image( skeleton_name, must_be_binary = True) skeleton = skeleton_image.pixel_data if skeleton_image.has_mask: skeleton = skeleton & skeleton_image.mask try: labels = skeleton_image.crop_image_similarly(labels) except: labels, m1 = cpo.size_similarly(skeleton, labels) labels[~m1] = 0 # # The following code makes a ring around the seed objects with # the skeleton trunks sticking out of it. # # Create a new skeleton with holes at the seed objects # First combine the seed objects with the skeleton so # that the skeleton trunks come out of the seed objects. # # Erode the labels once so that all of the trunk branchpoints # will be within the labels # # # Dilate the objects, then subtract them to make a ring # my_disk = morph.strel_disk(1.5).astype(int) dilated_labels = grey_dilation(labels, footprint=my_disk) seed_mask = dilated_labels > 0 combined_skel = skeleton | seed_mask closed_labels = grey_erosion(dilated_labels, footprint = my_disk) seed_center = closed_labels > 0 combined_skel = combined_skel & (~seed_center) # # Fill in single holes (but not a one-pixel hole made by # a one-pixel image) # if self.wants_to_fill_holes: def size_fn(area, is_object): return (~ is_object) and (area <= self.maximum_hole_size.value) combined_skel = morph.fill_labeled_holes( combined_skel, ~seed_center, size_fn) # # Reskeletonize to make true branchpoints at the ring boundaries # combined_skel = morph.skeletonize(combined_skel) # # The skeleton outside of the labels # outside_skel = combined_skel & (dilated_labels == 0) # # Associate all skeleton points with seed objects # dlabels, distance_map = propagate.propagate(np.zeros(labels.shape), dilated_labels, combined_skel, 1) # # Get rid of any branchpoints not connected to seeds # combined_skel[dlabels == 0] = False # # Find the branchpoints # branch_points = morph.branchpoints(combined_skel) # # Odd case: when four branches meet like this, branchpoints are not # assigned because they are arbitrary. So assign them. # # . . # B. # .B # . . # odd_case = (combined_skel[:-1,:-1] & combined_skel[1:,:-1] & combined_skel[:-1,1:] & combined_skel[1,1]) branch_points[:-1,:-1][odd_case] = True branch_points[1:,1:][odd_case] = True # # Find the branching counts for the trunks (# of extra branches # eminating from a point other than the line it might be on). # branching_counts = morph.branchings(combined_skel) branching_counts = np.array([0,0,0,1,2])[branching_counts] # # Only take branches within 1 of the outside skeleton # dilated_skel = scind.binary_dilation(outside_skel, morph.eight_connect) branching_counts[~dilated_skel] = 0 # # Find the endpoints # end_points = morph.endpoints(combined_skel) # # We use two ranges for classification here: # * anything within one pixel of the dilated image is a trunk # * anything outside of that range is a branch # nearby_labels = dlabels.copy() nearby_labels[distance_map > 1.5] = 0 outside_labels = dlabels.copy() outside_labels[nearby_labels > 0] = 0 # # The trunks are the branchpoints that lie within one pixel of # the dilated image. # if labels_count > 0: trunk_counts = fix(scind.sum(branching_counts, nearby_labels, label_range)).astype(int) else: trunk_counts = np.zeros((0,),int) # # The branches are the branchpoints that lie outside the seed objects # if labels_count > 0: branch_counts = fix(scind.sum(branch_points, outside_labels, label_range)) else: branch_counts = np.zeros((0,),int) # # Save the endpoints # if labels_count > 0: end_counts = fix(scind.sum(end_points, outside_labels, label_range)) else: end_counts = np.zeros((0,), int) # # Save measurements # m = workspace.measurements assert isinstance(m, cpmeas.Measurements) feature = "_".join((C_NEURON, F_NUMBER_TRUNKS, skeleton_name)) m.add_measurement(seed_objects_name, feature, trunk_counts) feature = "_".join((C_NEURON, F_NUMBER_NON_TRUNK_BRANCHES, skeleton_name)) m.add_measurement(seed_objects_name, feature, branch_counts) feature = "_".join((C_NEURON, F_NUMBER_BRANCH_ENDS, skeleton_name)) m.add_measurement(seed_objects_name, feature, end_counts) # # Collect the graph information # if self.wants_neuron_graph: trunk_mask = (branching_counts > 0) & (nearby_labels != 0) intensity_image = workspace.image_set.get_image( self.intensity_image_name.value) edge_graph, vertex_graph = self.make_neuron_graph( combined_skel, dlabels, trunk_mask, branch_points & ~trunk_mask, end_points, intensity_image.pixel_data) # # Add an image number column to both and change vertex index # to vertex number (one-based) # image_number = workspace.measurements.image_set_number vertex_graph = np.rec.fromarrays( (np.ones(len(vertex_graph)) * image_number, np.arange(1, len(vertex_graph) + 1), vertex_graph['i'], vertex_graph['j'], vertex_graph['labels'], vertex_graph['kind']), names = ("image_number", "vertex_number", "i", "j", "labels", "kind")) edge_graph = np.rec.fromarrays( (np.ones(len(edge_graph)) * image_number, edge_graph["v1"], edge_graph["v2"], edge_graph["length"], edge_graph["total_intensity"]), names = ("image_number", "v1", "v2", "length", "total_intensity")) path = self.directory.get_absolute_path(m) edge_file = m.apply_metadata(self.edge_file_name.value) edge_path = os.path.abspath(os.path.join(path, edge_file)) vertex_file = m.apply_metadata(self.vertex_file_name.value) vertex_path = os.path.abspath(os.path.join(path, vertex_file)) d = self.get_dictionary(workspace.image_set_list) for file_path, table, fmt in ( (edge_path, edge_graph, "%d,%d,%d,%d,%.4f"), (vertex_path, vertex_graph, "%d,%d,%d,%d,%d,%s")): # # Delete files first time through / otherwise append # if not d.has_key(file_path): d[file_path] = True if os.path.exists(file_path): if workspace.frame is not None: import wx if wx.MessageBox( "%s already exists. Do you want to overwrite it?" % file_path, "Warning: overwriting file", style = wx.YES_NO, parent = workspace.frame) != wx.YES: raise ValueError("Can't overwrite %s" % file_path) os.remove(file_path) fd = open(file_path, 'wt') header = ','.join(table.dtype.names) fd.write(header + '\n') else: fd = open(file_path, 'at') np.savetxt(fd, table, fmt) fd.close() if workspace.frame is not None: workspace.display_data.edge_graph = edge_graph workspace.display_data.vertex_graph = vertex_graph # # Make the display image # if workspace.frame is not None or self.wants_branchpoint_image: branchpoint_image = np.zeros((skeleton.shape[0], skeleton.shape[1], 3)) trunk_mask = (branching_counts > 0) & (nearby_labels != 0) branch_mask = branch_points & (outside_labels != 0) end_mask = end_points & (outside_labels != 0) branchpoint_image[outside_skel,:] = 1 branchpoint_image[trunk_mask | branch_mask | end_mask,:] = 0 branchpoint_image[trunk_mask,0] = 1 branchpoint_image[branch_mask,1] = 1 branchpoint_image[end_mask, 2] = 1 branchpoint_image[dilated_labels != 0,:] *= .875 branchpoint_image[dilated_labels != 0,:] += .1 if workspace.frame: workspace.display_data.branchpoint_image = branchpoint_image if self.wants_branchpoint_image: bi = cpi.Image(branchpoint_image, parent_image = skeleton_image) workspace.image_set.add(self.branchpoint_image_name.value, bi)
def run(self, workspace): '''Run the module on the image set''' seed_objects_name = self.seed_objects_name.value skeleton_name = self.image_name.value seed_objects = workspace.object_set.get_objects(seed_objects_name) labels = seed_objects.segmented labels_count = np.max(labels) label_range = np.arange(labels_count, dtype=np.int32) + 1 skeleton_image = workspace.image_set.get_image(skeleton_name, must_be_binary=True) skeleton = skeleton_image.pixel_data if skeleton_image.has_mask: skeleton = skeleton & skeleton_image.mask try: labels = skeleton_image.crop_image_similarly(labels) except: labels, m1 = cpo.size_similarly(skeleton, labels) labels[~m1] = 0 # # The following code makes a ring around the seed objects with # the skeleton trunks sticking out of it. # # Create a new skeleton with holes at the seed objects # First combine the seed objects with the skeleton so # that the skeleton trunks come out of the seed objects. # # Erode the labels once so that all of the trunk branchpoints # will be within the labels # # # Dilate the objects, then subtract them to make a ring # my_disk = morph.strel_disk(1.5).astype(int) dilated_labels = grey_dilation(labels, footprint=my_disk) seed_mask = dilated_labels > 0 combined_skel = skeleton | seed_mask closed_labels = grey_erosion(dilated_labels, footprint=my_disk) seed_center = closed_labels > 0 combined_skel = combined_skel & (~seed_center) # # Fill in single holes (but not a one-pixel hole made by # a one-pixel image) # if self.wants_to_fill_holes: def size_fn(area, is_object): return (~is_object) and (area <= self.maximum_hole_size.value) combined_skel = morph.fill_labeled_holes(combined_skel, ~seed_center, size_fn) # # Reskeletonize to make true branchpoints at the ring boundaries # combined_skel = morph.skeletonize(combined_skel) # # The skeleton outside of the labels # outside_skel = combined_skel & (dilated_labels == 0) # # Associate all skeleton points with seed objects # dlabels, distance_map = propagate.propagate(np.zeros(labels.shape), dilated_labels, combined_skel, 1) # # Get rid of any branchpoints not connected to seeds # combined_skel[dlabels == 0] = False # # Find the branchpoints # branch_points = morph.branchpoints(combined_skel) # # Odd case: when four branches meet like this, branchpoints are not # assigned because they are arbitrary. So assign them. # # . . # B. # .B # . . # odd_case = (combined_skel[:-1, :-1] & combined_skel[1:, :-1] & combined_skel[:-1, 1:] & combined_skel[1, 1]) branch_points[:-1, :-1][odd_case] = True branch_points[1:, 1:][odd_case] = True # # Find the branching counts for the trunks (# of extra branches # eminating from a point other than the line it might be on). # branching_counts = morph.branchings(combined_skel) branching_counts = np.array([0, 0, 0, 1, 2])[branching_counts] # # Only take branches within 1 of the outside skeleton # dilated_skel = scind.binary_dilation(outside_skel, morph.eight_connect) branching_counts[~dilated_skel] = 0 # # Find the endpoints # end_points = morph.endpoints(combined_skel) # # We use two ranges for classification here: # * anything within one pixel of the dilated image is a trunk # * anything outside of that range is a branch # nearby_labels = dlabels.copy() nearby_labels[distance_map > 1.5] = 0 outside_labels = dlabels.copy() outside_labels[nearby_labels > 0] = 0 # # The trunks are the branchpoints that lie within one pixel of # the dilated image. # if labels_count > 0: trunk_counts = fix( scind.sum(branching_counts, nearby_labels, label_range)).astype(int) else: trunk_counts = np.zeros((0, ), int) # # The branches are the branchpoints that lie outside the seed objects # if labels_count > 0: branch_counts = fix( scind.sum(branch_points, outside_labels, label_range)) else: branch_counts = np.zeros((0, ), int) # # Save the endpoints # if labels_count > 0: end_counts = fix(scind.sum(end_points, outside_labels, label_range)) else: end_counts = np.zeros((0, ), int) # # Save measurements # m = workspace.measurements assert isinstance(m, cpmeas.Measurements) feature = "_".join((C_NEURON, F_NUMBER_TRUNKS, skeleton_name)) m.add_measurement(seed_objects_name, feature, trunk_counts) feature = "_".join( (C_NEURON, F_NUMBER_NON_TRUNK_BRANCHES, skeleton_name)) m.add_measurement(seed_objects_name, feature, branch_counts) feature = "_".join((C_NEURON, F_NUMBER_BRANCH_ENDS, skeleton_name)) m.add_measurement(seed_objects_name, feature, end_counts) # # Collect the graph information # if self.wants_neuron_graph: trunk_mask = (branching_counts > 0) & (nearby_labels != 0) intensity_image = workspace.image_set.get_image( self.intensity_image_name.value) edge_graph, vertex_graph = self.make_neuron_graph( combined_skel, dlabels, trunk_mask, branch_points & ~trunk_mask, end_points, intensity_image.pixel_data) # # Add an image number column to both and change vertex index # to vertex number (one-based) # image_number = workspace.measurements.image_set_number vertex_graph = np.rec.fromarrays( (np.ones(len(vertex_graph)) * image_number, np.arange(1, len(vertex_graph) + 1), vertex_graph['i'], vertex_graph['j'], vertex_graph['labels'], vertex_graph['kind']), names=("image_number", "vertex_number", "i", "j", "labels", "kind")) edge_graph = np.rec.fromarrays( (np.ones(len(edge_graph)) * image_number, edge_graph["v1"], edge_graph["v2"], edge_graph["length"], edge_graph["total_intensity"]), names=("image_number", "v1", "v2", "length", "total_intensity")) path = self.directory.get_absolute_path(m) edge_file = m.apply_metadata(self.edge_file_name.value) edge_path = os.path.abspath(os.path.join(path, edge_file)) vertex_file = m.apply_metadata(self.vertex_file_name.value) vertex_path = os.path.abspath(os.path.join(path, vertex_file)) d = self.get_dictionary(workspace.image_set_list) for file_path, table, fmt in ((edge_path, edge_graph, "%d,%d,%d,%d,%.4f"), (vertex_path, vertex_graph, "%d,%d,%d,%d,%d,%s")): # # Delete files first time through / otherwise append # if not d.has_key(file_path): d[file_path] = True if os.path.exists(file_path): if workspace.frame is not None: import wx if wx.MessageBox( "%s already exists. Do you want to overwrite it?" % file_path, "Warning: overwriting file", style=wx.YES_NO, parent=workspace.frame) != wx.YES: raise ValueError("Can't overwrite %s" % file_path) os.remove(file_path) fd = open(file_path, 'wt') header = ','.join(table.dtype.names) fd.write(header + '\n') else: fd = open(file_path, 'at') np.savetxt(fd, table, fmt) fd.close() if workspace.frame is not None: workspace.display_data.edge_graph = edge_graph workspace.display_data.vertex_graph = vertex_graph # # Make the display image # if workspace.frame is not None or self.wants_branchpoint_image: branchpoint_image = np.zeros( (skeleton.shape[0], skeleton.shape[1], 3)) trunk_mask = (branching_counts > 0) & (nearby_labels != 0) branch_mask = branch_points & (outside_labels != 0) end_mask = end_points & (outside_labels != 0) branchpoint_image[outside_skel, :] = 1 branchpoint_image[trunk_mask | branch_mask | end_mask, :] = 0 branchpoint_image[trunk_mask, 0] = 1 branchpoint_image[branch_mask, 1] = 1 branchpoint_image[end_mask, 2] = 1 branchpoint_image[dilated_labels != 0, :] *= .875 branchpoint_image[dilated_labels != 0, :] += .1 if workspace.frame: workspace.display_data.branchpoint_image = branchpoint_image if self.wants_branchpoint_image: bi = cpi.Image(branchpoint_image, parent_image=skeleton_image) workspace.image_set.add(self.branchpoint_image_name.value, bi)