# Copyright (c) OpenMMLab. All rights reserved. import copy import os import os.path as osp import sys import unittest from unittest.mock import MagicMock, Mock, patch import mmcv import numpy as np from mmdet.datasets.transforms import (FilterAnnotations, LoadAnnotations, LoadEmptyAnnotations, LoadImageFromNDArray, LoadMultiChannelImageFromFiles, LoadProposals) from mmdet.evaluation import INSTANCE_OFFSET from mmdet.structures.mask import BitmapMasks, PolygonMasks try: import panopticapi except ImportError: panopticapi = None class TestLoadAnnotations(unittest.TestCase): def setUp(self): """Setup the model and optimizer which are used in every test method. TestCase calls functions in this order: setUp() -> testMethod() -> tearDown() -> cleanUp() """ data_prefix = osp.join(osp.dirname(__file__), '../../data') seg_map = osp.join(data_prefix, 'gray.jpg') self.results = { 'ori_shape': (300, 400), 'seg_map_path': seg_map, 'instances': [{ 'bbox': [0, 0, 10, 20], 'bbox_label': 1, 'mask': [[0, 0, 0, 20, 10, 20, 10, 0]], 'ignore_flag': 0 }, { 'bbox': [10, 10, 110, 120], 'bbox_label': 2, 'mask': [[10, 10, 110, 10, 110, 120, 110, 10]], 'ignore_flag': 0 }, { 'bbox': [50, 50, 60, 80], 'bbox_label': 2, 'mask': [[50, 50, 60, 50, 60, 80, 50, 80]], 'ignore_flag': 1 }] } def test_load_bboxes(self): transform = LoadAnnotations( with_bbox=True, with_label=False, with_seg=False, with_mask=False, box_type=None) results = transform(copy.deepcopy(self.results)) self.assertIn('gt_bboxes', results) self.assertTrue((results['gt_bboxes'] == np.array([[0, 0, 10, 20], [10, 10, 110, 120], [50, 50, 60, 80]])).all()) self.assertEqual(results['gt_bboxes'].dtype, np.float32) self.assertTrue((results['gt_ignore_flags'] == np.array([0, 0, 1])).all()) self.assertEqual(results['gt_ignore_flags'].dtype, bool) def test_load_labels(self): transform = LoadAnnotations( with_bbox=False, with_label=True, with_seg=False, with_mask=False, ) results = transform(copy.deepcopy(self.results)) self.assertIn('gt_bboxes_labels', results) self.assertTrue((results['gt_bboxes_labels'] == np.array([1, 2, 2])).all()) self.assertEqual(results['gt_bboxes_labels'].dtype, np.int64) def test_load_mask(self): transform = LoadAnnotations( with_bbox=False, with_label=False, with_seg=False, with_mask=True, poly2mask=False) results = transform(copy.deepcopy(self.results)) self.assertIn('gt_masks', results) self.assertEqual(len(results['gt_masks']), 3) self.assertIsInstance(results['gt_masks'], PolygonMasks) def test_load_mask_poly2mask(self): transform = LoadAnnotations( with_bbox=False, with_label=False, with_seg=False, with_mask=True, poly2mask=True) results = transform(copy.deepcopy(self.results)) self.assertIn('gt_masks', results) self.assertEqual(len(results['gt_masks']), 3) self.assertIsInstance(results['gt_masks'], BitmapMasks) def test_repr(self): transform = LoadAnnotations( with_bbox=True, with_label=False, with_seg=False, with_mask=False, ) self.assertEqual( repr(transform), ('LoadAnnotations(with_bbox=True, ' 'with_label=False, with_mask=False, ' 'with_seg=False, poly2mask=True, ' "imdecode_backend='cv2', " 'backend_args=None)')) class TestFilterAnnotations(unittest.TestCase): def setUp(self): """Setup the model and optimizer which are used in every test method. TestCase calls functions in this order: setUp() -> testMethod() -> tearDown() -> cleanUp() """ rng = np.random.RandomState(0) self.results = { 'img': np.random.random((224, 224, 3)), 'img_shape': (224, 224), 'gt_bboxes_labels': np.array([1, 2, 3], dtype=np.int64), 'gt_bboxes': np.array([[10, 10, 20, 20], [20, 20, 40, 40], [40, 40, 80, 80]]), 'gt_ignore_flags': np.array([0, 0, 1], dtype=np.bool8), 'gt_masks': BitmapMasks(rng.rand(3, 224, 224), height=224, width=224), } def test_transform(self): # test keep_empty = True transform = FilterAnnotations( min_gt_bbox_wh=(50, 50), keep_empty=True, ) results = transform(copy.deepcopy(self.results)) self.assertIsNone(results) # test keep_empty = False transform = FilterAnnotations( min_gt_bbox_wh=(50, 50), keep_empty=False, ) results = transform(copy.deepcopy(self.results)) self.assertTrue(isinstance(results, dict)) # test filter annotations transform = FilterAnnotations(min_gt_bbox_wh=(15, 15), ) results = transform(copy.deepcopy(self.results)) self.assertIsInstance(results, dict) self.assertTrue((results['gt_bboxes_labels'] == np.array([2, 3])).all()) self.assertTrue((results['gt_bboxes'] == np.array([[20, 20, 40, 40], [40, 40, 80, 80]])).all()) self.assertEqual(len(results['gt_masks']), 2) self.assertEqual(len(results['gt_ignore_flags']), 2) def test_repr(self): transform = FilterAnnotations( min_gt_bbox_wh=(1, 1), keep_empty=False, ) self.assertEqual( repr(transform), ('FilterAnnotations(min_gt_bbox_wh=(1, 1), ' 'keep_empty=False)')) class TestLoadPanopticAnnotations(unittest.TestCase): def setUp(self): seg_map = np.zeros((10, 10), dtype=np.int32) seg_map[:5, :10] = 1 + 10 * INSTANCE_OFFSET seg_map[5:10, :5] = 4 + 11 * INSTANCE_OFFSET seg_map[5:10, 5:10] = 6 + 0 * INSTANCE_OFFSET rgb_seg_map = np.zeros((10, 10, 3), dtype=np.uint8) rgb_seg_map[:, :, 0] = seg_map / (256 * 256) rgb_seg_map[:, :, 1] = seg_map % (256 * 256) / 256 rgb_seg_map[:, :, 2] = seg_map % 256 self.seg_map_path = './1.png' mmcv.imwrite(rgb_seg_map, self.seg_map_path) self.seg_map = seg_map self.rgb_seg_map = rgb_seg_map self.results = { 'ori_shape': (10, 10), 'instances': [{ 'bbox': [0, 0, 10, 5], 'bbox_label': 0, 'ignore_flag': 0, }, { 'bbox': [0, 5, 5, 10], 'bbox_label': 1, 'ignore_flag': 1, }], 'segments_info': [ { 'id': 1 + 10 * INSTANCE_OFFSET, 'category': 0, 'is_thing': True, }, { 'id': 4 + 11 * INSTANCE_OFFSET, 'category': 1, 'is_thing': True, }, { 'id': 6 + 0 * INSTANCE_OFFSET, 'category': 2, 'is_thing': False, }, ], 'seg_map_path': self.seg_map_path } self.gt_mask = BitmapMasks([ (seg_map == 1 + 10 * INSTANCE_OFFSET).astype(np.uint8), (seg_map == 4 + 11 * INSTANCE_OFFSET).astype(np.uint8), ], 10, 10) self.gt_bboxes = np.array([[0, 0, 10, 5], [0, 5, 5, 10]], dtype=np.float32) self.gt_bboxes_labels = np.array([0, 1], dtype=np.int64) self.gt_ignore_flags = np.array([0, 1], dtype=bool) self.gt_seg_map = np.zeros((10, 10), dtype=np.int32) self.gt_seg_map[:5, :10] = 0 self.gt_seg_map[5:10, :5] = 1 self.gt_seg_map[5:10, 5:10] = 2 def tearDown(self): os.remove(self.seg_map_path) @unittest.skipIf(panopticapi is not None, 'panopticapi is installed') def test_init_without_panopticapi(self): # test if panopticapi is not installed from mmdet.datasets.transforms import LoadPanopticAnnotations with self.assertRaisesRegex( ImportError, 'panopticapi is not installed, please install it by'): LoadPanopticAnnotations() def test_transform(self): sys.modules['panopticapi'] = MagicMock() sys.modules['panopticapi.utils'] = MagicMock() from mmdet.datasets.transforms import LoadPanopticAnnotations mock_rgb2id = Mock(return_value=self.seg_map) with patch('panopticapi.utils.rgb2id', mock_rgb2id): # test with all False transform = LoadPanopticAnnotations( with_bbox=False, with_label=False, with_mask=False, with_seg=False) results = transform(copy.deepcopy(self.results)) self.assertDictEqual(results, self.results) # test with with_mask=True transform = LoadPanopticAnnotations( with_bbox=False, with_label=False, with_mask=True, with_seg=False) results = transform(copy.deepcopy(self.results)) self.assertTrue( (results['gt_masks'].masks == self.gt_mask.masks).all()) # test with with_seg=True transform = LoadPanopticAnnotations( with_bbox=False, with_label=False, with_mask=False, with_seg=True) results = transform(copy.deepcopy(self.results)) self.assertNotIn('gt_masks', results) self.assertTrue((results['gt_seg_map'] == self.gt_seg_map).all()) # test with all True transform = LoadPanopticAnnotations( with_bbox=True, with_label=True, with_mask=True, with_seg=True, box_type=None) results = transform(copy.deepcopy(self.results)) self.assertTrue( (results['gt_masks'].masks == self.gt_mask.masks).all()) self.assertTrue((results['gt_bboxes'] == self.gt_bboxes).all()) self.assertTrue( (results['gt_bboxes_labels'] == self.gt_bboxes_labels).all()) self.assertTrue( (results['gt_ignore_flags'] == self.gt_ignore_flags).all()) self.assertTrue((results['gt_seg_map'] == self.gt_seg_map).all()) class TestLoadImageFromNDArray(unittest.TestCase): def setUp(self): """Setup the model and optimizer which are used in every test method. TestCase calls functions in this order: setUp() -> testMethod() -> tearDown() -> cleanUp() """ self.results = {'img': np.zeros((256, 256, 3), dtype=np.uint8)} def test_transform(self): transform = LoadImageFromNDArray() results = transform(copy.deepcopy(self.results)) self.assertEqual(results['img'].shape, (256, 256, 3)) self.assertEqual(results['img'].dtype, np.uint8) self.assertEqual(results['img_shape'], (256, 256)) self.assertEqual(results['ori_shape'], (256, 256)) # to_float32 transform = LoadImageFromNDArray(to_float32=True) results = transform(copy.deepcopy(results)) self.assertEqual(results['img'].dtype, np.float32) def test_repr(self): transform = LoadImageFromNDArray() self.assertEqual( repr(transform), ('LoadImageFromNDArray(' 'ignore_empty=False, ' 'to_float32=False, ' "color_type='color', " "imdecode_backend='cv2', " 'backend_args=None)')) class TestLoadMultiChannelImageFromFiles(unittest.TestCase): def setUp(self): """Setup the model and optimizer which are used in every test method. TestCase calls functions in this order: setUp() -> testMethod() -> tearDown() -> cleanUp() """ self.img_path = [] for i in range(4): img_channel_path = f'./part_{i}.jpg' img_channel = np.zeros((10, 10), dtype=np.uint8) mmcv.imwrite(img_channel, img_channel_path) self.img_path.append(img_channel_path) self.results = {'img_path': self.img_path} def tearDown(self): for filename in self.img_path: os.remove(filename) def test_transform(self): transform = LoadMultiChannelImageFromFiles() results = transform(copy.deepcopy(self.results)) self.assertEqual(results['img'].shape, (10, 10, 4)) self.assertEqual(results['img'].dtype, np.uint8) self.assertEqual(results['img_shape'], (10, 10)) self.assertEqual(results['ori_shape'], (10, 10)) # to_float32 transform = LoadMultiChannelImageFromFiles(to_float32=True) results = transform(copy.deepcopy(results)) self.assertEqual(results['img'].dtype, np.float32) def test_rper(self): transform = LoadMultiChannelImageFromFiles() self.assertEqual( repr(transform), ('LoadMultiChannelImageFromFiles(' 'to_float32=False, ' "color_type='unchanged', " "imdecode_backend='cv2', " 'backend_args=None)')) class TestLoadProposals(unittest.TestCase): def test_transform(self): transform = LoadProposals() results = { 'proposals': dict( bboxes=np.zeros((5, 4), dtype=np.int64), scores=np.zeros((5, ), dtype=np.int64)) } results = transform(results) self.assertEqual(results['proposals'].dtype, np.float32) self.assertEqual(results['proposals'].shape[-1], 4) self.assertEqual(results['proposals_scores'].dtype, np.float32) # bboxes.shape[1] should be 4 results = {'proposals': dict(bboxes=np.zeros((5, 5), dtype=np.int64))} with self.assertRaises(AssertionError): transform(results) # bboxes.shape[0] should equal to scores.shape[0] results = { 'proposals': dict( bboxes=np.zeros((5, 4), dtype=np.int64), scores=np.zeros((3, ), dtype=np.int64)) } with self.assertRaises(AssertionError): transform(results) # empty bboxes results = { 'proposals': dict(bboxes=np.zeros((0, 4), dtype=np.float32)) } results = transform(results) excepted_proposals = np.zeros((0, 4), dtype=np.float32) excepted_proposals_scores = np.zeros(0, dtype=np.float32) self.assertTrue((results['proposals'] == excepted_proposals).all()) self.assertTrue( (results['proposals_scores'] == excepted_proposals_scores).all()) transform = LoadProposals(num_max_proposals=2) results = { 'proposals': dict( bboxes=np.zeros((5, 4), dtype=np.int64), scores=np.zeros((5, ), dtype=np.int64)) } results = transform(results) self.assertEqual(results['proposals'].shape[0], 2) def test_repr(self): transform = LoadProposals() self.assertEqual( repr(transform), 'LoadProposals(num_max_proposals=None)') class TestLoadEmptyAnnotations(unittest.TestCase): def test_transform(self): transform = LoadEmptyAnnotations( with_bbox=True, with_label=True, with_mask=True, with_seg=True) results = {'img_shape': (224, 224)} results = transform(results) self.assertEqual(results['gt_bboxes'].dtype, np.float32) self.assertEqual(results['gt_bboxes'].shape[-1], 4) self.assertEqual(results['gt_ignore_flags'].dtype, bool) self.assertEqual(results['gt_bboxes_labels'].dtype, np.int64) self.assertEqual(results['gt_masks'].masks.dtype, np.uint8) self.assertEqual(results['gt_masks'].masks.shape[-2:], results['img_shape']) self.assertEqual(results['gt_seg_map'].dtype, np.uint8) self.assertEqual(results['gt_seg_map'].shape, results['img_shape']) def test_repr(self): transform = LoadEmptyAnnotations() self.assertEqual( repr(transform), 'LoadEmptyAnnotations(with_bbox=True, ' 'with_label=True, ' 'with_mask=False, ' 'with_seg=False, ' 'seg_ignore_label=255)')