def test_offsets(): original = BoundBoxArray.from_boundboxes([(0, 0, 0.5, 0.5), (0.3, 0.4, 0.6, 0.9)]) default_boxes = BoundBoxArray.from_boundboxes([(0, 0, 0.5, 0.4), (0.2, 0.4, 0.6, 0.9)]) # get offsets offsets = calculate_offsets(default_boxes, original) offsets = BoundBoxArray.from_centerboxes(offsets) # apply them to get original bboxes recovered = apply_offsets(default_boxes, offsets) recovered = BoundBoxArray.from_centerboxes(recovered) assert np.allclose(original, recovered)
def test_iou(non_overlapping_boundboxes, full_and_quarter_boundboxes): """Test IOU calculation""" first, second = non_overlapping_boundboxes first = BoundBoxArray.from_boundboxes(first) second = BoundBoxArray.from_boundboxes(second) assert np.allclose(first.iou(second), np.zeros((2, 2))) assert np.allclose(second.iou(first), np.zeros((2, 2))) assert np.allclose(first.iou(first), np.eye(2)) assert np.allclose(second.iou(second), np.eye(2)) full, quarter = full_and_quarter_boundboxes full = BoundBoxArray.from_boundboxes(full) quarter = BoundBoxArray.from_boundboxes(quarter) assert np.allclose(full.iou(quarter), np.array(0.25)) assert np.allclose(quarter.iou(full), np.array(0.25))
def test_random_hflip(): bboxes = BoundBoxArray.from_boundboxes([(100, 100, 200, 200)], classnames=["cat"]) image = AnnotatedImage(np.ones((300, 300, 3)), bboxes) image = image.normalize_bboxes() flipped_image = image.random_hflip(probability=1.0) # check that centered bbox wasn't flipped assert np.allclose(flipped_image.bboxes.boundboxes.as_matrix(), image.bboxes.boundboxes.as_matrix()) # but image was assert np.array_equal(np.fliplr(image.image), flipped_image.image) bboxes = BoundBoxArray.from_boundboxes([(0, 0, 150, 300)], classnames=["cat"]) image = AnnotatedImage(np.ones((300, 300, 3)), bboxes) image = image.normalize_bboxes() flipped_image = image.random_hflip(probability=1.0) assert np.allclose(flipped_image.bboxes.boundboxes.as_matrix(), np.array([0.5, 0, 1, 1])) assert np.array_equal(np.fliplr(image.image), flipped_image.image)
def parse_annotation(annotation): """Parses PascalVOC-like .xml annotation. BoundBoxArray is returned""" root = xml_parser.parse(annotation).getroot() boxes = list() classnames = list() for obj in root.findall('object'): # xmin, ymin, xmax, ymax boxes.append([int(coord.text) for coord in obj.find('bndbox')]) classnames.append(obj.find('name').text) return BoundBoxArray.from_boundboxes(boxes, classnames)
def crop(image, selection): """Crops selection (which is a 4-length tuple with normalized (to (0, 1) interval) (x_min, y_min, x_max, y_max) coordinates) from image""" x_min, y_min, x_max, y_max = selection height, width = image.size x_min_int = int(x_min * width) y_min_int = int(y_min * height) x_max_int = int(x_max * width) y_max_int = int(y_max * height) cropped_image = image.image[y_min_int:y_max_int, x_min_int:x_max_int] cropped_image = imresize(cropped_image, image.size) scale_x = (x_max - x_min) scale_y = (y_max - y_min) normalized_bboxes = image.normalize_bboxes().bboxes # discard all bboxes that lie outside the crop valid_bboxes = ( (normalized_bboxes.centerboxes.x_center > x_min) & (normalized_bboxes.centerboxes.x_center < x_max) & (normalized_bboxes.centerboxes.y_center < y_max) & (normalized_bboxes.centerboxes.y_center > y_min) ) # return original image if there are no bboxes in the selection if not valid_bboxes.any(): return image normalized_bboxes = normalized_bboxes[valid_bboxes] cropped_bboxes = normalized_bboxes.clip( vertical_clip_value=(y_min, y_max), horizontal_clip_value=(x_min, x_max) ) shift = [x_min, y_min, x_min, y_min] shifted_bboxes = cropped_bboxes.boundboxes.as_matrix() - shift shifted_bboxes = BoundBoxArray.from_boundboxes( shifted_bboxes, classnames=cropped_bboxes.classnames ) resized_bboxes = shifted_bboxes.rescale((scale_y, scale_x)).clip() cropped = image.__class__(cropped_image, resized_bboxes, filename=image.filename, bboxes_normalized=True) return cropped
def test_labels_and_offsets(): default_boxes = BoundBoxArray.from_boundboxes([(0, 0, 0.5, 0.5), (0, 0, 1.0, 1.0), (0.45, 0.45, 0.9, 0.9)]) bboxes = BoundBoxArray.from_boundboxes([(0, 0, 150, 150), (0, 0, 120, 120), (150, 150, 300, 300)], classnames=["cat", "pig", "dog"]) class_mapping = dict(cat=1, pig=2, dog=3) threshold = 0.5 image = AnnotatedImage(np.ones((300, 300, 3)), bboxes) image = image.normalize_bboxes() labels, offsets = image.labels_and_offsets(default_boxes, threshold, class_mapping) # cat matched to first, dog matched to third # pig wasn't matched since cat has higher IOU assert (labels == [1, 0, 3]).all() # cat matched perfectly, second default box # wasn't matched assert (offsets[[0, 1]] == [0, 0, 0, 0]).all()
def test_boundbox_creation(boundboxes, centerboxes, boxes, classnames): from_boundboxes = BoundBoxArray.from_boundboxes(boundboxes, classnames) assert from_boundboxes.shape == (2, 8) assert (from_boundboxes.index == classnames).all() from_centerboxes = BoundBoxArray.from_centerboxes(centerboxes, classnames) assert from_boundboxes.equals(from_centerboxes) from_boxes = BoundBoxArray.from_boxes(boxes, classnames) assert from_boundboxes.equals(from_boxes) assert from_centerboxes.equals(from_boxes)
def test_random_crop(): bboxes = BoundBoxArray.from_boundboxes([(0, 0, 150, 150), (0, 0, 120, 120), (150, 150, 300, 300)], classnames=["cat", "pig", "dog"]) image = AnnotatedImage(np.ones((300, 300, 3)), bboxes) image = image.normalize_bboxes() # perform 20 random crops for _ in range(20): cropped_image = image.random_crop(probability=0.9) assert cropped_image.size == (300, 300) assert (cropped_image.bboxes.x_min >= 0).all() assert (cropped_image.bboxes.y_min >= 0).all() assert (cropped_image.bboxes.x_max <= 1).all() assert (cropped_image.bboxes.y_max <= 1).all()
def hflip(image): """Flips an instance of AnnotatedImage""" flipped_image = np.fliplr(image.image) normalized_image = image.normalize_bboxes() bboxes = normalized_image.bboxes.boundboxes flipped_bboxes = (1 - bboxes.x_max, 1 - bboxes.y_max, 1 - bboxes.x_min, 1 - bboxes.y_min) flipped_bboxes = np.array(flipped_bboxes, dtype=np.float32).T flipped_bboxes = BoundBoxArray.from_boundboxes( flipped_bboxes, classnames=bboxes.classnames ) flipped = image.__class__(flipped_image, flipped_bboxes, filename=image.filename, bboxes_normalized=True) return flipped