Skip to content
This repository was archived by the owner on Jul 2, 2021. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
82e1b06
add random_erasing
akitotakeki Apr 8, 2018
0eebc97
add test
akitotakeki Apr 8, 2018
8dd6a42
fix test
akitotakeki Apr 8, 2018
5033569
Merge branch 'pr/432' into akitotakeki-add-random-erasing
akitotakeki Apr 8, 2018
f7e6390
add trial option
akitotakeki Apr 8, 2018
5aa696c
use _sample_parameters in random_sized_crop
akitotakeki Apr 8, 2018
dfb1712
fix doc
akitotakeki Apr 8, 2018
f0dc4e9
fix mean
akitotakeki Apr 8, 2018
4220892
use numpy.ndarray for mean
akitotakeki Apr 8, 2018
58037c3
fix mean
akitotakeki Apr 8, 2018
c5dd0e5
fix import
akitotakeki Apr 8, 2018
b527229
fix mean value in test
akitotakeki Apr 8, 2018
6612b8b
fix import
akitotakeki Apr 8, 2018
9f2d327
fix aspect range in test
akitotakeki Apr 8, 2018
9269d96
fix test
akitotakeki Apr 8, 2018
8fe9235
use random_sized_crop
akitotakeki Apr 11, 2018
15a1610
fix doc and add scale option
akitotakeki Apr 11, 2018
6024db8
fix test
akitotakeki Apr 11, 2018
01b6672
Merge branch 'master' into akitotakeki-add-random-erasing
akitotakeki Apr 11, 2018
39b93d8
fix params
akitotakeki Apr 11, 2018
e170fa5
Merge branch 'akitotakeki-add-random-erasing' of https://github.com/a…
akitotakeki Apr 11, 2018
78cb847
fix prob
akitotakeki Nov 1, 2018
6bf2ef6
reuse _sample_parameters
akitotakeki Nov 1, 2018
1ee30f1
Revert "reuse _sample_parameters"
akitotakeki Nov 1, 2018
d5efc32
use img.copy()
akitotakeki Nov 1, 2018
26b0f0e
change from fixed_value to fill
akitotakeki Nov 2, 2018
2f4fc64
fix the range of pixel
akitotakeki Nov 3, 2018
4dfd64b
Merge remote-tracking branch 'upstream/master' into akitotakeki-add-r…
akitotakeki Jan 3, 2019
c76eb27
Merge branch 'akitotakeki-add-random-erasing' of https://github.com/a…
akitotakeki Jan 3, 2019
4033e52
rm scale option
akitotakeki Jan 3, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions chainercv/transforms/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from chainercv.transforms.image.flip import flip # NOQA
from chainercv.transforms.image.pca_lighting import pca_lighting # NOQA
from chainercv.transforms.image.random_crop import random_crop # NOQA
from chainercv.transforms.image.random_erasing import random_erasing # NOQA
from chainercv.transforms.image.random_expand import random_expand # NOQA
from chainercv.transforms.image.random_flip import random_flip # NOQA
from chainercv.transforms.image.random_rotate import random_rotate # NOQA
Expand Down
105 changes: 105 additions & 0 deletions chainercv/transforms/image/random_erasing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
from __future__ import division

import numpy as np
import random

from chainercv.transforms.image.random_sized_crop import random_sized_crop


