Repository: kornia/kornia

Issue:
mode="vertices" in Boxes.from_tensor add +1 to boxes width and height
### Describe the bug

mode="vertices" in Boxes.from_tensor add +1 to boxes width and height

### Reproduction steps

```python
from kornia.geometry.boxes import Boxes

boxes = torch.tensor([[[355,10, 660,250], [10,125, 200,400]]]) 
vertices = Boxes.from_tensor(boxes).to_tensor('vertices')
assert (vertices == Boxes.from_tensor(vertices, mode="vertices").to_tensor("vertices")) # <-- fails
```
original vertices
```
tensor([[[[355.,  10.],
          [660.,  10.],
          [660., 250.],
          [355., 250.]],

         [[ 10., 125.],
          [200., 125.],
          [200., 400.],
          [ 10., 400.]]]])


```
after from and to boxes
```
tensor([[[[355.,  10.],
          [661.,  10.],
          [661., 251.],
          [355., 251.]],

         [[ 10., 125.],
          [201., 125.],
          [201., 401.],
          [ 10., 401.]]]])

```


### Expected behavior

To return the same original boxes

### Environment

```shell
kornia 0.6.5
torch 1.9.1+cpu
```


### Additional context

_No response_

Discussion:
hal-314: Failing CI seems to be due to mypy failing in a code region that I didn't touch it in this PR. I don't understand why this PR is failing and the other two (#1739 & #1741) that I made almost simultaneously didn't.


```bash

=================================== FAILURES ===================================
_________________________________ test session _________________________________
mypy exited with status 1.
=============================== warnings summary ===============================
....

===================================== mypy =====================================
kornia/augmentation/container/image.py:347: error: Invalid index type
"Union[str, Any]" for "List[ParamItem]"; expected type "SupportsIndex"  [index]
                    param = params[name] if name in params else param
                                   ^
kornia/augmentation/container/image.py:356: error: Item "None" of
"Union[ParamItem, Any, None]" has no attribute "data"  [union-attr]
                    input = module.inverse(input, param.data, extra_args=e...
                                                  ^
kornia/augmentation/container/image.py:356: error: Argument 2 to "inverse" of
"ImageSequential" has incompatible type
"Union[Dict[Any, Any], List[Any], None, Any]"; expected
"Optional[List[ParamItem]]"  [arg-type]
                    input = module.inverse(input, param.data, extra_args=e...
                                                  ^
kornia/augmentation/container/augment.py:247: error: Invalid index type
"Union[str, Any]" for "List[ParamItem]"; expected type "SupportsIndex"  [index]
                        param = params[name] if name in params else param
                                       ^
Found 4 errors in 2 files (checked 127 source files)
=========================== short test summary info ============================
FAILED test/__init__.py::mypy-status
= 1 failed, 15531 passed, 220 skipped, 41 xfailed, 20 xpassed, 997 warnings in 1016.60s (0:16:56) =
```
edgarriba: @hal-314 don't worry about this, is a mypy issue. I'll try to fix over the weekend. It's blocking other PRS too

Patch:
diff --git a/kornia/geometry/boxes.py b/kornia/geometry/boxes.py
index bbc005ec..79d14342 100644
--- a/kornia/geometry/boxes.py
+++ b/kornia/geometry/boxes.py
@@ -97,10 +97,15 @@ def _boxes_to_quadrilaterals(
 
     if mode.startswith("vertices"):
         if mode == "vertices":
-            # Avoid passing reference
             quadrilaterals = boxes.clone()
+            # Here, vertices are quadrilaterals with width and height defined as `width = xmax - xmin`  and
+            # `height = ymax - ymin`. We need to convert to `width = xmax - xmin + 1` and `height = ymax - ymin + 1` to
+            # match with internal Boxes Kornia representation.
+            quadrilaterals[..., 1:3, 0] = quadrilaterals[..., 1:3, 0] - 1
+            quadrilaterals[..., 2:, 1] = quadrilaterals[..., 2:, 1] - 1
         elif mode == "vertices_plus":
-            quadrilaterals = boxes.clone()  # TODO: perform +1
+            # Avoid passing reference
+            quadrilaterals = boxes.clone()
         else:
             raise ValueError(f"Unknown mode {mode}")
         validate_boxes or validate_bbox(quadrilaterals)


Test patch:
diff --git a/test/geometry/test_boxes.py b/test/geometry/test_boxes.py
index a7446ad5..b9053de7 100644
--- a/test/geometry/test_boxes.py
+++ b/test/geometry/test_boxes.py
@@ -84,12 +84,18 @@ class TestBoxes2D:
         box_xyxy = torch.as_tensor([[1, 2, 3, 4]], device=device, dtype=dtype).view(*shape)
         box_xyxy_plus = torch.as_tensor([[1, 2, 2, 3]], device=device, dtype=dtype).view(*shape)
         box_xywh = torch.as_tensor([[1, 2, 2, 2]], device=device, dtype=dtype).view(*shape)
+        box_vertices = torch.as_tensor([[[1, 2], [3, 2], [3, 4], [1, 4]]], device=device, dtype=dtype).view(*shape, 2)
+        box_vertices_plus = torch.as_tensor([[[1, 2], [2, 2], [2, 3], [1, 3]]], device=device, dtype=dtype).view(
+            *shape, 2
+        )
 
         expected_box = torch.as_tensor([[[1, 2], [2, 2], [2, 3], [1, 3]]], device=device, dtype=dtype).view(*shape, 2)
 
         boxes_xyxy = Boxes.from_tensor(box_xyxy, mode='xyxy').data
         boxes_xyxy_plus = Boxes.from_tensor(box_xyxy_plus, mode='xyxy_plus').data
         boxes_xywh = Boxes.from_tensor(box_xywh, mode='xywh').data
+        box_vertices = Boxes.from_tensor(box_vertices, mode='vertices').data
+        boxes_vertices_plus = Boxes.from_tensor(box_vertices_plus, mode='vertices_plus').data
 
         assert boxes_xyxy.shape == expected_box.shape
         assert_allclose(boxes_xyxy, expected_box)
@@ -100,6 +106,12 @@ class TestBoxes2D:
         assert boxes_xywh.shape == expected_box.shape
         assert_allclose(boxes_xywh, expected_box)
 
+        assert box_vertices.shape == expected_box.shape
+        assert_allclose(box_vertices, expected_box)
+
+        assert boxes_vertices_plus.shape == expected_box.shape
+        assert_allclose(boxes_vertices_plus, expected_box)
+
     @pytest.mark.parametrize('shape', [(1, 4), (1, 1, 4)])
     def test_from_invalid_tensor(self, shape: Tuple[int], device, dtype):
         box_xyxy = torch.as_tensor([[1, 2, -3, 4]], device=device, dtype=dtype).view(*shape)  # Invalid width