| | |
| | |
| |
|
| | 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=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) |
| |
|
| | |
| | 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) |
| |
|
| | |
| | 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) |
| |
|
| | |
| | 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 |
| | 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") |
| | |
| | self.assertTrue(o[:, :, 0].sum() > 0) |
| |
|
| | def test_draw_soft_mask(self): |
| | img = np.random.rand(100, 100, 3) * 255 |
| | img[:, :, 0] = 0 |
| | 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") |
| | |
| | self.assertTrue(o[:, :, 0].sum() > 0) |
| |
|
| | |
| | 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 |
| | |
| | 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]} |
| | |
| | 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]} |
| | |
| | |
| | |
| | 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() |
| |
|