def test_eq_is_equal(empty_bbox: BoundingBox, partial_bbox: BoundingBox, full_bbox: BoundingBox) -> None: for a, b in [ (empty_bbox, BoundingBox(0.0, 0.0, 0.0, 0.0)), (partial_bbox, BoundingBox(25.0, 75.0, 26.0, 70.0)), (full_bbox, BoundingBox(0.0, 100.0, 0.0, 100.0)), ]: assert (a == b)
def test_from_dict_input_format(raw_dict: Dict) -> None: bbox = BoundingBox.from_dict(raw_dict) assert (not bbox.is_empty()) assert (bbox.x_min == 1.0) assert (bbox.x_max == 2.0) assert (bbox.y_min == 3.0) assert (bbox.y_max == 4.0)
def test_from_points_valid_neg(full_bbox_points: List[Point], neg_point: Point, full_bbox: BoundingBox) -> None: points = full_bbox_points + [neg_point] bbox = BoundingBox.from_points(points) expected_bbox = replace(full_bbox, x_min=-10.0, y_min=-10.0) assert (not bbox.is_empty()) assert (bbox == expected_bbox)
def shape_image_to_bbox(image: np.ndarray, bbox: BoundingBox, thumbnail_size: Tuple[float, float]) -> np.ndarray: # TODO: Change thumbnail size to paitypes.geometry.Shape if bbox.is_empty(): return image # TODO: Refactor into `image.resizing` module as a helper method to be # shared across detectors. # TODO: Use paitypes.geometry.Shape for the following logic padded_bbox = pad_to_min_size(bbox, thumbnail_size) padded_bbox = pad_abs_amount(padded_bbox, (20.0, 20.0)) aspect_ratio = (float(thumbnail_size[0]) / float(thumbnail_size[1])) padded_bbox = pad_to_aspect_ratio(padded_bbox, aspect_ratio) # mypy can't figure out that `image.shape[:2]` is limited to two cells shape = (image.shape[0], image.shape[1]) cropped_bbox = snap_to_shape(padded_bbox, shape) # TODO: Type this call properly. cropped_image = crop_image_to_bounding_box(cast(Image, image), cropped_bbox) resized_image = cv2.resize(cropped_image, thumbnail_size, interpolation=cv2.INTER_AREA) return resized_image
def test_partially_contains(bbox1: BoundingBox) -> None: moved_bbox = BoundingBox(bbox1.x_min - bbox1.delta_x / 2.0, bbox1.x_min + bbox1.delta_x / 2.0, bbox1.y_min - bbox1.delta_y / 2.0, bbox1.y_min + bbox1.delta_y / 2.0) assume(moved_bbox.area != 0.0) assert isclose(contains_ratio(bbox1, moved_bbox), 0.25, abs_tol=1e-5)
def test_pad_out_of_bounds_neg(self) -> None: bbox = BoundingBox(0.0, 10.0, 2.0, 12.0) padded_bbox = pad_to_min_size(bbox, (20.0, 20.0)) assert (padded_bbox.y_min == -3.0) assert (padded_bbox.y_max == 17.0) assert (padded_bbox.x_min == -5.0) assert (padded_bbox.x_max == 15.0)
def test_pad_out_of_bounds_pos(self) -> None: bbox = BoundingBox(90.0, 100.0, 92.0, 102.0) padded_bbox = pad_to_min_size(bbox, (20.0, 20.0)) assert (padded_bbox.y_min == 87.0) assert (padded_bbox.y_max == 107.0) assert (padded_bbox.x_min == 85.0) assert (padded_bbox.x_max == 105.0)
class TestResizeCropImageToBoundingBox(): def test_crop_to_partial_bbox(self, all_valid_images: List[Image], partial_bbox: BoundingBox) -> None: for image in all_valid_images: cropped_image = crop_image_to_bounding_box(image, partial_bbox) assert (np.array_equal(cropped_image, image[26:70, 25:75])) def test_crop_to_full_bbox(self, all_valid_images: List[Image], full_bbox: BoundingBox) -> None: for image in all_valid_images: cropped_image = crop_image_to_bounding_box(image, full_bbox) assert (np.array_equal(cropped_image, image)) def test_crop_to_partial_float_bbox( self, all_valid_images: List[Image], partial_float_bbox: BoundingBox) -> None: for image in all_valid_images: cropped_image = crop_image_to_bounding_box(image, partial_float_bbox) assert (np.array_equal(cropped_image, image[27:74, 33:68])) def test_crop_to_empty_bbox_raises(self, all_valid_images: List[Image], empty_bbox: BoundingBox) -> None: for image in all_valid_images: with pytest.raises(ImageResizingException): crop_image_to_bounding_box(image, empty_bbox) def test_crop_to_empty_image_raises(self, empty_images: List[Image], full_bbox: BoundingBox) -> None: for empty_image in empty_images: with pytest.raises(ImageResizingException): crop_image_to_bounding_box(empty_image, full_bbox) @pytest.mark.parametrize('bbox', [ BoundingBox(-1.0, 0.0, 0.0, 100.0), BoundingBox(0.0, 0.0, -1.0, 100.0), BoundingBox(0.0, 101.0, 0.0, 100.0), BoundingBox(0.0, 100.0, 0.0, 101.0) ]) def test_crop_to_out_of_bounds_bbox_raises(self, all_valid_images: Image, bbox: BoundingBox) -> None: for image in all_valid_images: with pytest.raises(ImageResizingException): crop_image_to_bounding_box(image, bbox)
def test_scale_around_corners_maintains_corners(bbox: BoundingBox, sx: float, sy: float) -> None: scaled_bbox = bbox.scale(Shape(sx, sy), center=Point(bbox.x_min, bbox.y_min)) assert scaled_bbox.x_min == approx(bbox.x_min, abs=1e-6) assert scaled_bbox.y_min == approx(bbox.y_min, abs=1e-6) scaled_bbox = bbox.scale(Shape(sx, sy), center=Point(bbox.x_min, bbox.y_max)) assert scaled_bbox.x_min == approx(bbox.x_min, abs=1e-6) assert scaled_bbox.y_max == approx(bbox.y_max, abs=1e-6) scaled_bbox = bbox.scale(Shape(sx, sy), center=Point(bbox.x_max, bbox.y_min)) assert scaled_bbox.x_max == approx(bbox.x_max, abs=1e-6) assert scaled_bbox.y_min == approx(bbox.y_min, abs=1e-6) scaled_bbox = bbox.scale(Shape(sx, sy), center=Point(bbox.x_max, bbox.y_max)) assert scaled_bbox.x_max == approx(bbox.x_max, abs=1e-6) assert scaled_bbox.y_max == approx(bbox.y_max, abs=1e-6)
def bounding_boxes(draw: Any, container: BoundingBox = None) -> BoundingBox: x_min = -10000 y_min = -10000 w_max = 10000 h_max = 10000 if container: x_min = container.x_min y_min = container.y_min w_max = container.delta_x h_max = container.delta_y x = draw(floats(min_value=x_min, max_value=10000.0)) y = draw(floats(min_value=y_min, max_value=10000.0)) w = draw(floats(min_value=0.0, max_value=w_max)) h = draw(floats(min_value=0.0, max_value=h_max)) return BoundingBox(x, x + w, y, y + h)
def crop_image_to_bounding_box(image: ImageT, bbox: BoundingBox) -> ImageT: """ Crops `image` to the dimensions of `bbox`. """ if image.shape[0] == 0 or image.shape[1] == 0: raise ImageResizingException('image shape is invalid') if bbox.is_empty(): raise ImageResizingException('bbox is empty') y_min, y_max, x_min, x_max = (int(bbox.y_min), int(bbox.y_max), int(bbox.x_min), int(bbox.x_max)) if (y_min < 0 or x_min < 0 or y_max > image.shape[0] or x_max > image.shape[1]): raise ImageResizingException('bbox outside image bounds') return image[y_min:y_max, x_min:x_max]
def test_no_move(bbox: BoundingBox) -> None: assert bbox.move(0, 0) == bbox
def test_from_dict_invalid_input(raw_dict: Dict) -> None: with pytest.raises(BoundingBoxError): bbox = BoundingBox.from_dict(raw_dict)
def test_from_points_valid(full_bbox_points: List[Point], full_bbox: BoundingBox) -> None: bbox = BoundingBox.from_points(full_bbox_points) assert (not bbox.is_empty()) assert (bbox == full_bbox)
def test_from_single_point_empty(full_bbox_points: List[Point]) -> None: for point in full_bbox_points: bbox = BoundingBox.from_points([point]) assert (bbox.is_empty())
def test_offset_bboxes_returns_union(full_bbox: BoundingBox) -> None: other = BoundingBox(-100.0, 0.0, -100.0, 0.0) expected = BoundingBox(-100.0, 100.0, -100.0, 100.0) assert (full_bbox + other == expected)
def test_inverted_bbox_is_empty() -> None: bbox = BoundingBox(10.0, 10.0, 0.0, 0.0) assert (bbox.is_empty())
def test_fully_contains(bbox1: BoundingBox) -> None: contained = BoundingBox(bbox1.x_min, bbox1.x_min + bbox1.delta_x / 2.0, bbox1.y_min, bbox1.y_min + bbox1.delta_y / 2.0) assume(contained.area != 0.0) assert contains_ratio(bbox1, contained) == 1.0
def test_scale_around_center_maintains_center(bbox: BoundingBox, sx: float, sy: float) -> None: scaled_bbox = bbox.scale(Shape(sx, sy), center=bbox.center) assert scaled_bbox.center.x == approx(bbox.center.x, abs=1e-6) assert scaled_bbox.center.y == approx(bbox.center.y, abs=1e-6)
def test_scale_around_center_changes_size(bbox: BoundingBox, sx: float, sy: float) -> None: scaled_bbox = bbox.scale(Shape(sx, sy), center=bbox.center) assert scaled_bbox.delta_x == approx(bbox.delta_x * sx, abs=1e-6) assert scaled_bbox.delta_y == approx(bbox.delta_y * sy, abs=1e-6)
def test_to_dict_from_dict(empty_bbox: BoundingBox, partial_bbox: BoundingBox, full_bbox: BoundingBox) -> None: for bbox in [empty_bbox, partial_bbox, full_bbox]: assert BoundingBox.from_dict(bbox.to_dict()) == bbox
def test_scale_around_origin_moves_center(bbox: BoundingBox, sx: float, sy: float) -> None: scaled_bbox = bbox.scale(Shape(sx, sy)) assert scaled_bbox.center.x == approx(bbox.center.x * sx, abs=1e-6) assert scaled_bbox.center.y == approx(bbox.center.y * sy, abs=1e-6)
def test_from_dict_to_dict(raw_dict: Dict) -> None: bbox1 = BoundingBox.from_dict(raw_dict) bbox2 = BoundingBox.from_dict(BoundingBox.from_dict(raw_dict).to_dict()) assert (bbox1 == bbox2)