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 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'])
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 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
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