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