Source code for

"""Experimental bounding box transformations."""
from __future__ import division
import random
import numpy as np
from ..bbox import crop as bbox_crop
from ....utils import bbox_iou

[docs]def random_crop_with_constraints(bbox, size, min_scale=0.3, max_scale=1, max_aspect_ratio=2, constraints=None, max_trial=50): """Crop an image randomly with bounding box constraints. This data augmentation is used in training of Single Shot Multibox Detector [#]_. More details can be found in data augmentation section of the original paper. .. [#] Wei Liu, Dragomir Anguelov, Dumitru Erhan, Christian Szegedy, Scott Reed, Cheng-Yang Fu, Alexander C. Berg. SSD: Single Shot MultiBox Detector. ECCV 2016. Parameters ---------- bbox : numpy.ndarray Numpy.ndarray with shape (N, 4+) where N is the number of bounding boxes. The second axis represents attributes of the bounding box. Specifically, these are :math:`(x_{min}, y_{min}, x_{max}, y_{max})`, we allow additional attributes other than coordinates, which stay intact during bounding box transformations. size : tuple Tuple of length 2 of image shape as (width, height). min_scale : float The minimum ratio between a cropped region and the original image. The default value is :obj:`0.3`. max_scale : float The maximum ratio between a cropped region and the original image. The default value is :obj:`1`. max_aspect_ratio : float The maximum aspect ratio of cropped region. The default value is :obj:`2`. constraints : iterable of tuples An iterable of constraints. Each constraint should be :obj:`(min_iou, max_iou)` format. If means no constraint if set :obj:`min_iou` or :obj:`max_iou` to :obj:`None`. If this argument defaults to :obj:`None`, :obj:`((0.1, None), (0.3, None), (0.5, None), (0.7, None), (0.9, None), (None, 1))` will be used. max_trial : int Maximum number of trials for each constraint before exit no matter what. Returns ------- numpy.ndarray Cropped bounding boxes with shape :obj:`(M, 4+)` where M <= N. tuple Tuple of length 4 as (x_offset, y_offset, new_width, new_height). """ # default params in paper if constraints is None: constraints = ( (0.1, None), (0.3, None), (0.5, None), (0.7, None), (0.9, None), (None, 1), ) w, h = size candidates = [(0, 0, w, h)] for min_iou, max_iou in constraints: min_iou = -np.inf if min_iou is None else min_iou max_iou = np.inf if max_iou is None else max_iou for _ in range(max_trial): scale = random.uniform(min_scale, max_scale) aspect_ratio = random.uniform( max(1 / max_aspect_ratio, scale * scale), min(max_aspect_ratio, 1 / (scale * scale))) crop_h = int(h * scale / np.sqrt(aspect_ratio)) crop_w = int(w * scale * np.sqrt(aspect_ratio)) crop_t = random.randrange(h - crop_h) crop_l = random.randrange(w - crop_w) crop_bb = np.array((crop_l, crop_t, crop_l + crop_w, crop_t + crop_h)) if len(bbox) == 0: top, bottom = crop_t, crop_t + crop_h left, right = crop_l, crop_l + crop_w return bbox, (left, top, right-left, bottom-top) iou = bbox_iou(bbox, crop_bb[np.newaxis]) if min_iou <= iou.min() and iou.max() <= max_iou: top, bottom = crop_t, crop_t + crop_h left, right = crop_l, crop_l + crop_w candidates.append((left, top, right-left, bottom-top)) break # random select one while candidates: crop = candidates.pop(np.random.randint(0, len(candidates))) new_bbox = bbox_crop(bbox, crop, allow_outside_center=False) if new_bbox.size < 1: continue new_crop = (crop[0], crop[1], crop[2], crop[3]) return new_bbox, new_crop return bbox, (0, 0, w, h)