def random_erasing(img, prob=0.5,
scale_ratio_range=(0.02, 0.4),
aspect_ratio_range=(0.3, 1 / 0.3),
random_value=True,
fill=np.array((0.4914, 0.4822, 0.4465)),
return_param=False, copy=False):
"""Erase a rectangle region in an image with random or fixed values.

The size :math:`(H_{erase}, W_{erase})` and the left top coordinate
:math:`(y_{start}, x_{start})` of the region are calculated as follows:

+ :math:`H_{erase} = \\lfloor{\\sqrt{s \\times H \\times W \
\\times a}}\\rfloor`
+ :math:`W_{erase} = \\lfloor{\\sqrt{s \\times H \\times W \
\\div a}}\\rfloor`
+ :math:`y_{start} \\sim Uniform\\{0, H - H_{erase}\\}`
+ :math:`x_{start} \\sim Uniform\\{0, W - W_{erase}\\}`
+ :math:`s \\sim Uniform(s_1, s_2)`
+ :math:`b \\sim Uniform(a_1, a_2)` and \
:math:`a = b` or :math:`a = \\frac{1}{b}` in 50/50 probability.

Here, :math:`s_1, s_2` are the two floats in
:obj:`scale_ratio_range` and :math:`a_1, a_2` are the two floats
in :obj:`aspect_ratio_range`.
Also, :math:`H` and :math:`W` are the height and the width of the image.
Note that :math:`s \\approx \\frac{H_{erase} \\times
W_{erase}}{H \\times W}` and
:math:`a \\approx \\frac{H_{erase}}{W_{erase}}`.
The approximations come from flooring floats to integers.

.. note::

When it fails to sample a valid scale and aspect ratio for a hundred
times, it picks values in a non-uniform way.
If this happens, the selected scale ratio can be smaller
than :obj:`scale_ratio_range[0]`.

Args:
img (~numpy.ndarray): An image array. This is in CHW format.
prob (float): Erasing probability.
scale_ratio_range (tuple of two floats): Determines
the distribution from which a scale ratio is sampled.
aspect_ratio_range (tuple of two floats): Determines
the distribution from which an aspect ratio is sampled.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mean is not described.

random_value (bool): Fill the rectangle region with random values.
fill (~numpy.ndarray): Determines pixel values
to fill the rectangle region.
The default value is the ImageNet mean value.
return_param (bool): Returns parameters if :obj:`True`.

Returns:
~numpy.ndarray or (~numpy.ndarray, dict):

If :obj:`return_param = False`,
returns only the cropped image.

If :obj:`return_param = True`,
returns a tuple of erased image and :obj:`param`.
:obj:`param` is a dictionary of intermediate parameters whose
contents are listed below with key, value-type and the description
of the value.

* **y_slice** (*slice*): A slice used to erase a region in the input
image. The relation below holds together with :obj:`x_slice`.
* **x_slice** (*slice*): Similar to :obj:`y_slice`.
* **scale_ratio** (float): :math:`s` in the description (see above).
* **aspect_ratio** (float): :math:`a` in the description.

"""

if random.uniform(0.0, 1.0) < prob:
img = img.copy()
crop, params = random_sized_crop(img, scale_ratio_range,
aspect_ratio_range, return_param=True)
if random_value:
crop[:] = np.random.random(crop.shape) * 255
else:
crop[:] = fill[:, None, None]
y_slice = params['y_slice']
x_slice = params['x_slice']
scale_ratio = params['scale_ratio']
aspect_ratio = params['aspect_ratio']

else:
y_slice = None
x_slice = None
scale_ratio = None
aspect_ratio = None

if copy:
img = img.copy()
if return_param:
params = {'y_slice': y_slice, 'x_slice': x_slice,
'scale_ratio': scale_ratio, 'aspect_ratio': aspect_ratio}
return img, params
else:
return img
4 changes: 4 additions & 0 deletions docs/source/reference/transforms.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ random_crop
~~~~~~~~~~~
.. autofunction:: random_crop

random_erasing
~~~~~~~~~~~~~
.. autofunction:: random_erasing

random_expand
~~~~~~~~~~~~~
.. autofunction:: random_expand
Expand Down
57 changes: 57 additions & 0 deletions tests/transforms_tests/image_tests/test_random_erasing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
from __future__ import division

import unittest

import numpy as np

from chainer import testing
from chainercv.transforms import random_erasing


@testing.parameterize(*testing.product_dict(
[
{'H': 256, 'W': 256},
{'H': 129, 'W': 352},
{'H': 352, 'W': 129},
{'H': 35, 'W': 500},
],
[
{'prob': 0.0},
{'prob': 0.5},
{'prob': 1.0},
],
[
{'random_value': True},
{'random_value': False},
]
))
class TestRandomErasing(unittest.TestCase):

def test_random_sized_crop(self):
img = np.random.uniform(size=(3, self.H, self.W))
prob = self.prob
scale_ratio_range = (0.02, 0.4)
aspect_ratio_range = (0.3, 1 / 0.3)
fill = np.array((0.4914, 0.4822, 0.4465))
out, params = random_erasing(img, prob, scale_ratio_range,
aspect_ratio_range, self.random_value,
fill,
return_param=True)

scale_ratio = params['scale_ratio']
aspect_ratio = params['aspect_ratio']

if scale_ratio is not None:
self.assertTrue(
(aspect_ratio_range[0] <= aspect_ratio) and
(aspect_ratio <= aspect_ratio_range[1]))
self.assertTrue(
scale_ratio <= scale_ratio_range[1])
scale_ratio_max = min((scale_ratio_range[1],
self.H / (self.W * aspect_ratio),
(aspect_ratio * self.W) / self.H))
self.assertTrue(
min((scale_ratio_max, scale_ratio_range[0])) <= scale_ratio)


testing.run_module(__name__, __file__)