def rotate(img: np.ndarray, ann: Annotation, degrees: float, mode: str=RotationModes.KEEP) ->\ (np.ndarray, Annotation): # @TODO: add "preserve_size" mode """ Rotates the image by random angle. Args: img: Input image array. ann: Input annotation. degrees: Rotation angle, counter-clockwise. mode: parameter: "keep" - keep original image data, then new regions will be filled with black color; "crop" - crop rotated result to exclude black regions; Returns: A tuple containing rotated image array and annotation. """ _validate_image_annotation_shape(img, ann) rotator = ImageRotator(img.shape[:2], degrees) if mode == RotationModes.KEEP: rect_to_crop = None elif mode == RotationModes.CROP: rect_to_crop = rotator.inner_crop else: raise NotImplementedError('Wrong black_regions mode.') res_img = rotator.rotate_img(img, use_inter_nearest=False) res_ann = ann.rotate(rotator) if rect_to_crop is not None: res_img = sly_image.crop(res_img, rect_to_crop) res_ann = res_ann.relative_crop(rect_to_crop) return res_img, res_ann
def crop(img: np.ndarray, ann: Annotation, top_pad: int = 0, left_pad: int = 0, bottom_pad: int = 0, right_pad: int = 0) -> (np.ndarray, Annotation): """ Crops the given image array and annotation from all sides with the given values. Args: img: Input image array. ann: Input annotation. top_pad: The size in pixels of the piece of picture that will be cut from the top side. left_pad: The size in pixels of the piece of picture that will be cut from the left side. bottom_pad: The size in pixels of the piece of picture that will be cut from the bottom side. right_pad: The size in pixels of the piece of picture that will be cut from the right side. Returns: A tuple containing cropped image array and annotation. """ _validate_image_annotation_shape(img, ann) height, width = img.shape[:2] crop_rect = Rectangle(top_pad, left_pad, height - bottom_pad - 1, width - right_pad - 1) res_img = sly_image.crop(img, crop_rect) res_ann = ann.relative_crop(crop_rect) return res_img, res_ann
def instance_crop(img: np.ndarray, ann: Annotation, class_title: str, save_other_classes_in_crop: bool = True, padding_config: dict = None) -> list: """ Crops objects of specified classes from image with configurable padding. Args: img: Input image array. ann: Input annotation. class_title: Name of class to crop. save_other_classes_in_crop: save non-target classes in each cropped annotation. padding_config: Dict with padding Returns: List of cropped [image, annotation] pairs. """ padding_config = take_with_default(padding_config, {}) _validate_image_annotation_shape(img, ann) results = [] img_rect = Rectangle.from_size(img.shape[:2]) if save_other_classes_in_crop: non_target_labels = [ label for label in ann.labels if label.obj_class.name != class_title ] else: non_target_labels = [] ann_with_non_target_labels = ann.clone(labels=non_target_labels) for label in ann.labels: if label.obj_class.name == class_title: src_fig_rect = label.geometry.to_bbox() new_img_rect = _rect_from_bounds(padding_config, img_w=src_fig_rect.width, img_h=src_fig_rect.height) rect_to_crop = new_img_rect.translate(src_fig_rect.top, src_fig_rect.left) crops = rect_to_crop.crop(img_rect) if len(crops) == 0: continue rect_to_crop = crops[0] image_crop = sly_image.crop(img, rect_to_crop) cropped_ann = ann_with_non_target_labels.relative_crop( rect_to_crop) label_crops = label.relative_crop(rect_to_crop) for label_crop in label_crops: results.append((image_crop, cropped_ann.add_label(label_crop))) return results