def center_spline(traceback, distances, smoothing=None):
    '''Generate spline corresponding to the centerline of the worm
    
    Parameters:
    ------------
    traceback: list of 2-d tuples
        List of indices associated with the centerline, starting with
        one of the endpoints of the centerline and ending with the ending
        index of the centerline. 
    distances: ndarray shape(n,m)
        Distance transform from the medial axis transform of the worm mask

    Returns:
    -----------
    tck: parametric spline tuple
        spline tuple (see documentation for zplib.interpolate for more info)
    '''

    #NOTE: we will extrapolate the first/last few pixels to get the full length of the worm,
    #since medial axis transforms/skeltons don't always go to the edge of the mask
    #get the x,y positions for the centerline that can be
    #inputted into the fit spline function
    #NOTE: need to use traceback since order matters for spline creation
    if len(traceback) == 0:
        return (0, 0, 0)

    points = np.array(list(np.transpose(traceback))).T
    #print(points.shape)
    widths = distances[list(np.transpose(traceback))]

    if smoothing is None:
        smoothing = 0.2 * len(widths)

    #create splines for the first and last few points
    begin_tck = interpolate.fit_spline(points[:10],
                                       smoothing=smoothing,
                                       order=1)
    begin_xys = interpolate.spline_evaluate(
        begin_tck, np.linspace(-widths[0], 0, int(widths[0]), endpoint=False))
    #print(begin_xys.shape)
    #print(points[-10:])
    end_tck = interpolate.fit_spline(points[-10:],
                                     smoothing=smoothing,
                                     order=1)
    tmax = end_tck[0][-1]
    #print(tmax)
    #print(widths[-1])
    #print(tmax+tmax/widths[-1])
    #print(tmax+widths[-1])
    #remove the first point to prevent duplicate points in the end tck
    end_xys = interpolate.spline_evaluate(
        end_tck, np.linspace(tmax, tmax + widths[-1], int(widths[-1] + 1)))[1:]
    #print(end_xys)
    new_points = np.concatenate((begin_xys, points, end_xys))
    #print(new_points)
    #print("new_points: "+str(new_points.shape))
    tck = interpolate.fit_spline(new_points, smoothing=smoothing)
    return tck
def extrapolate_head(tck, width_tck, smoothing=None):
    '''Since the width/length splines don't get to the end of the worm,
    try to extrapolate the widths and such to the end of the worm mask

    NOTE: Not really used in the spline-fitting pipeline
    '''
    #get first and last points from the width_tck
    width_ends = interpolate.spline_interpolate(width_tck, 2)
    print(width_ends)
    #calculate new x's and y's that go to the end of the mask
    tmax = tck[0][-1]
    print("tmax: " + str(tmax))
    xys = interpolate.spline_evaluate(
        tck, np.linspace(-width_ends[0], tmax + width_ends[1], 600))
    #print(xys.shape)

    #interpolate the widths so that we can add on the end widths
    #NOTE: end widths will be zero since they are at the end of the mask
    widths = interpolate.spline_interpolate(width_tck, 600)
    new_widths = np.concatenate([[0], widths, [0]])
    #need to generate new x's to use to re-make the width splines with new_widths
    #new endpoint xs need to be reflective of where they are on the centerline
    new_xs = np.concatenate([[-widths[0] / tmax],
                             np.linspace(0, 1, 600), [1 + widths[-1] / tmax]])

    #re-make the splines
    if smoothing is None:
        smoothing = 0.2 * len(new_widths)

    new_tck = interpolate.fit_spline(xys, smoothing=smoothing)

    new_width_tck = interpolate.fit_nonparametric_spline(new_xs,
                                                         new_widths,
                                                         smoothing=smoothing)
    return new_tck, new_width_tck
Exemple #3
0
 def calculate_tck(self, points, smoothing=None):
     if smoothing is None:
         smoothing = self.REFIT_SMOOTH
     tck = interpolate.fit_spline(points, smoothing=smoothing * len(points))
     if not self.warping:
         tck = interpolate.reparameterize_spline(tck)
     return tck
