def test_crop_by_border(self): exterior = [[10, 10], [40, 10], [40, 40], [10, 40]] interiors = [[[11, 11], [11, 20], [20, 11]], [[20, 20], [21, 20], [20, 21]]] poly = Polygon(exterior=row_col_list_to_points(exterior, flip_row_col_order=True), interior=[row_col_list_to_points(interior, flip_row_col_order=True) for interior in interiors]) crop_rect = Rectangle(0, 0, 100, 10) res_geoms = poly.crop(crop_rect) self.assertEqual(len(res_geoms), 0)
def test_complex_crop(self): # Crop generate GeometryCollection here exterior = [[0, 0], [0, 3], [5, 8], [5, 9], [5, 10], [0, 15], [10, 20], [0, 25], [20, 25], [20, 0]] interiors = [[[2, 2], [4, 4], [4, 2]]] poly = Polygon(exterior=row_col_list_to_points(exterior, flip_row_col_order=True), interior=[row_col_list_to_points(interior, flip_row_col_order=True) for interior in interiors]) crop_rect = Rectangle(0, 0, 30, 5) res_geoms = poly.crop(crop_rect) self.assertEqual(len(res_geoms), 3) self.assertPolyEquals(res_geoms[0], [[0, 0], [5, 0], [5, 8], [0, 3]], interiors)
def to_contours(self): origin, mask = self.origin, self.data _, contours, hier = cv2.findContours( mask.astype(np.uint8), mode=cv2.RETR_CCOMP, # two-level hierarchy, to get polygons with holes method=cv2.CHAIN_APPROX_SIMPLE ) if (hier is None) or (contours is None): return [] res = [] for idx, hier_pos in enumerate(hier[0]): next_idx, prev_idx, child_idx, parent_idx = hier_pos if parent_idx < 0: external = contours[idx][:, 0].tolist() internals = [] while child_idx >= 0: internals.append(contours[child_idx][:, 0]) child_idx = hier[0][child_idx][0] if len(external) > 2: new_poly = Polygon(exterior=row_col_list_to_points(external, flip_row_col_order=True), interior=[row_col_list_to_points(x, flip_row_col_order=True) for x in internals]) res.append(new_poly) offset_row, offset_col = origin.row, origin.col res = [x.translate(offset_row, offset_col) for x in res] return res
def test_from_json(self): packed_obj = { 'some_stuff': 'aaa', POINTS: { EXTERIOR: self.exterior, INTERIOR: self.interiors } } res_poly = Polygon.from_json(packed_obj) self.assertPolyEquals(res_poly, self.exterior, self.interiors)
def geometry_to_polygon(geometry, approx_epsilon=None): if type(geometry) not in (Rectangle, Polyline, Polygon, Bitmap): raise KeyError('Can not convert {} to {}'.format( geometry.geometry_name(), Polygon.__name__)) if type(geometry) == Rectangle: return [Polygon(geometry.corners, [])] if type(geometry) == Polyline: return [Polygon(geometry.exterior, [])] if type(geometry) == Polygon: return [geometry] if type(geometry) == Bitmap: new_geometries = geometry.to_contours() if approx_epsilon is None: approx_epsilon = 1 new_geometries = [g.approx_dp(approx_epsilon) for g in new_geometries] return new_geometries
def test_draw_contour(self): exterior = [[0, 0], [0, 6], [6, 6], [6, 0]] interiors = [[[2, 2], [2, 4], [4, 4], [4, 2]]] poly = Polygon(exterior=row_col_list_to_points(exterior), interior=[row_col_list_to_points(interior) for interior in interiors]) bitmap_1 = np.zeros((7, 7), dtype=np.uint8) poly.draw_contour(bitmap_1, color=1) expected_mask = np.array([[1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 1], [1, 0, 1, 1, 1, 0, 1], [1, 0, 1, 0, 1, 0, 1], [1, 0, 1, 1, 1, 0, 1], [1, 0, 0, 0, 0, 0, 1], [1, 1, 1, 1, 1, 1, 1]], dtype=np.uint8) self.assertTrue(np.array_equal(bitmap_1, expected_mask)) # Extended test exterior = [[0, 0], [0, 6], [10, 6], [10, 0]] interiors = [[[1, 1], [1, 2], [2, 1]], [[2, 4], [3, 5], [4, 4], [3, 3]], [[6, 2], [8, 2], [8, 4], [6, 4]]] poly = Polygon(exterior=row_col_list_to_points(exterior), interior=[row_col_list_to_points(interior) for interior in interiors]) bitmap_2 = np.zeros((11, 7), dtype=np.uint8) poly.draw_contour(bitmap_2, color=1) expected_mask = np.array([[1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 0, 0, 0, 1], [1, 1, 0, 0, 1, 0, 1], [1, 0, 0, 1, 0, 1, 1], [1, 0, 0, 0, 1, 0, 1], [1, 0, 0, 0, 0, 0, 1], [1, 0, 1, 1, 1, 0, 1], [1, 0, 1, 0, 1, 0, 1], [1, 0, 1, 1, 1, 0, 1], [1, 0, 0, 0, 0, 0, 1], [1, 1, 1, 1, 1, 1, 1]], dtype=np.uint8) self.assertTrue(np.array_equal(bitmap_2, expected_mask))
def draw_mask(exterior_p, interior_p, h, w): mask = np.zeros((h, w), dtype=np.uint8) poly = Polygon(exterior=row_col_list_to_points(exterior_p), interior=[row_col_list_to_points(interior) for interior in interior_p]) poly.draw(mask, color=1) return mask
def setUp(self): self.exterior = [[10, 10], [40, 10], [30, 40], [10, 30]] self.interiors = [[[20, 20], [30, 20], [30, 30], [20, 30]]] self.poly = Polygon(exterior=row_col_list_to_points(self.exterior, flip_row_col_order=True), interior=[row_col_list_to_points(self.interiors[0], flip_row_col_order=True)])
class PolygonTest(unittest.TestCase): def setUp(self): self.exterior = [[10, 10], [40, 10], [30, 40], [10, 30]] self.interiors = [[[20, 20], [30, 20], [30, 30], [20, 30]]] self.poly = Polygon(exterior=row_col_list_to_points(self.exterior, flip_row_col_order=True), interior=[row_col_list_to_points(self.interiors[0], flip_row_col_order=True)]) def assertPolyEquals(self, poly, exterior, interiors): self.assertIsInstance(poly, Polygon) self.assertCountEqual(points_to_row_col_list(poly.exterior, flip_row_col_order=True), exterior) self.assertEqual(len(poly.interior), len(interiors)) for p_interior, interior in zip(poly.interior, interiors): self.assertCountEqual(points_to_row_col_list(p_interior, flip_row_col_order=True), interior) def test_empty_crop(self): crop_rect = Rectangle(100, 100, 200, 200) res_geoms = self.poly.crop(crop_rect) self.assertEqual(len(res_geoms), 0) def test_crop(self): crop_rect = Rectangle(25, 0, 200, 200) res_geoms = self.poly.crop(crop_rect) self.assertEqual(len(res_geoms), 1) crop = res_geoms[0] self.assertPolyEquals(crop, [[10, 25], [20, 25], [20, 30], [30, 30], [30, 25], [35, 25], [30, 40], [10, 30]], []) def test_complex_crop(self): # Crop generate GeometryCollection here exterior = [[0, 0], [0, 3], [5, 8], [5, 9], [5, 10], [0, 15], [10, 20], [0, 25], [20, 25], [20, 0]] interiors = [[[2, 2], [4, 4], [4, 2]]] poly = Polygon(exterior=row_col_list_to_points(exterior, flip_row_col_order=True), interior=[row_col_list_to_points(interior, flip_row_col_order=True) for interior in interiors]) crop_rect = Rectangle(0, 0, 30, 5) res_geoms = poly.crop(crop_rect) self.assertEqual(len(res_geoms), 3) self.assertPolyEquals(res_geoms[0], [[0, 0], [5, 0], [5, 8], [0, 3]], interiors) def test_crop_by_border(self): exterior = [[10, 10], [40, 10], [40, 40], [10, 40]] interiors = [[[11, 11], [11, 20], [20, 11]], [[20, 20], [21, 20], [20, 21]]] poly = Polygon(exterior=row_col_list_to_points(exterior, flip_row_col_order=True), interior=[row_col_list_to_points(interior, flip_row_col_order=True) for interior in interiors]) crop_rect = Rectangle(0, 0, 100, 10) res_geoms = poly.crop(crop_rect) self.assertEqual(len(res_geoms), 0) def test_relative_crop(self): crop_rect = Rectangle(25, 0, 200, 200) res_geoms = self.poly.relative_crop(crop_rect) self.assertEqual(len(res_geoms), 1) crop = res_geoms[0] self.assertPolyEquals(crop, [[10, 0], [20, 0], [20, 5], [30, 5], [30, 0], [35, 0], [30, 15], [10, 5]], []) def test_rotate(self): imsize = (101, 101) rotator = ImageRotator(imsize=imsize, angle_degrees_ccw=-90) res_poly = self.poly.rotate(rotator) self.assertPolyEquals(res_poly, [[90, 10], [90, 40], [60, 30], [70, 10]], [[[80, 20], [80, 30], [70, 30], [70, 20]]]) def test_resize(self): in_size = (100, 100) out_size = (200, 150) res_poly = self.poly.resize(in_size, out_size) self.assertPolyEquals(res_poly, [[15, 20], [60, 20], [45, 80], [15, 60]], [[[30, 40], [45, 40], [45, 60], [30, 60]]]) def test_translate(self): drow = 5 dcol = 10 res_poly = self.poly.translate(drow, dcol) self.assertPolyEquals(res_poly, [[20, 15], [50, 15], [40, 45], [20, 35]], [[[30, 25], [40, 25], [40, 35], [30, 35]]]) def test_fliplr(self): imsize = (100, 200) res_poly = self.poly.fliplr(imsize) self.assertPolyEquals(res_poly, [[190, 10], [160, 10], [170, 40], [190, 30]], [[[180, 20], [170, 20], [170, 30], [180, 30]]]) def test_area(self): # @TODO: only exterior area area = self.poly.area self.assertIsInstance(area, float) self.assertEqual(area, 650.0) def test_to_bbox(self): res_rect = self.poly.to_bbox() self.assertIsInstance(res_rect, Rectangle) self.assertEqual(res_rect.top, 10) self.assertEqual(res_rect.left, 10) self.assertEqual(res_rect.right, 40) self.assertEqual(res_rect.bottom, 40) def test_from_json(self): packed_obj = { 'some_stuff': 'aaa', POINTS: { EXTERIOR: self.exterior, INTERIOR: self.interiors } } res_poly = Polygon.from_json(packed_obj) self.assertPolyEquals(res_poly, self.exterior, self.interiors) def test_to_json(self): res_obj = self.poly.to_json() expected_dict = { POINTS: { EXTERIOR: self.exterior, INTERIOR: self.interiors } } self.assertDictEqual(res_obj, expected_dict) def test_clone(self): res_poly = self.poly.clone() self.assertIsInstance(res_poly, Polygon) self.assertTrue(np.array_equal(res_poly.exterior_np, self.poly.exterior_np)) self.assertTrue(np.array_equal(res_poly.interior_np, self.poly.interior_np)) self.assertIsNot(res_poly, self.poly) def test_draw(self): def draw_mask(exterior_p, interior_p, h, w): mask = np.zeros((h, w), dtype=np.uint8) poly = Polygon(exterior=row_col_list_to_points(exterior_p), interior=[row_col_list_to_points(interior) for interior in interior_p]) poly.draw(mask, color=1) return mask # Test 1 - draw interior triangles exterior = [[0, 0], [0, 6], [4, 6], [4, 0]] interiors = [[[1, 1], [1, 3], [3, 1]], # clockwise [[1, 5], [3, 3], [3, 5]]] # counterclockwise mask_1 = draw_mask(exterior, interiors, h=5, w=7) expected_mask = np.array([[1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 1, 0, 1], [1, 0, 0, 1, 0, 0, 1], [1, 0, 1, 0, 0, 0, 1], [1, 1, 1, 1, 1, 1, 1]], dtype=np.uint8) self.assertTrue(np.array_equal(mask_1, expected_mask)) # Test 1 - draw interior sandglass (bad poly case) exterior = [[0, 0], [0, 7], [7, 7], [7, 0]] interiors = [[[1, 1], [5, 5], [1, 5], [5, 1]]] # sandglass mask_2 = draw_mask(exterior, interiors, h=7, w=7) expected_mask = np.array([[1, 1, 1, 1, 1, 1, 1], [1, 0, 1, 1, 1, 0, 1], [1, 0, 0, 1, 0, 0, 1], [1, 0, 0, 0, 0, 0, 1], [1, 0, 0, 1, 0, 0, 1], [1, 0, 1, 1, 1, 0, 1], [1, 1, 1, 1, 1, 1, 1]], dtype=np.uint8) self.assertTrue(np.array_equal(mask_2, expected_mask)) def test_draw_contour(self): exterior = [[0, 0], [0, 6], [6, 6], [6, 0]] interiors = [[[2, 2], [2, 4], [4, 4], [4, 2]]] poly = Polygon(exterior=row_col_list_to_points(exterior), interior=[row_col_list_to_points(interior) for interior in interiors]) bitmap_1 = np.zeros((7, 7), dtype=np.uint8) poly.draw_contour(bitmap_1, color=1) expected_mask = np.array([[1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 1], [1, 0, 1, 1, 1, 0, 1], [1, 0, 1, 0, 1, 0, 1], [1, 0, 1, 1, 1, 0, 1], [1, 0, 0, 0, 0, 0, 1], [1, 1, 1, 1, 1, 1, 1]], dtype=np.uint8) self.assertTrue(np.array_equal(bitmap_1, expected_mask)) # Extended test exterior = [[0, 0], [0, 6], [10, 6], [10, 0]] interiors = [[[1, 1], [1, 2], [2, 1]], [[2, 4], [3, 5], [4, 4], [3, 3]], [[6, 2], [8, 2], [8, 4], [6, 4]]] poly = Polygon(exterior=row_col_list_to_points(exterior), interior=[row_col_list_to_points(interior) for interior in interiors]) bitmap_2 = np.zeros((11, 7), dtype=np.uint8) poly.draw_contour(bitmap_2, color=1) expected_mask = np.array([[1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 0, 0, 0, 1], [1, 1, 0, 0, 1, 0, 1], [1, 0, 0, 1, 0, 1, 1], [1, 0, 0, 0, 1, 0, 1], [1, 0, 0, 0, 0, 0, 1], [1, 0, 1, 1, 1, 0, 1], [1, 0, 1, 0, 1, 0, 1], [1, 0, 1, 1, 1, 0, 1], [1, 0, 0, 0, 0, 0, 1], [1, 1, 1, 1, 1, 1, 1]], dtype=np.uint8) self.assertTrue(np.array_equal(bitmap_2, expected_mask))