"StyleTextRec/tools/__init__.py" did not exist on "cb7afb8588b56f918b66676f19dbbef9bcba6358"
Commit 85529f35 authored by unknown's avatar unknown
Browse files

添加openmmlab测试用例

parent b21b0c01
import copy
import os.path as osp
import numpy as np
from mmcls.datasets.pipelines import LoadImageFromFile
class TestLoading(object):
@classmethod
def setup_class(cls):
cls.data_prefix = osp.join(osp.dirname(__file__), '../data')
def test_load_img(self):
results = dict(
img_prefix=self.data_prefix, img_info=dict(filename='color.jpg'))
transform = LoadImageFromFile()
results = transform(copy.deepcopy(results))
assert results['filename'] == osp.join(self.data_prefix, 'color.jpg')
assert results['ori_filename'] == 'color.jpg'
assert results['img'].shape == (300, 400, 3)
assert results['img'].dtype == np.uint8
assert results['img_shape'] == (300, 400, 3)
assert results['ori_shape'] == (300, 400, 3)
np.testing.assert_equal(results['img_norm_cfg']['mean'],
np.zeros(3, dtype=np.float32))
assert repr(transform) == transform.__class__.__name__ + \
"(to_float32=False, color_type='color', " + \
"file_client_args={'backend': 'disk'})"
# no img_prefix
results = dict(
img_prefix=None, img_info=dict(filename='tests/data/color.jpg'))
transform = LoadImageFromFile()
results = transform(copy.deepcopy(results))
assert results['filename'] == 'tests/data/color.jpg'
assert results['img'].shape == (300, 400, 3)
# to_float32
transform = LoadImageFromFile(to_float32=True)
results = transform(copy.deepcopy(results))
assert results['img'].dtype == np.float32
# gray image
results = dict(
img_prefix=self.data_prefix, img_info=dict(filename='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
np.testing.assert_equal(results['img_norm_cfg']['mean'],
np.zeros(1, dtype=np.float32))
import copy
import os.path as osp
import random
import mmcv
import numpy as np
import pytest
import torch
import torchvision
from mmcv.utils import build_from_cfg
from numpy.testing import assert_array_almost_equal, assert_array_equal
from PIL import Image
from torchvision import transforms
import mmcls.datasets.pipelines.transforms as mmcls_transforms
from mmcls.datasets.builder import PIPELINES
from mmcls.datasets.pipelines import Compose
def construct_toy_data():
img = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]],
dtype=np.uint8)
img = np.stack([img, img, img], axis=-1)
results = dict()
# image
results['ori_img'] = img
results['img'] = copy.deepcopy(img)
results['ori_shape'] = img.shape
results['img_shape'] = img.shape
return results
def test_resize():
# test assertion if size is smaller than 0
with pytest.raises(AssertionError):
transform = dict(type='Resize', size=-1)
build_from_cfg(transform, PIPELINES)
# test assertion if size is tuple but the second value is smaller than 0
# and the second value is not equal to -1
with pytest.raises(AssertionError):
transform = dict(type='Resize', size=(224, -2))
build_from_cfg(transform, PIPELINES)
# test assertion if size is tuple but the first value is smaller than 0
with pytest.raises(AssertionError):
transform = dict(type='Resize', size=(-1, 224))
build_from_cfg(transform, PIPELINES)
# test assertion if size is tuple and len(size) < 2
with pytest.raises(AssertionError):
transform = dict(type='Resize', size=(224, ))
build_from_cfg(transform, PIPELINES)
# test assertion if size is tuple len(size) > 2
with pytest.raises(AssertionError):
transform = dict(type='Resize', size=(224, 224, 3))
build_from_cfg(transform, PIPELINES)
# test assertion when interpolation is invalid
with pytest.raises(AssertionError):
transform = dict(type='Resize', size=224, interpolation='2333')
build_from_cfg(transform, PIPELINES)
# test repr
transform = dict(type='Resize', size=224)
resize_module = build_from_cfg(transform, PIPELINES)
assert isinstance(repr(resize_module), str)
# read test image
results = dict()
img = mmcv.imread(
osp.join(osp.dirname(__file__), '../data/color.jpg'), 'color')
original_img = copy.deepcopy(img)
results['img'] = img
results['img2'] = copy.deepcopy(img)
results['img_shape'] = img.shape
results['ori_shape'] = img.shape
results['img_fields'] = ['img', 'img2']
def reset_results(results, original_img):
results['img'] = copy.deepcopy(original_img)
results['img2'] = copy.deepcopy(original_img)
results['img_shape'] = original_img.shape
results['ori_shape'] = original_img.shape
results['img_fields'] = ['img', 'img2']
return results
# test resize when size is int
transform = dict(type='Resize', size=224, interpolation='bilinear')
resize_module = build_from_cfg(transform, PIPELINES)
results = resize_module(results)
assert np.equal(results['img'], results['img2']).all()
assert results['img_shape'] == (224, 224, 3)
# test resize when size is tuple and the second value is -1
transform = dict(type='Resize', size=(224, -1), interpolation='bilinear')
resize_module = build_from_cfg(transform, PIPELINES)
results = reset_results(results, original_img)
results = resize_module(results)
assert np.equal(results['img'], results['img2']).all()
assert results['img_shape'] == (224, 298, 3)
# test resize when size is tuple
transform = dict(type='Resize', size=(224, 224), interpolation='bilinear')
resize_module = build_from_cfg(transform, PIPELINES)
results = reset_results(results, original_img)
results = resize_module(results)
assert np.equal(results['img'], results['img2']).all()
assert results['img_shape'] == (224, 224, 3)
# test resize when resize_height != resize_width
transform = dict(type='Resize', size=(224, 256), interpolation='bilinear')
resize_module = build_from_cfg(transform, PIPELINES)
results = reset_results(results, original_img)
results = resize_module(results)
assert np.equal(results['img'], results['img2']).all()
assert results['img_shape'] == (224, 256, 3)
# test resize when size is larger than img.shape
img_height, img_width, _ = original_img.shape
transform = dict(
type='Resize',
size=(img_height * 2, img_width * 2),
interpolation='bilinear')
resize_module = build_from_cfg(transform, PIPELINES)
results = reset_results(results, original_img)
results = resize_module(results)
assert np.equal(results['img'], results['img2']).all()
assert results['img_shape'] == (img_height * 2, img_width * 2, 3)
# test resize with different backends
transform_cv2 = dict(
type='Resize',
size=(224, 256),
interpolation='bilinear',
backend='cv2')
transform_pil = dict(
type='Resize',
size=(224, 256),
interpolation='bilinear',
backend='pillow')
resize_module_cv2 = build_from_cfg(transform_cv2, PIPELINES)
resize_module_pil = build_from_cfg(transform_pil, PIPELINES)
results = reset_results(results, original_img)
results['img_fields'] = ['img']
results_cv2 = resize_module_cv2(results)
results['img_fields'] = ['img2']
results_pil = resize_module_pil(results)
assert np.allclose(results_cv2['img'], results_pil['img2'], atol=45)
# compare results with torchvision
transform = dict(type='Resize', size=(224, 224), interpolation='area')
resize_module = build_from_cfg(transform, PIPELINES)
results = reset_results(results, original_img)
results = resize_module(results)
resize_module = transforms.Resize(
size=(224, 224), interpolation=Image.BILINEAR)
pil_img = Image.fromarray(original_img)
resized_img = resize_module(pil_img)
resized_img = np.array(resized_img)
assert np.equal(results['img'], results['img2']).all()
assert results['img_shape'] == (224, 224, 3)
assert np.allclose(results['img'], resized_img, atol=30)
def test_center_crop():
# test assertion if size is smaller than 0
with pytest.raises(AssertionError):
transform = dict(type='CenterCrop', crop_size=-1)
build_from_cfg(transform, PIPELINES)
# test assertion if size is tuple but one value is smaller than 0
with pytest.raises(AssertionError):
transform = dict(type='CenterCrop', crop_size=(224, -1))
build_from_cfg(transform, PIPELINES)
# test assertion if size is tuple and len(size) < 2
with pytest.raises(AssertionError):
transform = dict(type='CenterCrop', crop_size=(224, ))
build_from_cfg(transform, PIPELINES)
# test assertion if size is tuple len(size) > 2
with pytest.raises(AssertionError):
transform = dict(type='CenterCrop', crop_size=(224, 224, 3))
build_from_cfg(transform, PIPELINES)
# test assertion if efficientnet is True and crop_size is tuple
with pytest.raises(AssertionError):
transform = dict(
type='CenterCrop',
crop_size=(224, 224),
efficientnet_style=True,
)
build_from_cfg(transform, PIPELINES)
# test assertion if efficientnet is True and interpolation is invalid
with pytest.raises(AssertionError):
transform = dict(
type='CenterCrop',
crop_size=224,
efficientnet_style=True,
interpolation='2333')
build_from_cfg(transform, PIPELINES)
# test assertion if efficientnet is True and crop_padding is negative
with pytest.raises(AssertionError):
transform = dict(
type='CenterCrop',
crop_size=224,
efficientnet_style=True,
crop_padding=-1)
build_from_cfg(transform, PIPELINES)
# test repr
transform = dict(type='CenterCrop', crop_size=224)
center_crop_module = build_from_cfg(transform, PIPELINES)
assert isinstance(repr(center_crop_module), str)
# read test image
results = dict()
img = mmcv.imread(
osp.join(osp.dirname(__file__), '../data/color.jpg'), 'color')
original_img = copy.deepcopy(img)
results['img'] = img
results['img2'] = copy.deepcopy(img)
results['img_shape'] = img.shape
results['ori_shape'] = img.shape
results['img_fields'] = ['img', 'img2']
def reset_results(results, original_img):
results['img'] = copy.deepcopy(original_img)
results['img2'] = copy.deepcopy(original_img)
results['img_shape'] = original_img.shape
results['ori_shape'] = original_img.shape
return results
# test CenterCrop when size is int
transform = dict(type='CenterCrop', crop_size=224)
center_crop_module = build_from_cfg(transform, PIPELINES)
results = center_crop_module(results)
assert np.equal(results['img'], results['img2']).all()
assert results['img_shape'] == (224, 224, 3)
# test CenterCrop when size is int and efficientnet_style is True
# and crop_padding=0
transform = dict(
type='CenterCrop',
crop_size=224,
efficientnet_style=True,
crop_padding=0)
center_crop_module = build_from_cfg(transform, PIPELINES)
results = reset_results(results, original_img)
results = center_crop_module(results)
assert np.equal(results['img'], results['img2']).all()
assert results['img_shape'] == (224, 224, 3)
results_img = copy.deepcopy(results['img'])
short_edge = min(*results['ori_shape'][:2])
transform = dict(type='CenterCrop', crop_size=short_edge)
baseline_center_crop_module = build_from_cfg(transform, PIPELINES)
transform = dict(type='Resize', size=224)
baseline_resize_module = build_from_cfg(transform, PIPELINES)
results = reset_results(results, original_img)
results = baseline_center_crop_module(results)
results = baseline_resize_module(results)
assert np.equal(results['img'], results_img).all()
# test CenterCrop when size is tuple
transform = dict(type='CenterCrop', crop_size=(224, 224))
center_crop_module = build_from_cfg(transform, PIPELINES)
results = reset_results(results, original_img)
results = center_crop_module(results)
assert np.equal(results['img'], results['img2']).all()
assert results['img_shape'] == (224, 224, 3)
# test CenterCrop when crop_height != crop_width
transform = dict(type='CenterCrop', crop_size=(256, 224))
center_crop_module = build_from_cfg(transform, PIPELINES)
results = reset_results(results, original_img)
results = center_crop_module(results)
assert np.equal(results['img'], results['img2']).all()
assert results['img_shape'] == (256, 224, 3)
# test CenterCrop when crop_size is equal to img.shape
img_height, img_width, _ = original_img.shape
transform = dict(type='CenterCrop', crop_size=(img_height, img_width))
center_crop_module = build_from_cfg(transform, PIPELINES)
results = reset_results(results, original_img)
results = center_crop_module(results)
assert np.equal(results['img'], results['img2']).all()
assert results['img_shape'] == (img_height, img_width, 3)
# test CenterCrop when crop_size is larger than img.shape
transform = dict(
type='CenterCrop', crop_size=(img_height * 2, img_width * 2))
center_crop_module = build_from_cfg(transform, PIPELINES)
results = reset_results(results, original_img)
results = center_crop_module(results)
assert np.equal(results['img'], results['img2']).all()
assert results['img_shape'] == (img_height, img_width, 3)
# test CenterCrop when crop_width is smaller than img_width
transform = dict(type='CenterCrop', crop_size=(img_height, img_width / 2))
center_crop_module = build_from_cfg(transform, PIPELINES)
results = reset_results(results, original_img)
results = center_crop_module(results)
assert np.equal(results['img'], results['img2']).all()
assert results['img_shape'] == (img_height, img_width / 2, 3)
# test CenterCrop when crop_height is smaller than img_height
transform = dict(type='CenterCrop', crop_size=(img_height / 2, img_width))
center_crop_module = build_from_cfg(transform, PIPELINES)
results = reset_results(results, original_img)
results = center_crop_module(results)
assert np.equal(results['img'], results['img2']).all()
assert results['img_shape'] == (img_height / 2, img_width, 3)
# compare results with torchvision
transform = dict(type='CenterCrop', crop_size=224)
center_crop_module = build_from_cfg(transform, PIPELINES)
results = reset_results(results, original_img)
results = center_crop_module(results)
center_crop_module = transforms.CenterCrop(size=224)
pil_img = Image.fromarray(original_img)
cropped_img = center_crop_module(pil_img)
cropped_img = np.array(cropped_img)
assert np.equal(results['img'], results['img2']).all()
assert np.equal(results['img'], cropped_img).all()
def test_normalize():
img_norm_cfg = dict(
mean=[123.675, 116.28, 103.53],
std=[58.395, 57.12, 57.375],
to_rgb=True)
# test repr
transform = dict(type='Normalize', **img_norm_cfg)
normalize_module = build_from_cfg(transform, PIPELINES)
assert isinstance(repr(normalize_module), str)
# read data
results = dict()
img = mmcv.imread(
osp.join(osp.dirname(__file__), '../data/color.jpg'), 'color')
original_img = copy.deepcopy(img)
results['img'] = img
results['img2'] = copy.deepcopy(img)
results['img_shape'] = img.shape
results['ori_shape'] = img.shape
results['img_fields'] = ['img', 'img2']
norm_results = normalize_module(results)
assert np.equal(norm_results['img'], norm_results['img2']).all()
# compare results with manual computation
mean = np.array(img_norm_cfg['mean'])
std = np.array(img_norm_cfg['std'])
normalized_img = (original_img[..., ::-1] - mean) / std
assert np.allclose(norm_results['img'], normalized_img)
# compare results with torchvision
normalize_module = transforms.Normalize(mean=mean, std=std)
tensor_img = original_img[..., ::-1].copy()
tensor_img = torch.Tensor(tensor_img.transpose(2, 0, 1))
normalized_img = normalize_module(tensor_img)
normalized_img = np.array(normalized_img).transpose(1, 2, 0)
assert np.equal(norm_results['img'], normalized_img).all()
def test_randomcrop():
ori_img = mmcv.imread(
osp.join(osp.dirname(__file__), '../data/color.jpg'), 'color')
ori_img_pil = Image.open(
osp.join(osp.dirname(__file__), '../data/color.jpg'))
seed = random.randint(0, 100)
# test crop size is int
kwargs = dict(size=200, padding=0, pad_if_needed=True, fill=0)
random.seed(seed)
np.random.seed(seed)
aug = []
aug.extend([torchvision.transforms.RandomCrop(**kwargs)])
composed_transform = Compose(aug)
baseline = composed_transform(ori_img_pil)
kwargs = dict(size=200, padding=0, pad_if_needed=True, pad_val=0)
random.seed(seed)
np.random.seed(seed)
aug = []
aug.extend([mmcls_transforms.RandomCrop(**kwargs)])
composed_transform = Compose(aug)
# test __repr__()
print(composed_transform)
results = dict()
results['img'] = ori_img
img = composed_transform(results)['img']
assert np.array(img).shape == (200, 200, 3)
assert np.array(baseline).shape == (200, 200, 3)
nonzero = len((ori_img - np.array(ori_img_pil)[:, :, ::-1]).nonzero())
nonzero_transform = len((img - np.array(baseline)[:, :, ::-1]).nonzero())
assert nonzero == nonzero_transform
# test crop size < image size
kwargs = dict(size=(200, 300), padding=0, pad_if_needed=True, fill=0)
random.seed(seed)
np.random.seed(seed)
aug = []
aug.extend([torchvision.transforms.RandomCrop(**kwargs)])
composed_transform = Compose(aug)
baseline = composed_transform(ori_img_pil)
kwargs = dict(size=(200, 300), padding=0, pad_if_needed=True, pad_val=0)
random.seed(seed)
np.random.seed(seed)
aug = []
aug.extend([mmcls_transforms.RandomCrop(**kwargs)])
composed_transform = Compose(aug)
results = dict()
results['img'] = ori_img
img = composed_transform(results)['img']
assert np.array(img).shape == (200, 300, 3)
assert np.array(baseline).shape == (200, 300, 3)
nonzero = len((ori_img - np.array(ori_img_pil)[:, :, ::-1]).nonzero())
nonzero_transform = len((img - np.array(baseline)[:, :, ::-1]).nonzero())
assert nonzero == nonzero_transform
# test crop size > image size
kwargs = dict(size=(600, 700), padding=0, pad_if_needed=True, fill=0)
random.seed(seed)
np.random.seed(seed)
aug = []
aug.extend([torchvision.transforms.RandomCrop(**kwargs)])
composed_transform = Compose(aug)
baseline = composed_transform(ori_img_pil)
kwargs = dict(size=(600, 700), padding=0, pad_if_needed=True, pad_val=0)
random.seed(seed)
np.random.seed(seed)
aug = []
aug.extend([mmcls_transforms.RandomCrop(**kwargs)])
composed_transform = Compose(aug)
results = dict()
results['img'] = ori_img
img = composed_transform(results)['img']
assert np.array(img).shape == (600, 700, 3)
assert np.array(baseline).shape == (600, 700, 3)
nonzero = len((ori_img - np.array(ori_img_pil)[:, :, ::-1]).nonzero())
nonzero_transform = len((img - np.array(baseline)[:, :, ::-1]).nonzero())
assert nonzero == nonzero_transform
# test crop size == image size
kwargs = dict(
size=(ori_img.shape[0], ori_img.shape[1]),
padding=0,
pad_if_needed=True,
fill=0)
random.seed(seed)
np.random.seed(seed)
aug = []
aug.extend([torchvision.transforms.RandomCrop(**kwargs)])
composed_transform = Compose(aug)
baseline = composed_transform(ori_img_pil)
kwargs = dict(
size=(ori_img.shape[0], ori_img.shape[1]),
padding=0,
pad_if_needed=True,
pad_val=0)
random.seed(seed)
np.random.seed(seed)
aug = []
aug.extend([mmcls_transforms.RandomCrop(**kwargs)])
composed_transform = Compose(aug)
results = dict()
results['img'] = ori_img
img = composed_transform(results)['img']
assert np.array(img).shape == (img.shape[0], img.shape[1], 3)
assert np.array(baseline).shape == (img.shape[0], img.shape[1], 3)
nonzero = len((ori_img - np.array(ori_img_pil)[:, :, ::-1]).nonzero())
nonzero_transform = len((img - np.array(baseline)[:, :, ::-1]).nonzero())
assert nonzero == nonzero_transform
assert_array_equal(ori_img, img)
assert_array_equal(np.array(baseline), np.array(ori_img_pil))
# test different padding mode
for mode in ['constant', 'edge', 'reflect', 'symmetric']:
kwargs = dict(size=(500, 600), padding=0, pad_if_needed=True, fill=0)
kwargs['padding_mode'] = mode
random.seed(seed)
np.random.seed(seed)
aug = []
aug.extend([torchvision.transforms.RandomCrop(**kwargs)])
composed_transform = Compose(aug)
baseline = composed_transform(ori_img_pil)
kwargs = dict(
size=(500, 600), padding=0, pad_if_needed=True, pad_val=0)
random.seed(seed)
np.random.seed(seed)
aug = []
aug.extend([mmcls_transforms.RandomCrop(**kwargs)])
composed_transform = Compose(aug)
results = dict()
results['img'] = ori_img
img = composed_transform(results)['img']
assert np.array(img).shape == (500, 600, 3)
assert np.array(baseline).shape == (500, 600, 3)
nonzero = len((ori_img - np.array(ori_img_pil)[:, :, ::-1]).nonzero())
nonzero_transform = len(
(img - np.array(baseline)[:, :, ::-1]).nonzero())
assert nonzero == nonzero_transform
def test_randomresizedcrop():
ori_img = mmcv.imread(
osp.join(osp.dirname(__file__), '../data/color.jpg'), 'color')
ori_img_pil = Image.open(
osp.join(osp.dirname(__file__), '../data/color.jpg'))
seed = random.randint(0, 100)
# test when scale is not of kind (min, max)
with pytest.raises(ValueError):
kwargs = dict(
size=(200, 300), scale=(1.0, 0.08), ratio=(3. / 4., 4. / 3.))
aug = []
aug.extend([mmcls_transforms.RandomResizedCrop(**kwargs)])
composed_transform = Compose(aug)
results = dict()
results['img'] = ori_img
composed_transform(results)['img']
# test when ratio is not of kind (min, max)
with pytest.raises(ValueError):
kwargs = dict(
size=(200, 300), scale=(0.08, 1.0), ratio=(4. / 3., 3. / 4.))
aug = []
aug.extend([mmcls_transforms.RandomResizedCrop(**kwargs)])
composed_transform = Compose(aug)
results = dict()
results['img'] = ori_img
composed_transform(results)['img']
# test when efficientnet_style is True and crop_padding < 0
with pytest.raises(AssertionError):
kwargs = dict(size=200, efficientnet_style=True, crop_padding=-1)
aug = []
aug.extend([mmcls_transforms.RandomResizedCrop(**kwargs)])
composed_transform = Compose(aug)
results = dict()
results['img'] = ori_img
composed_transform(results)['img']
# test crop size is int
kwargs = dict(size=200, scale=(0.08, 1.0), ratio=(3. / 4., 4. / 3.))
random.seed(seed)
np.random.seed(seed)
aug = []
aug.extend([torchvision.transforms.RandomResizedCrop(**kwargs)])
composed_transform = Compose(aug)
baseline = composed_transform(ori_img_pil)
random.seed(seed)
np.random.seed(seed)
aug = []
aug.extend([mmcls_transforms.RandomResizedCrop(**kwargs)])
composed_transform = Compose(aug)
# test __repr__()
print(composed_transform)
results = dict()
results['img'] = ori_img
img = composed_transform(results)['img']
assert np.array(img).shape == (200, 200, 3)
assert np.array(baseline).shape == (200, 200, 3)
nonzero = len((ori_img - np.array(ori_img_pil)[:, :, ::-1]).nonzero())
nonzero_transform = len((img - np.array(baseline)[:, :, ::-1]).nonzero())
assert nonzero == nonzero_transform
# test crop size < image size
kwargs = dict(size=(200, 300), scale=(0.08, 1.0), ratio=(3. / 4., 4. / 3.))
random.seed(seed)
np.random.seed(seed)
aug = []
aug.extend([torchvision.transforms.RandomResizedCrop(**kwargs)])
composed_transform = Compose(aug)
baseline = composed_transform(ori_img_pil)
random.seed(seed)
np.random.seed(seed)
aug = []
aug.extend([mmcls_transforms.RandomResizedCrop(**kwargs)])
composed_transform = Compose(aug)
results = dict()
results['img'] = ori_img
img = composed_transform(results)['img']
assert np.array(img).shape == (200, 300, 3)
assert np.array(baseline).shape == (200, 300, 3)
nonzero = len((ori_img - np.array(ori_img_pil)[:, :, ::-1]).nonzero())
nonzero_transform = len((img - np.array(baseline)[:, :, ::-1]).nonzero())
assert nonzero == nonzero_transform
# test crop size < image size when efficientnet_style = True
kwargs = dict(
size=200,
scale=(0.08, 1.0),
ratio=(3. / 4., 4. / 3.),
efficientnet_style=True)
random.seed(seed)
np.random.seed(seed)
aug = []
aug.extend([mmcls_transforms.RandomResizedCrop(**kwargs)])
composed_transform = Compose(aug)
results = dict()
results['img'] = ori_img
img = composed_transform(results)['img']
assert img.shape == (200, 200, 3)
# test crop size > image size
kwargs = dict(size=(600, 700), scale=(0.08, 1.0), ratio=(3. / 4., 4. / 3.))
random.seed(seed)
np.random.seed(seed)
aug = []
aug.extend([torchvision.transforms.RandomResizedCrop(**kwargs)])
composed_transform = Compose(aug)
baseline = composed_transform(ori_img_pil)
random.seed(seed)
np.random.seed(seed)
aug = []
aug.extend([mmcls_transforms.RandomResizedCrop(**kwargs)])
composed_transform = Compose(aug)
results = dict()
results['img'] = ori_img
img = composed_transform(results)['img']
assert np.array(img).shape == (600, 700, 3)
assert np.array(baseline).shape == (600, 700, 3)
nonzero = len((ori_img - np.array(ori_img_pil)[:, :, ::-1]).nonzero())
nonzero_transform = len((img - np.array(baseline)[:, :, ::-1]).nonzero())
assert nonzero == nonzero_transform
# test crop size < image size when efficientnet_style = True
kwargs = dict(
size=600,
scale=(0.08, 1.0),
ratio=(3. / 4., 4. / 3.),
efficientnet_style=True)
random.seed(seed)
np.random.seed(seed)
aug = []
aug.extend([mmcls_transforms.RandomResizedCrop(**kwargs)])
composed_transform = Compose(aug)
results = dict()
results['img'] = ori_img
img = composed_transform(results)['img']
assert img.shape == (600, 600, 3)
# test cropping the whole image
kwargs = dict(
size=(ori_img.shape[0], ori_img.shape[1]),
scale=(1.0, 2.0),
ratio=(1.0, 2.0))
random.seed(seed)
np.random.seed(seed)
aug = []
aug.extend([torchvision.transforms.RandomResizedCrop(**kwargs)])
composed_transform = Compose(aug)
baseline = composed_transform(ori_img_pil)
random.seed(seed)
np.random.seed(seed)
aug = []
aug.extend([mmcls_transforms.RandomResizedCrop(**kwargs)])
composed_transform = Compose(aug)
results = dict()
results['img'] = ori_img
img = composed_transform(results)['img']
assert np.array(img).shape == (ori_img.shape[0], ori_img.shape[1], 3)
assert np.array(baseline).shape == (ori_img.shape[0], ori_img.shape[1], 3)
nonzero = len((ori_img - np.array(ori_img_pil)[:, :, ::-1]).nonzero())
nonzero_transform = len((img - np.array(baseline)[:, :, ::-1]).nonzero())
assert nonzero == nonzero_transform
# assert_array_equal(ori_img, img)
# assert_array_equal(np.array(ori_img_pil), np.array(baseline))
# test central crop when in_ratio < min(ratio)
kwargs = dict(
size=(ori_img.shape[0], ori_img.shape[1]),
scale=(1.0, 2.0),
ratio=(2., 3.))
random.seed(seed)
np.random.seed(seed)
aug = []
aug.extend([torchvision.transforms.RandomResizedCrop(**kwargs)])
composed_transform = Compose(aug)
baseline = composed_transform(ori_img_pil)
random.seed(seed)
np.random.seed(seed)
aug = []
aug.extend([mmcls_transforms.RandomResizedCrop(**kwargs)])
composed_transform = Compose(aug)
results = dict()
results['img'] = ori_img
img = composed_transform(results)['img']
assert np.array(img).shape == (ori_img.shape[0], ori_img.shape[1], 3)
assert np.array(baseline).shape == (ori_img.shape[0], ori_img.shape[1], 3)
nonzero = len((ori_img - np.array(ori_img_pil)[:, :, ::-1]).nonzero())
nonzero_transform = len((img - np.array(baseline)[:, :, ::-1]).nonzero())
assert nonzero == nonzero_transform
# test central crop when in_ratio > max(ratio)
kwargs = dict(
size=(ori_img.shape[0], ori_img.shape[1]),
scale=(1.0, 2.0),
ratio=(3. / 4., 1))
random.seed(seed)
np.random.seed(seed)
aug = []
aug.extend([torchvision.transforms.RandomResizedCrop(**kwargs)])
composed_transform = Compose(aug)
baseline = composed_transform(ori_img_pil)
random.seed(seed)
np.random.seed(seed)
aug = []
aug.extend([mmcls_transforms.RandomResizedCrop(**kwargs)])
composed_transform = Compose(aug)
results = dict()
results['img'] = ori_img
img = composed_transform(results)['img']
assert np.array(img).shape == (ori_img.shape[0], ori_img.shape[1], 3)
assert np.array(baseline).shape == (ori_img.shape[0], ori_img.shape[1], 3)
nonzero = len((ori_img - np.array(ori_img_pil)[:, :, ::-1]).nonzero())
nonzero_transform = len((img - np.array(baseline)[:, :, ::-1]).nonzero())
assert nonzero == nonzero_transform
# test central crop when max_attempts = 0 and efficientnet_style = True
kwargs = dict(
size=200,
scale=(0.08, 1.0),
ratio=(3. / 4., 4. / 3.),
efficientnet_style=True,
max_attempts=0,
crop_padding=32)
random.seed(seed)
np.random.seed(seed)
aug = []
aug.extend([mmcls_transforms.RandomResizedCrop(**kwargs)])
composed_transform = Compose(aug)
results = dict()
results['img'] = ori_img
img = composed_transform(results)['img']
kwargs = dict(crop_size=200, efficientnet_style=True, crop_padding=32)
resize_kwargs = dict(size=200)
random.seed(seed)
np.random.seed(seed)
aug = []
aug.extend([mmcls_transforms.CenterCrop(**kwargs)])
aug.extend([mmcls_transforms.Resize(**resize_kwargs)])
composed_transform = Compose(aug)
results = dict()
results['img'] = ori_img
baseline = composed_transform(results)['img']
assert img.shape == baseline.shape
assert np.equal(img, baseline).all()
# test central crop when max_attempts = 0 and efficientnet_style = True
kwargs = dict(
size=200,
scale=(0.08, 1.0),
ratio=(3. / 4., 4. / 3.),
efficientnet_style=True,
max_attempts=100,
min_covered=1)
random.seed(seed)
np.random.seed(seed)
aug = []
aug.extend([mmcls_transforms.RandomResizedCrop(**kwargs)])
composed_transform = Compose(aug)
results = dict()
results['img'] = ori_img
img = composed_transform(results)['img']
kwargs = dict(crop_size=200, efficientnet_style=True, crop_padding=32)
resize_kwargs = dict(size=200)
random.seed(seed)
np.random.seed(seed)
aug = []
aug.extend([mmcls_transforms.CenterCrop(**kwargs)])
aug.extend([mmcls_transforms.Resize(**resize_kwargs)])
composed_transform = Compose(aug)
results = dict()
results['img'] = ori_img
baseline = composed_transform(results)['img']
assert img.shape == baseline.shape
assert np.equal(img, baseline).all()
# test different interpolation types
for mode in ['nearest', 'bilinear', 'bicubic', 'area', 'lanczos']:
kwargs = dict(
size=(600, 700),
scale=(0.08, 1.0),
ratio=(3. / 4., 4. / 3.),
interpolation=mode)
aug = []
aug.extend([mmcls_transforms.RandomResizedCrop(**kwargs)])
composed_transform = Compose(aug)
results = dict()
results['img'] = ori_img
img = composed_transform(results)['img']
assert img.shape == (600, 700, 3)
def test_randomgrayscale():
# test rgb2gray, return the grayscale image with p>1
in_img = np.random.rand(10, 10, 3).astype(np.float32)
kwargs = dict(gray_prob=2)
aug = []
aug.extend([mmcls_transforms.RandomGrayscale(**kwargs)])
composed_transform = Compose(aug)
print(composed_transform)
results = dict()
results['img'] = in_img
img = composed_transform(results)['img']
computed_gray = (
in_img[:, :, 0] * 0.299 + in_img[:, :, 1] * 0.587 +
in_img[:, :, 2] * 0.114)
for i in range(img.shape[2]):
assert_array_almost_equal(img[:, :, i], computed_gray, decimal=4)
assert img.shape == (10, 10, 3)
# test rgb2gray, return the original image with p=-1
in_img = np.random.rand(10, 10, 3).astype(np.float32)
kwargs = dict(gray_prob=-1)
aug = []
aug.extend([mmcls_transforms.RandomGrayscale(**kwargs)])
composed_transform = Compose(aug)
results = dict()
results['img'] = in_img
img = composed_transform(results)['img']
assert_array_equal(img, in_img)
assert img.shape == (10, 10, 3)
# test image with one channel with our method
# and the function from torchvision
in_img = np.random.rand(10, 10, 1).astype(np.float32)
kwargs = dict(gray_prob=2)
aug = []
aug.extend([mmcls_transforms.RandomGrayscale(**kwargs)])
composed_transform = Compose(aug)
results = dict()
results['img'] = in_img
img = composed_transform(results)['img']
assert_array_equal(img, in_img)
assert img.shape == (10, 10, 1)
in_img_pil = Image.fromarray(in_img[:, :, 0], mode='L')
kwargs = dict(p=2)
aug = []
aug.extend([torchvision.transforms.RandomGrayscale(**kwargs)])
composed_transform = Compose(aug)
img_pil = composed_transform(in_img_pil)
assert_array_equal(np.array(img_pil), np.array(in_img_pil))
assert np.array(img_pil).shape == (10, 10)
def test_randomflip():
# test assertion if flip probability is smaller than 0
with pytest.raises(AssertionError):
transform = dict(type='RandomFlip', flip_prob=-1)
build_from_cfg(transform, PIPELINES)
# test assertion if flip probability is larger than 1
with pytest.raises(AssertionError):
transform = dict(type='RandomFlip', flip_prob=2)
build_from_cfg(transform, PIPELINES)
# test assertion if direction is not horizontal and vertical
with pytest.raises(AssertionError):
transform = dict(type='RandomFlip', direction='random')
build_from_cfg(transform, PIPELINES)
# test assertion if direction is not lowercase
with pytest.raises(AssertionError):
transform = dict(type='RandomFlip', direction='Horizontal')
build_from_cfg(transform, PIPELINES)
# read test image
results = dict()
img = mmcv.imread(
osp.join(osp.dirname(__file__), '../data/color.jpg'), 'color')
original_img = copy.deepcopy(img)
results['img'] = img
results['img2'] = copy.deepcopy(img)
results['img_shape'] = img.shape
results['ori_shape'] = img.shape
results['img_fields'] = ['img', 'img2']
def reset_results(results, original_img):
results['img'] = copy.deepcopy(original_img)
results['img2'] = copy.deepcopy(original_img)
results['img_shape'] = original_img.shape
results['ori_shape'] = original_img.shape
return results
# test RandomFlip when flip_prob is 0
transform = dict(type='RandomFlip', flip_prob=0)
flip_module = build_from_cfg(transform, PIPELINES)
results = flip_module(results)
assert np.equal(results['img'], original_img).all()
assert np.equal(results['img'], results['img2']).all()
# test RandomFlip when flip_prob is 1
transform = dict(type='RandomFlip', flip_prob=1)
flip_module = build_from_cfg(transform, PIPELINES)
results = flip_module(results)
assert np.equal(results['img'], results['img2']).all()
# compare hotizontal flip with torchvision
transform = dict(type='RandomFlip', flip_prob=1, direction='horizontal')
flip_module = build_from_cfg(transform, PIPELINES)
results = reset_results(results, original_img)
results = flip_module(results)
flip_module = transforms.RandomHorizontalFlip(p=1)
pil_img = Image.fromarray(original_img)
flipped_img = flip_module(pil_img)
flipped_img = np.array(flipped_img)
assert np.equal(results['img'], results['img2']).all()
assert np.equal(results['img'], flipped_img).all()
# compare vertical flip with torchvision
transform = dict(type='RandomFlip', flip_prob=1, direction='vertical')
flip_module = build_from_cfg(transform, PIPELINES)
results = reset_results(results, original_img)
results = flip_module(results)
flip_module = transforms.RandomVerticalFlip(p=1)
pil_img = Image.fromarray(original_img)
flipped_img = flip_module(pil_img)
flipped_img = np.array(flipped_img)
assert np.equal(results['img'], results['img2']).all()
assert np.equal(results['img'], flipped_img).all()
def test_random_erasing():
# test erase_prob assertion
with pytest.raises(AssertionError):
cfg = dict(type='RandomErasing', erase_prob=-1.)
build_from_cfg(cfg, PIPELINES)
with pytest.raises(AssertionError):
cfg = dict(type='RandomErasing', erase_prob=1)
build_from_cfg(cfg, PIPELINES)
# test area_ratio assertion
with pytest.raises(AssertionError):
cfg = dict(type='RandomErasing', min_area_ratio=-1.)
build_from_cfg(cfg, PIPELINES)
with pytest.raises(AssertionError):
cfg = dict(type='RandomErasing', max_area_ratio=1)
build_from_cfg(cfg, PIPELINES)
with pytest.raises(AssertionError):
# min_area_ratio should be smaller than max_area_ratio
cfg = dict(
type='RandomErasing', min_area_ratio=0.6, max_area_ratio=0.4)
build_from_cfg(cfg, PIPELINES)
# test aspect_range assertion
with pytest.raises(AssertionError):
cfg = dict(type='RandomErasing', aspect_range='str')
build_from_cfg(cfg, PIPELINES)
with pytest.raises(AssertionError):
cfg = dict(type='RandomErasing', aspect_range=-1)
build_from_cfg(cfg, PIPELINES)
with pytest.raises(AssertionError):
# In aspect_range (min, max), min should be smaller than max.
cfg = dict(type='RandomErasing', aspect_range=[1.6, 0.6])
build_from_cfg(cfg, PIPELINES)
# test mode assertion
with pytest.raises(AssertionError):
cfg = dict(type='RandomErasing', mode='unknown')
build_from_cfg(cfg, PIPELINES)
# test fill_std assertion
with pytest.raises(AssertionError):
cfg = dict(type='RandomErasing', fill_std='unknown')
build_from_cfg(cfg, PIPELINES)
# test implicit conversion of aspect_range
cfg = dict(type='RandomErasing', aspect_range=0.5)
random_erasing = build_from_cfg(cfg, PIPELINES)
assert random_erasing.aspect_range == (0.5, 2.)
cfg = dict(type='RandomErasing', aspect_range=2.)
random_erasing = build_from_cfg(cfg, PIPELINES)
assert random_erasing.aspect_range == (0.5, 2.)
# test implicit conversion of fill_color
cfg = dict(type='RandomErasing', fill_color=15)
random_erasing = build_from_cfg(cfg, PIPELINES)
assert random_erasing.fill_color == [15, 15, 15]
# test implicit conversion of fill_std
cfg = dict(type='RandomErasing', fill_std=0.5)
random_erasing = build_from_cfg(cfg, PIPELINES)
assert random_erasing.fill_std == [0.5, 0.5, 0.5]
# test when erase_prob=0.
results = construct_toy_data()
cfg = dict(
type='RandomErasing',
erase_prob=0.,
mode='const',
fill_color=(255, 255, 255))
random_erasing = build_from_cfg(cfg, PIPELINES)
results = random_erasing(results)
np.testing.assert_array_equal(results['img'], results['ori_img'])
# test mode 'const'
random.seed(0)
np.random.seed(0)
results = construct_toy_data()
cfg = dict(
type='RandomErasing',
erase_prob=1.,
mode='const',
fill_color=(255, 255, 255))
random_erasing = build_from_cfg(cfg, PIPELINES)
results = random_erasing(results)
expect_out = np.array([[1, 255, 3, 4], [5, 255, 7, 8], [9, 10, 11, 12]],
dtype=np.uint8)
expect_out = np.stack([expect_out] * 3, axis=-1)
np.testing.assert_array_equal(results['img'], expect_out)
# test mode 'rand' with normal distribution
random.seed(0)
np.random.seed(0)
results = construct_toy_data()
cfg = dict(type='RandomErasing', erase_prob=1., mode='rand')
random_erasing = build_from_cfg(cfg, PIPELINES)
results = random_erasing(results)
expect_out = results['ori_img']
expect_out[:2, 1] = [[159, 98, 76], [14, 69, 122]]
np.testing.assert_array_equal(results['img'], expect_out)
# test mode 'rand' with uniform distribution
random.seed(0)
np.random.seed(0)
results = construct_toy_data()
cfg = dict(
type='RandomErasing',
erase_prob=1.,
mode='rand',
fill_std=(10, 255, 0))
random_erasing = build_from_cfg(cfg, PIPELINES)
results = random_erasing(results)
expect_out = results['ori_img']
expect_out[:2, 1] = [[113, 255, 128], [126, 83, 128]]
np.testing.assert_array_equal(results['img'], expect_out)
def test_color_jitter():
# read test image
results = dict()
img = mmcv.imread(
osp.join(osp.dirname(__file__), '../data/color.jpg'), 'color')
original_img = copy.deepcopy(img)
results['img'] = img
results['img2'] = copy.deepcopy(img)
results['img_shape'] = img.shape
results['ori_shape'] = img.shape
results['img_fields'] = ['img', 'img2']
def reset_results(results, original_img):
results['img'] = copy.deepcopy(original_img)
results['img2'] = copy.deepcopy(original_img)
results['img_shape'] = original_img.shape
results['ori_shape'] = original_img.shape
return results
transform = dict(
type='ColorJitter', brightness=0., contrast=0., saturation=0.)
colorjitter_module = build_from_cfg(transform, PIPELINES)
results = colorjitter_module(results)
assert np.equal(results['img'], original_img).all()
assert np.equal(results['img'], results['img2']).all()
results = reset_results(results, original_img)
transform = dict(
type='ColorJitter', brightness=0.3, contrast=0.3, saturation=0.3)
colorjitter_module = build_from_cfg(transform, PIPELINES)
results = colorjitter_module(results)
assert not np.equal(results['img'], original_img).all()
def test_lighting():
# test assertion if eigval or eigvec is wrong type or length
with pytest.raises(AssertionError):
transform = dict(type='Lighting', eigval=1, eigvec=[[1, 0, 0]])
build_from_cfg(transform, PIPELINES)
with pytest.raises(AssertionError):
transform = dict(type='Lighting', eigval=[1], eigvec=[1, 0, 0])
build_from_cfg(transform, PIPELINES)
with pytest.raises(AssertionError):
transform = dict(
type='Lighting', eigval=[1, 2], eigvec=[[1, 0, 0], [0, 1]])
build_from_cfg(transform, PIPELINES)
# read test image
results = dict()
img = mmcv.imread(
osp.join(osp.dirname(__file__), '../data/color.jpg'), 'color')
original_img = copy.deepcopy(img)
results['img'] = img
results['img2'] = copy.deepcopy(img)
results['img_shape'] = img.shape
results['ori_shape'] = img.shape
results['img_fields'] = ['img', 'img2']
def reset_results(results, original_img):
results['img'] = copy.deepcopy(original_img)
results['img2'] = copy.deepcopy(original_img)
results['img_shape'] = original_img.shape
results['ori_shape'] = original_img.shape
return results
eigval = [0.2175, 0.0188, 0.0045]
eigvec = [[-0.5675, 0.7192, 0.4009], [-0.5808, -0.0045, -0.8140],
[-0.5836, -0.6948, 0.4203]]
transform = dict(type='Lighting', eigval=eigval, eigvec=eigvec)
lightening_module = build_from_cfg(transform, PIPELINES)
results = lightening_module(results)
assert not np.equal(results['img'], results['img2']).all()
assert results['img'].dtype == float
assert results['img2'].dtype == float
results = reset_results(results, original_img)
transform = dict(
type='Lighting',
eigval=eigval,
eigvec=eigvec,
alphastd=0.,
to_rgb=False)
lightening_module = build_from_cfg(transform, PIPELINES)
results = lightening_module(results)
assert np.equal(results['img'], original_img).all()
assert np.equal(results['img'], results['img2']).all()
assert results['img'].dtype == float
assert results['img2'].dtype == float
def test_albu_transform():
results = dict(
img_prefix=osp.join(osp.dirname(__file__), '../data'),
img_info=dict(filename='color.jpg'))
# Define simple pipeline
load = dict(type='LoadImageFromFile')
load = build_from_cfg(load, PIPELINES)
albu_transform = dict(
type='Albu', transforms=[dict(type='ChannelShuffle', p=1)])
albu_transform = build_from_cfg(albu_transform, PIPELINES)
normalize = dict(type='Normalize', mean=[0] * 3, std=[0] * 3, to_rgb=True)
normalize = build_from_cfg(normalize, PIPELINES)
# Execute transforms
results = load(results)
results = albu_transform(results)
results = normalize(results)
assert results['img'].dtype == np.float32
import argparse
import os.path as osp
import mmcv
from mmcv import DictAction
from mmcls.datasets import build_dataset
from mmcls.models import build_classifier
def parse_args():
parser = argparse.ArgumentParser(
description='MMCls evaluate prediction success/fail')
parser.add_argument('config', help='test config file path')
parser.add_argument('result', help='test result json/pkl file')
parser.add_argument('--out-dir', help='dir to store output files')
parser.add_argument(
'--topk',
default=20,
type=int,
help='Number of images to select for success/fail')
parser.add_argument(
'--options',
nargs='+',
action=DictAction,
help='override some settings in the used config, the key-value pair '
'in xxx=yyy format will be merged into config file.')
args = parser.parse_args()
return args
def save_imgs(result_dir, folder_name, results, model):
full_dir = osp.join(result_dir, folder_name)
mmcv.mkdir_or_exist(full_dir)
mmcv.dump(results, osp.join(full_dir, folder_name + '.json'))
# save imgs
show_keys = ['pred_score', 'pred_class', 'gt_class']
for result in results:
result_show = dict((k, v) for k, v in result.items() if k in show_keys)
outfile = osp.join(full_dir, osp.basename(result['filename']))
model.show_result(result['filename'], result_show, out_file=outfile)
def main():
args = parse_args()
cfg = mmcv.Config.fromfile(args.config)
if args.options is not None:
cfg.merge_from_dict(args.options)
model = build_classifier(cfg.model)
# build the dataloader
dataset = build_dataset(cfg.data.test)
filenames = list()
for info in dataset.data_infos:
if info['img_prefix'] is not None:
filename = osp.join(info['img_prefix'],
info['img_info']['filename'])
else:
filename = info['img_info']['filename']
filenames.append(filename)
gt_labels = list(dataset.get_gt_labels())
gt_classes = [dataset.CLASSES[x] for x in gt_labels]
# load test results
outputs = mmcv.load(args.result)
outputs['filename'] = filenames
outputs['gt_label'] = gt_labels
outputs['gt_class'] = gt_classes
outputs_list = list()
for i in range(len(gt_labels)):
output = dict()
for k in outputs.keys():
output[k] = outputs[k][i]
outputs_list.append(output)
# sort result
outputs_list = sorted(outputs_list, key=lambda x: x['pred_score'])
success = list()
fail = list()
for output in outputs_list:
if output['pred_label'] == output['gt_label']:
success.append(output)
else:
fail.append(output)
success = success[:args.topk]
fail = fail[:args.topk]
save_imgs(args.out_dir, 'success', success, model)
save_imgs(args.out_dir, 'fail', fail, model)
if __name__ == '__main__':
main()
import argparse
import copy
import os
import os.path as osp
import time
import mmcv
import torch
from mmcv import Config, DictAction
from mmcv.runner import get_dist_info, init_dist
from mmcls import __version__
from mmcls.apis import set_random_seed, train_model
from mmcls.datasets import build_dataset
from mmcls.models import build_classifier
from mmcls.utils import collect_env, get_root_logger
def parse_args():
parser = argparse.ArgumentParser(
description='Benchmark Regression on mulit models')
parser.add_argument('--configs', nargs='+', help='train config files path')
parser.add_argument('--work-dir', help='the dir to save logs and models')
parser.add_argument(
'--epochs',
type=int,
default=0,
help='how many epochs to train, if 0, use config setting.')
parser.add_argument(
'--no-validate',
action='store_true',
help='whether not to evaluate the checkpoint during training')
group_gpus = parser.add_mutually_exclusive_group()
group_gpus.add_argument('--device', help='device used for training')
group_gpus.add_argument(
'--gpus',
type=int,
help='number of gpus to use '
'(only applicable to non-distributed training)')
group_gpus.add_argument(
'--gpu-ids',
type=int,
nargs='+',
help='ids of gpus to use '
'(only applicable to non-distributed training)')
parser.add_argument('--seed', type=int, default=None, help='random seed')
parser.add_argument(
'--deterministic',
action='store_true',
help='whether to set deterministic options for CUDNN backend.')
parser.add_argument(
'--options', nargs='+', action=DictAction, help='arguments in dict')
parser.add_argument(
'--launcher',
choices=['none', 'pytorch', 'slurm', 'mpi'],
default='none',
help='job launcher')
parser.add_argument('--local_rank', type=int, default=0)
args = parser.parse_args()
if 'LOCAL_RANK' not in os.environ:
os.environ['LOCAL_RANK'] = str(args.local_rank)
return args
def main():
args = parse_args()
dist_inited = False
for config in args.configs:
cfg = Config.fromfile(config)
if args.options is not None:
cfg.merge_from_dict(args.options)
# set cudnn_benchmark
if cfg.get('cudnn_benchmark', False):
torch.backends.cudnn.benchmark = True
if args.work_dir is not None:
# update configs according to CLI args if args.work_dir is not None
work_dir_root = args.work_dir
else:
# use config filename as default work_dir if cfg.work_dir is None
work_dir_root = './work_dirs/benchmark_regression'
cfg.work_dir = osp.join(work_dir_root,
osp.splitext(osp.basename(config))[0])
if args.gpu_ids is not None:
cfg.gpu_ids = args.gpu_ids
else:
cfg.gpu_ids = range(1) if args.gpus is None else range(args.gpus)
if args.epochs > 0:
cfg.runner.max_epochs = args.epochs
# init distributed env first, since logger depends on the dist info.
if args.launcher == 'none':
distributed = False
elif not dist_inited:
distributed = True
init_dist(args.launcher, **cfg.dist_params)
_, world_size = get_dist_info()
cfg.gpu_ids = range(world_size)
dist_inited = True
# create work_dir
mmcv.mkdir_or_exist(osp.abspath(cfg.work_dir))
# dump config
cfg.dump(osp.join(cfg.work_dir, osp.basename(config)))
# init the logger before other steps
timestamp = time.strftime('%Y%m%d_%H%M%S', time.localtime())
log_file = osp.join(cfg.work_dir, f'{timestamp}.log')
logger = get_root_logger(log_file=log_file, log_level=cfg.log_level)
# init the meta dict to record some important information such as
# environment info and seed, which will be logged
meta = dict()
# log env info
env_info_dict = collect_env()
env_info = '\n'.join([(f'{k}: {v}') for k, v in env_info_dict.items()])
dash_line = '-' * 60 + '\n'
logger.info('Environment info:\n' + dash_line + env_info + '\n' +
dash_line)
meta['env_info'] = env_info
# log some basic info
logger.info(f'Distributed training: {distributed}')
logger.info(f'Config:\n{cfg.pretty_text}')
# set random seeds
if args.seed is not None:
logger.info(f'Set random seed to {args.seed}, '
f'deterministic: {args.deterministic}')
set_random_seed(args.seed, deterministic=args.deterministic)
cfg.seed = args.seed
meta['seed'] = args.seed
model = build_classifier(cfg.model)
model.init_weights()
datasets = [build_dataset(cfg.data.train)]
if len(cfg.workflow) == 2:
val_dataset = copy.deepcopy(cfg.data.val)
val_dataset.pipeline = cfg.data.train.pipeline
datasets.append(build_dataset(val_dataset))
if cfg.checkpoint_config is not None:
# save mmcls version, config file content and class names in
# checkpoints as meta data
cfg.checkpoint_config.meta = dict(
mmcls_version=__version__,
config=cfg.pretty_text,
CLASSES=datasets[0].CLASSES)
# add an attribute for visualization convenience
train_model(
model,
datasets,
cfg,
distributed=distributed,
validate=(not args.no_validate),
timestamp=timestamp,
device='cpu' if args.device == 'cpu' else 'cuda',
meta=meta)
if __name__ == '__main__':
main()
import argparse
from collections import OrderedDict
import torch
def convert_conv1(model_key, model_weight, state_dict, converted_names):
if model_key.find('features.0.0') >= 0:
new_key = model_key.replace('features.0.0', 'backbone.conv1.conv')
else:
new_key = model_key.replace('features.0.1', 'backbone.conv1.bn')
state_dict[new_key] = model_weight
converted_names.add(model_key)
print(f'Convert {model_key} to {new_key}')
def convert_conv5(model_key, model_weight, state_dict, converted_names):
if model_key.find('features.18.0') >= 0:
new_key = model_key.replace('features.18.0', 'backbone.conv2.conv')
else:
new_key = model_key.replace('features.18.1', 'backbone.conv2.bn')
state_dict[new_key] = model_weight
converted_names.add(model_key)
print(f'Convert {model_key} to {new_key}')
def convert_head(model_key, model_weight, state_dict, converted_names):
new_key = model_key.replace('classifier.1', 'head.fc')
state_dict[new_key] = model_weight
converted_names.add(model_key)
print(f'Convert {model_key} to {new_key}')
def convert_block(model_key, model_weight, state_dict, converted_names):
split_keys = model_key.split('.')
layer_id = int(split_keys[1])
new_layer_id = 0
sub_id = 0
if layer_id == 1:
new_layer_id = 1
sub_id = 0
elif layer_id in range(2, 4):
new_layer_id = 2
sub_id = layer_id - 2
elif layer_id in range(4, 7):
new_layer_id = 3
sub_id = layer_id - 4
elif layer_id in range(7, 11):
new_layer_id = 4
sub_id = layer_id - 7
elif layer_id in range(11, 14):
new_layer_id = 5
sub_id = layer_id - 11
elif layer_id in range(14, 17):
new_layer_id = 6
sub_id = layer_id - 14
elif layer_id == 17:
new_layer_id = 7
sub_id = 0
new_key = model_key.replace(f'features.{layer_id}',
f'backbone.layer{new_layer_id}.{sub_id}')
if new_layer_id == 1:
if new_key.find('conv.0.0') >= 0:
new_key = new_key.replace('conv.0.0', 'conv.0.conv')
elif new_key.find('conv.0.1') >= 0:
new_key = new_key.replace('conv.0.1', 'conv.0.bn')
elif new_key.find('conv.1') >= 0:
new_key = new_key.replace('conv.1', 'conv.1.conv')
elif new_key.find('conv.2') >= 0:
new_key = new_key.replace('conv.2', 'conv.1.bn')
else:
raise ValueError(f'Unsupported conversion of key {model_key}')
else:
if new_key.find('conv.0.0') >= 0:
new_key = new_key.replace('conv.0.0', 'conv.0.conv')
elif new_key.find('conv.0.1') >= 0:
new_key = new_key.replace('conv.0.1', 'conv.0.bn')
elif new_key.find('conv.1.0') >= 0:
new_key = new_key.replace('conv.1.0', 'conv.1.conv')
elif new_key.find('conv.1.1') >= 0:
new_key = new_key.replace('conv.1.1', 'conv.1.bn')
elif new_key.find('conv.2') >= 0:
new_key = new_key.replace('conv.2', 'conv.2.conv')
elif new_key.find('conv.3') >= 0:
new_key = new_key.replace('conv.3', 'conv.2.bn')
else:
raise ValueError(f'Unsupported conversion of key {model_key}')
print(f'Convert {model_key} to {new_key}')
state_dict[new_key] = model_weight
converted_names.add(model_key)
def convert(src, dst):
"""Convert keys in torchvision pretrained MobileNetV2 models to mmcls
style."""
# load pytorch model
blobs = torch.load(src, map_location='cpu')
# convert to pytorch style
state_dict = OrderedDict()
converted_names = set()
for key, weight in blobs.items():
if 'features.0' in key:
convert_conv1(key, weight, state_dict, converted_names)
elif 'classifier' in key:
convert_head(key, weight, state_dict, converted_names)
elif 'features.18' in key:
convert_conv5(key, weight, state_dict, converted_names)
else:
convert_block(key, weight, state_dict, converted_names)
# check if all layers are converted
for key in blobs:
if key not in converted_names:
print(f'not converted: {key}')
# save checkpoint
checkpoint = dict()
checkpoint['state_dict'] = state_dict
torch.save(checkpoint, dst)
def main():
parser = argparse.ArgumentParser(description='Convert model keys')
parser.add_argument('src', help='src detectron model path')
parser.add_argument('dst', help='save path')
args = parser.parse_args()
convert(args.src, args.dst)
if __name__ == '__main__':
main()
import argparse
from collections import OrderedDict
import torch
def convert_conv1(model_key, model_weight, state_dict, converted_names):
if model_key.find('conv1.0') >= 0:
new_key = model_key.replace('conv1.0', 'backbone.conv1.conv')
else:
new_key = model_key.replace('conv1.1', 'backbone.conv1.bn')
state_dict[new_key] = model_weight
converted_names.add(model_key)
print(f'Convert {model_key} to {new_key}')
def convert_conv5(model_key, model_weight, state_dict, converted_names):
if model_key.find('conv5.0') >= 0:
new_key = model_key.replace('conv5.0', 'backbone.layers.3.conv')
else:
new_key = model_key.replace('conv5.1', 'backbone.layers.3.bn')
state_dict[new_key] = model_weight
converted_names.add(model_key)
print(f'Convert {model_key} to {new_key}')
def convert_head(model_key, model_weight, state_dict, converted_names):
new_key = model_key.replace('fc', 'head.fc')
state_dict[new_key] = model_weight
converted_names.add(model_key)
print(f'Convert {model_key} to {new_key}')
def convert_block(model_key, model_weight, state_dict, converted_names):
split_keys = model_key.split('.')
layer, block, branch = split_keys[:3]
layer_id = int(layer[-1]) - 2
new_key = model_key.replace(layer, f'backbone.layers.{layer_id}')
if branch == 'branch1':
if new_key.find('branch1.0') >= 0:
new_key = new_key.replace('branch1.0', 'branch1.0.conv')
elif new_key.find('branch1.1') >= 0:
new_key = new_key.replace('branch1.1', 'branch1.0.bn')
elif new_key.find('branch1.2') >= 0:
new_key = new_key.replace('branch1.2', 'branch1.1.conv')
elif new_key.find('branch1.3') >= 0:
new_key = new_key.replace('branch1.3', 'branch1.1.bn')
elif branch == 'branch2':
if new_key.find('branch2.0') >= 0:
new_key = new_key.replace('branch2.0', 'branch2.0.conv')
elif new_key.find('branch2.1') >= 0:
new_key = new_key.replace('branch2.1', 'branch2.0.bn')
elif new_key.find('branch2.3') >= 0:
new_key = new_key.replace('branch2.3', 'branch2.1.conv')
elif new_key.find('branch2.4') >= 0:
new_key = new_key.replace('branch2.4', 'branch2.1.bn')
elif new_key.find('branch2.5') >= 0:
new_key = new_key.replace('branch2.5', 'branch2.2.conv')
elif new_key.find('branch2.6') >= 0:
new_key = new_key.replace('branch2.6', 'branch2.2.bn')
else:
raise ValueError(f'Unsupported conversion of key {model_key}')
else:
raise ValueError(f'Unsupported conversion of key {model_key}')
print(f'Convert {model_key} to {new_key}')
state_dict[new_key] = model_weight
converted_names.add(model_key)
def convert(src, dst):
"""Convert keys in torchvision pretrained ShuffleNetV2 models to mmcls
style."""
# load pytorch model
blobs = torch.load(src, map_location='cpu')
# convert to pytorch style
state_dict = OrderedDict()
converted_names = set()
for key, weight in blobs.items():
if 'conv1' in key:
convert_conv1(key, weight, state_dict, converted_names)
elif 'fc' in key:
convert_head(key, weight, state_dict, converted_names)
elif key.startswith('s'):
convert_block(key, weight, state_dict, converted_names)
elif 'conv5' in key:
convert_conv5(key, weight, state_dict, converted_names)
# check if all layers are converted
for key in blobs:
if key not in converted_names:
print(f'not converted: {key}')
# save checkpoint
checkpoint = dict()
checkpoint['state_dict'] = state_dict
torch.save(checkpoint, dst)
def main():
parser = argparse.ArgumentParser(description='Convert model keys')
parser.add_argument('src', help='src detectron model path')
parser.add_argument('dst', help='save path')
args = parser.parse_args()
convert(args.src, args.dst)
if __name__ == '__main__':
main()
import argparse
import os
from collections import OrderedDict
import torch
def get_layer_maps(layer_num, with_bn):
layer_maps = {'conv': {}, 'bn': {}}
if with_bn:
if layer_num == 11:
layer_idxs = [0, 4, 8, 11, 15, 18, 22, 25]
elif layer_num == 13:
layer_idxs = [0, 3, 7, 10, 14, 17, 21, 24, 28, 31]
elif layer_num == 16:
layer_idxs = [0, 3, 7, 10, 14, 17, 20, 24, 27, 30, 34, 37, 40]
elif layer_num == 19:
layer_idxs = [
0, 3, 7, 10, 14, 17, 20, 23, 27, 30, 33, 36, 40, 43, 46, 49
]
else:
raise ValueError(f'Invalid number of layers: {layer_num}')
for i, layer_idx in enumerate(layer_idxs):
if i == 0:
new_layer_idx = layer_idx
else:
new_layer_idx += int((layer_idx - layer_idxs[i - 1]) / 2)
layer_maps['conv'][layer_idx] = new_layer_idx
layer_maps['bn'][layer_idx + 1] = new_layer_idx
else:
if layer_num == 11:
layer_idxs = [0, 3, 6, 8, 11, 13, 16, 18]
new_layer_idxs = [0, 2, 4, 5, 7, 8, 10, 11]
elif layer_num == 13:
layer_idxs = [0, 2, 5, 7, 10, 12, 15, 17, 20, 22]
new_layer_idxs = [0, 1, 3, 4, 6, 7, 9, 10, 12, 13]
elif layer_num == 16:
layer_idxs = [0, 2, 5, 7, 10, 12, 14, 17, 19, 21, 24, 26, 28]
new_layer_idxs = [0, 1, 3, 4, 6, 7, 8, 10, 11, 12, 14, 15, 16]
elif layer_num == 19:
layer_idxs = [
0, 2, 5, 7, 10, 12, 14, 16, 19, 21, 23, 25, 28, 30, 32, 34
]
new_layer_idxs = [
0, 1, 3, 4, 6, 7, 8, 9, 11, 12, 13, 14, 16, 17, 18, 19
]
else:
raise ValueError(f'Invalid number of layers: {layer_num}')
layer_maps['conv'] = {
layer_idx: new_layer_idx
for layer_idx, new_layer_idx in zip(layer_idxs, new_layer_idxs)
}
return layer_maps
def convert(src, dst, layer_num, with_bn=False):
"""Convert keys in torchvision pretrained VGG models to mmcls style."""
# load pytorch model
assert os.path.isfile(src), f'no checkpoint found at {src}'
blobs = torch.load(src, map_location='cpu')
# convert to pytorch style
state_dict = OrderedDict()
layer_maps = get_layer_maps(layer_num, with_bn)
prefix = 'backbone'
delimiter = '.'
for key, weight in blobs.items():
if 'features' in key:
module, layer_idx, weight_type = key.split(delimiter)
new_key = delimiter.join([prefix, key])
layer_idx = int(layer_idx)
for layer_key, maps in layer_maps.items():
if layer_idx in maps:
new_layer_idx = maps[layer_idx]
new_key = delimiter.join([
prefix, 'features',
str(new_layer_idx), layer_key, weight_type
])
state_dict[new_key] = weight
print(f'Convert {key} to {new_key}')
elif 'classifier' in key:
new_key = delimiter.join([prefix, key])
state_dict[new_key] = weight
print(f'Convert {key} to {new_key}')
else:
state_dict[key] = weight
# save checkpoint
checkpoint = dict()
checkpoint['state_dict'] = state_dict
torch.save(checkpoint, dst)
def main():
parser = argparse.ArgumentParser(description='Convert model keys')
parser.add_argument('src', help='src torchvision model path')
parser.add_argument('dst', help='save path')
parser.add_argument(
'--bn', action='store_true', help='whether original vgg has BN')
parser.add_argument(
'--layer_num',
type=int,
choices=[11, 13, 16, 19],
default=11,
help='number of VGG layers')
args = parser.parse_args()
convert(args.src, args.dst, layer_num=args.layer_num, with_bn=args.bn)
if __name__ == '__main__':
main()
from argparse import ArgumentParser, Namespace
from pathlib import Path
from tempfile import TemporaryDirectory
import mmcv
try:
from model_archiver.model_packaging import package_model
from model_archiver.model_packaging_utils import ModelExportUtils
except ImportError:
package_model = None
def mmcls2torchserve(
config_file: str,
checkpoint_file: str,
output_folder: str,
model_name: str,
model_version: str = '1.0',
force: bool = False,
):
"""Converts mmclassification model (config + checkpoint) to TorchServe
`.mar`.
Args:
config_file:
In MMClassification config format.
The contents vary for each task repository.
checkpoint_file:
In MMClassification checkpoint format.
The contents vary for each task repository.
output_folder:
Folder where `{model_name}.mar` will be created.
The file created will be in TorchServe archive format.
model_name:
If not None, used for naming the `{model_name}.mar` file
that will be created under `output_folder`.
If None, `{Path(checkpoint_file).stem}` will be used.
model_version:
Model's version.
force:
If True, if there is an existing `{model_name}.mar`
file under `output_folder` it will be overwritten.
"""
mmcv.mkdir_or_exist(output_folder)
config = mmcv.Config.fromfile(config_file)
with TemporaryDirectory() as tmpdir:
config.dump(f'{tmpdir}/config.py')
args = Namespace(
**{
'model_file': f'{tmpdir}/config.py',
'serialized_file': checkpoint_file,
'handler': f'{Path(__file__).parent}/mmcls_handler.py',
'model_name': model_name or Path(checkpoint_file).stem,
'version': model_version,
'export_path': output_folder,
'force': force,
'requirements_file': None,
'extra_files': None,
'runtime': 'python',
'archive_format': 'default'
})
manifest = ModelExportUtils.generate_manifest_json(args)
package_model(args, manifest)
def parse_args():
parser = ArgumentParser(
description='Convert mmcls models to TorchServe `.mar` format.')
parser.add_argument('config', type=str, help='config file path')
parser.add_argument('checkpoint', type=str, help='checkpoint file path')
parser.add_argument(
'--output-folder',
type=str,
required=True,
help='Folder where `{model_name}.mar` will be created.')
parser.add_argument(
'--model-name',
type=str,
default=None,
help='If not None, used for naming the `{model_name}.mar`'
'file that will be created under `output_folder`.'
'If None, `{Path(checkpoint_file).stem}` will be used.')
parser.add_argument(
'--model-version',
type=str,
default='1.0',
help='Number used for versioning.')
parser.add_argument(
'-f',
'--force',
action='store_true',
help='overwrite the existing `{model_name}.mar`')
args = parser.parse_args()
return args
if __name__ == '__main__':
args = parse_args()
if package_model is None:
raise ImportError('`torch-model-archiver` is required.'
'Try: pip install torch-model-archiver')
mmcls2torchserve(args.config, args.checkpoint, args.output_folder,
args.model_name, args.model_version, args.force)
import base64
import os
import mmcv
import torch
from ts.torch_handler.base_handler import BaseHandler
from mmcls.apis import inference_model, init_model
class MMclsHandler(BaseHandler):
def initialize(self, context):
properties = context.system_properties
self.map_location = 'cuda' if torch.cuda.is_available() else 'cpu'
self.device = torch.device(self.map_location + ':' +
str(properties.get('gpu_id')) if torch.cuda.
is_available() else self.map_location)
self.manifest = context.manifest
model_dir = properties.get('model_dir')
serialized_file = self.manifest['model']['serializedFile']
checkpoint = os.path.join(model_dir, serialized_file)
self.config_file = os.path.join(model_dir, 'config.py')
self.model = init_model(self.config_file, checkpoint, self.device)
self.initialized = True
def preprocess(self, data):
images = []
for row in data:
image = row.get('data') or row.get('body')
if isinstance(image, str):
image = base64.b64decode(image)
image = mmcv.imfrombytes(image)
images.append(image)
return images
def inference(self, data, *args, **kwargs):
results = []
for image in data:
results.append(inference_model(self.model, image))
return results
def postprocess(self, data):
for result in data:
result['pred_label'] = int(result['pred_label'])
return data
import argparse
import os
import os.path as osp
import numpy as np
def get_GiB(x: int):
"""return x GiB."""
return x * (1 << 30)
def onnx2tensorrt(onnx_file,
trt_file,
input_shape,
max_batch_size,
fp16_mode=False,
verify=False,
workspace_size=1):
"""Create tensorrt engine from onnx model.
Args:
onnx_file (str): Filename of the input ONNX model file.
trt_file (str): Filename of the output TensorRT engine file.
input_shape (list[int]): Input shape of the model.
eg [1, 3, 224, 224].
max_batch_size (int): Max batch size of the model.
verify (bool, optional): Whether to verify the converted model.
Defaults to False.
workspace_size (int, optional): Maximium workspace of GPU.
Defaults to 1.
"""
import onnx
from mmcv.tensorrt import TRTWraper, onnx2trt, save_trt_engine
onnx_model = onnx.load(onnx_file)
# create trt engine and wraper
assert max_batch_size >= 1
max_shape = [max_batch_size] + list(input_shape[1:])
opt_shape_dict = {'input': [input_shape, input_shape, max_shape]}
max_workspace_size = get_GiB(workspace_size)
trt_engine = onnx2trt(
onnx_model,
opt_shape_dict,
fp16_mode=fp16_mode,
max_workspace_size=max_workspace_size)
save_dir, _ = osp.split(trt_file)
if save_dir:
os.makedirs(save_dir, exist_ok=True)
save_trt_engine(trt_engine, trt_file)
print(f'Successfully created TensorRT engine: {trt_file}')
if verify:
import torch
import onnxruntime as ort
input_img = torch.randn(*input_shape)
input_img_cpu = input_img.detach().cpu().numpy()
input_img_cuda = input_img.cuda()
# Get results from ONNXRuntime
session_options = ort.SessionOptions()
sess = ort.InferenceSession(onnx_file, session_options)
# get input and output names
input_names = [_.name for _ in sess.get_inputs()]
output_names = [_.name for _ in sess.get_outputs()]
onnx_outputs = sess.run(None, {
input_names[0]: input_img_cpu,
})
# Get results from TensorRT
trt_model = TRTWraper(trt_file, input_names, output_names)
with torch.no_grad():
trt_outputs = trt_model({input_names[0]: input_img_cuda})
trt_outputs = [
trt_outputs[_].detach().cpu().numpy() for _ in output_names
]
# Compare results
np.testing.assert_allclose(
onnx_outputs[0], trt_outputs[0], rtol=1e-05, atol=1e-05)
print('The numerical values are the same ' +
'between ONNXRuntime and TensorRT')
def parse_args():
parser = argparse.ArgumentParser(
description='Convert MMClassification models from ONNX to TensorRT')
parser.add_argument('model', help='Filename of the input ONNX model')
parser.add_argument(
'--trt-file',
type=str,
default='tmp.trt',
help='Filename of the output TensorRT engine')
parser.add_argument(
'--verify',
action='store_true',
help='Verify the outputs of ONNXRuntime and TensorRT')
parser.add_argument(
'--shape',
type=int,
nargs='+',
default=[224, 224],
help='Input size of the model')
parser.add_argument(
'--max-batch-size',
type=int,
default=1,
help='Maximum batch size of TensorRT model.')
parser.add_argument('--fp16', action='store_true', help='Enable fp16 mode')
parser.add_argument(
'--workspace-size',
type=int,
default=1,
help='Max workspace size of GPU in GiB')
args = parser.parse_args()
return args
if __name__ == '__main__':
args = parse_args()
if len(args.shape) == 1:
input_shape = (1, 3, args.shape[0], args.shape[0])
elif len(args.shape) == 2:
input_shape = (1, 3) + tuple(args.shape)
else:
raise ValueError('invalid input shape')
# Create TensorRT engine
onnx2tensorrt(
args.model,
args.trt_file,
input_shape,
args.max_batch_size,
fp16_mode=args.fp16,
verify=args.verify,
workspace_size=args.workspace_size)
import argparse
from functools import partial
import mmcv
import numpy as np
import onnxruntime as rt
import torch
from mmcv.onnx import register_extra_symbolics
from mmcv.runner import load_checkpoint
from mmcls.models import build_classifier
torch.manual_seed(3)
def _demo_mm_inputs(input_shape, num_classes):
"""Create a superset of inputs needed to run test or train batches.
Args:
input_shape (tuple):
input batch dimensions
num_classes (int):
number of semantic classes
"""
(N, C, H, W) = input_shape
rng = np.random.RandomState(0)
imgs = rng.rand(*input_shape)
gt_labels = rng.randint(
low=0, high=num_classes, size=(N, 1)).astype(np.uint8)
mm_inputs = {
'imgs': torch.FloatTensor(imgs).requires_grad_(True),
'gt_labels': torch.LongTensor(gt_labels),
}
return mm_inputs
def pytorch2onnx(model,
input_shape,
opset_version=11,
dynamic_export=False,
show=False,
output_file='tmp.onnx',
do_simplify=False,
verify=False):
"""Export Pytorch model to ONNX model and verify the outputs are same
between Pytorch and ONNX.
Args:
model (nn.Module): Pytorch model we want to export.
input_shape (tuple): Use this input shape to construct
the corresponding dummy input and execute the model.
opset_version (int): The onnx op version. Default: 11.
show (bool): Whether print the computation graph. Default: False.
output_file (string): The path to where we store the output ONNX model.
Default: `tmp.onnx`.
verify (bool): Whether compare the outputs between Pytorch and ONNX.
Default: False.
"""
model.cpu().eval()
num_classes = model.head.num_classes
mm_inputs = _demo_mm_inputs(input_shape, num_classes)
imgs = mm_inputs.pop('imgs')
img_list = [img[None, :] for img in imgs]
# replace original forward function
origin_forward = model.forward
model.forward = partial(model.forward, img_metas={}, return_loss=False)
register_extra_symbolics(opset_version)
# support dynamic shape export
if dynamic_export:
dynamic_axes = {
'input': {
0: 'batch',
2: 'width',
3: 'height'
},
'probs': {
0: 'batch'
}
}
else:
dynamic_axes = {}
with torch.no_grad():
torch.onnx.export(
model, (img_list, ),
output_file,
input_names=['input'],
output_names=['probs'],
export_params=True,
keep_initializers_as_inputs=True,
dynamic_axes=dynamic_axes,
verbose=show,
opset_version=opset_version)
print(f'Successfully exported ONNX model: {output_file}')
model.forward = origin_forward
if do_simplify:
from mmcv import digit_version
import onnxsim
min_required_version = '0.3.0'
assert digit_version(mmcv.__version__) >= digit_version(
min_required_version
), f'Requires to install onnx-simplify>={min_required_version}'
if dynamic_axes:
input_shape = (input_shape[0], input_shape[1], input_shape[2] * 2,
input_shape[3] * 2)
else:
input_shape = (input_shape[0], input_shape[1], input_shape[2],
input_shape[3])
imgs = _demo_mm_inputs(input_shape, model.head.num_classes).pop('imgs')
input_dic = {'input': imgs.detach().cpu().numpy()}
input_shape_dic = {'input': list(input_shape)}
onnxsim.simplify(
output_file,
input_shapes=input_shape_dic,
input_data=input_dic,
dynamic_input_shape=dynamic_export)
if verify:
# check by onnx
import onnx
onnx_model = onnx.load(output_file)
onnx.checker.check_model(onnx_model)
# test the dynamic model
if dynamic_export:
dynamic_test_inputs = _demo_mm_inputs(
(input_shape[0], input_shape[1], input_shape[2] * 2,
input_shape[3] * 2), model.head.num_classes)
imgs = dynamic_test_inputs.pop('imgs')
img_list = [img[None, :] for img in imgs]
# check the numerical value
# get pytorch output
pytorch_result = model(img_list, img_metas={}, return_loss=False)[0]
# get onnx output
input_all = [node.name for node in onnx_model.graph.input]
input_initializer = [
node.name for node in onnx_model.graph.initializer
]
net_feed_input = list(set(input_all) - set(input_initializer))
assert (len(net_feed_input) == 1)
sess = rt.InferenceSession(output_file)
onnx_result = sess.run(
None, {net_feed_input[0]: img_list[0].detach().numpy()})[0]
if not np.allclose(pytorch_result, onnx_result):
raise ValueError(
'The outputs are different between Pytorch and ONNX')
print('The outputs are same between Pytorch and ONNX')
def parse_args():
parser = argparse.ArgumentParser(description='Convert MMCls to ONNX')
parser.add_argument('config', help='test config file path')
parser.add_argument('--checkpoint', help='checkpoint file', default=None)
parser.add_argument('--show', action='store_true', help='show onnx graph')
parser.add_argument(
'--verify', action='store_true', help='verify the onnx model')
parser.add_argument('--output-file', type=str, default='tmp.onnx')
parser.add_argument('--opset-version', type=int, default=11)
parser.add_argument(
'--simplify',
action='store_true',
help='Whether to simplify onnx model.')
parser.add_argument(
'--shape',
type=int,
nargs='+',
default=[224, 224],
help='input image size')
parser.add_argument(
'--dynamic-export',
action='store_true',
help='Whether to export ONNX with dynamic input shape. \
Defaults to False.')
args = parser.parse_args()
return args
if __name__ == '__main__':
args = parse_args()
if len(args.shape) == 1:
input_shape = (1, 3, args.shape[0], args.shape[0])
elif len(args.shape) == 2:
input_shape = (
1,
3,
) + tuple(args.shape)
else:
raise ValueError('invalid input shape')
cfg = mmcv.Config.fromfile(args.config)
cfg.model.pretrained = None
# build the model and load checkpoint
classifier = build_classifier(cfg.model)
if args.checkpoint:
load_checkpoint(classifier, args.checkpoint, map_location='cpu')
# conver model to onnx file
pytorch2onnx(
classifier,
input_shape,
opset_version=args.opset_version,
show=args.show,
dynamic_export=args.dynamic_export,
output_file=args.output_file,
do_simplify=args.simplify,
verify=args.verify)
import argparse
import os
import os.path as osp
from functools import partial
import mmcv
import numpy as np
import torch
from mmcv.runner import load_checkpoint
from torch import nn
from mmcls.models import build_classifier
torch.manual_seed(3)
def _demo_mm_inputs(input_shape: tuple, num_classes: int):
"""Create a superset of inputs needed to run test or train batches.
Args:
input_shape (tuple):
input batch dimensions
num_classes (int):
number of semantic classes
"""
(N, C, H, W) = input_shape
rng = np.random.RandomState(0)
imgs = rng.rand(*input_shape)
gt_labels = rng.randint(
low=0, high=num_classes, size=(N, 1)).astype(np.uint8)
mm_inputs = {
'imgs': torch.FloatTensor(imgs).requires_grad_(False),
'gt_labels': torch.LongTensor(gt_labels),
}
return mm_inputs
def pytorch2torchscript(model: nn.Module, input_shape: tuple, output_file: str,
verify: bool):
"""Export Pytorch model to TorchScript model through torch.jit.trace and
verify the outputs are same between Pytorch and TorchScript.
Args:
model (nn.Module): Pytorch model we want to export.
input_shape (tuple): Use this input shape to construct
the corresponding dummy input and execute the model.
show (bool): Whether print the computation graph. Default: False.
output_file (string): The path to where we store the output
TorchScript model.
verify (bool): Whether compare the outputs between Pytorch
and TorchScript through loading generated output_file.
"""
model.cpu().eval()
num_classes = model.head.num_classes
mm_inputs = _demo_mm_inputs(input_shape, num_classes)
imgs = mm_inputs.pop('imgs')
img_list = [img[None, :] for img in imgs]
# replace original forward function
origin_forward = model.forward
model.forward = partial(model.forward, img_metas={}, return_loss=False)
with torch.no_grad():
trace_model = torch.jit.trace(model, img_list[0])
save_dir, _ = osp.split(output_file)
if save_dir:
os.makedirs(save_dir, exist_ok=True)
trace_model.save(output_file)
print(f'Successfully exported TorchScript model: {output_file}')
model.forward = origin_forward
if verify:
# load by torch.jit
jit_model = torch.jit.load(output_file)
# check the numerical value
# get pytorch output
pytorch_result = model(img_list, img_metas={}, return_loss=False)[0]
# get jit output
jit_result = jit_model(img_list[0])[0].detach().numpy()
if not np.allclose(pytorch_result, jit_result):
raise ValueError(
'The outputs are different between Pytorch and TorchScript')
print('The outputs are same between Pytorch and TorchScript')
def parse_args():
parser = argparse.ArgumentParser(
description='Convert MMCls to TorchScript')
parser.add_argument('config', help='test config file path')
parser.add_argument('--checkpoint', help='checkpoint file', type=str)
parser.add_argument(
'--verify',
action='store_true',
help='verify the TorchScript model',
default=False)
parser.add_argument('--output-file', type=str, default='tmp.pt')
parser.add_argument(
'--shape',
type=int,
nargs='+',
default=[224, 224],
help='input image size')
args = parser.parse_args()
return args
if __name__ == '__main__':
args = parse_args()
if len(args.shape) == 1:
input_shape = (1, 3, args.shape[0], args.shape[0])
elif len(args.shape) == 2:
input_shape = (
1,
3,
) + tuple(args.shape)
else:
raise ValueError('invalid input shape')
cfg = mmcv.Config.fromfile(args.config)
cfg.model.pretrained = None
# build the model and load checkpoint
classifier = build_classifier(cfg.model)
if args.checkpoint:
load_checkpoint(classifier, args.checkpoint, map_location='cpu')
# conver model to TorchScript file
pytorch2torchscript(
classifier,
input_shape,
output_file=args.output_file,
verify=args.verify)
import argparse
import warnings
import mmcv
import numpy as np
from mmcv import DictAction
from mmcv.parallel import MMDataParallel
from mmcls.apis import single_gpu_test
from mmcls.core.export import ONNXRuntimeClassifier, TensorRTClassifier
from mmcls.datasets import build_dataloader, build_dataset
def parse_args():
parser = argparse.ArgumentParser(
description='Test (and eval) an ONNX model using ONNXRuntime.')
parser.add_argument('config', help='model config file')
parser.add_argument('model', help='filename of the input ONNX model')
parser.add_argument(
'--backend',
help='Backend of the model.',
choices=['onnxruntime', 'tensorrt'])
parser.add_argument(
'--out', type=str, help='output result file in pickle format')
parser.add_argument(
'--cfg-options',
nargs='+',
action=DictAction,
help='override some settings in the used config, the key-value pair '
'in xxx=yyy format will be merged into config file.')
parser.add_argument(
'--metrics',
type=str,
nargs='+',
help='evaluation metrics, which depends on the dataset, e.g., '
'"accuracy", "precision", "recall", "f1_score", "support" for single '
'label dataset, and "mAP", "CP", "CR", "CF1", "OP", "OR", "OF1" for '
'multi-label dataset')
parser.add_argument(
'--metric-options',
nargs='+',
action=DictAction,
default={},
help='custom options for evaluation, the key-value pair in xxx=yyy '
'format will be parsed as a dict metric_options for dataset.evaluate()'
' function.')
parser.add_argument('--show', action='store_true', help='show results')
parser.add_argument(
'--show-dir', help='directory where painted images will be saved')
args = parser.parse_args()
return args
def main():
args = parse_args()
if args.out is not None and not args.out.endswith(('.pkl', '.pickle')):
raise ValueError('The output file must be a pkl file.')
cfg = mmcv.Config.fromfile(args.config)
if args.cfg_options is not None:
cfg.merge_from_dict(args.cfg_options)
# build dataset and dataloader
dataset = build_dataset(cfg.data.test)
data_loader = build_dataloader(
dataset,
samples_per_gpu=cfg.data.samples_per_gpu,
workers_per_gpu=cfg.data.workers_per_gpu,
shuffle=False,
round_up=False)
# build onnxruntime model and run inference.
if args.backend == 'onnxruntime':
model = ONNXRuntimeClassifier(
args.model, class_names=dataset.CLASSES, device_id=0)
elif args.backend == 'tensorrt':
model = TensorRTClassifier(
args.model, class_names=dataset.CLASSES, device_id=0)
else:
print('Unknown backend: {}.'.format(args.model))
exit()
model = MMDataParallel(model, device_ids=[0])
model.CLASSES = dataset.CLASSES
outputs = single_gpu_test(model, data_loader, args.show, args.show_dir)
if args.metrics:
results = dataset.evaluate(outputs, args.metrics, args.metric_options)
for k, v in results.items():
print(f'\n{k} : {v:.2f}')
else:
warnings.warn('Evaluation metrics are not specified.')
scores = np.vstack(outputs)
pred_score = np.max(scores, axis=1)
pred_label = np.argmax(scores, axis=1)
pred_class = [dataset.CLASSES[lb] for lb in pred_label]
results = {
'pred_score': pred_score,
'pred_label': pred_label,
'pred_class': pred_class
}
if not args.out:
print('\nthe predicted result for the first element is '
f'pred_score = {pred_score[0]:.2f}, '
f'pred_label = {pred_label[0]} '
f'and pred_class = {pred_class[0]}. '
'Specify --out to save all results to files.')
if args.out:
print(f'\nwriting results to {args.out}')
mmcv.dump(results, args.out)
if __name__ == '__main__':
main()
#!/usr/bin/env bash
CONFIG=$1
CHECKPOINT=$2
GPUS=$3
PORT=${PORT:-29500}
PYTHONPATH="$(dirname $0)/..":$PYTHONPATH \
python -m torch.distributed.launch --nproc_per_node=$GPUS --master_port=$PORT \
$(dirname "$0")/test.py $CONFIG $CHECKPOINT --launcher pytorch ${@:4}
#!/usr/bin/env bash
CONFIG=$1
GPUS=$2
PORT=${PORT:-29500}
PYTHONPATH="$(dirname $0)/..":$PYTHONPATH \
python -m torch.distributed.launch --nproc_per_node=$GPUS --master_port=$PORT \
$(dirname "$0")/train.py $CONFIG --launcher pytorch ${@:3}
import argparse
from mmcv import Config
from mmcv.cnn.utils import get_model_complexity_info
from mmcls.models import build_classifier
def parse_args():
parser = argparse.ArgumentParser(description='Get model flops and params')
parser.add_argument('config', help='config file path')
parser.add_argument(
'--shape',
type=int,
nargs='+',
default=[224, 224],
help='input image size')
args = parser.parse_args()
return args
def main():
args = parse_args()
if len(args.shape) == 1:
input_shape = (3, args.shape[0], args.shape[0])
elif len(args.shape) == 2:
input_shape = (3, ) + tuple(args.shape)
else:
raise ValueError('invalid input shape')
cfg = Config.fromfile(args.config)
model = build_classifier(cfg.model)
model.eval()
if hasattr(model, 'extract_feat'):
model.forward = model.extract_feat
else:
raise NotImplementedError(
'FLOPs counter is currently not currently supported with {}'.
format(model.__class__.__name__))
flops, params = get_model_complexity_info(model, input_shape)
split_line = '=' * 30
print(f'{split_line}\nInput shape: {input_shape}\n'
f'Flops: {flops}\nParams: {params}\n{split_line}')
print('!!!Please be cautious if you use the results in papers. '
'You may need to check if all ops are supported and verify that the '
'flops computation is correct.')
if __name__ == '__main__':
main()
import argparse
import subprocess
import torch
def parse_args():
parser = argparse.ArgumentParser(
description='Process a checkpoint to be published')
parser.add_argument('in_file', help='input checkpoint filename')
parser.add_argument('out_file', help='output checkpoint filename')
args = parser.parse_args()
return args
def process_checkpoint(in_file, out_file):
checkpoint = torch.load(in_file, map_location='cpu')
# remove optimizer for smaller file size
if 'optimizer' in checkpoint:
del checkpoint['optimizer']
# if it is necessary to remove some sensitive data in checkpoint['meta'],
# add the code here.
torch.save(checkpoint, out_file)
sha = subprocess.check_output(['sha256sum', out_file]).decode()
if out_file.endswith('.pth'):
out_file_name = out_file[:-4]
else:
out_file_name = out_file
final_file = out_file_name + f'-{sha[:8]}.pth'
subprocess.Popen(['mv', out_file, final_file])
def main():
args = parse_args()
process_checkpoint(args.in_file, args.out_file)
if __name__ == '__main__':
main()
#!/usr/bin/env bash
set -x
PARTITION=$1
JOB_NAME=$2
CONFIG=$3
CHECKPOINT=$4
GPUS=${GPUS:-8}
GPUS_PER_NODE=${GPUS_PER_NODE:-8}
CPUS_PER_TASK=${CPUS_PER_TASK:-5}
PY_ARGS=${@:5}
SRUN_ARGS=${SRUN_ARGS:-""}
PYTHONPATH="$(dirname $0)/..":$PYTHONPATH \
srun -p ${PARTITION} \
--job-name=${JOB_NAME} \
--gres=gpu:${GPUS_PER_NODE} \
--ntasks=${GPUS} \
--ntasks-per-node=${GPUS_PER_NODE} \
--cpus-per-task=${CPUS_PER_TASK} \
--kill-on-bad-exit=1 \
${SRUN_ARGS} \
python -u tools/test.py ${CONFIG} ${CHECKPOINT} --launcher="slurm" ${PY_ARGS}
#!/usr/bin/env bash
set -x
PARTITION=$1
JOB_NAME=$2
CONFIG=$3
WORK_DIR=$4
GPUS=${GPUS:-8}
GPUS_PER_NODE=${GPUS_PER_NODE:-8}
CPUS_PER_TASK=${CPUS_PER_TASK:-5}
SRUN_ARGS=${SRUN_ARGS:-""}
PY_ARGS=${@:5}
PYTHONPATH="$(dirname $0)/..":$PYTHONPATH \
srun -p ${PARTITION} \
--job-name=${JOB_NAME} \
--gres=gpu:${GPUS_PER_NODE} \
--ntasks=${GPUS} \
--ntasks-per-node=${GPUS_PER_NODE} \
--cpus-per-task=${CPUS_PER_TASK} \
--kill-on-bad-exit=1 \
${SRUN_ARGS} \
python -u tools/train.py ${CONFIG} --work-dir=${WORK_DIR} --launcher="slurm" ${PY_ARGS}
import argparse
import os
import warnings
import mmcv
import numpy as np
import torch
from mmcv import DictAction
from mmcv.parallel import MMDataParallel, MMDistributedDataParallel
from mmcv.runner import get_dist_info, init_dist, load_checkpoint
from mmcls.apis import multi_gpu_test, single_gpu_test
from mmcls.datasets import build_dataloader, build_dataset
from mmcls.models import build_classifier
# TODO import `wrap_fp16_model` from mmcv and delete them from mmcls
try:
from mmcv.runner import wrap_fp16_model
except ImportError:
warnings.warn('wrap_fp16_model from mmcls will be deprecated.'
'Please install mmcv>=1.1.4.')
from mmcls.core import wrap_fp16_model
def parse_args():
parser = argparse.ArgumentParser(description='mmcls test model')
parser.add_argument('config', help='test config file path')
parser.add_argument('checkpoint', help='checkpoint file')
parser.add_argument('--out', help='output result file')
parser.add_argument(
'--metrics',
type=str,
nargs='+',
help='evaluation metrics, which depends on the dataset, e.g., '
'"accuracy", "precision", "recall", "f1_score", "support" for single '
'label dataset, and "mAP", "CP", "CR", "CF1", "OP", "OR", "OF1" for '
'multi-label dataset')
parser.add_argument('--show', action='store_true', help='show results')
parser.add_argument(
'--show-dir', help='directory where painted images will be saved')
parser.add_argument(
'--gpu_collect',
action='store_true',
help='whether to use gpu to collect results')
parser.add_argument('--tmpdir', help='tmp dir for writing some results')
parser.add_argument(
'--options',
nargs='+',
action=DictAction,
help='override some settings in the used config, the key-value pair '
'in xxx=yyy format will be merged into config file.')
parser.add_argument(
'--metric-options',
nargs='+',
action=DictAction,
default={},
help='custom options for evaluation, the key-value pair in xxx=yyy '
'format will be parsed as a dict metric_options for dataset.evaluate()'
' function.')
parser.add_argument(
'--show-options',
nargs='+',
action=DictAction,
help='custom options for show_result. key-value pair in xxx=yyy.'
'Check available options in `model.show_result`.')
parser.add_argument(
'--launcher',
choices=['none', 'pytorch', 'slurm', 'mpi'],
default='none',
help='job launcher')
parser.add_argument('--local_rank', type=int, default=0)
parser.add_argument(
'--device',
choices=['cpu', 'cuda'],
default='cuda',
help='device used for testing')
args = parser.parse_args()
if 'LOCAL_RANK' not in os.environ:
os.environ['LOCAL_RANK'] = str(args.local_rank)
return args
def main():
args = parse_args()
cfg = mmcv.Config.fromfile(args.config)
if args.options is not None:
cfg.merge_from_dict(args.options)
# set cudnn_benchmark
if cfg.get('cudnn_benchmark', False):
torch.backends.cudnn.benchmark = True
cfg.model.pretrained = None
cfg.data.test.test_mode = True
# init distributed env first, since logger depends on the dist info.
if args.launcher == 'none':
distributed = False
else:
distributed = True
init_dist(args.launcher, **cfg.dist_params)
# build the dataloader
dataset = build_dataset(cfg.data.test)
# the extra round_up data will be removed during gpu/cpu collect
data_loader = build_dataloader(
dataset,
samples_per_gpu=cfg.data.samples_per_gpu,
workers_per_gpu=cfg.data.workers_per_gpu,
dist=distributed,
shuffle=False,
round_up=True)
# build the model and load checkpoint
model = build_classifier(cfg.model)
fp16_cfg = cfg.get('fp16', None)
if fp16_cfg is not None:
wrap_fp16_model(model)
checkpoint = load_checkpoint(model, args.checkpoint, map_location='cpu')
if 'CLASSES' in checkpoint.get('meta', {}):
CLASSES = checkpoint['meta']['CLASSES']
else:
from mmcls.datasets import ImageNet
warnings.simplefilter('once')
warnings.warn('Class names are not saved in the checkpoint\'s '
'meta data, use imagenet by default.')
CLASSES = ImageNet.CLASSES
if not distributed:
if args.device == 'cpu':
model = model.cpu()
else:
model = MMDataParallel(model, device_ids=[0])
model.CLASSES = CLASSES
show_kwargs = {} if args.show_options is None else args.show_options
outputs = single_gpu_test(model, data_loader, args.show, args.show_dir,
**show_kwargs)
else:
model = MMDistributedDataParallel(
model.cuda(),
device_ids=[torch.cuda.current_device()],
broadcast_buffers=False)
outputs = multi_gpu_test(model, data_loader, args.tmpdir,
args.gpu_collect)
rank, _ = get_dist_info()
if rank == 0:
if args.metrics:
results = dataset.evaluate(outputs, args.metrics,
args.metric_options)
for k, v in results.items():
print(f'\n{k} : {v:.2f}')
else:
warnings.warn('Evaluation metrics are not specified.')
scores = np.vstack(outputs)
pred_score = np.max(scores, axis=1)
pred_label = np.argmax(scores, axis=1)
pred_class = [CLASSES[lb] for lb in pred_label]
results = {
'pred_score': pred_score,
'pred_label': pred_label,
'pred_class': pred_class
}
if not args.out:
print('\nthe predicted result for the first element is '
f'pred_score = {pred_score[0]:.2f}, '
f'pred_label = {pred_label[0]} '
f'and pred_class = {pred_class[0]}. '
'Specify --out to save all results to files.')
if args.out and rank == 0:
print(f'\nwriting results to {args.out}')
mmcv.dump(results, args.out)
if __name__ == '__main__':
main()
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment