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 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_inverted_bbox_is_empty() -> None: bbox = BoundingBox(10.0, 10.0, 0.0, 0.0) assert (bbox.is_empty())