Exemple #4
0
def _detect_edges(image, optocoupler, center_tck, width_tck, image_gamma,
                  downscale, gradient_sigma, sigmoid_midpoint,
                  sigmoid_growth_rate, edge_weight, roughness_penalty,
                  post_smoothing):
    """Trace the edges of a worm and return a new center_tck and width_tck.

    Parameters:
        image: ndarray of the brightfield image
        optocoupler: optocoupler magnification (to correctly calculate the image
            vignette)
        center_tck: spline defining the pose of the worm.
        width_tck: spline defining the distance from centerline to worm edges.
        image_gamma: gamma value for intensity transform to highlight worm edges
        downscale: factor by which to downsample the image
        gradient_sigma: sigma for gaussian gradient to find worm edges
        sigmoid_midpoint: midpoint of edge_highlighting sigmoid function for
            gradient values, expressed as a percentile of the gradient value
            over the whole image. (Somewhere around the 50th percentile
            seems generally good.)
        sigmoid_growth_rate: steepness of the sigmoid function. (The gradient is
            over an image rescaled from 0-1; such images have a max gradient
            in the worm region in the range 0.1-0.2. So the range of the sigmoid
            function is generally 0-0.2 or so; getting a steep curve requires
            growth rates in the 500-1000 range.)
        edge_weight: how much to weight image edge strength vs. distance from
            the average widths in the cost function. (The edge part of the
            cost function goes from 0 to 1, and the distance part goes from
            0 to the maximum pixel distance from the average edge; typically
            15 or so for a 2x downscaled image. So to have both terms equally
            weighted, edge_weight should be around 10-20.)
        roughness_penalty: how much to penalize diagonal steps vs. straight
            steps in the edge tracing (to penalize jagged edges). The penalty
            is multiplicative and scaled by the length of the step: a value of 1
            doesn't penalize diagonal steps, while a value of 2 means that a
            1-pixel-up diagonal step costs 2*sqrt(2) times more.
        post_smoothing: spline smoothing factor for re-fit centerline.

    Returns: (cost_image, new_center_tck, new_width_tck)
        cost_image: image defining the cost function for edge tracing
        new_center_tck: new centerline spline
        new_width_tck: new width spline
    """
    cost_image = get_cost_image(image, optocoupler, image_gamma, center_tck,
                                width_tck, downscale, gradient_sigma,
                                sigmoid_midpoint, sigmoid_growth_rate,
                                edge_weight)

    # trace edges to calculate new centerline and widths
    center_coordinates, widths = edge_coordinates(cost_image,
                                                  roughness_penalty)
    lab_center_coordinates = worm_spline.coordinates_to_lab_frame(
        center_coordinates, cost_image.shape, center_tck, zoom=1 / downscale)
    new_center_tck = interpolate.fit_spline(lab_center_coordinates,
                                            smoothing=post_smoothing *
                                            len(lab_center_coordinates))
    x = numpy.linspace(0, 1, len(widths))
    # NB: expand widths to account for downsampling
    new_width_tck = interpolate.fit_nonparametric_spline(
        x, widths * downscale, smoothing=post_smoothing * len(widths))
    return cost_image, new_center_tck, new_width_tck
