Skip to content

Commit

Permalink
Added transform "RandomCropFromBorders" and support for BBox in Elast…
Browse files Browse the repository at this point in the history
…icTransform, GridDistortion, OpticalDistortion (#476)

* Added transform "RandomCropFromBorders"

* Added transform "RandomCropFromBorders"

* Added support for BBox in ElasticTransform, GridDistortion, OpticalDistortion

* Fix new line

* Fix whitespace

* Fix for empty mask in function bbox_for_mask

* Small fix in bbox_for_mask function to prevent x_min == x_max and y_min == y_max

* Update README.md

* Renamed bbox_for_mask -> bbox_from_mask. Added function mask_from_bbox

* Black reformat

* Added description for crop_value, crop_top, crop_bottom, crop_left and crop_right parameters

Co-authored-by: Eugene Khvedchenya <ekhvedchenya@gmail.com>
Co-authored-by: Mikhail Druzhinin <dipetm@gmail.com>
  • Loading branch information
3 people committed Jul 29, 2022
1 parent a8dc46e commit 1cd328b
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 3 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,14 +180,14 @@ Spatial-level transforms will simultaneously change both an input image as well
| [CropNonEmptyMaskIfExists](https://albumentations.ai/docs/api_reference/augmentations/crops/transforms/#albumentations.augmentations.crops.transforms.CropNonEmptyMaskIfExists) |||||
| [ElasticTransform](https://albumentations.ai/docs/api_reference/augmentations/geometric/transforms/#albumentations.augmentations.geometric.transforms.ElasticTransform) ||| | |
| [Flip](https://albumentations.ai/docs/api_reference/augmentations/transforms/#albumentations.augmentations.transforms.Flip) |||||
| [GridDistortion](https://albumentations.ai/docs/api_reference/augmentations/transforms/#albumentations.augmentations.transforms.GridDistortion) ||| | |
| [GridDistortion](https://albumentations.ai/docs/api_reference/augmentations/transforms/#albumentations.augmentations.transforms.GridDistortion) ||| | |
| [GridDropout](https://albumentations.ai/docs/api_reference/augmentations/dropout/grid_dropout/#albumentations.augmentations.dropout.grid_dropout.GridDropout) ||| | |
| [HorizontalFlip](https://albumentations.ai/docs/api_reference/augmentations/transforms/#albumentations.augmentations.transforms.HorizontalFlip) |||||
| [Lambda](https://albumentations.ai/docs/api_reference/augmentations/transforms/#albumentations.augmentations.transforms.Lambda) |||||
| [LongestMaxSize](https://albumentations.ai/docs/api_reference/augmentations/geometric/resize/#albumentations.augmentations.geometric.resize.LongestMaxSize) |||||
| [MaskDropout](https://albumentations.ai/docs/api_reference/augmentations/dropout/mask_dropout/#albumentations.augmentations.dropout.mask_dropout.MaskDropout) ||| | |
| [NoOp](https://albumentations.ai/docs/api_reference/core/transforms_interface/#albumentations.core.transforms_interface.NoOp) |||||
| [OpticalDistortion](https://albumentations.ai/docs/api_reference/augmentations/transforms/#albumentations.augmentations.transforms.OpticalDistortion) ||| | |
| [OpticalDistortion](https://albumentations.ai/docs/api_reference/augmentations/transforms/#albumentations.augmentations.transforms.OpticalDistortion) ||| | |
| [PadIfNeeded](https://albumentations.ai/docs/api_reference/augmentations/transforms/#albumentations.augmentations.transforms.PadIfNeeded) |||||
| [Perspective](https://albumentations.ai/docs/api_reference/augmentations/geometric/transforms/#albumentations.augmentations.geometric.transforms.Perspective) |||||
| [PiecewiseAffine](https://albumentations.ai/docs/api_reference/augmentations/geometric/transforms/#albumentations.augmentations.geometric.transforms.PiecewiseAffine) |||||
Expand Down
37 changes: 37 additions & 0 deletions albumentations/augmentations/functional.py
Original file line number Diff line number Diff line change
Expand Up @@ -1653,6 +1653,43 @@ def multiply(img, multiplier):
return _multiply_non_uint8(img, multiplier)


def bbox_from_mask(mask):
"""Create bounding box from binary mask (fast version)
Args:
mask (numpy.ndarray): binary mask.
Returns:
tuple: A bounding box tuple `(x_min, y_min, x_max, y_max)`.
"""
rows = np.any(mask, axis=1)
if not rows.any():
return -1, -1, -1, -1
cols = np.any(mask, axis=0)
y_min, y_max = np.where(rows)[0][[0, -1]]
x_min, x_max = np.where(cols)[0][[0, -1]]
return x_min, y_min, x_max + 1, y_max + 1


def mask_from_bbox(img, bbox):
"""Create binary mask from bounding box
Args:
img (numpy.ndarray): input image
bbox: A bounding box tuple `(x_min, y_min, x_max, y_max)`
Returns:
mask (numpy.ndarray): binary mask
"""

mask = np.zeros(img.shape[:2], dtype=np.uint8)
x_min, y_min, x_max, y_max = bbox
mask[y_min:y_max, x_min:x_max] = 1
return mask


def fancy_pca(img, alpha=0.1):
"""Perform 'Fancy PCA' augmentation from:
http://papers.nips.cc/paper/4824-imagenet-classification-with-deep-convolutional-neural-networks.pdf
Expand Down
28 changes: 27 additions & 1 deletion albumentations/augmentations/transforms.py
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,7 @@ class OpticalDistortion(DualTransform):
list of float): padding value if border_mode is cv2.BORDER_CONSTANT applied for masks.
Targets:
image, mask
image, mask, bbox
Image types:
uint8, float32
Expand Down Expand Up @@ -439,6 +439,18 @@ def apply(self, img, k=0, dx=0, dy=0, interpolation=cv2.INTER_LINEAR, **params):
def apply_to_mask(self, img, k=0, dx=0, dy=0, **params):
return F.optical_distortion(img, k, dx, dy, cv2.INTER_NEAREST, self.border_mode, self.mask_value)

def apply_to_bbox(self, bbox, k=0, dx=0, dy=0, **params):
rows, cols = params["rows"], params["cols"]
mask = np.zeros((rows, cols), dtype=np.uint8)
bbox_denorm = F.denormalize_bbox(bbox, rows, cols)
x_min, y_min, x_max, y_max = bbox_denorm[:4]
x_min, y_min, x_max, y_max = int(x_min), int(y_min), int(x_max), int(y_max)
mask[y_min:y_max, x_min:x_max] = 1
mask = F.optical_distortion(mask, k, dx, dy, cv2.INTER_NEAREST, self.border_mode, self.mask_value)
bbox_returned = F.bbox_from_mask(mask)
bbox_returned = F.normalize_bbox(bbox_returned, rows, cols)
return bbox_returned

def get_params(self):
return {
"k": random.uniform(self.distort_limit[0], self.distort_limit[1]),
Expand Down Expand Up @@ -522,6 +534,20 @@ def apply_to_mask(self, img, stepsx=(), stepsy=(), **params):
self.mask_value,
)

def apply_to_bbox(self, bbox, stepsx=[], stepsy=[], **params):
rows, cols = params["rows"], params["cols"]
mask = np.zeros((rows, cols), dtype=np.uint8)
bbox_denorm = F.denormalize_bbox(bbox, rows, cols)
x_min, y_min, x_max, y_max = bbox_denorm[:4]
x_min, y_min, x_max, y_max = int(x_min), int(y_min), int(x_max), int(y_max)
mask[y_min:y_max, x_min:x_max] = 1
mask = F.grid_distortion(
mask, self.num_steps, stepsx, stepsy, cv2.INTER_NEAREST, self.border_mode, self.mask_value
)
bbox_returned = F.bbox_from_mask(mask)
bbox_returned = F.normalize_bbox(bbox_returned, rows, cols)
return bbox_returned

def get_params(self):
stepsx = [1 + random.uniform(self.distort_limit[0], self.distort_limit[1]) for i in range(self.num_steps + 1)]
stepsy = [1 + random.uniform(self.distort_limit[0], self.distort_limit[1]) for i in range(self.num_steps + 1)]
Expand Down

0 comments on commit 1cd328b

Please sign in to comment.