def _by_points(self, case_name, n, *, angle_min=0, angle_max=359, noise_error=0.0): w, h = 300, 150 ellipse = shapes.Ellipse(w // 2, h // 2, w // 3, h // 3, 20) img_contour = np.zeros((h, w), np.uint8) ellipse.draw(img_contour, 255) self.dump_debug_img(Path(case_name) / '0_ellipse.png', img_contour) np.random.seed(239) angles = np.random.randint(angle_min, angle_max, (n, )) xys = np.float32(list(map(ellipse.calculate_point, angles))) xys += np.random.random_sample((len(xys), 2)) * noise_error img_points = img_contour // 3 draw_pixels(img_points, xys, 255) self.dump_debug_img(Path(case_name) / '1_points.png', img_points) estimation = fit_ellipse(xys) estimation.draw(img_points, 255) self.dump_debug_img(Path(case_name) / '2_estimation.png', img_points) self.assertTrue( np.all(np.abs(np.array(ellipse) - np.array(estimation)) < 3.0))
def _by_points(self, case_name, n, *, angle_min=0, angle_max=359, noise_error=0.0): w, h = 300, 150 ellipse = shapes.Ellipse(w // 2, h // 2, w // 3, h // 3, 20) img_contour = np.zeros((h, w), np.uint8) ellipse.draw(img_contour, 255) self.dump_debug_img(Path(case_name) / '0_ellipse.png', img_contour) np.random.seed(239) angles = np.random.randint(angle_min, angle_max, (n,)) xys = np.float32(list(map(ellipse.calculate_point, angles))) xys += np.random.random_sample((len(xys), 2)) * noise_error img_points = img_contour // 3 draw_pixels(img_points, xys, 255) self.dump_debug_img(Path(case_name) / '1_points.png', img_points) estimation = fit_ellipse(xys) estimation.draw(img_points, 255) self.dump_debug_img(Path(case_name) / '2_estimation.png', img_points) self.assertTrue(np.all(np.abs(np.array(ellipse) - np.array(estimation)) < 3.0))
def process_contour(self, contour): points = contour.reshape(-1, 2) found_ellipses = [] n = len(points) mask = np.ones(n, np.bool) if n < self._min_pixels: return found_ellipses for ellipse in self.ellipses: err = self._distance_to_ellipse_as_circle(points, ellipse) mask[err < self._err_threshold] = False for from_i in range(0, len(points), self._min_pixels // 4): number_to_sample = 3 * len(points) // 5 sub_points = points[from_i:][:number_to_sample] sub_points = sub_points[mask[from_i:][:number_to_sample]] if len(sub_points) < self._min_pixels // 2: continue max_inliers = 0 best_ellipse = None for i in range(20): indices = np.arange(len(sub_points)) np.random.shuffle(indices) sample_points = sub_points[indices[:self._min_pixels // 2]] assert len(sample_points) >= 5 ellipse = fit_ellipse(sample_points) if not self._is_ok(ellipse): continue err = self._distance_to_ellipse_as_circle(points[mask], ellipse) inliers_mask = err < self._err_threshold inliers = inliers_mask.sum() if inliers < self._min_pixels: continue if inliers > max_inliers: max_inliers = inliers best_ellipse = ellipse if best_ellipse is not None: err = self._distance_to_ellipse_as_circle(points[mask], best_ellipse) inliers_mask = err < self._err_threshold for i in range(3): if inliers_mask.sum() < 5: break ellipse = fit_ellipse(points[mask][inliers_mask]) if not self._is_ok(ellipse): break err = self._distance_to_ellipse_as_circle(points[mask], ellipse) inliers_mask = err < self._err_threshold if inliers_mask.sum() < 5 or not self._is_ok(ellipse): break sectors_number = self._min_pixels // 2 covered_sectors_number = len(self._calculate_sectors(points[mask][inliers_mask], ellipse, sectors_number)) if covered_sectors_number / sectors_number < self._min_sectors_fraction: continue err = self._distance_to_ellipse_as_circle(points, ellipse) mask[err < self._err_threshold] = False self.ellipses.append(ellipse) found_ellipses.append(ellipse) return found_ellipses