Exemple #5
0
def path(pts, smooth=True, filter=False):
    global pen
    #print resolution
    if draw:
        if filter:
            fpts = []
            fpts.append(pts[0])
            for pt in pts[1:]:
                if delta(fpts[-1],pt) > filter:
                    fpts.append(pt)
            if (pt != fpts[-1]).all():
                fpts.append(pt)
            pts = np.array(fpts)
        if smooth and len(pts) > 3:
            tck = spline.fit_spline(pts,smoothing=0.0)
            beziers = spline.spline_to_bezier(tck)
            bezier = beziers.pop(0)
            gcode = "G0 X%.3F Y%.3F F%.0f" % (bezier[0, 0] + OFFSET[0], bezier[0, 1] + OFFSET[1], MOVESPEED)
            gwrite(gcode)
            gwrite(pendown)
            if bezier.shape[0] == 4:
                gcode = "G5 I%.3F J%.3F P%.3F Q%.3F X%.3F Y%.3F F%.0f" % (bezier[1, 0] - bezier[0, 0] + OFFSET[0], bezier[1, 1] - bezier[0, 1] + OFFSET[1],\
                                                                           bezier[2, 0] - bezier[3, 0], bezier[2, 1] - bezier[3, 1],\
                                                                           bezier[3, 0] + OFFSET[0], bezier[3, 1] + OFFSET[1], DRAWSPEED)
            elif bezier.shape[0] == 3:
                gcode = "G5 I%.3F J%.3F P%.3F Q%.3F X%.3F Y%.3F F%.0f" % (bezier[1, 0] - bezier[0, 0] + OFFSET[0], bezier[1, 1] - bezier[0, 1] + OFFSET[1], \
                                                                        bezier[1, 0] - bezier[2, 0], bezier[1, 1] - bezier[2, 1], \
                                                                        bezier[2, 0] + OFFSET[0], bezier[2, 1] + OFFSET[1], DRAWSPEED)
            else:
                gcode = "G1 X%.3F Y%.3F F%.0f" % (bezier[1,0] + OFFSET[0], bezier[1,1] + OFFSET[1], DRAWSPEED)
            gwrite(gcode)

            for bezier in beziers:
                if bezier.shape[0] == 4:
                    gcode = "G5 I%.3F J%.3F P%.3F Q%.3F X%.3F Y%.3F F%.0f" % (bezier[1, 0] - bezier[0, 0] + OFFSET[0], bezier[1, 1] - bezier[0, 1] + OFFSET[1],
                                                                            bezier[2, 0] - bezier[3, 0], bezier[2, 1] - bezier[3, 1], \
                                                                            bezier[3, 0] + OFFSET[0], bezier[3, 1] + OFFSET[1], DRAWSPEED)
                else:
                    gcode = "G5 I%.3F J%.3F P%.3F Q%.3F X%.3F Y%.3F F%.0f" % (bezier[1, 0] - bezier[0, 0] + OFFSET[0], bezier[1, 1] - bezier[0, 1] + OFFSET[1], \
                                                                        bezier[1, 0] - bezier[2, 0], bezier[1, 1] - bezier[2, 1], \
                                                                        bezier[2, 0] + OFFSET[0], bezier[2, 1] + OFFSET[1], DRAWSPEED)
                gwrite(gcode)
        else:
            if delta(pts[0], pts[1]) > 1:
                print "Large Gap ",pts[0],pts[1]
            ptx = pts[:,0]
            pty = pts[:,1]
            gcode = "G0 X%.3F Y%.3F F%.0f" % (ptx[0] + OFFSET[0], pty[0] + OFFSET[1], MOVESPEED)
            gwrite(gcode)
            gwrite(pendown)
            gcode = "G1 X%.3F Y%.3F F%.0f" % (ptx[1] + OFFSET[0], pty[1] + OFFSET[1], DRAWSPEED)
            gwrite(gcode)

        gwrite(penup)
        #gwrite("G4 P1000")
        #draw.flush()
    else:
        print "Nothing open to draw onto"
Exemple #6
0
def plot_spline(rw, points, smoothing, rgba):
    tck = interpolate.fit_spline(points, smoothing=smoothing)
    bezier_elements = interpolate.spline_to_bezier(tck)
    path = Qt.QPainterPath()
    path.moveTo(*bezier_elements[0][0])
    for (sx, sy), (c1x, c1y), (c2x, c2y), (ex, ey) in bezier_elements:
        path.cubicTo(c1x, c1y, c2x, c2y, ex, ey)
    display_path = Qt.QGraphicsPathItem(path, parent=rw.image_scene.layer_stack_item)
    pen = Qt.QPen(Qt.QColor(*rgba))
    pen.setWidth(2)
    pen.setCosmetic(True)
    display_path.setPen(pen)
    return tck, display_path
Exemple #7
0
 def _generate_path_from_positions(self, positions):
     if len(positions) < 4:
         return super()._generate_path_from_positions(positions)
     points = [(pos.x(), pos.y()) for pos in positions]
     self.tck = interpolate.fit_spline(points,
                                       smoothing=self._smoothing *
                                       len(points))
     bezier_elements = interpolate.spline_to_bezier(self.tck)
     path = Qt.QPainterPath()
     path.moveTo(*bezier_elements[0][0])
     for (sx, sy), (c1x, c1y), (c2x, c2y), (ex, ey) in bezier_elements:
         path.cubicTo(c1x, c1y, c2x, c2y, ex, ey)
     return path
