Beispiel #1
0
 def centerline(self):
     """ determine the center line of the tail """
     mask, offset = self.mask
     dist_map = cv2.distanceTransform(mask, cv2.DIST_L2, 5)
     
     # setup active contour algorithm
     ac = ActiveContour(blur_radius=self.centerline_blur_radius,
                        closed_loop=False,
                        alpha=0, #< line length is constraint by beta
                        beta=self.centerline_bending_stiffness,
                        gamma=self.centerline_adaptation_rate)
     ac.max_iterations = self.centerline_max_iterations
     ac.set_potential(dist_map)
     
     # find centerline starting from the ventral_side
     points = curves.translate_points(self.ventral_side,
                                      -offset[0], -offset[1])
     spacing = self.centerline_spacing
     points = curves.make_curve_equidistant(points, spacing=spacing)
     # use the active contour algorithm
     points = ac.find_contour(points)
     points = curves.make_curve_equidistant(points, spacing=spacing)
     # translate points back into global coordinate system
     points = curves.translate_points(points, offset[0], offset[1])
     
     # orient the centerline such that it starts at the posterior end
     dist1 = curves.point_distance(points[0], self.endpoints[0])
     dist2 = curves.point_distance(points[-1], self.endpoints[0])
     if dist1 > dist2:
         points = points[::-1]
     
     return points
    def refine_ground(self, frame):
        """ adapts a points profile given as points to a given frame.
        Here, we fit a ridge profile in the vicinity of every point of the curve.
        The only fitting parameter is the distance by which a single points moves
        in the direction perpendicular to the curve. """
        
        # prepare image
        potential = self.get_gradient_strenght(frame)
        
        # make sure the curve has equidistant points
        spacing = int(self.params['ground/point_spacing'])
        self.ground.make_equidistant(spacing=spacing)

        frame_margin = int(self.params['ground/frame_margin'])
        x_max = frame.shape[1] - frame_margin
        points = [point for point in self.ground.points
                  if frame_margin < point[0] < x_max]
        
        if self.contour_finder is None:
            # first contour fitting
            while self.blur_radius > 0:
                self.contour_finder = \
                    ActiveContour(blur_radius=self.blur_radius,
                                  closed_loop=False,
                                  alpha=0, #< line length is constraint by beta
                                  beta=self.params['ground/active_snake_beta'],
                                  gamma=self.params['ground/active_snake_gamma'])
                self.contour_finder.set_potential(potential)
                points = self.contour_finder.find_contour(points, anchor_x=(0, -1))
                if self.blur_radius < 2:
                    self.blur_radius = 0
                else:
                    self.blur_radius /= 2
        else:
            # reuse the previous contour finder
            self.contour_finder.set_potential(potential)
            points = self.contour_finder.find_contour(points, anchor_x=(0, -1))

        points = points.tolist()

#         from shapely import geometry
#         debug.show_shape(geometry.LineString(points),
#                    background=potential, mark_points=True)
                
        # extend the ground line toward the left edge of the cage
        edge_point = self._get_cage_boundary(points[0], frame, 'left')
        if edge_point is not None:
            points.insert(0, edge_point)
             
        # extend the ground line toward the right edge of the cage
        edge_point = self._get_cage_boundary(points[-1], frame, 'right')
        if edge_point is not None:
            points.append(edge_point)

        self.ground = GroundProfile(points)
        return self.ground
Beispiel #3
0
    def centerline(self):
        """ determine the center line of the tail """
        mask, offset = self.mask
        dist_map = cv2.distanceTransform(mask, cv2.DIST_L2, 5)

        # setup active contour algorithm
        ac = ActiveContour(
            blur_radius=self.centerline_blur_radius,
            closed_loop=False,
            alpha=0,  #< line length is constraint by beta
            beta=self.centerline_bending_stiffness,
            gamma=self.centerline_adaptation_rate)
        ac.max_iterations = self.centerline_max_iterations
        ac.set_potential(dist_map)

        # find centerline starting from the ventral_side
        points = curves.translate_points(self.ventral_side, -offset[0],
                                         -offset[1])
        spacing = self.centerline_spacing
        points = curves.make_curve_equidistant(points, spacing=spacing)
        # use the active contour algorithm
        points = ac.find_contour(points)
        points = curves.make_curve_equidistant(points, spacing=spacing)
        # translate points back into global coordinate system
        points = curves.translate_points(points, offset[0], offset[1])

        # orient the centerline such that it starts at the posterior end
        dist1 = curves.point_distance(points[0], self.endpoints[0])
        dist2 = curves.point_distance(points[-1], self.endpoints[0])
        if dist1 > dist2:
            points = points[::-1]

        return points
