-
Notifications
You must be signed in to change notification settings - Fork 7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Unified Tensor/PIL crop #2342
Unified Tensor/PIL crop #2342
Conversation
…-2292-unify-crop-op
- crop with padding - other tests using mising private functions: _is_pil_image, _get_image_size
- sorted includes in transforms.py - used py3 annotations
0a166fd
to
94e29a4
Compare
def ten_crop(img, size, vertical_flip=False): | ||
"""Generate ten cropped images from the given PIL Image. | ||
Crop the given PIL Image into four corners and the central crop plus the | ||
def ten_crop(img: Tensor, size: List[int], vertical_flip: bool = False) -> List[Tensor]: |
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.
List[Tensor]
instead of Tuple[Tensor, ..., Tensor]
due to torch.jit
behaviour when concat two tuples.
@fmassa could you please review this PR |
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.
Thanks a lot for the PR Victor!
I only really have two comments (about @torch.jit.export
), apart from that the PR looks good to merge, thanks!
@@ -3,12 +3,19 @@ | |||
from torch.jit.annotations import List, BroadcastingList2 | |||
|
|||
|
|||
def _is_tensor_a_torch_image(input): | |||
return input.ndim >= 2 | |||
@torch.jit.export |
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.
I'm not sure @torch.jit.export
is needed?
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.
Yes, you are right about that, I'll remove it
|
||
|
||
def vflip(img): | ||
# type: (Tensor) -> Tensor | ||
@torch.jit.export |
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.
same comment here, we now have recursive compilation, so I don't think this is actually needed. Plus, I thought that torch.jit.export
was needed only for decorating nn.Module
methods?
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.
same here, i'll remove it
@@ -39,12 +45,11 @@ def hflip(img): | |||
return img.flip(-1) | |||
|
|||
|
|||
def crop(img, top, left, height, width): | |||
# type: (Tensor, int, int, int, int) -> Tensor | |||
def crop(img: Tensor, top: int, left: int, height: int, width: int): |
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.
nit: missing the return type?
torchvision/transforms/transforms.py
Outdated
@@ -623,7 +640,7 @@ def __call__(self, img): | |||
Returns: | |||
PIL Image: Random perspectivley transformed image. | |||
""" | |||
if not F._is_pil_image(img): | |||
if not F.F_pil._is_pil_image(img): |
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.
This looks weird? Although I see the point, I'm not sure I would recommend users importing F_pil
from the functional.py
file.
Plus, both the functional_pil
and functional_tensor
are meant to be just implementation details functions, and might be preferable if they didn't leak to transforms.py
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.
I agree, looks weird. I can introduce in functional.py
as shortcut
from . import functional_pil as F_pil
from . import functional_tensor as F_t
_is_pil_image = F_pil._is_pil_image
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.
sounds good to me!
- removed useless torch.jit.export - added missing typing return type - fixed F.F_pil._is_pil_image -> F._is_pil_image
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.
Thanks for the review Francisco !
I'll push modification according to the review !
@@ -3,12 +3,19 @@ | |||
from torch.jit.annotations import List, BroadcastingList2 | |||
|
|||
|
|||
def _is_tensor_a_torch_image(input): | |||
return input.ndim >= 2 | |||
@torch.jit.export |
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.
Yes, you are right about that, I'll remove it
|
||
|
||
def vflip(img): | ||
# type: (Tensor) -> Tensor | ||
@torch.jit.export |
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.
same here, i'll remove it
torchvision/transforms/transforms.py
Outdated
@@ -623,7 +640,7 @@ def __call__(self, img): | |||
Returns: | |||
PIL Image: Random perspectivley transformed image. | |||
""" | |||
if not F._is_pil_image(img): | |||
if not F.F_pil._is_pil_image(img): |
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.
I agree, looks weird. I can introduce in functional.py
as shortcut
from . import functional_pil as F_pil
from . import functional_tensor as F_t
_is_pil_image = F_pil._is_pil_image
torchvision/transforms/functional.py
Outdated
_is_pil_image = F_pil._is_pil_image | ||
|
||
|
||
@torch.jit.export |
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.
Maybe event this one is unnecessary
crop_left = int(round((image_width - crop_width) / 2.)) | ||
|
||
# crop_top = int(round((image_height - crop_height) / 2.)) | ||
# Result can be different between python func and scripted func |
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.
Can you explain why the results can be different? If yes this is a bug that should be reported / fixed
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.
This is, probably, a bug with torch.jit.script
:
import torch
def foo(x: int) -> int:
return int(round(x / 2.0))
sfoo = torch.jit.script(foo)
assert foo(7) == sfoo(7)
# but
assert foo(5) == sfoo(5)
> 2 vs 3
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.
Thanks for the snippet, this can be further simplified as follows
import torch
def foo():
return round(2.5)
sfoo = torch.jit.script(foo)
print(foo(), sfoo())
# 2, 3.0
# different values and types
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.
Opened an issue in PyTorch to track this pytorch/pytorch#40771
Can you add a comment to revert back to the original implementation once that issue is fixed?
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.
Actually, I saw that you left the code commented out, so this is fine for now
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.
Have 3 comments left, otherwise looks good to go!
torchvision/transforms/functional.py
Outdated
|
||
def _is_numpy(img): | ||
|
||
@torch.jit.ignore |
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.
I think torch.jit.unused
is better here, otherwise those functions won't be able to be serialized / exported
torchvision/transforms/functional.py
Outdated
return isinstance(img, np.ndarray) | ||
|
||
|
||
def _is_numpy_image(img): | ||
@torch.jit.ignore |
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.
Same here, I think torch.jit.unused
is better in this case
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.
Thanks a lot Victor!
* [WIP] Unified Tensor/PIL crop * Fixed misplaced type annotation * Fixed tests - crop with padding - other tests using mising private functions: _is_pil_image, _get_image_size * Unified CenterCrop and F.center_crop - sorted includes in transforms.py - used py3 annotations * Unified FiveCrop and F.five_crop * Improved tests and docs * Unified TenCrop and F.ten_crop * Removed useless typing in functional_pil * Updated code according to the review - removed useless torch.jit.export - added missing typing return type - fixed F.F_pil._is_pil_image -> F._is_pil_image * Removed useless torch.jit.export * Improved code according to the review
Addresses #2292
Description:
RandomCrop
andF.crop
CenterCrop
andF.center_crop
FiveCrop
andF.five_crop
TenCrop
andF.ten_crop