From 9664ec87f84e3db2974486016b9fe4a2c39f9e0d Mon Sep 17 00:00:00 2001 From: surgan12 Date: Tue, 15 Oct 2019 19:36:58 +0530 Subject: [PATCH 1/8] vflip and hflip tensor --- torchvision/transforms/functional_tensor.py | 32 +++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 torchvision/transforms/functional_tensor.py diff --git a/torchvision/transforms/functional_tensor.py b/torchvision/transforms/functional_tensor.py new file mode 100644 index 00000000000..6ce84e063da --- /dev/null +++ b/torchvision/transforms/functional_tensor.py @@ -0,0 +1,32 @@ +import torch +import torchvision.transforms.functional as F + +def vflip(img_tensor): + """Vertically flip the given the Image Tensor. + + Args: + img_tensor (Tensor): Image Tensor to be flipped. + + Returns: + Tensor: Vertically flipped image Tensor. + """ + if not F._is_tensor_image(img_tensor): + raise TypeError('tensor is not a torch image.') + + return img_tensor.flip(1) + + +def hflip(img_tensor): + """Horizontally flip the given the Image Tensor. + + Args: + img_tensor (Tensor): Image Tensor to be flipped. + + Returns: + Tensor: Horizontally flipped image Tensor. + """ + + if not F._is_tensor_image(img_tensor): + raise TypeError('tensor is not a torch image.') + + return img_tensor.flip(2) From 4a3946d4f8b7523892654fa5c781616a10b1fad4 Mon Sep 17 00:00:00 2001 From: surgan12 Date: Tue, 15 Oct 2019 19:38:14 +0530 Subject: [PATCH 2/8] vflip and hflip tensor --- test/test_functional_tensor.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 test/test_functional_tensor.py diff --git a/test/test_functional_tensor.py b/test/test_functional_tensor.py new file mode 100644 index 00000000000..e68eed0e9a1 --- /dev/null +++ b/test/test_functional_tensor.py @@ -0,0 +1,24 @@ +import torchvision.transforms.functional_tensor as F_t +import unittest +import torch + +class Tester(unittest.TestCase): + + def test_vflip(self): + img_tensor = torch.randn(3,16,16) + vflipped_img = F_t.vflip(img_tensor) + vflipped_img_again = F_t.vflip(vflipped_img) + + assert vflipped_img.shape == img_tensor.shape + assert torch.equal(img_tensor, vflipped_img_again) + + def test_hflip(self): + img_tensor = torch.randn(3,16,16) + hflipped_img = F_t.hflip(img_tensor) + hflipped_img_again = F_t.hflip(hflipped_img) + + assert hflipped_img.shape == img_tensor.shape + assert torch.equal(img_tensor, hflipped_img_again) + +if __name__ == '__main__': + unittest.main() From 444a69f84df7d08bba07d87b19b15e684fee0aae Mon Sep 17 00:00:00 2001 From: surgan12 Date: Tue, 15 Oct 2019 20:08:53 +0530 Subject: [PATCH 3/8] changes made --- test/test_functional_tensor.py | 29 ++++++++++----------- torchvision/transforms/functional_tensor.py | 17 ++++++------ 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/test/test_functional_tensor.py b/test/test_functional_tensor.py index e68eed0e9a1..02ce19a6462 100644 --- a/test/test_functional_tensor.py +++ b/test/test_functional_tensor.py @@ -2,23 +2,22 @@ import unittest import torch -class Tester(unittest.TestCase): - - def test_vflip(self): - img_tensor = torch.randn(3,16,16) - vflipped_img = F_t.vflip(img_tensor) - vflipped_img_again = F_t.vflip(vflipped_img) - assert vflipped_img.shape == img_tensor.shape - assert torch.equal(img_tensor, vflipped_img_again) +class Tester(unittest.TestCase): - def test_hflip(self): - img_tensor = torch.randn(3,16,16) - hflipped_img = F_t.hflip(img_tensor) - hflipped_img_again = F_t.hflip(hflipped_img) + def test_vflip(self): + img_tensor = torch.randn(3,16,16) + vflipped_img = F_t.vflip(img_tensor) + vflipped_img_again = F_t.vflip(vflipped_img) + self.assertEqual(vflipped_img.shape, img_tensor.shape) + self.assertTrue(torch.equal(img_tensor, vflipped_img_again)) - assert hflipped_img.shape == img_tensor.shape - assert torch.equal(img_tensor, hflipped_img_again) + def test_hflip(self): + img_tensor = torch.randn(3,16,16) + hflipped_img = F_t.hflip(img_tensor) + hflipped_img_again = F_t.hflip(hflipped_img) + self.assertEqual(hflipped_img.shape, img_tensor.shape) + self.assertTrue(torch.equal(img_tensor, hflipped_img_again)) if __name__ == '__main__': - unittest.main() + unittest.main() \ No newline at end of file diff --git a/torchvision/transforms/functional_tensor.py b/torchvision/transforms/functional_tensor.py index 6ce84e063da..abb802fc9bf 100644 --- a/torchvision/transforms/functional_tensor.py +++ b/torchvision/transforms/functional_tensor.py @@ -1,32 +1,33 @@ import torch import torchvision.transforms.functional as F + def vflip(img_tensor): """Vertically flip the given the Image Tensor. Args: - img_tensor (Tensor): Image Tensor to be flipped. + img_tensor (Tensor): Image Tensor to be flipped in the form CXHXW. Returns: Tensor: Vertically flipped image Tensor. """ if not F._is_tensor_image(img_tensor): - raise TypeError('tensor is not a torch image.') - - return img_tensor.flip(1) + raise TypeError('tensor is not a torch image.') + + return img_tensor.flip(-2) def hflip(img_tensor): """Horizontally flip the given the Image Tensor. Args: - img_tensor (Tensor): Image Tensor to be flipped. + img_tensor (Tensor): Image Tensor to be flipped in the form CXHXW. Returns: Tensor: Horizontally flipped image Tensor. """ if not F._is_tensor_image(img_tensor): - raise TypeError('tensor is not a torch image.') - - return img_tensor.flip(2) + raise TypeError('tensor is not a torch image.') + + return img_tensor.flip(-1) \ No newline at end of file From 75d1396ad8aee08654724c052acb2a666a9a60a8 Mon Sep 17 00:00:00 2001 From: surgan12 Date: Tue, 15 Oct 2019 20:16:51 +0530 Subject: [PATCH 4/8] lint --- test/test_functional_tensor.py | 5 +++-- torchvision/transforms/functional_tensor.py | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/test/test_functional_tensor.py b/test/test_functional_tensor.py index 02ce19a6462..4bdab11f6be 100644 --- a/test/test_functional_tensor.py +++ b/test/test_functional_tensor.py @@ -6,18 +6,19 @@ class Tester(unittest.TestCase): def test_vflip(self): - img_tensor = torch.randn(3,16,16) + img_tensor = torch.randn(3, 16, 16) vflipped_img = F_t.vflip(img_tensor) vflipped_img_again = F_t.vflip(vflipped_img) self.assertEqual(vflipped_img.shape, img_tensor.shape) self.assertTrue(torch.equal(img_tensor, vflipped_img_again)) def test_hflip(self): - img_tensor = torch.randn(3,16,16) + img_tensor = torch.randn(3, 16, 16) hflipped_img = F_t.hflip(img_tensor) hflipped_img_again = F_t.hflip(hflipped_img) self.assertEqual(hflipped_img.shape, img_tensor.shape) self.assertTrue(torch.equal(img_tensor, hflipped_img_again)) + if __name__ == '__main__': unittest.main() \ No newline at end of file diff --git a/torchvision/transforms/functional_tensor.py b/torchvision/transforms/functional_tensor.py index abb802fc9bf..bc12327f39b 100644 --- a/torchvision/transforms/functional_tensor.py +++ b/torchvision/transforms/functional_tensor.py @@ -30,4 +30,4 @@ def hflip(img_tensor): if not F._is_tensor_image(img_tensor): raise TypeError('tensor is not a torch image.') - return img_tensor.flip(-1) \ No newline at end of file + return img_tensor.flip(-1) From 8aaa8254f5047a36b6cd73bc8efe9ae68fc102e1 Mon Sep 17 00:00:00 2001 From: surgan12 Date: Tue, 15 Oct 2019 21:18:31 +0530 Subject: [PATCH 5/8] lint --- test/test_functional_tensor.py | 3 ++- torchvision/transforms/functional_tensor.py | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/test/test_functional_tensor.py b/test/test_functional_tensor.py index 4bdab11f6be..b39e53dcdee 100644 --- a/test/test_functional_tensor.py +++ b/test/test_functional_tensor.py @@ -21,4 +21,5 @@ def test_hflip(self): if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main() + \ No newline at end of file diff --git a/torchvision/transforms/functional_tensor.py b/torchvision/transforms/functional_tensor.py index bc12327f39b..ec530dc2f3a 100644 --- a/torchvision/transforms/functional_tensor.py +++ b/torchvision/transforms/functional_tensor.py @@ -6,7 +6,7 @@ def vflip(img_tensor): """Vertically flip the given the Image Tensor. Args: - img_tensor (Tensor): Image Tensor to be flipped in the form CXHXW. + img_tensor (Tensor): Image Tensor to be flipped in the form [C, H, W]. Returns: Tensor: Vertically flipped image Tensor. @@ -21,7 +21,7 @@ def hflip(img_tensor): """Horizontally flip the given the Image Tensor. Args: - img_tensor (Tensor): Image Tensor to be flipped in the form CXHXW. + img_tensor (Tensor): Image Tensor to be flipped in the form [C, H, W]. Returns: Tensor: Horizontally flipped image Tensor. From e52e7700868fa0662599f6d7d25bb04ee3301172 Mon Sep 17 00:00:00 2001 From: surgan12 Date: Tue, 15 Oct 2019 21:39:26 +0530 Subject: [PATCH 6/8] lint failing --- test/test_functional_tensor.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test/test_functional_tensor.py b/test/test_functional_tensor.py index b39e53dcdee..d944a923387 100644 --- a/test/test_functional_tensor.py +++ b/test/test_functional_tensor.py @@ -22,4 +22,3 @@ def test_hflip(self): if __name__ == '__main__': unittest.main() - \ No newline at end of file From c5b084f2db7dd0139629a47054cbe68368f27aeb Mon Sep 17 00:00:00 2001 From: surgan12 Date: Wed, 16 Oct 2019 17:07:49 +0530 Subject: [PATCH 7/8] moving to tensor file --- torchvision/transforms/functional.py | 55 --------------------- torchvision/transforms/functional_tensor.py | 55 +++++++++++++++++++++ torchvision/transforms/transforms.py | 5 +- 3 files changed, 58 insertions(+), 57 deletions(-) diff --git a/torchvision/transforms/functional.py b/torchvision/transforms/functional.py index 6f43d5d263f..a2332bc7e4d 100644 --- a/torchvision/transforms/functional.py +++ b/torchvision/transforms/functional.py @@ -188,36 +188,6 @@ def to_pil_image(pic, mode=None): return Image.fromarray(npimg, mode=mode) -def normalize(tensor, mean, std, inplace=False): - """Normalize a tensor image with mean and standard deviation. - - .. note:: - This transform acts out of place by default, i.e., it does not mutates the input tensor. - - See :class:`~torchvision.transforms.Normalize` for more details. - - Args: - tensor (Tensor): Tensor image of size (C, H, W) to be normalized. - mean (sequence): Sequence of means for each channel. - std (sequence): Sequence of standard deviations for each channel. - inplace(bool,optional): Bool to make this operation inplace. - - Returns: - Tensor: Normalized Tensor image. - """ - if not _is_tensor_image(tensor): - raise TypeError('tensor is not a torch image.') - - if not inplace: - tensor = tensor.clone() - - dtype = tensor.dtype - mean = torch.as_tensor(mean, dtype=dtype, device=tensor.device) - std = torch.as_tensor(std, dtype=dtype, device=tensor.device) - tensor.sub_(mean[:, None, None]).div_(std[:, None, None]) - return tensor - - def resize(img, size, interpolation=Image.BILINEAR): r"""Resize the input PIL Image to the given size. @@ -830,28 +800,3 @@ def to_grayscale(img, num_output_channels=1): raise ValueError('num_output_channels should be either 1 or 3') return img - - -def erase(img, i, j, h, w, v, inplace=False): - """ Erase the input Tensor Image with given value. - - Args: - img (Tensor Image): Tensor image of size (C, H, W) to be erased - i (int): i in (i,j) i.e coordinates of the upper left corner. - j (int): j in (i,j) i.e coordinates of the upper left corner. - h (int): Height of the erased region. - w (int): Width of the erased region. - v: Erasing value. - inplace(bool, optional): For in-place operations. By default is set False. - - Returns: - Tensor Image: Erased image. - """ - if not isinstance(img, torch.Tensor): - raise TypeError('img should be Tensor Image. Got {}'.format(type(img))) - - if not inplace: - img = img.clone() - - img[:, i:i + h, j:j + w] = v - return img diff --git a/torchvision/transforms/functional_tensor.py b/torchvision/transforms/functional_tensor.py index ec530dc2f3a..0e9b022c3ed 100644 --- a/torchvision/transforms/functional_tensor.py +++ b/torchvision/transforms/functional_tensor.py @@ -31,3 +31,58 @@ def hflip(img_tensor): raise TypeError('tensor is not a torch image.') return img_tensor.flip(-1) + + +def erase(img, i, j, h, w, v, inplace=False): + """ Erase the input Tensor Image with given value. + + Args: + img (Tensor Image): Tensor image of size (C, H, W) to be erased + i (int): i in (i,j) i.e coordinates of the upper left corner. + j (int): j in (i,j) i.e coordinates of the upper left corner. + h (int): Height of the erased region. + w (int): Width of the erased region. + v: Erasing value. + inplace(bool, optional): For in-place operations. By default is set False. + + Returns: + Tensor Image: Erased image. + """ + if not isinstance(img, torch.Tensor): + raise TypeError('img should be Tensor Image. Got {}'.format(type(img))) + + if not inplace: + img = img.clone() + + img[:, i:i + h, j:j + w] = v + return img + + +def normalize(tensor, mean, std, inplace=False): + """Normalize a tensor image with mean and standard deviation. + + .. note:: + This transform acts out of place by default, i.e., it does not mutates the input tensor. + + See :class:`~torchvision.transforms.Normalize` for more details. + + Args: + tensor (Tensor): Tensor image of size (C, H, W) to be normalized. + mean (sequence): Sequence of means for each channel. + std (sequence): Sequence of standard deviations for each channel. + inplace(bool,optional): Bool to make this operation inplace. + + Returns: + Tensor: Normalized Tensor image. + """ + if not _is_tensor_image(tensor): + raise TypeError('tensor is not a torch image.') + + if not inplace: + tensor = tensor.clone() + + dtype = tensor.dtype + mean = torch.as_tensor(mean, dtype=dtype, device=tensor.device) + std = torch.as_tensor(std, dtype=dtype, device=tensor.device) + tensor.sub_(mean[:, None, None]).div_(std[:, None, None]) + return tensor diff --git a/torchvision/transforms/transforms.py b/torchvision/transforms/transforms.py index 3ec84aae84c..f7340e18572 100644 --- a/torchvision/transforms/transforms.py +++ b/torchvision/transforms/transforms.py @@ -15,6 +15,7 @@ import warnings from . import functional as F +from . import functional_tensor as F_t if sys.version_info < (3, 3): Sequence = collections.Sequence @@ -172,7 +173,7 @@ def __call__(self, tensor): Returns: Tensor: Normalized Tensor image. """ - return F.normalize(tensor, self.mean, self.std, self.inplace) + return F_t.normalize(tensor, self.mean, self.std, self.inplace) def __repr__(self): return self.__class__.__name__ + '(mean={0}, std={1})'.format(self.mean, self.std) @@ -1297,5 +1298,5 @@ def __call__(self, img): """ if random.uniform(0, 1) < self.p: x, y, h, w, v = self.get_params(img, scale=self.scale, ratio=self.ratio, value=self.value) - return F.erase(img, x, y, h, w, v, self.inplace) + return F_t.erase(img, x, y, h, w, v, self.inplace) return img From 6097d19d9b80d9048ef17aee2595edf2b11cdfbc Mon Sep 17 00:00:00 2001 From: surgan12 Date: Wed, 16 Oct 2019 17:56:44 +0530 Subject: [PATCH 8/8] updates --- references/segmentation/transforms.py | 4 ++-- test/test_transforms.py | 5 +++-- torchvision/transforms/functional_tensor.py | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/references/segmentation/transforms.py b/references/segmentation/transforms.py index bce4bfbe639..d555d1ad751 100644 --- a/references/segmentation/transforms.py +++ b/references/segmentation/transforms.py @@ -5,7 +5,7 @@ import torch from torchvision import transforms as T from torchvision.transforms import functional as F - +import torchvision.transforms.functional_tensor as F_t def pad_if_smaller(img, size, fill=0): min_size = min(img.size) @@ -88,5 +88,5 @@ def __init__(self, mean, std): self.std = std def __call__(self, image, target): - image = F.normalize(image, mean=self.mean, std=self.std) + image = F_t.normalize(image, mean=self.mean, std=self.std) return image, target diff --git a/test/test_transforms.py b/test/test_transforms.py index 7e8320d6d6c..19689b7b7d6 100644 --- a/test/test_transforms.py +++ b/test/test_transforms.py @@ -3,6 +3,7 @@ import torch import torchvision.transforms as transforms import torchvision.transforms.functional as F +import torchvision.transforms.functional_tensor as F_t from torch._utils_internal import get_file_path_2 import unittest import math @@ -838,7 +839,7 @@ def test_normalize_different_dtype(self): mean = torch.tensor([1, 2, 3], dtype=dtype2) std = torch.tensor([1, 2, 1], dtype=dtype2) # checks that it doesn't crash - transforms.functional.normalize(img, mean, std) + transforms.functional_tensor.normalize(img, mean, std) def test_adjust_brightness(self): x_shape = [2, 2, 3] @@ -1363,7 +1364,7 @@ def test_random_erasing(self): # Test Set 1: Erasing with int value img_re = transforms.RandomErasing(value=0.2) i, j, h, w, v = img_re.get_params(img, scale=img_re.scale, ratio=img_re.ratio, value=img_re.value) - img_output = F.erase(img, i, j, h, w, v) + img_output = F_t.erase(img, i, j, h, w, v) assert img_output.size(0) == 3 # Test Set 2: Check if the unerased region is preserved diff --git a/torchvision/transforms/functional_tensor.py b/torchvision/transforms/functional_tensor.py index 0e9b022c3ed..b1724b081b9 100644 --- a/torchvision/transforms/functional_tensor.py +++ b/torchvision/transforms/functional_tensor.py @@ -75,7 +75,7 @@ def normalize(tensor, mean, std, inplace=False): Returns: Tensor: Normalized Tensor image. """ - if not _is_tensor_image(tensor): + if not F._is_tensor_image(tensor): raise TypeError('tensor is not a torch image.') if not inplace: