def get_skeleton_points(self, obj):
     '''Get points by skeletonizing the objects and decimating'''
     ii = []
     jj = []
     total_skel = np.zeros(obj.shape, bool)
     for labels, indexes in obj.get_labels():
         colors = morph.color_labels(labels)
         for color in range(1, np.max(colors) + 1):
             labels_mask = colors == color
             skel = morph.skeletonize(
                 labels_mask,
                 ordering=distance_transform_edt(labels_mask) *
                 poisson_equation(labels_mask))
             total_skel = total_skel | skel
     n_pts = np.sum(total_skel)
     if n_pts == 0:
         return np.zeros(0, np.int32), np.zeros(0, np.int32)
     i, j = np.where(total_skel)
     if n_pts > self.max_points.value:
         #
         # Decimate the skeleton by finding the branchpoints in the
         # skeleton and propagating from those.
         #
         markers = np.zeros(total_skel.shape, np.int32)
         branchpoints = \
             morph.branchpoints(total_skel) | morph.endpoints(total_skel)
         markers[branchpoints] = np.arange(np.sum(branchpoints)) + 1
         #
         # We compute the propagation distance to that point, then impose
         # a slightly arbitarary order to get an unambiguous ordering
         # which should number the pixels in a skeleton branch monotonically
         #
         ts_labels, distances = propagate(np.zeros(markers.shape), markers,
                                          total_skel, 1)
         order = np.lexsort((j, i, distances[i, j], ts_labels[i, j]))
         #
         # Get a linear space of self.max_points elements with bounds at
         # 0 and len(order)-1 and use that to select the points.
         #
         order = order[np.linspace(0,
                                   len(order) - 1,
                                   self.max_points.value).astype(int)]
         return i[order], j[order]
     return i, j
 def get_skeleton_points(self, obj):
     '''Get points by skeletonizing the objects and decimating'''
     ii = []
     jj = []
     total_skel = np.zeros(obj.shape, bool)
     for labels, indexes in obj.get_labels():
         colors = morph.color_labels(labels)
         for color in range(1, np.max(colors) + 1):
             labels_mask = colors == color
             skel = morph.skeletonize(
                 labels_mask,
                 ordering = distance_transform_edt(labels_mask) *
                 poisson_equation(labels_mask))
             total_skel = total_skel | skel
     n_pts = np.sum(total_skel)
     if n_pts == 0:
         return np.zeros(0, np.int32), np.zeros(0, np.int32)
     i, j = np.where(total_skel)
     if n_pts > self.max_points.value:
         #
         # Decimate the skeleton by finding the branchpoints in the
         # skeleton and propagating from those.
         #
         markers = np.zeros(total_skel.shape, np.int32)
         branchpoints = \
             morph.branchpoints(total_skel) | morph.endpoints(total_skel)
         markers[branchpoints] = np.arange(np.sum(branchpoints))+1
         #
         # We compute the propagation distance to that point, then impose
         # a slightly arbitarary order to get an unambiguous ordering
         # which should number the pixels in a skeleton branch monotonically
         #
         ts_labels, distances = propagate(np.zeros(markers.shape),
                                  markers, total_skel, 1)
         order = np.lexsort((j, i, distances[i, j], ts_labels[i, j]))
         #
         # Get a linear space of self.max_points elements with bounds at
         # 0 and len(order)-1 and use that to select the points.
         #
         order = order[
             np.linspace(0, len(order)-1, self.max_points.value).astype(int)]
         return i[order], j[order]
     return i, j
Example #3
0
    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, F_OPENLINES)
                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_OPENLINES:
                return morph.openlines(pixel_data,
                                       linelength=custom_repeats,
                                       mask=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
Example #4
0
    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
        custom_repeats = function.custom_repeats.value

        is_binary = pixel_data.dtype.kind == 'b'

        if (function_name in (F_BRANCHPOINTS, F_BRIDGE, F_CLEAN, F_DIAG,
                              F_CONVEX_HULL, F_DISTANCE, F_ENDPOINTS, F_FILL,
                              F_HBREAK, F_MAJORITY, F_REMOVE, F_SHRINK,
                              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_HBREAK, F_MAJORITY, F_REMOVE, F_SHRINK,
                             F_SKELPE, F_SPUR, F_THICKEN, F_THIN, F_VBREAK,
                             F_OPENLINES):
            # 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_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_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_FILL:
                return morph.fill(pixel_data, mask, count)
            elif function_name == F_HBREAK:
                return morph.hbreak(pixel_data, mask, count)
            elif function_name == F_MAJORITY:
                return morph.majority(pixel_data, mask, count)
            elif function_name == F_OPENLINES:
                return morph.openlines(pixel_data,
                                       linelength=custom_repeats,
                                       mask=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_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)
            return pixel_data
Example #5
0
    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)
        #
        # Calculate the distances
        #
        total_distance = morph.skeleton_length(
                dlabels * outside_skel, label_range)
        #
        # 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)
        feature = "_".join((C_NEURON, F_TOTAL_NEURITE_LENGTH, skeleton_name))
        m[seed_objects_name, feature] = total_distance
        #
        # 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
        # emanating 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)
        #
        # Calculate the distances
        #
        total_distance = morph.skeleton_length(dlabels * outside_skel,
                                               label_range)
        #
        # Save measurements
        #
        m = workspace.measurements
        assert isinstance(m, cpmeas.Measurements)
        feature = "_".join((C_OBJSKELETON, F_NUMBER_TRUNKS, skeleton_name))
        m.add_measurement(seed_objects_name, feature, trunk_counts)
        feature = "_".join(
            (C_OBJSKELETON, F_NUMBER_NON_TRUNK_BRANCHES, skeleton_name))
        m.add_measurement(seed_objects_name, feature, branch_counts)
        feature = "_".join(
            (C_OBJSKELETON, F_NUMBER_BRANCH_ENDS, skeleton_name))
        m.add_measurement(seed_objects_name, feature, end_counts)
        feature = "_".join(
            (C_OBJSKELETON, F_TOTAL_OBJSKELETON_LENGTH, skeleton_name))
        m[seed_objects_name, feature] = total_distance
        #
        # Collect the graph information
        #
        if self.wants_objskeleton_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_objskeleton_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)
Example #7
0
    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, F_OPENLINES) 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_OPENLINES:
                return morph.openlines(pixel_data, linelength=custom_repeats, mask=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
Example #8
0
    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
        custom_repeats = function.custom_repeats.value

        is_binary = pixel_data.dtype.kind == 'b'

        if (function_name in (F_BRANCHPOINTS, F_BRIDGE, F_CLEAN, F_DIAG,
                              F_CONVEX_HULL, F_DISTANCE, F_ENDPOINTS, F_FILL,
                              F_HBREAK, F_MAJORITY,
                              F_REMOVE, F_SHRINK, 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_HBREAK, F_MAJORITY, F_REMOVE, F_SHRINK, F_SKELPE, F_SPUR, F_THICKEN,
                             F_THIN, F_VBREAK, F_OPENLINES):
            # 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_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_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_FILL:
                return morph.fill(pixel_data, mask, count)
            elif function_name == F_HBREAK:
                return morph.hbreak(pixel_data, mask, count)
            elif function_name == F_MAJORITY:
                return morph.majority(pixel_data, mask, count)
            elif function_name == F_OPENLINES:
                return morph.openlines(pixel_data, linelength=custom_repeats, mask=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_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)
            return pixel_data