Beispiel #4
0
    def adapt_tail_contours_initially(self, tails):
        """ adapt tail contour to _frame, assuming that they could be quite far
        away """
        
        # setup active contour algorithm
        ac = ActiveContour(blur_radius=self.params['contour/blur_radius_initial'],
                           closed_loop=True,
                           alpha=self.params['contour/line_tension'], 
                           beta=self.params['contour/bending_stiffness'],
                           gamma=self.params['contour/adaptation_rate'])
        ac.max_iterations = self.params['contour/max_iterations']
        ac.set_potential(self.contour_potential)
        
        # get rectangle describing the interior of the _frame
        height, width = self._frame.shape
        region_center = shapes.Rectangle(0, 0, width, height)
        region_center.buffer(-self.params['contour/border_anchor_distance'])

        # adapt all the tails
        for tail in tails:
            # find points not at the center
            anchor_idx = ~region_center.points_inside(tail.contour)
            tail.contour = ac.find_contour(tail.contour, anchor_idx, anchor_idx)

            logging.debug('Active contour took %d iterations.',
                          ac.info['iteration_count'])
Beispiel #5
0
    def find_burrows_using_active_contour(self, frame, gradient_strength):
        """ finds burrows using the previous estimate from pass3 and adapting
        it to the image using an active contour model """
        burrow_tracks = self.result['burrows/tracks']
        ground_line = self.ground.linestring
        dist_max = self.params['burrows/ground_point_distance']
        parameters = self.params['burrows/active_contour']
        
        for burrow_track in burrow_tracks:
            # get the burrow at the current time from this track 
            try:
                burrow = burrow_track.get_burrow(self.frame_id)
            except IndexError:
                # this burrow does not exist for this frame
                continue
        
            # setup active contour algorithm
            ac = ActiveContour(blur_radius=parameters['blur_radius'],
                               closed_loop=True,
                               alpha=0, 
                               beta=parameters['stiffness'],
                               gamma=parameters['convergence_rate'])
            ac.max_iterations = parameters['max_iterations']
            ac.set_potential(gradient_strength)

            # find the points close to the ground line, which will be anchored
            ground_line = self.ground.linestring
            anchor_idx = [idx for idx, point in enumerate(burrow.contour)
                          if ground_line.distance(geometry.Point(point)) < dist_max]

            # adapt the contour
            burrow.contour = ac.find_contour(burrow.contour, anchor_idx, anchor_idx)
class GroundDetectorGlobal(GroundDetectorBase):
    """ class that handles the detection and adaptation of the ground line """

    def __init__(self, *args, **kwargs):
        super(GroundDetectorGlobal, self).__init__(*args, **kwargs)
        self.img_shape = None
        self.blur_radius = 20
        self.contour_finder = None

    
    def get_buffers(self, indices, shape):
        # prepare buffers
        if ('img_buffer' not in self._cache or
            len(self._cache['img_buffer']) <= max(indices)):
            self.img_shape = shape
            self._cache['img_buffer'] = [np.zeros(self.img_shape, np.double)
                                         for _ in xrange(max(indices) + 1)]
            
        return [self._cache['img_buffer'][i] for i in indices]

    
    def get_gradient_strenght(self, frame):
        """ calculates the gradient strength of the image in frame
        This function returns its result in buffer1
        """
        # smooth the frame_blurred to be able to find smoothed edges
        blur_radius = self.params['ground/ridge_width']
        frame_blurred = cv2.GaussianBlur(frame, (0, 0), blur_radius)
        
        # scale frame_blurred to [0, 1]
        cv2.divide(frame_blurred, 256, dst=frame_blurred) 
        # do Sobel filtering to find the frame_blurred edges
        grad_x = cv2.Sobel(frame_blurred, cv2.CV_64F, 1, 0, ksize=5)
        grad_y = cv2.Sobel(frame_blurred, cv2.CV_64F, 0, 1, ksize=5)

        # restrict to edges that go from dark to white (from top to bottom)        
        grad_y[grad_y < 0] = 0
        
        # calculate the gradient strength
        gradient_mag = frame_blurred #< reuse memory
        np.hypot(grad_x, grad_y, out=gradient_mag)
        
        return gradient_mag
        
        
    def get_gradient_vector_flow(self, potential):
        """ calculate the gradient vector flow from the given potential.
        This function is not very well tested, yet """
        # use Eq. 12 from Paper `Gradient Vector Flow: A New External Force for Snakes`
        u, v = self.get_buffers([0, 1], potential.shape)
        
        fx = cv2.Sobel(potential, cv2.CV_64F, 1, 0, ksize=5)
        fy = cv2.Sobel(potential, cv2.CV_64F, 0, 1, ksize=5)
        fxy = fx**2 + fy**2
        
#         print fx.max(), fy.max(), fxy.max()
#         debug.show_image(potential, fx, fy, fxy, u, v)
        
        mu = 10
        def dudt(u):
            return mu*cv2.Laplacian(u, cv2.CV_64F) - (u - fx)*fxy
        def dvdt(v):
            return mu*cv2.Laplacian(v, cv2.CV_64F) - (v - fy)*fxy
        
        N = 10000 #< maximum number of steps that the integrator is allowed
        dt = 1e-4 #< time step
        
        for n in xrange(N):
            rhs = dudt(u)
            residual = np.abs(rhs).sum()
            if n % 1000 == 0:
                print n*dt, '%e' % residual 
            u += dt*rhs
        
        for n in xrange(N):
            rhs = dvdt(v)
