|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import numpy as np |
|
from dataclasses import dataclass |
|
from typing import Tuple, List |
|
|
|
|
|
@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 |
|
|
|
|
|
top = min_border |
|
while is_above_tolerance(gray_image[top, :], w) and top < h-1: |
|
top += 1 |
|
if top > cut_off: |
|
break |
|
|
|
|
|
bottom = h - min_border |
|
while is_above_tolerance(gray_image[bottom, :], w) and bottom > 0: |
|
bottom -= 1 |
|
if h - bottom > cut_off: |
|
break |
|
|
|
|
|
left = min_border |
|
while is_above_tolerance(gray_image[:, left], h) and left < w-1: |
|
left += 1 |
|
if left > cut_off: |
|
break |
|
|
|
|
|
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 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 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_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_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 |
|
|