Unverified Commit 06556c84 authored by Qiaofei Li's avatar Qiaofei Li Committed by GitHub
Browse files

Add shear augmentation (#526)

* Add shear augmentation

* Update geometric.py

* Update geometric.py
parent 15b37b0b
...@@ -3,7 +3,7 @@ from .colorspace import (bgr2gray, bgr2hls, bgr2hsv, bgr2rgb, bgr2ycbcr, ...@@ -3,7 +3,7 @@ from .colorspace import (bgr2gray, bgr2hls, bgr2hsv, bgr2rgb, bgr2ycbcr,
gray2bgr, gray2rgb, hls2bgr, hsv2bgr, imconvert, gray2bgr, gray2rgb, hls2bgr, hsv2bgr, imconvert,
rgb2bgr, rgb2gray, rgb2ycbcr, ycbcr2bgr, ycbcr2rgb) rgb2bgr, rgb2gray, rgb2ycbcr, ycbcr2bgr, ycbcr2rgb)
from .geometric import (imcrop, imflip, imflip_, impad, impad_to_multiple, from .geometric import (imcrop, imflip, imflip_, impad, impad_to_multiple,
imrescale, imresize, imresize_like, imrotate, imrescale, imresize, imresize_like, imrotate, imshear,
rescale_size) rescale_size)
from .io import imfrombytes, imread, imwrite, supported_backends, use_backend from .io import imfrombytes, imread, imwrite, supported_backends, use_backend
from .misc import tensor2imgs from .misc import tensor2imgs
...@@ -17,5 +17,6 @@ __all__ = [ ...@@ -17,5 +17,6 @@ __all__ = [
'impad', 'impad_to_multiple', 'imrotate', 'imfrombytes', 'imread', 'impad', 'impad_to_multiple', 'imrotate', 'imfrombytes', 'imread',
'imwrite', 'supported_backends', 'use_backend', 'imdenormalize', 'imwrite', 'supported_backends', 'use_backend', 'imdenormalize',
'imnormalize', 'imnormalize_', 'iminvert', 'posterize', 'solarize', 'imnormalize', 'imnormalize_', 'iminvert', 'posterize', 'solarize',
'rgb2ycbcr', 'bgr2ycbcr', 'ycbcr2rgb', 'ycbcr2bgr', 'tensor2imgs' 'rgb2ycbcr', 'bgr2ycbcr', 'ycbcr2rgb', 'ycbcr2bgr', 'tensor2imgs',
'imshear'
] ]
...@@ -460,3 +460,67 @@ def impad_to_multiple(img, divisor, pad_val=0): ...@@ -460,3 +460,67 @@ def impad_to_multiple(img, divisor, pad_val=0):
pad_h = int(np.ceil(img.shape[0] / divisor)) * divisor pad_h = int(np.ceil(img.shape[0] / divisor)) * divisor
pad_w = int(np.ceil(img.shape[1] / divisor)) * divisor pad_w = int(np.ceil(img.shape[1] / divisor)) * divisor
return impad(img, shape=(pad_h, pad_w)) return impad(img, shape=(pad_h, pad_w))
def _get_shear_matrix(magnitude, direction='horizontal'):
"""Generate the shear matrix for transformation.
Args:
magnitude (int | float): The magnitude used for shear.
direction (str): Thie flip direction, either "horizontal"
or "vertical".
Returns:
ndarray: The shear matrix with dtype float32.
"""
if direction == 'horizontal':
shear_matrix = np.float32([[1, magnitude, 0], [0, 1, 0]])
elif direction == 'vertical':
shear_matrix = np.float32([[1, 0, 0], [magnitude, 1, 0]])
return shear_matrix
def imshear(img,
magnitude,
direction='horizontal',
border_value=0,
interpolation='bilinear'):
"""Shear an image.
Args:
img (ndarray): Image to be sheared with format (h, w)
or (h, w, c).
magnitude (int | float): The magnitude used for shear.
direction (str): Thie flip direction, either "horizontal"
or "vertical".
border_value (int | tuple[int]): Value used in case of a
constant border.
interpolation (str): Same as :func:`resize`.
Returns:
ndarray: The sheared image.
"""
assert direction in ['horizontal',
'vertical'], f'Invalid direction: {direction}'
height, width = img.shape[:2]
if img.ndim == 2:
channels = 1
elif img.ndim == 3:
channels = img.shape[-1]
if isinstance(border_value, int):
border_value = tuple([border_value] * channels)
elif isinstance(border_value, tuple):
assert len(border_value) == channels, \
'Expected the num of elements in tuple equals the channels' \
'of input image. Found {} vs {}'.format(
len(border_value), channels)
else:
raise ValueError(
f'Invalid type {type(border_value)} for `border_value`')
shear_matrix = _get_shear_matrix(magnitude, direction)
sheared = cv2.warpAffine(
img,
shear_matrix, (width, height),
borderValue=border_value,
flags=cv2_interp_codes[interpolation])
return sheared
...@@ -443,3 +443,44 @@ class TestGeometric: ...@@ -443,3 +443,44 @@ class TestGeometric:
with pytest.raises(ValueError): with pytest.raises(ValueError):
mmcv.imrotate(img, 90, center=(0, 0), auto_bound=True) mmcv.imrotate(img, 90, center=(0, 0), auto_bound=True)
def test_imshear(self):
img = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]).astype(np.uint8)
assert_array_equal(mmcv.imshear(img, 0), img)
# magnitude=1, horizontal
img_sheared = np.array([[1, 2, 3], [0, 4, 5], [0, 0, 7]],
dtype=np.uint8)
assert_array_equal(mmcv.imshear(img, 1), img_sheared)
# magnitude=-1, vertical
img_sheared = np.array([[1, 5, 9], [4, 8, 0], [7, 0, 0]],
dtype=np.uint8)
assert_array_equal(mmcv.imshear(img, -1, 'vertical'), img_sheared)
# magnitude=1, vertical, borderValue=100
borderValue = 100
img_sheared = np.array(
[[1, borderValue, borderValue], [4, 2, borderValue], [7, 5, 3]],
dtype=np.uint8)
assert_array_equal(
mmcv.imshear(img, 1, 'vertical', borderValue), img_sheared)
# magnitude=1, vertical, borderValue=100, img shape (h,w,3)
img = np.stack([img, img, img], axis=-1)
img_sheared = np.stack([img_sheared, img_sheared, img_sheared],
axis=-1)
assert_array_equal(
mmcv.imshear(img, 1, 'vertical', borderValue), img_sheared)
# test tuple format of borderValue
assert_array_equal(
mmcv.imshear(img, 1, 'vertical',
(borderValue, borderValue, borderValue)), img_sheared)
# test invalid length of borderValue
with pytest.raises(AssertionError):
mmcv.imshear(img, 0.5, 'horizontal', (borderValue, ))
# test invalid type of borderValue
with pytest.raises(ValueError):
mmcv.imshear(img, 0.5, 'horizontal', [borderValue])
# test invalid value of direction
with pytest.raises(AssertionError):
mmcv.imshear(img, 0.5, 'diagonal')
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