This repository has been archived by the owner on Jul 2, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 303
Add random_erasing #558
Open
akitotakeki
wants to merge
30
commits into
chainer:master
Choose a base branch
from
akitotakeki:akitotakeki-add-random-erasing
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Add random_erasing #558
Changes from 20 commits
Commits
Show all changes
30 commits
Select commit
Hold shift + click to select a range
82e1b06
add random_erasing
akitotakeki 0eebc97
add test
akitotakeki 8dd6a42
fix test
akitotakeki 5033569
Merge branch 'pr/432' into akitotakeki-add-random-erasing
akitotakeki f7e6390
add trial option
akitotakeki 5aa696c
use _sample_parameters in random_sized_crop
akitotakeki dfb1712
fix doc
akitotakeki f0dc4e9
fix mean
akitotakeki 4220892
use numpy.ndarray for mean
akitotakeki 58037c3
fix mean
akitotakeki c5dd0e5
fix import
akitotakeki b527229
fix mean value in test
akitotakeki 6612b8b
fix import
akitotakeki 9f2d327
fix aspect range in test
akitotakeki 9269d96
fix test
akitotakeki 8fe9235
use random_sized_crop
akitotakeki 15a1610
fix doc and add scale option
akitotakeki 6024db8
fix test
akitotakeki 01b6672
Merge branch 'master' into akitotakeki-add-random-erasing
akitotakeki 39b93d8
fix params
akitotakeki e170fa5
Merge branch 'akitotakeki-add-random-erasing' of https://github.com/a…
akitotakeki 78cb847
fix prob
akitotakeki 6bf2ef6
reuse _sample_parameters
akitotakeki 1ee30f1
Revert "reuse _sample_parameters"
akitotakeki d5efc32
use img.copy()
akitotakeki 26b0f0e
change from fixed_value to fill
akitotakeki 2f4fc64
fix the range of pixel
akitotakeki 4dfd64b
Merge remote-tracking branch 'upstream/master' into akitotakeki-add-r…
akitotakeki c76eb27
Merge branch 'akitotakeki-add-random-erasing' of https://github.com/a…
akitotakeki 4033e52
rm scale option
akitotakeki File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
from __future__ import division | ||
|
||
import math | ||
import numpy as np | ||
import random | ||
|
||
from random_sized_crop import _sample_parameters | ||
|
||
|
||
def random_erasing(img, prob=0.5, | ||
scale_ratio_range=(0.02, 0.4), | ||
aspect_ratio_range=(0.3, 1 / 0.3), | ||
mean=[0.4914, 0.4822, 0.4465], | ||
return_param=False, copy=False): | ||
"""Select a rectangle region in an image and erase its pixels with mean 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_interval` and :math:`a_1, a_2` are the two floats | ||
in :obj:`aspect_ratio_interval`. | ||
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_interval[0]`. | ||
|
||
Args: | ||
img (~numpy.ndarray): An image array. This is in CHW format. | ||
prob (float): Erasing probability. | ||
scale_ratio_interval (tuple of two floats): Determines | ||
the distribution from which a scale ratio is sampled. | ||
aspect_ratio_interval (tuple of two floats): Determines | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the arguments, |
||
the distribution from which an aspect ratio is sampled. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
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.randint(0, 1) > prob: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
_, H, W = img.shape | ||
scale_ratio, aspect_ratio =\ | ||
_sample_parameters( | ||
(H, W), scale_ratio_range, aspect_ratio_range, trial=100) | ||
|
||
H_crop = int(math.floor(np.sqrt(scale_ratio * H * W * aspect_ratio))) | ||
W_crop = int(math.floor(np.sqrt(scale_ratio * H * W / aspect_ratio))) | ||
y_start = random.randint(0, H - H_crop) | ||
x_start = random.randint(0, W - W_crop) | ||
y_slice = slice(y_start, y_start + H_crop) | ||
x_slice = slice(x_start, x_start + W_crop) | ||
|
||
img[:, y_slice, x_slice] = mean | ||
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
from __future__ import division | ||
|
||
import math | ||
import numpy as np | ||
import random | ||
|
||
|
||
def random_sized_crop(img, | ||
scale_ratio_interval=(0.08, 1), | ||
aspect_ratio_interval=(3 / 4, 4 / 3), | ||
return_param=False, copy=False): | ||
"""Crop an image to random size and aspect ratio. | ||
|
||
The size :math:`(H_{crop}, W_{crop})` and the left top coordinate | ||
:math:`(y_{start}, x_{start})` of the crop are calculated as follows: | ||
|
||
+ :math:`H_{crop} = \\lfloor{\\sqrt{s \\times H \\times W \ | ||
\\times a}}\\rfloor` | ||
+ :math:`W_{crop} = \\lfloor{\\sqrt{s \\times H \\times W \ | ||
\\div a}}\\rfloor` | ||
+ :math:`y_{start} \\sim Uniform\\{0, H - H_{crop}\\}` | ||
+ :math:`x_{start} \\sim Uniform\\{0, W - W_{crop}\\}` | ||
+ :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_interval` and :math:`a_1, a_2` are the two floats | ||
in :obj:`aspect_ratio_interval`. | ||
Also, :math:`H` and :math:`W` are the height and the width of the image. | ||
Note that :math:`s \\approx \\frac{H_{crop} \\times W_{crop}}{H \\times W}` | ||
and :math:`a \\approx \\frac{H_{crop}}{W_{crop}}`. | ||
The approximations come from flooring floats to integers. | ||
|
||
.. note:: | ||
|
||
When it fails to sample a valid scale and aspect ratio for ten | ||
times, it picks values in a non-uniform way. | ||
If this happens, the selected scale ratio can be smaller | ||
than :obj:`scale_ratio_interval[0]`. | ||
|
||
Args: | ||
img (~numpy.ndarray): An image array. This is in CHW format. | ||
scale_ratio_interval (tuple of two floats): Determines | ||
the distribution from which a scale ratio is sampled. | ||
The default values are selected so that the area of the crop is | ||
8~100% of the original image. This is the default | ||
setting used to train ResNets in Torch style. | ||
aspect_ratio_interval (tuple of two floats): Determines | ||
the distribution from which an aspect ratio is sampled. | ||
The default values are | ||
:math:`\\frac{3}{4}` and :math:`\\frac{4}{3}`, which | ||
are also the default setting to train ResNets in Torch style. | ||
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 cropped 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 crop the input image.\ | ||
The relation below holds together with :obj:`x_slice`. | ||
* **x_slice** (*slice*): Similar to :obj:`y_slice`. | ||
|
||
.. code:: | ||
|
||
out_img = img[:, y_slice, x_slice] | ||
|
||
* **scale_ratio** (float): :math:`s` in the description (see above). | ||
* **aspect_ratio** (float): :math:`a` in the description. | ||
|
||
""" | ||
_, H, W = img.shape | ||
scale_ratio, aspect_ratio =\ | ||
_sample_parameters( | ||
(H, W), scale_ratio_interval, aspect_ratio_interval) | ||
|
||
H_crop = int(math.floor(np.sqrt(scale_ratio * H * W * aspect_ratio))) | ||
W_crop = int(math.floor(np.sqrt(scale_ratio * H * W / aspect_ratio))) | ||
y_start = random.randint(0, H - H_crop) | ||
x_start = random.randint(0, W - W_crop) | ||
y_slice = slice(y_start, y_start + H_crop) | ||
x_slice = slice(x_start, x_start + W_crop) | ||
|
||
img = img[:, y_slice, x_slice] | ||
|
||
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 | ||
|
||
|
||
def _sample_parameters(size, scale_ratio_interval, aspect_ratio_interval, | ||
trial=10): | ||
H, W = size | ||
for _ in range(trial): | ||
aspect_ratio = random.uniform( | ||
aspect_ratio_interval[0], aspect_ratio_interval[1]) | ||
if random.uniform(0, 1) < 0.5: | ||
aspect_ratio = 1 / aspect_ratio | ||
# This is determined so that relationships "H - H_crop >= 0" and | ||
# "W - W_crop >= 0" are always satisfied. | ||
scale_ratio_max = min((scale_ratio_interval[1], | ||
H / (W * aspect_ratio), | ||
(aspect_ratio * W) / H)) | ||
|
||
scale_ratio = random.uniform( | ||
scale_ratio_interval[0], scale_ratio_interval[1]) | ||
if scale_ratio_interval[0] <= scale_ratio <= scale_ratio_max: | ||
return scale_ratio, aspect_ratio | ||
|
||
# This scale_ratio is outside the given interval when | ||
# scale_ratio_max < scale_ratio_interval[0]. | ||
scale_ratio = random.uniform( | ||
min((scale_ratio_interval[0], scale_ratio_max)), scale_ratio_max) | ||
return scale_ratio, aspect_ratio |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
from __future__ import division | ||
|
||
import unittest | ||
|
||
import numpy as np | ||
|
||
from chainer import testing | ||
from chainercv.transforms import random_erasing | ||
|
||
|
||
@testing.parameterize( | ||
{'H': 256, 'W': 256}, | ||
{'H': 129, 'W': 352}, | ||
{'H': 352, 'W': 129}, | ||
{'H': 35, 'W': 500}, | ||
) | ||
class TestRandomErasing(unittest.TestCase): | ||
|
||
def test_random_sized_crop(self): | ||
img = np.random.uniform(size=(3, self.H, self.W)) | ||
prob = 0.5 | ||
scale_ratio_interval = (0.02, 4) | ||
aspect_ratio_interval = (0.3, 1 / 0.3) | ||
mean = [0.4914, 0.4822, 0.4465] | ||
out, params = random_erasing(img, prob, scale_ratio_interval, | ||
aspect_ratio_interval, mean, | ||
return_param=True) | ||
|
||
scale_ratio = params['scale_ratio'] | ||
aspect_ratio = params['aspect_ratio'] | ||
|
||
self.assertTrue( | ||
(aspect_ratio_interval[0] <= aspect_ratio) and | ||
(aspect_ratio <= aspect_ratio_interval[1])) | ||
self.assertTrue( | ||
scale_ratio <= scale_ratio_interval[1]) | ||
scale_ratio_max = min((scale_ratio_interval[1], | ||
self.H / (self.W * aspect_ratio), | ||
(aspect_ratio * self.W) / self.H)) | ||
self.assertTrue( | ||
min((scale_ratio_max, scale_ratio_interval[0])) <= scale_ratio) | ||
|
||
|
||
testing.run_module(__name__, __file__) |
54 changes: 54 additions & 0 deletions
54
tests/transforms_tests/image_tests/test_random_sized_crop.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
from __future__ import division | ||
|
||
import unittest | ||
|
||
import math | ||
import numpy as np | ||
|
||
from chainer import testing | ||
from chainercv.transforms import random_sized_crop | ||
|
||
|
||
@testing.parameterize( | ||
{'H': 256, 'W': 256}, | ||
{'H': 129, 'W': 352}, | ||
{'H': 352, 'W': 129}, | ||
{'H': 35, 'W': 500}, | ||
) | ||
class TestRandomSizedCrop(unittest.TestCase): | ||
|
||
def test_random_sized_crop(self): | ||
img = np.random.uniform(size=(3, self.H, self.W)) | ||
scale_ratio_interval = (0.08, 1) | ||
aspect_ratio_interval = (3 / 4, 4 / 3) | ||
out, params = random_sized_crop(img, scale_ratio_interval, | ||
aspect_ratio_interval, | ||
return_param=True) | ||
|
||
expected = img[:, params['y_slice'], params['x_slice']] | ||
np.testing.assert_equal(out, expected) | ||
|
||
_, H_crop, W_crop = out.shape | ||
scale_ratio = params['scale_ratio'] | ||
aspect_ratio = params['aspect_ratio'] | ||
area = scale_ratio * self.H * self.W | ||
expected_H_crop = int(math.floor( | ||
np.sqrt(area * aspect_ratio))) | ||
expected_W_crop = int(math.floor( | ||
np.sqrt(area / aspect_ratio))) | ||
self.assertEqual(H_crop, expected_H_crop) | ||
self.assertEqual(W_crop, expected_W_crop) | ||
|
||
self.assertTrue( | ||
(aspect_ratio_interval[0] <= aspect_ratio) and | ||
(aspect_ratio <= aspect_ratio_interval[1])) | ||
self.assertTrue( | ||
scale_ratio <= scale_ratio_interval[1]) | ||
scale_ratio_max = min((scale_ratio_interval[1], | ||
self.H / (self.W * aspect_ratio), | ||
(aspect_ratio * self.W) / self.H)) | ||
self.assertTrue( | ||
min((scale_ratio_max, scale_ratio_interval[0])) <= scale_ratio) | ||
|
||
|
||
testing.run_module(__name__, __file__) |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the arguments,
scale_ratio_range
is used.