Exemple #8
0
def fit_splines(center_path,
                mask,
                dv_coords,
                width_range=(50, 100),
                width_step=1,
                center_smoothing=0.1,
                width_smoothing=0.0001):
    """Find a (center_tck, width_tck) pose from the centerline path.

    The main challenge is that the dv coordinates are relative to that worm, so an
    absolute scale must be determined. This is done by trying various absolute
    scale factors to find the pixel width of the worm as a function of the dv
    coordinate value along the centerline, and choosing that which best recapitulates
    the mask image provided.

    Parameters:
        center_path: (n, 2)-shape array of coordinates along the centerline of a worm
        mask: approximate mask of worm outline
        dv_coords: dv coordinate image
        width_range: range of possible scale factors to try for the worm (though note that the
            width_tck values are in terms of half-widths from the centerline to the edge)
        width_step: step size for evaluating possible worm widths.
        center_smoothing: average distance the center_tck spline is allowed
            to deviate from the input path coordinates.
        width_smoothing: average distance the width_tck spline is allowed to deviate
            from the input dv coordinate values.

    Returns: (center_tck, width_tck) pose tuple
    """
    center_smoothing *= len(center_path)
    center_tck = interpolate.fit_spline(center_path,
                                        smoothing=center_smoothing,
                                        force_endpoints=False)
    width_profile = dv_coords[tuple(
        center_path.T)] / 6  # scale widths in [0, 0.5] range
    # This range is so that when the width_profile is multiplied by the total worm width, the
    # resulting width_tck will produce the expected half-width distances from centerline to edge
    x = numpy.linspace(0, 1, len(width_profile))
    width_smoothing *= len(width_profile)
    width_profile_tck = interpolate.fit_nonparametric_spline(
        x, width_profile, smoothing=width_smoothing)
    # do coarse / fine search for best width multiplier
    width_tck, max_width = _fit_widths_to_mask(center_tck, width_profile_tck,
                                               width_range, width_step * 4,
                                               mask)
    width_range = max_width - 8 * width_step, max_width + 8 * width_step
    width_tck, max_width = _fit_widths_to_mask(center_tck, width_profile_tck,
                                               width_range, width_step, mask)
    return center_tck, width_tck
Exemple #9
0
def path(pts, smooth=True, filter=False):
    global pen
    #print resolution
    if draw:
        if filter:
            fpts = []
            fpts.append(pts[0])
            for pt in pts[1:]:
                if delta(fpts[-1], pt) > filter:
                    fpts.append(pt)
            if (pt != fpts[-1]).all():
                fpts.append(pt)
            pts = np.array(fpts)
        gcode = "G0 X%.3f Y%.3f F%.0f" % (pts[0, 0], pts[0, 1], MOVESPEED)
        gwrite(gcode)
        gwrite(pendown)
        if smooth and len(pts) > 2:
            tck = spline.fit_spline(pts, smoothing=0.0)
            beziers = spline.spline_to_bezier(tck)
            for bezier in beziers:
                #                   bez = map(tuple,beziers.pop(0))
                cubicB = CubicBezier(bezier[0], bezier[1], bezier[2],
                                     bezier[3])
                arcs = cubicB.biarc_approximation()
                for arc in arcs:
                    if isinstance(arc, Line):
                        gcode = "G1 X%.3f Y%.3f F%.0f" % (arc.p2.x, arc.p2.y,
                                                          DRAWSPEED)
                    elif isinstance(arc, Arc):
                        arcv = arc.center - arc.p1
                        if arc.is_clockwise():
                            gcode = "G2 X%.3f Y%.3f I%.3f J%.3f F%.0f" % (
                                arc.p2.x, arc.p2.y, arcv.x, arcv.y, DRAWSPEED)
                        else:
                            gcode = "G3 X%.3f Y%.3f I%.3f J%.3f F%.0f" % (
                                arc.p2.x, arc.p2.y, arcv.x, arcv.y, DRAWSPEED)
                    gwrite(gcode)
        else:
            for pt in pts[1:]:
                gcode = "G1 X%.3f Y%.3f F%.0f" % (pt[0], pt[1], DRAWSPEED)
                gwrite(gcode)
        gwrite(penup)
        #gwrite("G4 P1000")
        #draw.flush()
    else:
        print "Nothing open to draw onto"
