Awiny's picture
first version submission
c3a1897
raw
history blame
10.5 kB
# -*- coding: utf-8 -*-
# Copyright (c) Facebook, Inc. and its affiliates.
import numpy as np
import os
import tempfile
import unittest
import cv2
import torch
from detectron2.data import MetadataCatalog
from detectron2.structures import BoxMode, Instances, RotatedBoxes
from detectron2.utils.visualizer import ColorMode, Visualizer
class TestVisualizer(unittest.TestCase):
def _random_data(self):
H, W = 100, 100
N = 10
img = np.random.rand(H, W, 3) * 255
boxxy = np.random.rand(N, 2) * (H // 2)
boxes = np.concatenate((boxxy, boxxy + H // 2), axis=1)
def _rand_poly():
return np.random.rand(3, 2).flatten() * H
polygons = [[_rand_poly() for _ in range(np.random.randint(1, 5))] for _ in range(N)]
mask = np.zeros_like(img[:, :, 0], dtype=np.bool)
mask[:40, 10:20] = 1
labels = [str(i) for i in range(N)]
return img, boxes, labels, polygons, [mask] * N
@property
def metadata(self):
return MetadataCatalog.get("coco_2017_train")
def test_draw_dataset_dict(self):
img = np.random.rand(512, 512, 3) * 255
dic = {
"annotations": [
{
"bbox": [
368.9946492271106,
330.891438763377,
13.148537455410235,
13.644708680142685,
],
"bbox_mode": BoxMode.XYWH_ABS,
"category_id": 0,
"iscrowd": 1,
"segmentation": {
"counts": "_jh52m?2N2N2N2O100O10O001N1O2MceP2",
"size": [512, 512],
},
}
],
"height": 512,
"image_id": 1,
"width": 512,
}
v = Visualizer(img)
v.draw_dataset_dict(dic)
v = Visualizer(img, self.metadata)
v.draw_dataset_dict(dic)
def test_draw_rotated_dataset_dict(self):
img = np.random.rand(512, 512, 3) * 255
dic = {
"annotations": [
{
"bbox": [
368.9946492271106,
330.891438763377,
13.148537455410235,
13.644708680142685,
45.0,
],
"bbox_mode": BoxMode.XYWHA_ABS,
"category_id": 0,
"iscrowd": 1,
}
],
"height": 512,
"image_id": 1,
"width": 512,
}
v = Visualizer(img, self.metadata)
v.draw_dataset_dict(dic)
def test_overlay_instances(self):
img, boxes, labels, polygons, masks = self._random_data()
v = Visualizer(img, self.metadata)
output = v.overlay_instances(masks=polygons, boxes=boxes, labels=labels).get_image()
self.assertEqual(output.shape, img.shape)
# Test 2x scaling
v = Visualizer(img, self.metadata, scale=2.0)
output = v.overlay_instances(masks=polygons, boxes=boxes, labels=labels).get_image()
self.assertEqual(output.shape[0], img.shape[0] * 2)
# Test overlay masks
v = Visualizer(img, self.metadata)
output = v.overlay_instances(masks=masks, boxes=boxes, labels=labels).get_image()
self.assertEqual(output.shape, img.shape)
def test_overlay_instances_no_boxes(self):
img, boxes, labels, polygons, _ = self._random_data()
v = Visualizer(img, self.metadata)
v.overlay_instances(masks=polygons, boxes=None, labels=labels).get_image()
def test_draw_instance_predictions(self):
img, boxes, _, _, masks = self._random_data()
num_inst = len(boxes)
inst = Instances((img.shape[0], img.shape[1]))
inst.pred_classes = torch.randint(0, 80, size=(num_inst,))
inst.scores = torch.rand(num_inst)
inst.pred_boxes = torch.from_numpy(boxes)
inst.pred_masks = torch.from_numpy(np.asarray(masks))
v = Visualizer(img)
v.draw_instance_predictions(inst)
v = Visualizer(img, self.metadata)
v.draw_instance_predictions(inst)
def test_BWmode_nomask(self):
img, boxes, _, _, masks = self._random_data()
num_inst = len(boxes)
inst = Instances((img.shape[0], img.shape[1]))
inst.pred_classes = torch.randint(0, 80, size=(num_inst,))
inst.scores = torch.rand(num_inst)
inst.pred_boxes = torch.from_numpy(boxes)
v = Visualizer(img, self.metadata, instance_mode=ColorMode.IMAGE_BW)
v.draw_instance_predictions(inst)
# check that output is grayscale
inst = inst[:0]
v = Visualizer(img, self.metadata, instance_mode=ColorMode.IMAGE_BW)
output = v.draw_instance_predictions(inst).get_image()
self.assertTrue(np.allclose(output[:, :, 0], output[:, :, 1]))
self.assertTrue(np.allclose(output[:, :, 0], output[:, :, 2]))
def test_draw_empty_mask_predictions(self):
img, boxes, _, _, masks = self._random_data()
num_inst = len(boxes)
inst = Instances((img.shape[0], img.shape[1]))
inst.pred_classes = torch.randint(0, 80, size=(num_inst,))
inst.scores = torch.rand(num_inst)
inst.pred_boxes = torch.from_numpy(boxes)
inst.pred_masks = torch.from_numpy(np.zeros_like(np.asarray(masks)))
v = Visualizer(img, self.metadata)
v.draw_instance_predictions(inst)
def test_correct_output_shape(self):
img = np.random.rand(928, 928, 3) * 255
v = Visualizer(img, self.metadata)
out = v.output.get_image()
self.assertEqual(out.shape, img.shape)
def test_overlay_rotated_instances(self):
H, W = 100, 150
img = np.random.rand(H, W, 3) * 255
num_boxes = 50
boxes_5d = torch.zeros(num_boxes, 5)
boxes_5d[:, 0] = torch.FloatTensor(num_boxes).uniform_(-0.1 * W, 1.1 * W)
boxes_5d[:, 1] = torch.FloatTensor(num_boxes).uniform_(-0.1 * H, 1.1 * H)
boxes_5d[:, 2] = torch.FloatTensor(num_boxes).uniform_(0, max(W, H))
boxes_5d[:, 3] = torch.FloatTensor(num_boxes).uniform_(0, max(W, H))
boxes_5d[:, 4] = torch.FloatTensor(num_boxes).uniform_(-1800, 1800)
rotated_boxes = RotatedBoxes(boxes_5d)
labels = [str(i) for i in range(num_boxes)]
v = Visualizer(img, self.metadata)
output = v.overlay_instances(boxes=rotated_boxes, labels=labels).get_image()
self.assertEqual(output.shape, img.shape)
def test_draw_no_metadata(self):
img, boxes, _, _, masks = self._random_data()
num_inst = len(boxes)
inst = Instances((img.shape[0], img.shape[1]))
inst.pred_classes = torch.randint(0, 80, size=(num_inst,))
inst.scores = torch.rand(num_inst)
inst.pred_boxes = torch.from_numpy(boxes)
inst.pred_masks = torch.from_numpy(np.asarray(masks))
v = Visualizer(img, MetadataCatalog.get("asdfasdf"))
v.draw_instance_predictions(inst)
def test_draw_binary_mask(self):
img, boxes, _, _, masks = self._random_data()
img[:, :, 0] = 0 # remove red color
mask = masks[0]
mask_with_hole = np.zeros_like(mask).astype("uint8")
mask_with_hole = cv2.rectangle(mask_with_hole, (10, 10), (50, 50), 1, 5)
for m in [mask, mask_with_hole]:
for save in [True, False]:
v = Visualizer(img)
o = v.draw_binary_mask(m, color="red", text="test")
if save:
with tempfile.TemporaryDirectory(prefix="detectron2_viz") as d:
path = os.path.join(d, "output.png")
o.save(path)
o = cv2.imread(path)[:, :, ::-1]
else:
o = o.get_image().astype("float32")
# red color is drawn on the image
self.assertTrue(o[:, :, 0].sum() > 0)
def test_draw_soft_mask(self):
img = np.random.rand(100, 100, 3) * 255
img[:, :, 0] = 0 # remove red color
mask = np.zeros((100, 100), dtype=np.float32)
mask[30:50, 40:50] = 1.0
cv2.GaussianBlur(mask, (21, 21), 10)
v = Visualizer(img)
o = v.draw_soft_mask(mask, color="red", text="test")
o = o.get_image().astype("float32")
# red color is drawn on the image
self.assertTrue(o[:, :, 0].sum() > 0)
# test draw empty mask
v = Visualizer(img)
o = v.draw_soft_mask(np.zeros((100, 100), dtype=np.float32), color="red", text="test")
o = o.get_image().astype("float32")
def test_border_mask_with_holes(self):
H, W = 200, 200
img = np.zeros((H, W, 3))
img[:, :, 0] = 255.0
v = Visualizer(img, scale=3)
mask = np.zeros((H, W))
mask[:, 100:150] = 1
# create a hole, to trigger imshow
mask = cv2.rectangle(mask, (110, 110), (130, 130), 0, thickness=-1)
output = v.draw_binary_mask(mask, color="blue")
output = output.get_image()[:, :, ::-1]
first_row = {tuple(x.tolist()) for x in output[0]}
last_row = {tuple(x.tolist()) for x in output[-1]}
# Check quantization / off-by-1 error: the first and last row must have two colors
self.assertEqual(len(last_row), 2)
self.assertEqual(len(first_row), 2)
self.assertIn((0, 0, 255), last_row)
self.assertIn((0, 0, 255), first_row)
def test_border_polygons(self):
H, W = 200, 200
img = np.zeros((H, W, 3))
img[:, :, 0] = 255.0
v = Visualizer(img, scale=3)
mask = np.zeros((H, W))
mask[:, 100:150] = 1
output = v.draw_binary_mask(mask, color="blue")
output = output.get_image()[:, :, ::-1]
first_row = {tuple(x.tolist()) for x in output[0]}
last_row = {tuple(x.tolist()) for x in output[-1]}
# Check quantization / off-by-1 error:
# the first and last row must have >=2 colors, because the polygon
# touches both rows
self.assertGreaterEqual(len(last_row), 2)
self.assertGreaterEqual(len(first_row), 2)
self.assertIn((0, 0, 255), last_row)
self.assertIn((0, 0, 255), first_row)
if __name__ == "__main__":
unittest.main()