#             residual = np.abs(rhs).sum()
#             if n % 100 == 0:
#                 print n*dt, '%e' % residual 
            v += dt*rhs
        
        
        debug.show_image(potential, (u, v))
        return (u, v)
    
    
    def refine_ground(self, frame):
        """ adapts a points profile given as points to a given frame.
        Here, we fit a ridge profile in the vicinity of every point of the curve.
        The only fitting parameter is the distance by which a single points moves
        in the direction perpendicular to the curve. """
        
        # prepare image
        potential = self.get_gradient_strenght(frame)
        
        # make sure the curve has equidistant points
        spacing = int(self.params['ground/point_spacing'])
        self.ground.make_equidistant(spacing=spacing)

        frame_margin = int(self.params['ground/frame_margin'])
        x_max = frame.shape[1] - frame_margin
        points = [point for point in self.ground.points
                  if frame_margin < point[0] < x_max]
        
        if self.contour_finder is None:
            # first contour fitting
            while self.blur_radius > 0:
                self.contour_finder = \
                    ActiveContour(blur_radius=self.blur_radius,
                                  closed_loop=False,
                                  alpha=0, #< line length is constraint by beta
                                  beta=self.params['ground/active_snake_beta'],
                                  gamma=self.params['ground/active_snake_gamma'])
                self.contour_finder.set_potential(potential)
                points = self.contour_finder.find_contour(points, anchor_x=(0, -1))
                if self.blur_radius < 2:
                    self.blur_radius = 0
                else:
                    self.blur_radius /= 2
        else:
            # reuse the previous contour finder
            self.contour_finder.set_potential(potential)
            points = self.contour_finder.find_contour(points, anchor_x=(0, -1))

        points = points.tolist()

#         from shapely import geometry
#         debug.show_shape(geometry.LineString(points),
#                    background=potential, mark_points=True)
                
        # extend the ground line toward the left edge of the cage
        edge_point = self._get_cage_boundary(points[0], frame, 'left')
        if edge_point is not None:
            points.insert(0, edge_point)
             
        # extend the ground line toward the right edge of the cage
        edge_point = self._get_cage_boundary(points[-1], frame, 'right')
        if edge_point is not None:
            points.append(edge_point)

        self.ground = GroundProfile(points)
        return self.ground
Beispiel #7
0
    def adapt_tail_contours(self, tails):
        """ adapt tail contour to _frame, assuming that they are already close """
        # get the tails that we want to adapt
        if self.params['detection/every_frame']:
            tails_estimate = self.locate_tails_roughly(tails)
        else:
            tails_estimate = tails[:] #< copy list

        if len(tails_estimate) != len(tails):
            raise RuntimeError('Found %d instead of %d tails in this frame.' % 
                               (len(tails), len(tails_estimate)))
        
        # setup active contour algorithm
        ac = ActiveContour(blur_radius=self.params['contour/blur_radius'],
                           closed_loop=True,
                           alpha=self.params['contour/line_tension'],
                           beta=self.params['contour/bending_stiffness'],
                           gamma=self.params['contour/adaptation_rate'])
        ac.max_iterations = self.params['contour/max_iterations']
        #potential_approx = self.threshold_gradient_strength(gradient_mag)
        ac.set_potential(self.contour_potential)

#         debug.show_shape(*[t.contour for t in tails],
#                          background=self.contour_potential)        
        
        # get rectangle describing the interior of the _frame
        height, width = self._frame.shape
        region_center = shapes.Rectangle(0, 0, width, height)
        region_center.buffer(-self.params['contour/border_anchor_distance'])
        
        # iterate through all previous tails
        for k, tail_prev in enumerate(tails):
            # find the tail that is closest
            center = tail_prev.centroid
            idx = np.argmin([curves.point_distance(center, t.centroid)
                             for t in tails_estimate])
            
            # adapt this contour to the potential
            tail_estimate = tails_estimate.pop(idx)
            
            # determine the points close to the boundary that will be anchored
            # at their position, because there are no features to track at the
            # boundary 
            anchor_idx = ~region_center.points_inside(tail_estimate.contour)
            
            # disable anchoring for points at the posterior end of the tail
            ps = tail_estimate.contour[anchor_idx]
            dists = spatial.distance.cdist(ps, [tail_estimate.endpoints[0]])
            dist_threshold = self.params['contour/typical_width']
            anchor_idx[anchor_idx] = (dists.flat > dist_threshold)
            
            # use an active contour algorithm to adapt the contour points
            contour = ac.find_contour(tail_estimate.contour, anchor_idx,
                                      anchor_idx)
            logging.debug('Active contour took %d iterations.',
                          ac.info['iteration_count'])
            
            # update the old tail to keep the identity of sides
            tails[k] = Tail.create_similar(contour, tail_prev)
            
        return tails