Repository: kornia/kornia

Issue:
[Bug] RandomPerspective returns wrong transformation matrix
## 🐛 Bug

RandomPerspective returns wrong transformation matrix.

## To Reproduce

Steps to reproduce the behavior:

```
import cv2
import torch
from kornia.augmentation import *
import numpy as np
from kornia.geometry.linalg import transform_points
torch.manual_seed(1)
augm = RandomPerspective(1.,0.0,align_corners = False,return_transform=True)

input = torch.ones((1,3,800,1600)).float()*0.1
input[:,0,300:500,600:1000]=1.
img = (input.numpy().transpose((0,2,3,1))[0,...]*255.).astype(np.uint8)

corners = torch.tensor([[[600.,300.],
                         [1000.,300.],
                         [600.,500.],
                         [1000.,500.]]])
def draw(display_img,coords,path):
    for i in range(coords.shape[-2]):
        display_img=cv2.circle(display_img,(int(coords[0,i,0]),int(coords[0,i,1])),10,(0.,0.,255.),-1)
    cv2.imwrite(path,display_img)
draw(img.copy(),corners,"test.png")

outimg,T = augm(input)
outimg = (outimg.numpy().transpose((0,2,3,1))[0,...]*255).astype(np.uint8)

out_coord = transform_points(T,corners)
draw(outimg.copy(),out_coord,"transformedtest.png")
```
## Expected behavior

The modul should return the correct transformation matrix.

## Environment

PyTorch version: 1.6.0
Is debug build: False
CUDA used to build PyTorch: 10.2

OS: Ubuntu 18.04.4 LTS (x86_64)
GCC version: (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0
Clang version: Could not collect
CMake version: version 3.18.1

Python version: 3.6 (64-bit runtime)
Is CUDA available: True
CUDA runtime version: 10.2.89
GPU models and configuration: GPU 0: GeForce RTX 2080 Ti
Nvidia driver version: 440.44
cuDNN version: /usr/lib/x86_64-linux-gnu/libcudnn.so.7.6.5

Versions of relevant libraries:
[pip3] numpy==1.19.1
[pip3] torch==1.6.0
[pip3] torchvision==0.7.0




Discussion:
@FGeri in `out_coord = T[0,...] @ corners` you need to divide by the last component once you transform the points.
@edgarriba Ah right, but if it doesn't transform the image, it still returns a perspective transform matrix instead of the identity. I updated the example code.
The issue seems to be in ```functional.py``` that ```compute_perspective_transformation``` doesn't take into account if a transformation has been done or not.

Resolving patch:
diff --git a/kornia/augmentation/functional.py b/kornia/augmentation/functional.py
--- a/kornia/augmentation/functional.py
+++ b/kornia/augmentation/functional.py
@@ -1,5 +1,6 @@
 from typing import Tuple, List, Union, Dict, cast, Optional
 
+import kornia as K
 import torch
 import torch.nn as nn
 
@@ -473,8 +474,15 @@ def compute_perspective_transformation(input: torch.Tensor, params: Dict[str, to
     """
     input = _transform_input(input)
     _validate_input_dtype(input, accepted_dtypes=[torch.float16, torch.float32, torch.float64])
-    transform: torch.Tensor = get_perspective_transform(
+    perspective_transform: torch.Tensor = get_perspective_transform(
         params['start_points'], params['end_points']).type_as(input)
+
+    transform: torch.Tensor = K.eye_like(3, input)
+
+    to_transform = params['batch_prob'].to(input.device)
+
+    transform[to_transform] = perspective_transform[to_transform]
+
     return transform
 
 


Test patch:
diff --git a/test/augmentation/test_perspective.py b/test/augmentation/test_perspective.py
--- a/test/augmentation/test_perspective.py
+++ b/test/augmentation/test_perspective.py
@@ -83,16 +83,48 @@ def test_no_transform_module(self, device):
         out_perspective = kornia.augmentation.RandomPerspective()(x_data)
         assert out_perspective.shape == x_data.shape
 
-    def test_transform_module(self, device):
+    def test_transform_module_should_return_identity(self, device):
+        torch.manual_seed(0)
         x_data = torch.rand(1, 2, 4, 5).to(device)
 
-        out_perspective = kornia.augmentation.RandomPerspective(
-            return_transform=True)(x_data)
+        out_perspective = kornia.augmentation.RandomPerspective(p=0.0,
+                                                                return_transform=True)(x_data)
+        assert isinstance(out_perspective, tuple)
+        assert len(out_perspective) == 2
+        assert out_perspective[0].shape == x_data.shape
+        assert out_perspective[1].shape == (1, 3, 3)
+        assert_allclose(out_perspective[0], x_data)
+        assert_allclose(out_perspective[1], torch.eye(3, device=device))
+
+    def test_transform_module_should_return_expected_transform(self, device):
+        torch.manual_seed(0)
+        x_data = torch.rand(1, 2, 4, 5).to(device)
+
+        expected_output = torch.tensor([[[[0.0000000000, 0.0000000000, 0.0000000000, 0.0197417457, 0.0429493971],
+                                          [0.0000000000, 0.5632190704, 0.5321710110, 0.3676981330, 0.1430126727],
+                                          [0.0000000000, 0.3082636893, 0.4031507671, 0.1760708243, 0.0000000000],
+                                          [0.0000000000, 0.0000000000, 0.0000000000, 0.0000000000, 0.0000000000]],
+
+                                         [[0.0000000000, 0.0000000000, 0.0000000000, 0.1189093292, 0.0585946590],
+                                          [0.0000000000, 0.7087295055, 0.5419756770, 0.3995491862, 0.0863459259],
+                                          [0.0000000000, 0.2694899142, 0.5981453061, 0.5887590051, 0.0000000000],
+                                          [0.0000000000, 0.0000000000, 0.0000000000, 0.0000000000, 0.0000000000]]]],
+                                       device=device, dtype=x_data.dtype)
+
+        expected_transform = torch.tensor([[[1.0522739887, 0.3492536247, 0.3045728207],
+                                            [-0.1066057906, 1.0426188707, 0.5845923424],
+                                            [0.0350575559, 0.1213315651, 1.0000000000]]],
+                                          device=device, dtype=x_data.dtype)
+
+        out_perspective = kornia.augmentation.RandomPerspective(p=1.0,
+                                                                return_transform=True)(x_data)
 
         assert isinstance(out_perspective, tuple)
         assert len(out_perspective) == 2
         assert out_perspective[0].shape == x_data.shape
         assert out_perspective[1].shape == (1, 3, 3)
+        assert_allclose(out_perspective[0], expected_output)
+        assert_allclose(out_perspective[1], expected_transform)
 
     def test_gradcheck(self, device):
         input = torch.rand(1, 2, 5, 7).to(device)