[Feature] Add shear pipeline (#163)

* half-done auto_augmentation

* remove auto_augcode and support shear pipeline

* fix typo

* fix typo

* use a non-square toy data instead
pull/165/head
LXXXXR 2021-02-25 16:00:46 +08:00 committed by GitHub
parent fb11a23cfe
commit 8c11c01fdb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 195 additions and 1 deletions

View File

@ -1,3 +1,4 @@
from .auto_augment import Shear
from .compose import Compose
from .formating import (Collect, ImageToTensor, ToNumpy, ToPIL, ToTensor,
Transpose, to_tensor)
@ -9,5 +10,5 @@ __all__ = [
'Compose', 'to_tensor', 'ToTensor', 'ImageToTensor', 'ToPIL', 'ToNumpy',
'Transpose', 'Collect', 'LoadImageFromFile', 'Resize', 'CenterCrop',
'RandomFlip', 'Normalize', 'RandomCrop', 'RandomResizedCrop',
'RandomGrayscale'
'RandomGrayscale', 'Shear'
]

View File

@ -0,0 +1,86 @@
import mmcv
import numpy as np
from ..builder import PIPELINES
def random_negative(value, random_negative_prob):
"""Randomly negate value based on random_negative_prob."""
return -value if np.random.rand() < random_negative_prob else value
@PIPELINES.register_module()
class Shear(object):
"""Shear images.
Args:
magnitude (int | float): The magnitude used for shear.
pad_val (int, tuple[int]): Pixel pad_val value for constant fill. If a
tuple of length 3, it is used to pad_val R, G, B channels
respectively. Defaults to 128.
prob (float): The probability for performing Shear therefore should be
in range [0, 1]. Defaults to 0.5.
direction (str): The shearing direction. Options are 'horizontal' and
'vertical'. Defaults to 'horizontal'.
random_negative_prob (float): The probability that turns the magnitude
negative, which should be in range [0,1]. Defaults to 0.5.
interpolation (str): Interpolation method. Options are 'nearest',
'bilinear', 'bicubic', 'area', 'lanczos'. Defaults to 'bicubic'.
"""
def __init__(self,
magnitude,
pad_val=128,
prob=0.5,
direction='horizontal',
random_negative_prob=0.5,
interpolation='bicubic'):
assert isinstance(magnitude, (int, float)), 'The magnitude type must '\
f'be int or float, but got {type(magnitude)} instead.'
if isinstance(pad_val, int):
pad_val = tuple([pad_val] * 3)
elif isinstance(pad_val, tuple):
assert len(pad_val) == 3, 'pad_val as a tuple must have 3 ' \
f'elements, got {len(pad_val)} instead.'
assert all(isinstance(i, int) for i in pad_val), 'pad_val as a '\
'tuple must got elements of int type.'
else:
raise TypeError('pad_val must be int or tuple with 3 elements.')
assert 0 <= prob <= 1.0, 'The prob should be in range [0,1], ' \
f'got {prob} instead.'
assert direction in ('horizontal', 'vertical'), 'direction must be ' \
f'either "horizontal" or "vertical", got {direction} instead.'
assert 0 <= random_negative_prob <= 1.0, 'The random_negative_prob ' \
f'should be in range [0,1], got {random_negative_prob} instead.'
self.magnitude = magnitude
self.pad_val = pad_val
self.prob = prob
self.direction = direction
self.random_negative_prob = random_negative_prob
self.interpolation = interpolation
def __call__(self, results):
if np.random.rand() > self.prob:
return results
magnitude = random_negative(self.magnitude, self.random_negative_prob)
for key in results.get('img_fields', ['img']):
img = results[key]
img_sheared = mmcv.imshear(
img,
magnitude,
direction=self.direction,
border_value=self.pad_val,
interpolation=self.interpolation)
results[key] = img_sheared.astype(img.dtype)
return results
def __repr__(self):
repr_str = self.__class__.__name__
repr_str += f'(magnitude={self.magnitude}, '
repr_str += f'pad_val={self.pad_val}, '
repr_str += f'prob={self.prob}, '
repr_str += f'direction={self.direction}, '
repr_str += f'random_negative_prob={self.random_negative_prob}, '
repr_str += f'interpolation={self.interpolation})'
return repr_str

View File

@ -0,0 +1,107 @@
import copy
import numpy as np
import pytest
from mmcv.utils import build_from_cfg
from mmcls.datasets.builder import PIPELINES
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'] = img
results['img2'] = copy.deepcopy(img)
results['img_shape'] = img.shape
results['ori_shape'] = img.shape
results['img_fields'] = ['img', 'img2']
return results
def test_shear():
# test assertion for invalid type of magnitude
with pytest.raises(AssertionError):
transform = dict(type='Shear', magnitude=None)
build_from_cfg(transform, PIPELINES)
# test assertion for invalid pad_val
with pytest.raises(AssertionError):
transform = dict(type='Shear', magnitude=0.5, pad_val=(0, 0))
build_from_cfg(transform, PIPELINES)
# test assertion for invalid value of prob
with pytest.raises(AssertionError):
transform = dict(type='Shear', magnitude=0.5, prob=100)
build_from_cfg(transform, PIPELINES)
# test assertion for invalid direction
with pytest.raises(AssertionError):
transform = dict(type='Shear', magnitude=0.5, direction='diagonal')
build_from_cfg(transform, PIPELINES)
# test assertion for invalid value of random_negative_prob
with pytest.raises(AssertionError):
transform = dict(type='Shear', magnitude=0.5, random_negative_prob=100)
build_from_cfg(transform, PIPELINES)
# test case when magnitude = 0, therefore no shear
results = construct_toy_data()
transform = dict(type='Shear', magnitude=0., prob=1.)
pipeline = build_from_cfg(transform, PIPELINES)
results = pipeline(results)
assert (results['img'] == results['ori_img']).all()
# test case when prob = 0, therefore no shear
results = construct_toy_data()
transform = dict(type='Shear', magnitude=0.5, prob=0.)
pipeline = build_from_cfg(transform, PIPELINES)
results = pipeline(results)
assert (results['img'] == results['ori_img']).all()
# test shear horizontally, magnitude=1
results = construct_toy_data()
transform = dict(
type='Shear', magnitude=1, pad_val=0, prob=1., random_negative_prob=0.)
pipeline = build_from_cfg(transform, PIPELINES)
results = pipeline(results)
sheared_img = np.array([[1, 2, 3, 4], [0, 5, 6, 7], [0, 0, 9, 10]],
dtype=np.uint8)
sheared_img = np.stack([sheared_img, sheared_img, sheared_img], axis=-1)
assert (results['img'] == sheared_img).all()
assert (results['img'] == results['img2']).all()
# test shear vertically, magnitude=-1
results = construct_toy_data()
transform = dict(
type='Shear',
magnitude=-1,
pad_val=0,
prob=1.,
direction='vertical',
random_negative_prob=0.)
pipeline = build_from_cfg(transform, PIPELINES)
results = pipeline(results)
sheared_img = np.array([[1, 6, 11, 0], [5, 10, 0, 0], [9, 0, 0, 0]],
dtype=np.uint8)
sheared_img = np.stack([sheared_img, sheared_img, sheared_img], axis=-1)
assert (results['img'] == sheared_img).all()
# test shear vertically, magnitude=1, random_negative_prob=1
results = construct_toy_data()
transform = dict(
type='Shear',
magnitude=1,
pad_val=0,
prob=1.,
direction='vertical',
random_negative_prob=1.)
pipeline = build_from_cfg(transform, PIPELINES)
results = pipeline(results)
sheared_img = np.array([[1, 6, 11, 0], [5, 10, 0, 0], [9, 0, 0, 0]],
dtype=np.uint8)
sheared_img = np.stack([sheared_img, sheared_img, sheared_img], axis=-1)
assert (results['img'] == sheared_img).all()