# MIT License # Copyright (c) 2022 Intelligent Systems Lab Org # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. # File author: Shariq Farooq Bhat import numpy as np from dataclasses import dataclass from typing import Tuple, List # dataclass to store the crop parameters @dataclass class CropParams: top: int bottom: int left: int right: int def get_border_params(rgb_image, tolerance=0.1, cut_off=20, value=0, level_diff_threshold=5, channel_axis=-1, min_border=5) -> CropParams: gray_image = np.mean(rgb_image, axis=channel_axis) h, w = gray_image.shape def num_value_pixels(arr): return np.sum(np.abs(arr - value) < level_diff_threshold) def is_above_tolerance(arr, total_pixels): return (num_value_pixels(arr) / total_pixels) > tolerance # Crop top border until number of value pixels become below tolerance top = min_border while is_above_tolerance(gray_image[top, :], w) and top < h-1: top += 1 if top > cut_off: break # Crop bottom border until number of value pixels become below tolerance bottom = h - min_border while is_above_tolerance(gray_image[bottom, :], w) and bottom > 0: bottom -= 1 if h - bottom > cut_off: break # Crop left border until number of value pixels become below tolerance left = min_border while is_above_tolerance(gray_image[:, left], h) and left < w-1: left += 1 if left > cut_off: break # Crop right border until number of value pixels become below tolerance right = w - min_border while is_above_tolerance(gray_image[:, right], h) and right > 0: right -= 1 if w - right > cut_off: break return CropParams(top, bottom, left, right) def get_white_border(rgb_image, value=255, **kwargs) -> CropParams: """Crops the white border of the RGB. Args: rgb: RGB image, shape (H, W, 3). Returns: Crop parameters. """ if value == 255: # assert range of values in rgb image is [0, 255] assert np.max(rgb_image) <= 255 and np.min(rgb_image) >= 0, "RGB image values are not in range [0, 255]." assert rgb_image.max() > 1, "RGB image values are not in range [0, 255]." elif value == 1: # assert range of values in rgb image is [0, 1] assert np.max(rgb_image) <= 1 and np.min(rgb_image) >= 0, "RGB image values are not in range [0, 1]." return get_border_params(rgb_image, value=value, **kwargs) def get_black_border(rgb_image, **kwargs) -> CropParams: """Crops the black border of the RGB. Args: rgb: RGB image, shape (H, W, 3). Returns: Crop parameters. """ return get_border_params(rgb_image, value=0, **kwargs) def crop_image(image: np.ndarray, crop_params: CropParams) -> np.ndarray: """Crops the image according to the crop parameters. Args: image: RGB or depth image, shape (H, W, 3) or (H, W). crop_params: Crop parameters. Returns: Cropped image. """ return image[crop_params.top:crop_params.bottom, crop_params.left:crop_params.right] def crop_images(*images: np.ndarray, crop_params: CropParams) -> Tuple[np.ndarray]: """Crops the images according to the crop parameters. Args: images: RGB or depth images, shape (H, W, 3) or (H, W). crop_params: Crop parameters. Returns: Cropped images. """ return tuple(crop_image(image, crop_params) for image in images) def crop_black_or_white_border(rgb_image, *other_images: np.ndarray, tolerance=0.1, cut_off=20, level_diff_threshold=5) -> Tuple[np.ndarray]: """Crops the white and black border of the RGB and depth images. Args: rgb: RGB image, shape (H, W, 3). This image is used to determine the border. other_images: The other images to crop according to the border of the RGB image. Returns: Cropped RGB and other images. """ # crop black border crop_params = get_black_border(rgb_image, tolerance=tolerance, cut_off=cut_off, level_diff_threshold=level_diff_threshold) cropped_images = crop_images(rgb_image, *other_images, crop_params=crop_params) # crop white border crop_params = get_white_border(cropped_images[0], tolerance=tolerance, cut_off=cut_off, level_diff_threshold=level_diff_threshold) cropped_images = crop_images(*cropped_images, crop_params=crop_params) return cropped_images