3a2c66702c
Tests on CPU (scheduled) / check-skip (push) Has been cancelled
Tests on CPU (scheduled) / pre-tests (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-ubuntu (float32) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-ubuntu (float64) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-windows (3.11, float32, 2.5.1) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-windows (3.11, float32, 2.9.1) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-windows (3.11, float64, 2.5.1) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-windows (3.11, float64, 2.9.1) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-windows (3.12, float32, 2.5.1) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-windows (3.12, float32, 2.9.1) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-windows (3.12, float64, 2.5.1) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-windows (3.12, float64, 2.9.1) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-windows (3.13, float32, 2.9.1) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-windows (3.13, float64, 2.9.1) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-mac (3.11, float32, 2.5.1) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-mac (3.11, float32, 2.9.1) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-mac (3.12, float32, 2.5.1) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-mac (3.12, float32, 2.9.1) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-mac (3.13, float32, 2.9.1) (push) Has been cancelled
Tests on CPU (scheduled) / coverage (push) Has been cancelled
Tests on CPU (scheduled) / typing (push) Has been cancelled
Tests on CPU (scheduled) / tutorials (push) Has been cancelled
Tests on CPU (scheduled) / docs (push) Has been cancelled
Lint / TOML Format (push) Has been cancelled
307 lines
11 KiB
Python
307 lines
11 KiB
Python
# LICENSE HEADER MANAGED BY add-license-header
|
|
#
|
|
# Copyright 2018 Kornia Team
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
#
|
|
|
|
import torch
|
|
|
|
import kornia
|
|
from kornia.geometry.bbox import (
|
|
infer_bbox_shape,
|
|
infer_bbox_shape3d,
|
|
nms,
|
|
transform_bbox,
|
|
validate_bbox,
|
|
validate_bbox3d,
|
|
)
|
|
|
|
from testing.base import BaseTester
|
|
|
|
|
|
class TestBbox2D(BaseTester):
|
|
def test_smoke(self, device, dtype):
|
|
# Sample two points of the rectangle
|
|
points = torch.rand(1, 4, device=device, dtype=dtype)
|
|
|
|
# Fill according missing points
|
|
bbox = torch.zeros(1, 4, 2, device=device, dtype=dtype)
|
|
bbox[0, 0] = points[0][:2]
|
|
bbox[0, 1, 0] = points[0][2]
|
|
bbox[0, 1, 1] = points[0][1]
|
|
bbox[0, 2] = points[0][2:]
|
|
bbox[0, 3, 0] = points[0][0]
|
|
bbox[0, 3, 1] = points[0][3]
|
|
|
|
# Validate
|
|
assert validate_bbox(bbox)
|
|
|
|
def test_bounding_boxes_dim_inferring(self, device, dtype):
|
|
boxes = torch.tensor([[[1.0, 1.0], [3.0, 1.0], [3.0, 2.0], [1.0, 2.0]]], device=device, dtype=dtype)
|
|
h, w = infer_bbox_shape(boxes)
|
|
assert (h, w) == (2, 3)
|
|
|
|
def test_bounding_boxes_dim_inferring_batch(self, device, dtype):
|
|
boxes = torch.tensor(
|
|
[[[1.0, 1.0], [3.0, 1.0], [3.0, 2.0], [1.0, 2.0]], [[2.0, 2.0], [4.0, 2.0], [4.0, 3.0], [2.0, 3.0]]],
|
|
device=device,
|
|
dtype=dtype,
|
|
)
|
|
h, w = infer_bbox_shape(boxes)
|
|
assert (h.unique().item(), w.unique().item()) == (2, 3)
|
|
|
|
def test_gradcheck(self, device):
|
|
boxes = torch.tensor([[[1.0, 1.0], [3.0, 1.0], [3.0, 2.0], [1.0, 2.0]]], device=device, dtype=torch.float64)
|
|
self.gradcheck(infer_bbox_shape, (boxes,))
|
|
|
|
def test_dynamo(self, device, dtype, torch_optimizer):
|
|
# Define script
|
|
op = infer_bbox_shape
|
|
op_optimized = torch_optimizer(op)
|
|
# Define input
|
|
boxes = torch.tensor([[[1.0, 1.0], [3.0, 1.0], [3.0, 2.0], [1.0, 2.0]]], device=device, dtype=dtype)
|
|
# Run
|
|
expected = op(boxes)
|
|
actual = op_optimized(boxes)
|
|
# Compare
|
|
self.assert_close(actual, expected)
|
|
|
|
def test_jit(self, device, dtype):
|
|
# Test with valid rectangular box
|
|
boxes = torch.tensor([[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]]], device=device, dtype=dtype)
|
|
|
|
# JIT compile the validate_bbox function
|
|
scripted_fn = torch.jit.script(validate_bbox)
|
|
|
|
# Test with valid box
|
|
self.assert_close(scripted_fn(boxes), validate_bbox(boxes))
|
|
|
|
# Test with non-rectangular box
|
|
boxes_invalid = torch.tensor([[[0.0, 0.0], [2.0, 0.0], [3.0, 1.0], [1.0, 1.0]]], device=device, dtype=dtype)
|
|
self.assert_close(scripted_fn(boxes_invalid), validate_bbox(boxes_invalid))
|
|
|
|
# Test with invalid shape
|
|
boxes_wrong_shape = torch.rand(1, 3, 2, device=device, dtype=dtype)
|
|
self.assert_close(scripted_fn(boxes_wrong_shape), validate_bbox(boxes_wrong_shape))
|
|
|
|
|
|
class TestTransformBoxes2D(BaseTester):
|
|
def test_transform_boxes(self, device, dtype):
|
|
boxes = torch.tensor([[139.2640, 103.0150, 397.3120, 410.5225]], device=device, dtype=dtype)
|
|
|
|
expected = torch.tensor([[114.6880, 103.0150, 372.7360, 410.5225]], device=device, dtype=dtype)
|
|
|
|
trans_mat = torch.tensor([[[-1.0, 0.0, 512.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]], device=device, dtype=dtype)
|
|
|
|
out = transform_bbox(trans_mat, boxes, restore_coordinates=True)
|
|
self.assert_close(out, expected, atol=1e-4, rtol=1e-4)
|
|
|
|
def test_transform_multiple_boxes(self, device, dtype):
|
|
boxes = torch.tensor(
|
|
[
|
|
[139.2640, 103.0150, 397.3120, 410.5225],
|
|
[1.0240, 80.5547, 512.0000, 512.0000],
|
|
[165.2053, 262.1440, 510.6347, 508.9280],
|
|
[119.8080, 144.2067, 257.0240, 410.1292],
|
|
],
|
|
device=device,
|
|
dtype=dtype,
|
|
)
|
|
|
|
boxes = boxes.repeat(2, 1, 1) # 2 x 4 x 4 two images 4 boxes each
|
|
|
|
expected = torch.tensor(
|
|
[
|
|
[
|
|
[114.6880, 103.0150, 372.7360, 410.5225],
|
|
[0.0000, 80.5547, 510.9760, 512.0000],
|
|
[1.3652, 262.1440, 346.7947, 508.9280],
|
|
[254.9760, 144.2067, 392.1920, 410.1292],
|
|
],
|
|
[
|
|
[139.2640, 103.0150, 397.3120, 410.5225],
|
|
[1.0240, 80.5547, 512.0000, 512.0000],
|
|
[165.2053, 262.1440, 510.6347, 508.9280],
|
|
[119.8080, 144.2067, 257.0240, 410.1292],
|
|
],
|
|
],
|
|
device=device,
|
|
dtype=dtype,
|
|
)
|
|
|
|
trans_mat = torch.tensor(
|
|
[
|
|
[[-1.0, 0.0, 512.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]],
|
|
[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]],
|
|
],
|
|
device=device,
|
|
dtype=dtype,
|
|
)
|
|
|
|
out = transform_bbox(trans_mat, boxes, restore_coordinates=True)
|
|
self.assert_close(out, expected, atol=1e-4, rtol=1e-4)
|
|
|
|
def test_transform_boxes_wh(self, device, dtype):
|
|
boxes = torch.tensor(
|
|
[
|
|
[139.2640, 103.0150, 258.0480, 307.5075],
|
|
[1.0240, 80.5547, 510.9760, 431.4453],
|
|
[165.2053, 262.1440, 345.4293, 246.7840],
|
|
[119.8080, 144.2067, 137.2160, 265.9225],
|
|
],
|
|
device=device,
|
|
dtype=dtype,
|
|
)
|
|
|
|
expected = torch.tensor(
|
|
[
|
|
[114.6880, 103.0150, 258.0480, 307.5075],
|
|
[0.0000, 80.5547, 510.9760, 431.4453],
|
|
[1.3654, 262.1440, 345.4293, 246.7840],
|
|
[254.9760, 144.2067, 137.2160, 265.9225],
|
|
],
|
|
device=device,
|
|
dtype=dtype,
|
|
)
|
|
|
|
trans_mat = torch.tensor([[[-1.0, 0.0, 512.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]], device=device, dtype=dtype)
|
|
|
|
out = transform_bbox(trans_mat, boxes, mode="xywh", restore_coordinates=True)
|
|
self.assert_close(out, expected, atol=1e-4, rtol=1e-4)
|
|
|
|
def test_gradcheck(self, device):
|
|
boxes = torch.tensor(
|
|
[
|
|
[139.2640, 103.0150, 258.0480, 307.5075],
|
|
[1.0240, 80.5547, 510.9760, 431.4453],
|
|
[165.2053, 262.1440, 345.4293, 246.7840],
|
|
[119.8080, 144.2067, 137.2160, 265.9225],
|
|
],
|
|
device=device,
|
|
dtype=torch.float64,
|
|
)
|
|
|
|
trans_mat = torch.tensor(
|
|
[[[-1.0, 0.0, 512.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]], device=device, dtype=torch.float64
|
|
)
|
|
|
|
self.gradcheck(transform_bbox, (trans_mat, boxes, "xyxy", True))
|
|
|
|
def test_dynamo(self, device, dtype, torch_optimizer):
|
|
boxes = torch.tensor([[139.2640, 103.0150, 258.0480, 307.5075]], device=device, dtype=dtype)
|
|
trans_mat = torch.tensor([[[-1.0, 0.0, 512.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]], device=device, dtype=dtype)
|
|
args = (boxes, trans_mat)
|
|
op = kornia.geometry.transform_points
|
|
op_optimized = torch_optimizer(op)
|
|
self.assert_close(op(*args), op_optimized(*args))
|
|
|
|
|
|
class TestBbox3D(BaseTester):
|
|
def test_smoke(self, device, dtype):
|
|
# Sample two points of the 3d rect
|
|
points = torch.rand(1, 6, device=device, dtype=dtype)
|
|
|
|
# Fill according missing points
|
|
bbox = torch.zeros(1, 8, 3, device=device, dtype=dtype)
|
|
bbox[0, 0] = points[0][:3]
|
|
bbox[0, 1, 0] = points[0][3]
|
|
bbox[0, 1, 1] = points[0][1]
|
|
bbox[0, 1, 2] = points[0][2]
|
|
bbox[0, 2, 0] = points[0][3]
|
|
bbox[0, 2, 1] = points[0][4]
|
|
bbox[0, 2, 2] = points[0][2]
|
|
bbox[0, 3, 0] = points[0][0]
|
|
bbox[0, 3, 1] = points[0][4]
|
|
bbox[0, 3, 2] = points[0][2]
|
|
bbox[0, 4, 0] = points[0][0]
|
|
bbox[0, 4, 1] = points[0][1]
|
|
bbox[0, 4, 2] = points[0][5]
|
|
bbox[0, 5, 0] = points[0][3]
|
|
bbox[0, 5, 1] = points[0][1]
|
|
bbox[0, 5, 2] = points[0][5]
|
|
bbox[0, 6] = points[0][3:]
|
|
bbox[0, 7, 0] = points[0][0]
|
|
bbox[0, 7, 1] = points[0][4]
|
|
bbox[0, 7, 2] = points[0][5]
|
|
|
|
# Validate
|
|
assert validate_bbox3d(bbox)
|
|
|
|
def test_bounding_boxes_dim_inferring(self, device, dtype):
|
|
boxes = torch.tensor(
|
|
[
|
|
[[0, 1, 2], [10, 1, 2], [10, 21, 2], [0, 21, 2], [0, 1, 32], [10, 1, 32], [10, 21, 32], [0, 21, 32]],
|
|
[[3, 4, 5], [43, 4, 5], [43, 54, 5], [3, 54, 5], [3, 4, 65], [43, 4, 65], [43, 54, 65], [3, 54, 65]],
|
|
],
|
|
device=device,
|
|
dtype=dtype,
|
|
) # 2x8x3
|
|
d, h, w = infer_bbox_shape3d(boxes)
|
|
|
|
self.assert_close(d, torch.tensor([31.0, 61.0], device=device, dtype=dtype))
|
|
self.assert_close(h, torch.tensor([21.0, 51.0], device=device, dtype=dtype))
|
|
self.assert_close(w, torch.tensor([11.0, 41.0], device=device, dtype=dtype))
|
|
|
|
def test_gradcheck(self, device):
|
|
boxes = torch.tensor(
|
|
[
|
|
[
|
|
[0.0, 1.0, 2.0],
|
|
[10, 1, 2],
|
|
[10, 21, 2],
|
|
[0, 21, 2],
|
|
[0, 1, 32],
|
|
[10, 1, 32],
|
|
[10, 21, 32],
|
|
[0, 21, 32],
|
|
]
|
|
],
|
|
device=device,
|
|
dtype=torch.float64,
|
|
)
|
|
self.gradcheck(infer_bbox_shape3d, (boxes,))
|
|
|
|
def test_dynamo(self, device, dtype, torch_optimizer):
|
|
# Define script
|
|
op = infer_bbox_shape3d
|
|
op_script = torch_optimizer(op)
|
|
|
|
boxes = torch.tensor(
|
|
[[[0, 0, 1], [3, 0, 1], [3, 2, 1], [0, 2, 1], [0, 0, 3], [3, 0, 3], [3, 2, 3], [0, 2, 3]]],
|
|
device=device,
|
|
dtype=dtype,
|
|
) # 1x8x3
|
|
|
|
actual = op_script(boxes)
|
|
expected = op(boxes)
|
|
self.assert_close(actual, expected)
|
|
|
|
|
|
class TestNMS(BaseTester):
|
|
def test_smoke(self, device, dtype):
|
|
boxes = torch.tensor(
|
|
[
|
|
[10.0, 10.0, 20.0, 20.0],
|
|
[15.0, 5.0, 15.0, 25.0],
|
|
[100.0, 100.0, 200.0, 200.0],
|
|
[100.0, 100.0, 200.0, 200.0],
|
|
],
|
|
device=device,
|
|
dtype=dtype,
|
|
)
|
|
scores = torch.tensor([0.9, 0.8, 0.7, 0.9], device=device, dtype=dtype)
|
|
expected = torch.tensor([0, 3, 1], device=device, dtype=torch.long)
|
|
actual = nms(boxes, scores, iou_threshold=0.8)
|
|
self.assert_close(actual, expected)
|