def crop_with_padding(img: np.ndarray, rect: Rectangle) -> np.ndarray: ''' The function crop cut out part of the image with rectangle size. If rectangle for crop is out of image area it generates additional padding. :param img: image(numpy matrix) to be cropped :param rect: class object Rectangle of a certain size :return: cropped image ''' img_rect = Rectangle.from_array(img) if not img_rect.contains(rect): row, col = img.shape[:2] new_img = cv2.copyMakeBorder(img, top=rect.height, bottom=rect.height, left=rect.width, right=rect.width, borderType=cv2.BORDER_CONSTANT) new_rect = rect.translate(drow=rect.height, dcol=rect.width) return new_rect.get_cropped_numpy_slice(new_img) else: return rect.get_cropped_numpy_slice(img)
class RectangleTest(unittest.TestCase): def setUp(self): self.rect = Rectangle(top=5, left=10, bottom=30, right=30) def assertRectEquals(self, rect, top, left, bottom, right): self.assertIsInstance(rect, Rectangle) self.assertEqual(rect.top, top) self.assertEqual(rect.left, left) self.assertEqual(rect.bottom, bottom) self.assertEqual(rect.right, right) def test_empty_crop(self): crop_rect = Rectangle(100, 100, 200, 200) res_geoms = self.rect.crop(crop_rect) self.assertEqual(len(res_geoms), 0) def test_crop(self): crop_rect = Rectangle(0, 0, 100, 100) res_geoms = self.rect.crop(crop_rect) self.assertEqual(len(res_geoms), 1) res_rect = res_geoms[0] self.assertRectEquals(res_rect, self.rect.top, self.rect.left, self.rect.bottom, self.rect.right) def test_relative_crop(self): crop_rect = Rectangle(3, 4, 100, 100) res_geoms = self.rect.relative_crop(crop_rect) self.assertEqual(len(res_geoms), 1) res_rect = res_geoms[0] self.assertRectEquals(res_rect, 2, 6, 27, 26) def test_rotate(self): imsize = (101, 101) rotator = ImageRotator(imsize=imsize, angle_degrees_ccw=90) res_rect = self.rect.rotate(rotator) self.assertRectEquals(res_rect, 70, 5, 90, 30) def test_resize(self): in_size = (100, 100) out_size = (200, 150) res_rect = self.rect.resize(in_size, out_size) self.assertRectEquals(res_rect, 10, 15, 60, 45) def test_scale(self): factor = 1.3 res_rect = self.rect.scale(factor) self.assertRectEquals(res_rect, 6, 13, 39, 39) def test_translate(self): drows = 8 dcols = 259 res_rect = self.rect.translate(drows, dcols) self.assertRectEquals(res_rect, 13, 269, 38, 289) def test_fliplr(self): im_size = (100, 200) res_rect = self.rect.fliplr(im_size) self.assertRectEquals(res_rect, 5, 170, 30, 190) def test_flipud(self): im_size = (100, 200) res_rect = self.rect.flipud(im_size) self.assertRectEquals(res_rect, 70, 10, 95, 30) def test_area(self): area = self.rect.area self.assertIsInstance(area, float) self.assertEqual(area, 546.0) def test_to_bbox(self): res_rect = self.rect.to_bbox() self.assertRectEquals(res_rect, self.rect.top, self.rect.left, self.rect.bottom, self.rect.right) def test_clone(self): res_rect = self.rect.clone() self.assertRectEquals(res_rect, self.rect.top, self.rect.left, self.rect.bottom, self.rect.right) self.assertIsNot(res_rect, self.rect) def test_from_json(self): packed_obj = { 'some_stuff': 'aaa', POINTS: { EXTERIOR: [[17, 3], [34, 45]], INTERIOR: [] } } res_rect = Rectangle.from_json(packed_obj) self.assertRectEquals(res_rect, 3, 17, 45, 34) def test_to_json(self): res_obj = self.rect.to_json() expected_dict = { POINTS: { EXTERIOR: [[10, 5], [30, 30]], INTERIOR: [] } } self.assertDictEqual(res_obj, expected_dict) def test_draw(self): rect = Rectangle(1, 1, 3, 3) bitmap_1 = np.zeros((5, 5), dtype=np.uint8) rect.draw_contour(bitmap_1, 1) expected_mask_1 = np.array([[0, 0, 0, 0, 0], [0, 1, 1, 1, 0], [0, 1, 0, 1, 0], [0, 1, 1, 1, 0], [0, 0, 0, 0, 0]], dtype=np.uint8) self.assertTrue(np.array_equal(bitmap_1, expected_mask_1)) bitmap_2 = np.zeros((5, 5), dtype=np.uint8) rect.draw(bitmap_2, 1) expected_mask_2 = np.array([[0, 0, 0, 0, 0], [0, 1, 1, 1, 0], [0, 1, 1, 1, 0], [0, 1, 1, 1, 0], [0, 0, 0, 0, 0]], dtype=np.uint8) self.assertTrue(np.array_equal(bitmap_2, expected_mask_2))