def fit_simple_skeleton(image_masks, save_dir='', reverse_spine=np.array([])):
    def prune_skeleton(skeleton_graph, verbose_mode):
        '''
        Prune the graph of the skeleton so that only the single linear longest path remains.
        '''
        if verbose_mode:
            print('Pruning skeleton graph.')

        def farthest_node(my_graph, a_node, verbose_mode):
            '''
            Find the farthest node from a_node.
            '''
            reached_nodes = [a_node]
            distance_series = pd.Series(
                index=[str(a_node) for a_node in skeleton_graph.node_list])
            distance_series.loc[str(a_node)] = 0
            steps = 1
            current_circle = [a_node]
            next_circle = []

            while len(reached_nodes) < len(my_graph.node_list):
                if verbose_mode:
                    print('Reached nodes: ' + str(len(reached_nodes)))
                for current_node in current_circle:
                    next_steps = my_graph.edges(current_node)
                    if verbose_mode:
                        print('Current circle')
                        print(current_circle)
                        print('next_steps')
                        print(next_steps)
                    for one_step in next_steps:
                        other_node = [
                            the_node for the_node in one_step
                            if the_node != current_node
                        ][0]
                        if other_node not in reached_nodes:
                            distance_series.loc[str(other_node)] = steps
                            next_circle.append(other_node)
                            reached_nodes.append(other_node)
                steps += 1
                current_circle = next_circle
                next_circle = []
            my_node = distance_series.argmax()
            my_node = (int(my_node[1:-1].split(',')[0]),
                       int(my_node[1:-1].split(',')[1]))
            return my_node

        def find_minimal_path(my_graph, node_a, node_b, verbose_mode):
            '''
            Find the minimal path between node_a and node_b using my_graph.
            '''
            reached_nodes = [node_a]
            steps = 1
            current_circle = [[node_a]]
            next_circle = []
            got_to_b = False
            while not got_to_b:
                if verbose_mode:
                    print(len(reached_nodes))
                    print([
                        the_node for the_node in reached_nodes
                        if the_node not in my_graph.node_list
                    ])
                for current_path in current_circle:
                    current_node = current_path[-1]
                    next_steps = my_graph.edges(current_node)
                    for one_step in next_steps:
                        other_node = [
                            the_node for the_node in one_step
                            if the_node != current_node
                        ][0]
                        if other_node == node_b:
                            final_path = list(current_path)
                            final_path.append(other_node)
                            return (final_path, steps)

                        elif other_node not in reached_nodes:
                            next_path = list(current_path)
                            next_path.append(other_node)
                            next_circle.append(next_path)
                            reached_nodes.append(other_node)
                steps += 1
                current_circle = next_circle
                next_circle = []
            return

        one_end = farthest_node(skeleton_graph,
                                skeleton_graph.node_list[0],
                                verbose_mode=verbose_mode)
        if verbose_mode:
            print('First end is: ' + str(one_end))
        other_end = farthest_node(skeleton_graph,
                                  one_end,
                                  verbose_mode=verbose_mode)
        if verbose_mode:
            print('Second end is: ' + str(other_end))

        (my_path, path_length) = find_minimal_path(skeleton_graph,
                                                   one_end,
                                                   other_end,
                                                   verbose_mode=verbose_mode)
        my_path = np.array(my_path)
        return my_path

    def skeletonize_mask(raster_worm, verbose_mode):
        '''
        Given a masked worm in raster format, return a skeletonized version of it.
        '''
        if verbose_mode:
            print('Skeletonizing mask.')
        zero_one_mask = np.zeros(raster_worm.shape)
        zero_one_mask[raster_worm > 0] = 1
        zero_one_mask = zplib.image.mask.get_largest_object(zero_one_mask)
        my_skeleton = skimage.morphology.skeletonize(zero_one_mask)
        skeleton_mask = np.zeros(raster_worm.shape).astype('uint8')
        skeleton_mask[my_skeleton] = -1
        return skeleton_mask

    def skeleton_to_graph(skeleton_mask, verbose_mode):
        '''
        Converts a skeleton to a graph, which consists of a dictionary with a list of nodes (tuples containing the coordinates of each node) in 'nodes' and a list of edges (lists of two tuples containing coordinates of the nodes connected by the edge; all edges have length 1).
        '''
        if verbose_mode:
            print('Converting skeleton to graph.')
        node_list = [
            tuple(a_point)
            for a_point in np.transpose(np.array(np.where(skeleton_mask > 0)))
        ]
        edge_list = []
        for point_a in node_list:
            for point_b in node_list:
                distance_vector = np.array(point_a) - np.array(point_b)
                check_distance = np.max(np.abs(distance_vector))
                my_edge = sorted([point_a, point_b])
                if check_distance == 1:
                    if my_edge not in edge_list:
                        edge_list.append(my_edge)

        class a_graph():
            def __init__(self, node_list, edge_list):
                self.node_list = node_list
                self.edge_list = edge_list
                return

            def edges(self, a_node):
                return [an_edge for an_edge in edge_list if a_node in an_edge]

        my_graph = a_graph(node_list, edge_list)
        return my_graph

    n_points = 10
    spine_out = np.zeros([(mask_imgs.shape)[0], n_points, 2])

    if (reverse_spine.size == 0):
        print('No orientation for spine found')
        reverse_spine = np.zeros([(mask_imgs.shape)[0]])
    else:
        print('Using supplied orientation')

    for (mask_idx,
         mask_img), reverse_this_spine in zip(enumerate(mask_imgs),
                                              reverse_spine):
        print('processing simple spine for mask img {:03}'.format(mask_idx))
        pruned_graph = prune_skeleton(
            skeleton_to_graph(skeletonize_mask(mask_img, True), True), True)
        spine_tck = zplib_interpolate.fit_spline(pruned_graph)
        spine_out[mask_idx, :, :] = zplib_interpolate.spline_interpolate(
            spine_tck, n_points)
        if reverse_this_spine:
            spine_out[mask_idx, :, :] = np.flipud(spine_out[mask_idx, :, :])
        if save_dir != '':
            plt.figure(0)
            plt.gcf().clf()
            plt.imshow(mask_img.T)
            plt.scatter(spine_out[mask_idx, :, 0],
                        spine_out[mask_idx, :, 1],
                        c='b')
            plt.scatter(spine_out[mask_idx, 0, 0],
                        spine_out[mask_idx, 0, 1],
                        c='r')
            plt.figure(0).savefig(save_dir + os.path.sep +
                                  'mask_spine_{:03}.png'.format(mask_idx))

    return spine_out
