Skip to content

Commit

Permalink
Add Cutout test comparison with official impl
Browse files Browse the repository at this point in the history
  • Loading branch information
aleju committed Jan 5, 2020
1 parent 76b722c commit 70ff777
Show file tree
Hide file tree
Showing 2 changed files with 128 additions and 4 deletions.
8 changes: 4 additions & 4 deletions imgaug/augmenters/arithmetic.py
Original file line number Diff line number Diff line change
Expand Up @@ -775,10 +775,10 @@ def cutout_(image, x1, y1, x2, y2,
import importlib

height, width = image.shape[0:2]
x1 = min(max(int(x1), 0), width - 1)
y1 = min(max(int(y1), 0), height - 1)
x2 = min(max(int(x2), 0), width - 1)
y2 = min(max(int(y2), 0), height - 1)
x1 = min(max(int(x1), 0), width)
y1 = min(max(int(y1), 0), height)
x2 = min(max(int(x2), 0), width)
y2 = min(max(int(y2), 0), height)

if x2 > x1 and y2 > y1:
assert fill_mode in _CUTOUT_FILL_MODES, (
Expand Down
124 changes: 124 additions & 0 deletions test/augmenters/test_arithmetic.py
Original file line number Diff line number Diff line change
Expand Up @@ -2042,6 +2042,79 @@ def test_sampling_when_position_is_stochastic_parameter(self):
assert np.allclose(samples.pos_x, [0.5, 0.5])
assert np.allclose(samples.pos_y, [0.6, 0.6])

def test_by_comparison_to_official_implementation(self):
image = np.ones((10, 8, 2), dtype=np.uint8)
aug = iaa.Cutout(1, position="uniform", size=0.2, squared=True,
cval=0)
aug_official = _CutoutOfficial(n_holes=1, length=int(10*0.2))

dropped = np.zeros((10, 8, 2), dtype=np.int32)
dropped_official = np.copy(dropped)
height = np.zeros((10, 8, 2), dtype=np.int32)
width = np.copy(height)
height_official = np.copy(height)
width_official = np.copy(width)

nb_iterations = 3 * 1000

images_aug = aug(images=[image] * nb_iterations)
for image_aug in images_aug:
image_aug_off = aug_official(image)

mask = (image_aug == 0)
mask_off = (image_aug_off == 0)

dropped += mask
dropped_official += mask_off

ydrop = np.max(mask, axis=(2, 1))
xdrop = np.max(mask, axis=(2, 0))
wx = np.where(xdrop)
wy = np.where(ydrop)
x1 = wx[0][0]
x2 = wx[0][-1]
y1 = wy[0][0]
y2 = wy[0][-1]

ydrop_off = np.max(mask_off, axis=(2, 1))
xdrop_off = np.max(mask_off, axis=(2, 0))
wx_off = np.where(xdrop_off)
wy_off = np.where(ydrop_off)
x1_off = wx_off[0][0]
x2_off = wx_off[0][-1]
y1_off = wy_off[0][0]
y2_off = wy_off[0][-1]

height += (
np.full(height.shape, 1 + (y2 - y1), dtype=np.int32)
* mask)
width += (
np.full(width.shape, 1 + (x2 - x1), dtype=np.int32)
* mask)
height_official += (
np.full(height_official.shape, 1 + (y2_off - y1_off),
dtype=np.int32)
* mask_off)
width_official += (
np.full(width_official.shape, 1 + (x2_off - x1_off),
dtype=np.int32)
* mask_off)

dropped_prob = dropped / nb_iterations
dropped_prob_off = dropped_official / nb_iterations
height_avg = height / (dropped + 1e-4)
height_avg_off = height_official / (dropped_official + 1e-4)
width_avg = width / (dropped + 1e-4)
width_avg_off = width_official / (dropped_official + 1e-4)

prob_max_diff = np.max(np.abs(dropped_prob - dropped_prob_off))
height_avg_max_diff = np.max(np.abs(height_avg - height_avg_off))
width_avg_max_diff = np.max(np.abs(width_avg - width_avg_off))

assert prob_max_diff < 0.04
assert height_avg_max_diff < 0.3
assert width_avg_max_diff < 0.3

def test_determinism(self):
aug = iaa.Cutout(nb_iterations=(1, 3),
size=(0.1, 0.2),
Expand Down Expand Up @@ -2080,6 +2153,57 @@ def test_get_parameters(self):
assert params[6] is aug.fill_per_channel


# this is mostly copy-pasted cutout code from
# https://github.com/uoguelph-mlrg/Cutout/blob/master/util/cutout.py
# we use this to compare our implementation against
# we changed some pytorch to numpy stuff
class _CutoutOfficial(object):
"""Randomly mask out one or more patches from an image.
Args:
n_holes (int): Number of patches to cut out of each image.
length (int): The length (in pixels) of each square patch.
"""
def __init__(self, n_holes, length):
self.n_holes = n_holes
self.length = length

def __call__(self, img):
"""
Args:
img (Tensor): Tensor image of size (C, H, W).
Returns:
Tensor: Image with n_holes of dimension length x length cut out of
it.
"""
# h = img.size(1)
# w = img.size(2)
h = img.shape[0]
w = img.shape[1]

mask = np.ones((h, w), np.float32)

for n in range(self.n_holes):
y = np.random.randint(h)
x = np.random.randint(w)

y1 = np.clip(y - self.length // 2, 0, h)
y2 = np.clip(y + self.length // 2, 0, h)
x1 = np.clip(x - self.length // 2, 0, w)
x2 = np.clip(x + self.length // 2, 0, w)

# note that in the paper they normalize to 0-mean,
# i.e. 0 here is actually not black but grayish pixels
mask[y1: y2, x1: x2] = 0

# mask = torch.from_numpy(mask)
# mask = mask.expand_as(img)
if img.ndim != 2:
mask = np.tile(mask[:, :, np.newaxis], (1, 1, img.shape[-1]))
img = img * mask

return img


class TestDropout(unittest.TestCase):
def setUp(self):
reseed()
Expand Down

0 comments on commit 70ff777

Please sign in to comment.