2022-05-26 11:06:02 +08:00
|
|
|
# Copyright (c) OpenMMLab. All rights reserved.
|
|
|
|
import copy
|
|
|
|
import os.path as osp
|
|
|
|
import tempfile
|
|
|
|
|
|
|
|
import mmcv
|
|
|
|
import numpy as np
|
|
|
|
from mmcv.transforms import LoadImageFromFile
|
|
|
|
|
2022-10-18 16:30:08 +08:00
|
|
|
from mmseg.datasets.transforms import (LoadAnnotations,
|
|
|
|
LoadBiomedicalAnnotation,
|
|
|
|
LoadBiomedicalData,
|
|
|
|
LoadBiomedicalImageFromFile,
|
|
|
|
LoadImageFromNDArray)
|
2022-05-26 11:06:02 +08:00
|
|
|
|
|
|
|
|
2022-09-19 14:06:29 +08:00
|
|
|
class TestLoading:
|
2022-05-26 11:06:02 +08:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def setup_class(cls):
|
|
|
|
cls.data_prefix = osp.join(osp.dirname(__file__), '../data')
|
|
|
|
|
|
|
|
def test_load_img(self):
|
|
|
|
results = dict(img_path=osp.join(self.data_prefix, 'color.jpg'))
|
|
|
|
transform = LoadImageFromFile()
|
|
|
|
results = transform(copy.deepcopy(results))
|
|
|
|
assert results['img_path'] == osp.join(self.data_prefix, 'color.jpg')
|
|
|
|
assert results['img'].shape == (288, 512, 3)
|
|
|
|
assert results['img'].dtype == np.uint8
|
|
|
|
assert results['ori_shape'] == results['img'].shape[:2]
|
|
|
|
assert repr(transform) == transform.__class__.__name__ + \
|
2022-06-27 22:36:18 +08:00
|
|
|
"(ignore_empty=False, to_float32=False, color_type='color'," + \
|
2023-02-01 17:53:22 +08:00
|
|
|
" imdecode_backend='cv2', backend_args=None)"
|
2022-05-26 11:06:02 +08:00
|
|
|
|
|
|
|
# to_float32
|
|
|
|
transform = LoadImageFromFile(to_float32=True)
|
|
|
|
results = transform(copy.deepcopy(results))
|
|
|
|
assert results['img'].dtype == np.float32
|
|
|
|
|
|
|
|
# gray image
|
|
|
|
results = dict(img_path=osp.join(self.data_prefix, 'gray.jpg'))
|
|
|
|
transform = LoadImageFromFile()
|
|
|
|
results = transform(copy.deepcopy(results))
|
|
|
|
assert results['img'].shape == (288, 512, 3)
|
|
|
|
assert results['img'].dtype == np.uint8
|
|
|
|
|
|
|
|
transform = LoadImageFromFile(color_type='unchanged')
|
|
|
|
results = transform(copy.deepcopy(results))
|
|
|
|
assert results['img'].shape == (288, 512)
|
|
|
|
assert results['img'].dtype == np.uint8
|
|
|
|
|
|
|
|
def test_load_seg(self):
|
|
|
|
seg_path = osp.join(self.data_prefix, 'seg.png')
|
2022-06-09 20:23:36 +08:00
|
|
|
results = dict(
|
|
|
|
seg_map_path=seg_path, reduce_zero_label=True, seg_fields=[])
|
2022-05-26 11:06:02 +08:00
|
|
|
transform = LoadAnnotations()
|
|
|
|
results = transform(copy.deepcopy(results))
|
|
|
|
assert results['gt_seg_map'].shape == (288, 512)
|
|
|
|
assert results['gt_seg_map'].dtype == np.uint8
|
2023-02-16 15:33:52 +08:00
|
|
|
assert repr(transform) == transform.__class__.__name__ + \
|
|
|
|
"(reduce_zero_label=True, imdecode_backend='pillow', " + \
|
|
|
|
'backend_args=None)'
|
2022-05-26 11:06:02 +08:00
|
|
|
|
|
|
|
# reduce_zero_label
|
|
|
|
transform = LoadAnnotations(reduce_zero_label=True)
|
|
|
|
results = transform(copy.deepcopy(results))
|
|
|
|
assert results['gt_seg_map'].shape == (288, 512)
|
|
|
|
assert results['gt_seg_map'].dtype == np.uint8
|
|
|
|
|
|
|
|
def test_load_seg_custom_classes(self):
|
|
|
|
|
|
|
|
test_img = np.random.rand(10, 10)
|
|
|
|
test_gt = np.zeros_like(test_img)
|
|
|
|
test_gt[2:4, 2:4] = 1
|
|
|
|
test_gt[2:4, 6:8] = 2
|
|
|
|
test_gt[6:8, 2:4] = 3
|
|
|
|
test_gt[6:8, 6:8] = 4
|
|
|
|
|
|
|
|
tmp_dir = tempfile.TemporaryDirectory()
|
|
|
|
img_path = osp.join(tmp_dir.name, 'img.jpg')
|
|
|
|
gt_path = osp.join(tmp_dir.name, 'gt.png')
|
|
|
|
|
|
|
|
mmcv.imwrite(test_img, img_path)
|
|
|
|
mmcv.imwrite(test_gt, gt_path)
|
|
|
|
|
|
|
|
# test only train with label with id 3
|
|
|
|
results = dict(
|
|
|
|
img_path=img_path,
|
|
|
|
seg_map_path=gt_path,
|
|
|
|
label_map={
|
|
|
|
0: 0,
|
|
|
|
1: 0,
|
|
|
|
2: 0,
|
|
|
|
3: 1,
|
|
|
|
4: 0
|
|
|
|
},
|
2022-06-09 20:23:36 +08:00
|
|
|
reduce_zero_label=False,
|
2022-05-26 11:06:02 +08:00
|
|
|
seg_fields=[])
|
|
|
|
|
|
|
|
load_imgs = LoadImageFromFile()
|
|
|
|
results = load_imgs(copy.deepcopy(results))
|
|
|
|
|
|
|
|
load_anns = LoadAnnotations()
|
|
|
|
results = load_anns(copy.deepcopy(results))
|
|
|
|
|
|
|
|
gt_array = results['gt_seg_map']
|
|
|
|
|
|
|
|
true_mask = np.zeros_like(gt_array)
|
|
|
|
true_mask[6:8, 2:4] = 1
|
|
|
|
|
|
|
|
assert results['seg_fields'] == ['gt_seg_map']
|
|
|
|
assert gt_array.shape == (10, 10)
|
|
|
|
assert gt_array.dtype == np.uint8
|
|
|
|
np.testing.assert_array_equal(gt_array, true_mask)
|
|
|
|
|
|
|
|
# test only train with label with id 4 and 3
|
|
|
|
results = dict(
|
|
|
|
img_path=osp.join(self.data_prefix, 'color.jpg'),
|
|
|
|
seg_map_path=gt_path,
|
|
|
|
label_map={
|
|
|
|
0: 0,
|
|
|
|
1: 0,
|
|
|
|
2: 0,
|
|
|
|
3: 2,
|
|
|
|
4: 1
|
|
|
|
},
|
2022-06-09 20:23:36 +08:00
|
|
|
reduce_zero_label=False,
|
2022-05-26 11:06:02 +08:00
|
|
|
seg_fields=[])
|
|
|
|
|
|
|
|
load_imgs = LoadImageFromFile()
|
|
|
|
results = load_imgs(copy.deepcopy(results))
|
|
|
|
|
|
|
|
load_anns = LoadAnnotations()
|
|
|
|
results = load_anns(copy.deepcopy(results))
|
|
|
|
|
|
|
|
gt_array = results['gt_seg_map']
|
|
|
|
|
|
|
|
true_mask = np.zeros_like(gt_array)
|
|
|
|
true_mask[6:8, 2:4] = 2
|
|
|
|
true_mask[6:8, 6:8] = 1
|
|
|
|
|
|
|
|
assert results['seg_fields'] == ['gt_seg_map']
|
|
|
|
assert gt_array.shape == (10, 10)
|
|
|
|
assert gt_array.dtype == np.uint8
|
|
|
|
np.testing.assert_array_equal(gt_array, true_mask)
|
|
|
|
|
[Fix] Switch order of `reduce_zero_label` and applying `label_map` in 1.x (#2517)
This is an almost exact duplicate of #2500 (that was made to the
`master` branch) now applied to the `1.x` branch.
---
## Motivation
I want to fix a bug through this PR. The bug occurs when two options --
`reduce_zero_label=True`, and custom classes are used.
`reduce_zero_label` remaps the GT seg labels by remapping the zero-class
to 255 which is ignored. Conceptually, this should occur *before* the
`label_map` is applied, which maps *already reduced labels*. However,
currently, the `label_map` is applied before the zero label is reduced.
## Modification
The modification is simple:
- I've just interchanged the order of the two operations by moving a few
lines from bottom to top.
- I've added a test that passes when the fix is introduced, and fails on
the original `master` branch.
## BC-breaking (Optional)
I do not anticipate this change braking any backward-compatibility.
## Checklist
- [x] Pre-commit or other linting tools are used to fix the potential
lint issues.
- _I've fixed all linting/pre-commit errors._
- [x] The modification is covered by complete unit tests. If not, please
add more unit test to ensure the correctness.
- _I've added a unit test._
- [x] If the modification has potential influence on downstream
projects, this PR should be tested with downstream projects, like MMDet
or MMDet3D.
- _I don't think this change affects MMDet or MMDet3D._
- [x] The documentation has been modified accordingly, like docstring or
example tutorials.
- _This change fixes an existing bug and doesn't require modifying any
documentation/docstring._
2023-01-30 12:17:15 +08:00
|
|
|
# test with removing a class and reducing zero label simultaneously
|
|
|
|
results = dict(
|
|
|
|
img_path=img_path,
|
|
|
|
seg_map_path=gt_path,
|
|
|
|
# since reduce_zero_label is True, there are only 4 real classes.
|
|
|
|
# if the full set of classes is ["A", "B", "C", "D"], the
|
|
|
|
# following label map simulates the dataset option
|
|
|
|
# classes=["A", "C", "D"] which removes class "B".
|
|
|
|
label_map={
|
|
|
|
0: 0,
|
|
|
|
1: 255, # simulate removing class 1
|
|
|
|
2: 1,
|
|
|
|
3: 2
|
|
|
|
},
|
|
|
|
reduce_zero_label=True, # reduce zero label
|
|
|
|
seg_fields=[])
|
|
|
|
|
|
|
|
load_imgs = LoadImageFromFile()
|
|
|
|
results = load_imgs(copy.deepcopy(results))
|
|
|
|
|
|
|
|
# reduce zero label
|
|
|
|
load_anns = LoadAnnotations()
|
|
|
|
results = load_anns(copy.deepcopy(results))
|
|
|
|
|
|
|
|
gt_array = results['gt_seg_map']
|
|
|
|
|
|
|
|
true_mask = np.ones_like(gt_array) * 255 # all zeros get mapped to 255
|
|
|
|
true_mask[2:4, 2:4] = 0 # 1s are reduced to class 0 mapped to class 0
|
|
|
|
true_mask[2:4, 6:8] = 255 # 2s are reduced to class 1 which is removed
|
|
|
|
true_mask[6:8, 2:4] = 1 # 3s are reduced to class 2 mapped to class 1
|
|
|
|
true_mask[6:8, 6:8] = 2 # 4s are reduced to class 3 mapped to class 2
|
|
|
|
|
|
|
|
assert results['seg_fields'] == ['gt_seg_map']
|
|
|
|
assert gt_array.shape == (10, 10)
|
|
|
|
assert gt_array.dtype == np.uint8
|
|
|
|
np.testing.assert_array_equal(gt_array, true_mask)
|
|
|
|
|
2022-05-26 11:06:02 +08:00
|
|
|
# test no custom classes
|
2022-06-09 20:23:36 +08:00
|
|
|
results = dict(
|
|
|
|
img_path=img_path,
|
|
|
|
seg_map_path=gt_path,
|
|
|
|
reduce_zero_label=False,
|
|
|
|
seg_fields=[])
|
2022-05-26 11:06:02 +08:00
|
|
|
|
|
|
|
load_imgs = LoadImageFromFile()
|
|
|
|
results = load_imgs(copy.deepcopy(results))
|
|
|
|
|
|
|
|
load_anns = LoadAnnotations()
|
|
|
|
results = load_anns(copy.deepcopy(results))
|
|
|
|
|
|
|
|
gt_array = results['gt_seg_map']
|
|
|
|
|
|
|
|
assert results['seg_fields'] == ['gt_seg_map']
|
|
|
|
assert gt_array.shape == (10, 10)
|
|
|
|
assert gt_array.dtype == np.uint8
|
|
|
|
np.testing.assert_array_equal(gt_array, test_gt)
|
|
|
|
|
|
|
|
tmp_dir.cleanup()
|
2022-07-22 19:40:00 +08:00
|
|
|
|
|
|
|
def test_load_image_from_ndarray(self):
|
|
|
|
results = {'img': np.zeros((256, 256, 3), dtype=np.uint8)}
|
|
|
|
transform = LoadImageFromNDArray()
|
|
|
|
results = transform(results)
|
|
|
|
|
|
|
|
assert results['img'].shape == (256, 256, 3)
|
|
|
|
assert results['img'].dtype == np.uint8
|
|
|
|
assert results['img_shape'] == (256, 256)
|
|
|
|
assert results['ori_shape'] == (256, 256)
|
|
|
|
|
|
|
|
# to_float32
|
|
|
|
transform = LoadImageFromNDArray(to_float32=True)
|
|
|
|
results = transform(copy.deepcopy(results))
|
|
|
|
assert results['img'].dtype == np.float32
|
|
|
|
|
|
|
|
# test repr
|
|
|
|
transform = LoadImageFromNDArray()
|
|
|
|
assert repr(transform) == ('LoadImageFromNDArray('
|
|
|
|
'ignore_empty=False, '
|
|
|
|
'to_float32=False, '
|
|
|
|
"color_type='color', "
|
|
|
|
"imdecode_backend='cv2', "
|
2023-02-01 17:53:22 +08:00
|
|
|
'backend_args=None)')
|
2022-10-18 16:30:08 +08:00
|
|
|
|
|
|
|
def test_load_biomedical_img(self):
|
|
|
|
results = dict(
|
|
|
|
img_path=osp.join(self.data_prefix, 'biomedical.nii.gz'))
|
|
|
|
transform = LoadBiomedicalImageFromFile()
|
|
|
|
results = transform(copy.deepcopy(results))
|
|
|
|
assert results['img_path'] == osp.join(self.data_prefix,
|
|
|
|
'biomedical.nii.gz')
|
|
|
|
assert len(results['img'].shape) == 4
|
|
|
|
assert results['img'].dtype == np.float32
|
|
|
|
assert results['ori_shape'] == results['img'].shape[1:]
|
|
|
|
assert repr(transform) == ('LoadBiomedicalImageFromFile('
|
|
|
|
"decode_backend='nifti', "
|
|
|
|
'to_xyz=False, '
|
|
|
|
'to_float32=True, '
|
2023-02-16 15:33:52 +08:00
|
|
|
'backend_args=None)')
|
2022-10-18 16:30:08 +08:00
|
|
|
|
|
|
|
def test_load_biomedical_annotation(self):
|
|
|
|
results = dict(
|
|
|
|
seg_map_path=osp.join(self.data_prefix, 'biomedical_ann.nii.gz'))
|
|
|
|
transform = LoadBiomedicalAnnotation()
|
|
|
|
results = transform(copy.deepcopy(results))
|
|
|
|
assert len(results['gt_seg_map'].shape) == 3
|
|
|
|
assert results['gt_seg_map'].dtype == np.float32
|
|
|
|
|
|
|
|
def test_load_biomedical_data(self):
|
|
|
|
input_results = dict(
|
|
|
|
img_path=osp.join(self.data_prefix, 'biomedical.npy'))
|
|
|
|
transform = LoadBiomedicalData(with_seg=True)
|
|
|
|
results = transform(copy.deepcopy(input_results))
|
|
|
|
assert results['img_path'] == osp.join(self.data_prefix,
|
|
|
|
'biomedical.npy')
|
|
|
|
assert results['img'][0].shape == results['gt_seg_map'].shape
|
|
|
|
assert results['img'].dtype == np.float32
|
|
|
|
assert results['ori_shape'] == results['img'].shape[1:]
|
|
|
|
assert repr(transform) == ('LoadBiomedicalData('
|
|
|
|
'with_seg=True, '
|
|
|
|
"decode_backend='numpy', "
|
|
|
|
'to_xyz=False, '
|
2023-02-16 15:33:52 +08:00
|
|
|
'backend_args=None)')
|
2022-10-18 16:30:08 +08:00
|
|
|
|
|
|
|
transform = LoadBiomedicalData(with_seg=False)
|
|
|
|
results = transform(copy.deepcopy(input_results))
|
|
|
|
assert len(results['img'].shape) == 4
|
|
|
|
assert results.get('gt_seg_map') is None
|
|
|
|
assert repr(transform) == ('LoadBiomedicalData('
|
|
|
|
'with_seg=False, '
|
|
|
|
"decode_backend='numpy', "
|
|
|
|
'to_xyz=False, '
|
2023-02-16 15:33:52 +08:00
|
|
|
'backend_args=None)')
|