Exemple #11
0
    def initialize_outline(standard_mask, verbose_mode, reverse_flag=False):
        '''
		Given a mask of a worm, this function will guess at the outline of the worm, returning a spline fit to a pruned skeleton of the mask.
		'''
        def prune_skeleton(skeleton_graph, verbose_mode):
            '''
			Prune the graph of the skeleton so that only the single linear longest path remains.
			'''
            if verbose_mode:
                print('Pruning skeleton graph.')

            def farthest_node(my_graph, a_node, verbose_mode):
                '''
				Find the farthest node from a_node.
				'''
                reached_nodes = [a_node]
                distance_series = pd.Series(
                    index=[str(a_node) for a_node in skeleton_graph.node_list])
                distance_series.loc[str(a_node)] = 0
                steps = 1
                current_circle = [a_node]
                next_circle = []

                while len(reached_nodes) < len(my_graph.node_list):
                    if verbose_mode:
                        print('Reached nodes: ' + str(len(reached_nodes)))
                    for current_node in current_circle:
                        next_steps = my_graph.edges(current_node)
                        if verbose_mode:
                            print('Current circle')
                            print(current_circle)
                            print('next_steps')
                            print(next_steps)
                        for one_step in next_steps:
                            other_node = [
                                the_node for the_node in one_step
                                if the_node != current_node
                            ][0]
                            if other_node not in reached_nodes:
                                distance_series.loc[str(other_node)] = steps
                                next_circle.append(other_node)
                                reached_nodes.append(other_node)
                    steps += 1
                    current_circle = next_circle
                    next_circle = []
                my_node = distance_series.argmax()
                my_node = (int(my_node[1:-1].split(',')[0]),
                           int(my_node[1:-1].split(',')[1]))
                return my_node

            def find_minimal_path(my_graph, node_a, node_b, verbose_mode):
                '''
				Find the minimal path between node_a and node_b using my_graph.
				'''
                reached_nodes = [node_a]
                steps = 1
                current_circle = [[node_a]]
                next_circle = []
                got_to_b = False
                while not got_to_b:
                    if verbose_mode:
                        print(len(reached_nodes))
                        print([
                            the_node for the_node in reached_nodes
                            if the_node not in my_graph.node_list
                        ])
                    for current_path in current_circle:
                        current_node = current_path[-1]
                        next_steps = my_graph.edges(current_node)
                        for one_step in next_steps:
                            other_node = [
                                the_node for the_node in one_step
                                if the_node != current_node
                            ][0]
                            if other_node == node_b:
                                final_path = list(current_path)
                                final_path.append(other_node)
                                return (final_path, steps)

                            elif other_node not in reached_nodes:
                                next_path = list(current_path)
                                next_path.append(other_node)
                                next_circle.append(next_path)
                                reached_nodes.append(other_node)
                    steps += 1
                    current_circle = next_circle
                    next_circle = []
                return

            one_end = farthest_node(skeleton_graph,
                                    skeleton_graph.node_list[0],
                                    verbose_mode=verbose_mode)
            if verbose_mode:
                print('First end is: ' + str(one_end))
            other_end = farthest_node(skeleton_graph,
                                      one_end,
                                      verbose_mode=verbose_mode)
            if verbose_mode:
                print('Second end is: ' + str(other_end))

            (my_path,
             path_length) = find_minimal_path(skeleton_graph,
                                              one_end,
                                              other_end,
                                              verbose_mode=verbose_mode)
            my_path = np.array(my_path)
            return my_path

        def skeletonize_mask(raster_worm, verbose_mode):
            '''
			Given a masked worm in raster format, return a skeletonized version of it.
			'''
            if verbose_mode:
                print('Skeletonizing mask.')
            zero_one_mask = np.zeros(raster_worm.shape)
            zero_one_mask[raster_worm > 0] = 1
            zero_one_mask = zplib_image_mask.get_largest_object(zero_one_mask)
            my_skeleton = skimage.morphology.skeletonize(zero_one_mask)
            skeleton_mask = np.zeros(raster_worm.shape).astype('uint8')
            skeleton_mask[my_skeleton] = -1
            return skeleton_mask

        def skeleton_to_graph(skeleton_mask, verbose_mode):
            '''
			Converts a skeleton to a graph, which consists of a dictionary with a list of nodes (tuples containing the coordinates of each node) in 'nodes' and a list of edges (lists of two tuples containing coordinates of the nodes connected by the edge; all edges have length 1).
			'''
            if verbose_mode:
                print('Converting skeleton to graph.')
            node_list = [
                tuple(a_point) for a_point in np.transpose(
                    np.array(np.where(skeleton_mask > 0)))
            ]
            edge_list = []
            for point_a in node_list:
                for point_b in node_list:
                    distance_vector = np.array(point_a) - np.array(point_b)
                    check_distance = np.max(np.abs(distance_vector))
                    my_edge = sorted([point_a, point_b])
                    if check_distance == 1:
                        if my_edge not in edge_list:
                            edge_list.append(my_edge)

            class a_graph():
                def __init__(self, node_list, edge_list):
                    self.node_list = node_list
                    self.edge_list = edge_list
                    return

                def edges(self, a_node):
                    return [
                        an_edge for an_edge in edge_list if a_node in an_edge
                    ]

            my_graph = a_graph(node_list, edge_list)
            return my_graph

        messy_skeleton = skeletonize_mask(standard_mask,
                                          verbose_mode=verbose_mode)
        skeleton_graph = skeleton_to_graph(messy_skeleton,
                                           verbose_mode=verbose_mode)
        pruned_graph = prune_skeleton(skeleton_graph,
                                      verbose_mode=verbose_mode)
        if reverse_flag:
            pruned_graph = np.flipud(pruned_graph)
        spine_tck = zplib_interpolate.fit_spline(pruned_graph)
        return spine_tck
	def initialize_outline(standard_mask, verbose_mode, reverse_flag = False):
		'''
		Given a mask of a worm, this function will guess at the outline of the worm, returning a spline fit to a pruned skeleton of the mask.
		'''
		def prune_skeleton(skeleton_graph, verbose_mode):
			'''
			Prune the graph of the skeleton so that only the single linear longest path remains.
			'''
			if verbose_mode:
				print('Pruning skeleton graph.')

			def farthest_node(my_graph, a_node, verbose_mode):
				'''
				Find the farthest node from a_node.
				'''
				reached_nodes = [a_node]
				distance_series = pd.Series(index = [str(a_node) for a_node in skeleton_graph.node_list])
				distance_series.loc[str(a_node)] = 0
				steps = 1
				current_circle = [a_node]
				next_circle = []
								
				while len(reached_nodes) < len(my_graph.node_list):
					if verbose_mode:		
						print('Reached nodes: ' + str(len(reached_nodes)))
					for current_node in current_circle:
						next_steps = my_graph.edges(current_node)
						if verbose_mode:
							print('Current circle')
							print(current_circle)
							print('next_steps')
							print(next_steps)
						for one_step in next_steps:
							other_node = [the_node for the_node in one_step if the_node != current_node][0]
							if other_node not in reached_nodes:
								distance_series.loc[str(other_node)] = steps 
								next_circle.append(other_node)
								reached_nodes.append(other_node)
					steps += 1
					current_circle = next_circle
					next_circle = []
				my_node = distance_series.argmax()
				my_node = (int(my_node[1:-1].split(',')[0]), int(my_node[1:-1].split(',')[1]))
				return my_node
			
			def find_minimal_path(my_graph, node_a, node_b, verbose_mode):
				'''
				Find the minimal path between node_a and node_b using my_graph.
				'''
				reached_nodes = [node_a]
				steps = 1
				current_circle = [[node_a]]
				next_circle = []
				got_to_b = False
				while not got_to_b:
					if verbose_mode:
						print(len(reached_nodes))		
						print([the_node for the_node in reached_nodes if the_node not in my_graph.node_list])		
					for current_path in current_circle:
						current_node = current_path[-1]
						next_steps = my_graph.edges(current_node)
						for one_step in next_steps:
							other_node = [the_node for the_node in one_step if the_node != current_node][0]
							if other_node == node_b:
								final_path = list(current_path)
								final_path.append(other_node)
								return (final_path, steps)
								
							elif other_node not in reached_nodes:
								next_path = list(current_path)
								next_path.append(other_node)
								next_circle.append(next_path)
								reached_nodes.append(other_node)
					steps += 1
					current_circle = next_circle
					next_circle = []	
				return
								
			one_end = farthest_node(skeleton_graph, skeleton_graph.node_list[0], verbose_mode = verbose_mode)
			if verbose_mode:
				print('First end is: ' + str(one_end))
			other_end = farthest_node(skeleton_graph, one_end, verbose_mode = verbose_mode)
			if verbose_mode:
				print('Second end is: ' + str(other_end))
				
			(my_path, path_length) = find_minimal_path(skeleton_graph, one_end, other_end, verbose_mode = verbose_mode)
			my_path = np.array(my_path)
			return my_path

		def skeletonize_mask(raster_worm, verbose_mode):
			'''
			Given a masked worm in raster format, return a skeletonized version of it.
			'''
			if verbose_mode:
				print('Skeletonizing mask.')
			zero_one_mask = np.zeros(raster_worm.shape)
			zero_one_mask[raster_worm > 0] = 1
			zero_one_mask = zplib_image_mask.get_largest_object(zero_one_mask)
			my_skeleton = skimage.morphology.skeletonize(zero_one_mask)
			skeleton_mask = np.zeros(raster_worm.shape).astype('uint8')
			skeleton_mask[my_skeleton] = -1
			return skeleton_mask
		
		def skeleton_to_graph(skeleton_mask, verbose_mode):
			'''
			Converts a skeleton to a graph, which consists of a dictionary with a list of nodes (tuples containing the coordinates of each node) in 'nodes' and a list of edges (lists of two tuples containing coordinates of the nodes connected by the edge; all edges have length 1).
			'''
			if verbose_mode:
				print('Converting skeleton to graph.')
			node_list = [tuple(a_point) for a_point in np.transpose(np.array(np.where(skeleton_mask > 0)))]
			edge_list = []
			for point_a in node_list:
				for point_b in node_list:
					distance_vector = np.array(point_a) - np.array(point_b)
					check_distance = np.max(np.abs(distance_vector))
					my_edge = sorted([point_a, point_b])
					if check_distance == 1:
						if my_edge not in edge_list:
							edge_list.append(my_edge)
			
			class a_graph():
				def __init__(self, node_list, edge_list):
					self.node_list = node_list
					self.edge_list = edge_list
					return
				def edges(self, a_node):
					return [an_edge for an_edge in edge_list if a_node in an_edge]
		
			my_graph = a_graph(node_list, edge_list)
			return my_graph
		
		messy_skeleton = skeletonize_mask(standard_mask, verbose_mode = verbose_mode)
		skeleton_graph = skeleton_to_graph(messy_skeleton, verbose_mode = verbose_mode)	
		pruned_graph = prune_skeleton(skeleton_graph, verbose_mode = verbose_mode)	
		if reverse_flag:
			pruned_graph = np.flipud(pruned_graph)
		spine_tck = zplib_interpolate.fit_spline(pruned_graph)
		return spine_tck