Skip to content
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

Fixes F.affine and F.rotate to support rectangular tensor images #2553

Merged
merged 14 commits into from
Aug 6, 2020

Conversation

vfdev-5
Copy link
Collaborator

@vfdev-5 vfdev-5 commented Aug 4, 2020

Description:

Currently, F.affine does not work correctly on rectangular tensor images due to normalized output of affine_grid.
In this PR there is an attempt to fix the issue. Code is not nice due to dispatch in two part of implementations.

EDIT: Same is for F.rotate.

image

  • Added tests on square and rectangular images for F.affine and F.rotate

@vfdev-5 vfdev-5 requested a review from fmassa August 4, 2020 20:39

matrix = _get_inverse_affine_matrix([0, 0], angle, translate, scale, shear)
matrix = _get_inverse_affine_matrix([0, 0], angle, translate_f, scale, shear)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Here, I do not like that depending on if image is square or not matrice's translation part is normalized or not...

Copy link
Member

Choose a reason for hiding this comment

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

Can't we just make everything follow the same code-path?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The problem is that in case of square image we use affine_grid and it requires rescaled translation part, so we compute matrix here with rescaled translate_f. In case of rectangular images, we use custom affine grid implementation _gen_affine_grid where coords normalization is applied a posteriori and we need to deal with matrix where translation part is not normalized. Online normalization,denormalization of matrix is not evident neither. That's why there are two pathes.

Copy link
Member

Choose a reason for hiding this comment

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

can't we just use _gen_affine_grid everywhere?

pts = torch.stack([x, y, torch.ones_like(x)], dim=-1)
output_grid = torch.matmul(pts, theta.t())

output_grid = output_grid / torch.tensor([0.5 * w, 0.5 * h])
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Here is the principal difference to affine_grid-like implementation. In affine_grid-lik implementation it would be

    x = (torch.arange(ow) + d - ow * 0.5) / (0.5 * w)
    y = (torch.arange(oh) + d - oh * 0.5) / (0.5 * h)

instead of output_grid scaling.

Copy link
Member

@fmassa fmassa left a comment

Choose a reason for hiding this comment

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

Thanks for fixing this!

I have a few questions, let me know what you think

Comment on lines 670 to 675
if shape[-2] == shape[-1]:
# here we need normalized translation part of theta
grid = affine_grid(theta, size=(1, shape[-3], shape[-2], shape[-1]), align_corners=False)
else:
# here we need denormalized translation part of theta
grid = _gen_affine_grid(theta[0, :, :], w=shape[-1], h=shape[-2], ow=shape[-1], oh=shape[-2])
Copy link
Member

Choose a reason for hiding this comment

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

Can you replace everything to use _gen_affine_grid, and make a comment on why affine_grid is not suited for this use-case? I even wonder if we shouldn't open an issue in PyTorch about this

Copy link
Collaborator Author

@vfdev-5 vfdev-5 Aug 5, 2020

Choose a reason for hiding this comment

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

I didn't benchmarked both methods to compare the performances, I assumed that affine_grid is better optimized that manual _gen_affine_grid. That's why I've chosen to split here. Do you mean to wrap everything with _gen_affine_grid and split inside this method ?

About an issue in PyTorch, I think pytorch/pytorch#24870 and pytorch/pytorch#36107 already speak about absolute pixel coords. Probably, they are related to this problem.

Copy link
Member

Choose a reason for hiding this comment

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

I mean to just dispatch to _gen_affine_grid, and not use nn.functional.affine_grid. The difference in speed should be very small I think, I'm not sure we dispatch to a cudnn-optimized affine_grid so it would be basically the same operations but called from C++


matrix = _get_inverse_affine_matrix([0, 0], angle, translate, scale, shear)
matrix = _get_inverse_affine_matrix([0, 0], angle, translate_f, scale, shear)
Copy link
Member

Choose a reason for hiding this comment

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

Can't we just make everything follow the same code-path?

@vfdev-5 vfdev-5 changed the title [WIP] Fixes F.affine to support rectangular tensor images Fixes F.affine and F.rotate to support rectangular tensor images Aug 6, 2020
@vfdev-5
Copy link
Collaborator Author

vfdev-5 commented Aug 6, 2020

@fmassa PR is ready to review. What is done:

  • F_t.affine and F_t.rotate use common method _gen_affine_grid to generate a grid which supports square and rectangular images (due normalization issue with torch native affine_grid). No more routing depending on image shape as it was proposed previously.
  • Code is tested on square/rect images for F.affine and F.rotate
  • _gen_affine_grid mimicks affine_grid C++ implementation and optimized such that on CPU it is almost same execution time as for affine_grid.

@codecov
Copy link

codecov bot commented Aug 6, 2020

Codecov Report

Merging #2553 into master will increase coverage by 0.00%.
The diff coverage is 100.00%.

Impacted file tree graph

@@           Coverage Diff           @@
##           master    #2553   +/-   ##
=======================================
  Coverage   71.81%   71.81%           
=======================================
  Files          94       94           
  Lines        8079     8080    +1     
  Branches     1283     1282    -1     
=======================================
+ Hits         5802     5803    +1     
  Misses       1868     1868           
  Partials      409      409           
Impacted Files Coverage Δ
torchvision/transforms/functional.py 80.11% <100.00%> (ø)
torchvision/transforms/functional_tensor.py 67.50% <100.00%> (+0.10%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 7666252...c862126. Read the comment docs.

@vfdev-5 vfdev-5 requested a review from fmassa August 6, 2020 12:27
Copy link
Member

@fmassa fmassa left a comment

Choose a reason for hiding this comment

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

Looks great, thanks a lot!

@fmassa fmassa merged commit 025b71d into pytorch:master Aug 6, 2020
@vfdev-5 vfdev-5 deleted the fix-affine-rect-imgs branch August 6, 2020 13:01
@vfdev-5 vfdev-5 mentioned this pull request Aug 7, 2020
16 tasks
bryant1410 pushed a commit to bryant1410/vision-1 that referenced this pull request Nov 22, 2020
…orch#2553)

* Added code for F_t.rotate with test
- updated F.affine tests

* Rotate test tolerance to 2%

* Fixes failing test

* Optimized _expanded_affine_grid with a single matmul op

* Recoded _compute_output_size

* [WIP] recoded F_t.rotate internal methods

* [WIP] Fixed F.affine to support rectangular images

* Recoded _gen_affine_grid to optimized version ~ affine_grid
- Fixes flake8

* [WIP] Use _gen_affine_grid for affine and rotate

* Fixed tests on square / rectangular images for affine and rotate ops

* Removed redefinition of F.rotate
- due to bad merge
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants