|
- # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
- from __future__ import absolute_import, division, print_function, unicode_literals
- import logging
- import math
- import random
- import unittest
- import torch
- from fvcore.common.benchmark import benchmark
-
- from detectron2.layers.rotated_boxes import pairwise_iou_rotated
- from detectron2.structures.boxes import Boxes
- from detectron2.structures.rotated_boxes import RotatedBoxes, pairwise_iou
-
- logger = logging.getLogger(__name__)
-
-
- class TestRotatedBoxesLayer(unittest.TestCase):
- def test_iou_0_dim_cpu(self):
- boxes1 = torch.rand(0, 5, dtype=torch.float32)
- boxes2 = torch.rand(10, 5, dtype=torch.float32)
- expected_ious = torch.zeros(0, 10, dtype=torch.float32)
- ious = pairwise_iou_rotated(boxes1, boxes2)
- assert torch.allclose(ious, expected_ious)
-
- boxes1 = torch.rand(10, 5, dtype=torch.float32)
- boxes2 = torch.rand(0, 5, dtype=torch.float32)
- expected_ious = torch.zeros(10, 0, dtype=torch.float32)
- ious = pairwise_iou_rotated(boxes1, boxes2)
- assert torch.allclose(ious, expected_ious)
-
- @unittest.skipIf(not torch.cuda.is_available(), "CUDA not available")
- def test_iou_0_dim_cuda(self):
- boxes1 = torch.rand(0, 5, dtype=torch.float32)
- boxes2 = torch.rand(10, 5, dtype=torch.float32)
- expected_ious = torch.zeros(0, 10, dtype=torch.float32)
- ious_cuda = pairwise_iou_rotated(boxes1.cuda(), boxes2.cuda())
- assert torch.allclose(ious_cuda.cpu(), expected_ious)
-
- boxes1 = torch.rand(10, 5, dtype=torch.float32)
- boxes2 = torch.rand(0, 5, dtype=torch.float32)
- expected_ious = torch.zeros(10, 0, dtype=torch.float32)
- ious_cuda = pairwise_iou_rotated(boxes1.cuda(), boxes2.cuda())
- assert torch.allclose(ious_cuda.cpu(), expected_ious)
-
- def test_iou_half_overlap_cpu(self):
- boxes1 = torch.tensor([[0.5, 0.5, 1.0, 1.0, 0.0]], dtype=torch.float32)
- boxes2 = torch.tensor([[0.25, 0.5, 0.5, 1.0, 0.0]], dtype=torch.float32)
- expected_ious = torch.tensor([[0.5]], dtype=torch.float32)
- ious = pairwise_iou_rotated(boxes1, boxes2)
- assert torch.allclose(ious, expected_ious)
-
- @unittest.skipIf(not torch.cuda.is_available(), "CUDA not available")
- def test_iou_half_overlap_cuda(self):
- boxes1 = torch.tensor([[0.5, 0.5, 1.0, 1.0, 0.0]], dtype=torch.float32)
- boxes2 = torch.tensor([[0.25, 0.5, 0.5, 1.0, 0.0]], dtype=torch.float32)
- expected_ious = torch.tensor([[0.5]], dtype=torch.float32)
- ious_cuda = pairwise_iou_rotated(boxes1.cuda(), boxes2.cuda())
- assert torch.allclose(ious_cuda.cpu(), expected_ious)
-
- def test_iou_0_degree_cpu(self):
- boxes1 = torch.tensor(
- [[0.5, 0.5, 1.0, 1.0, 0.0], [0.5, 0.5, 1.0, 1.0, 0.0]], dtype=torch.float32
- )
- boxes2 = torch.tensor(
- [
- [0.5, 0.5, 1.0, 1.0, 0.0],
- [0.25, 0.5, 0.5, 1.0, 0.0],
- [0.5, 0.25, 1.0, 0.5, 0.0],
- [0.25, 0.25, 0.5, 0.5, 0.0],
- [0.75, 0.75, 0.5, 0.5, 0.0],
- [1.0, 1.0, 1.0, 1.0, 0.0],
- ],
- dtype=torch.float32,
- )
- expected_ious = torch.tensor(
- [
- [1.0, 0.5, 0.5, 0.25, 0.25, 0.25 / (2 - 0.25)],
- [1.0, 0.5, 0.5, 0.25, 0.25, 0.25 / (2 - 0.25)],
- ],
- dtype=torch.float32,
- )
- ious = pairwise_iou_rotated(boxes1, boxes2)
- assert torch.allclose(ious, expected_ious)
-
- @unittest.skipIf(not torch.cuda.is_available(), "CUDA not available")
- def test_iou_0_degree_cuda(self):
- boxes1 = torch.tensor(
- [[0.5, 0.5, 1.0, 1.0, 0.0], [0.5, 0.5, 1.0, 1.0, 0.0]], dtype=torch.float32
- )
- boxes2 = torch.tensor(
- [
- [0.5, 0.5, 1.0, 1.0, 0.0],
- [0.25, 0.5, 0.5, 1.0, 0.0],
- [0.5, 0.25, 1.0, 0.5, 0.0],
- [0.25, 0.25, 0.5, 0.5, 0.0],
- [0.75, 0.75, 0.5, 0.5, 0.0],
- [1.0, 1.0, 1.0, 1.0, 0.0],
- ],
- dtype=torch.float32,
- )
- expected_ious = torch.tensor(
- [
- [1.0, 0.5, 0.5, 0.25, 0.25, 0.25 / (2 - 0.25)],
- [1.0, 0.5, 0.5, 0.25, 0.25, 0.25 / (2 - 0.25)],
- ],
- dtype=torch.float32,
- )
- ious_cuda = pairwise_iou_rotated(boxes1.cuda(), boxes2.cuda())
- assert torch.allclose(ious_cuda.cpu(), expected_ious)
-
- def test_iou_45_degrees_cpu(self):
- boxes1 = torch.tensor(
- [
- [1, 1, math.sqrt(2), math.sqrt(2), 45],
- [1, 1, 2 * math.sqrt(2), 2 * math.sqrt(2), -45],
- ],
- dtype=torch.float32,
- )
- boxes2 = torch.tensor([[1, 1, 2, 2, 0]], dtype=torch.float32)
- expected_ious = torch.tensor([[0.5], [0.5]], dtype=torch.float32)
- ious = pairwise_iou_rotated(boxes1, boxes2)
- assert torch.allclose(ious, expected_ious)
-
- @unittest.skipIf(not torch.cuda.is_available(), "CUDA not available")
- def test_iou_45_degrees_cuda(self):
- boxes1 = torch.tensor(
- [
- [1, 1, math.sqrt(2), math.sqrt(2), 45],
- [1, 1, 2 * math.sqrt(2), 2 * math.sqrt(2), -45],
- ],
- dtype=torch.float32,
- )
- boxes2 = torch.tensor([[1, 1, 2, 2, 0]], dtype=torch.float32)
- expected_ious = torch.tensor([[0.5], [0.5]], dtype=torch.float32)
- ious_cuda = pairwise_iou_rotated(boxes1.cuda(), boxes2.cuda())
- assert torch.allclose(ious_cuda.cpu(), expected_ious)
-
- def test_iou_perpendicular_cpu(self):
- boxes1 = torch.tensor([[5, 5, 10.0, 6, 55]], dtype=torch.float32)
- boxes2 = torch.tensor([[5, 5, 10.0, 6, -35]], dtype=torch.float32)
- iou = (6.0 * 6.0) / (6.0 * 6.0 + 4.0 * 6.0 + 4.0 * 6.0)
- expected_ious = torch.tensor([[iou]], dtype=torch.float32)
- ious = pairwise_iou_rotated(boxes1, boxes2)
- assert torch.allclose(ious, expected_ious)
-
- @unittest.skipIf(not torch.cuda.is_available(), "CUDA unavailable")
- def test_iou_perpendicular_cuda(self):
- boxes1 = torch.tensor([[5, 5, 10.0, 6, 55]], dtype=torch.float32)
- boxes2 = torch.tensor([[5, 5, 10.0, 6, -35]], dtype=torch.float32)
- iou = (6.0 * 6.0) / (6.0 * 6.0 + 4.0 * 6.0 + 4.0 * 6.0)
- expected_ious = torch.tensor([[iou]], dtype=torch.float32)
- ious_cuda = pairwise_iou_rotated(boxes1.cuda(), boxes2.cuda())
- assert torch.allclose(ious_cuda.cpu(), expected_ious)
-
- def test_iou_large_close_boxes_cpu(self):
- boxes1 = torch.tensor(
- [[299.500000, 417.370422, 600.000000, 364.259186, 27.1828]], dtype=torch.float32
- )
- boxes2 = torch.tensor(
- [[299.500000, 417.370422, 600.000000, 364.259155, 27.1828]], dtype=torch.float32
- )
- iou = 364.259155 / 364.259186
- expected_ious = torch.tensor([[iou]], dtype=torch.float32)
- ious = pairwise_iou_rotated(boxes1, boxes2)
- assert torch.allclose(ious, expected_ious)
-
- @unittest.skipIf(not torch.cuda.is_available(), "CUDA not available")
- def test_iou_large_close_boxes_cuda(self):
- boxes1 = torch.tensor(
- [[299.500000, 417.370422, 600.000000, 364.259186, 27.1828]], dtype=torch.float32
- )
- boxes2 = torch.tensor(
- [[299.500000, 417.370422, 600.000000, 364.259155, 27.1828]], dtype=torch.float32
- )
- iou = 364.259155 / 364.259186
- expected_ious = torch.tensor([[iou]], dtype=torch.float32)
- ious_cuda = pairwise_iou_rotated(boxes1.cuda(), boxes2.cuda())
- assert torch.allclose(ious_cuda.cpu(), expected_ious)
-
- def test_iou_precision_cpu(self):
- boxes1 = torch.tensor([[565, 565, 10, 10, 0]], dtype=torch.float32)
- boxes2 = torch.tensor([[565, 565, 10, 8.3, 0]], dtype=torch.float32)
- iou = 8.3 / 10.0
- expected_ious = torch.tensor([[iou]], dtype=torch.float32)
- ious = pairwise_iou_rotated(boxes1, boxes2)
- assert torch.allclose(ious, expected_ious)
-
- @unittest.skipIf(not torch.cuda.is_available(), "CUDA not available")
- def test_iou_precision_cuda(self):
- boxes1 = torch.tensor([[565, 565, 10, 10, 0]], dtype=torch.float32)
- boxes2 = torch.tensor([[565, 565, 10, 8.3, 0]], dtype=torch.float32)
- iou = 8.3 / 10.0
- expected_ious = torch.tensor([[iou]], dtype=torch.float32)
- ious_cuda = pairwise_iou_rotated(boxes1.cuda(), boxes2.cuda())
- assert torch.allclose(ious_cuda.cpu(), expected_ious)
-
- def test_iou_many_boxes_cpu(self):
- num_boxes1 = 100
- num_boxes2 = 200
- boxes1 = torch.stack(
- [
- torch.tensor([5 + 20 * i, 5 + 20 * i, 10, 10, 0], dtype=torch.float32)
- for i in range(num_boxes1)
- ]
- )
- boxes2 = torch.stack(
- [
- torch.tensor(
- [5 + 20 * i, 5 + 20 * i, 10, 1 + 9 * i / num_boxes2, 0], dtype=torch.float32
- )
- for i in range(num_boxes2)
- ]
- )
- expected_ious = torch.zeros(num_boxes1, num_boxes2, dtype=torch.float32)
- for i in range(min(num_boxes1, num_boxes2)):
- expected_ious[i][i] = (1 + 9 * i / num_boxes2) / 10.0
- ious = pairwise_iou_rotated(boxes1, boxes2)
- assert torch.allclose(ious, expected_ious)
-
- @unittest.skipIf(not torch.cuda.is_available(), "CUDA not available")
- def test_iou_many_boxes_cuda(self):
- num_boxes1 = 100
- num_boxes2 = 200
- boxes1 = torch.stack(
- [
- torch.tensor([5 + 20 * i, 5 + 20 * i, 10, 10, 0], dtype=torch.float32)
- for i in range(num_boxes1)
- ]
- )
- boxes2 = torch.stack(
- [
- torch.tensor(
- [5 + 20 * i, 5 + 20 * i, 10, 1 + 9 * i / num_boxes2, 0], dtype=torch.float32
- )
- for i in range(num_boxes2)
- ]
- )
- expected_ious = torch.zeros(num_boxes1, num_boxes2, dtype=torch.float32)
- for i in range(min(num_boxes1, num_boxes2)):
- expected_ious[i][i] = (1 + 9 * i / num_boxes2) / 10.0
- ious_cuda = pairwise_iou_rotated(boxes1.cuda(), boxes2.cuda())
- assert torch.allclose(ious_cuda.cpu(), expected_ious)
-
-
- class TestRotatedBoxesStructure(unittest.TestCase):
- def test_clip_area_0_degree(self):
- for _ in range(50):
- num_boxes = 100
- boxes_5d = torch.zeros(num_boxes, 5)
- boxes_5d[:, 0] = torch.FloatTensor(num_boxes).uniform_(-100, 500)
- boxes_5d[:, 1] = torch.FloatTensor(num_boxes).uniform_(-100, 500)
- boxes_5d[:, 2] = torch.FloatTensor(num_boxes).uniform_(0, 500)
- boxes_5d[:, 3] = torch.FloatTensor(num_boxes).uniform_(0, 500)
- # Convert from (x_ctr, y_ctr, w, h, 0) to (x1, y1, x2, y2)
- boxes_4d = torch.zeros(num_boxes, 4)
- boxes_4d[:, 0] = boxes_5d[:, 0] - boxes_5d[:, 2] / 2.0
- boxes_4d[:, 1] = boxes_5d[:, 1] - boxes_5d[:, 3] / 2.0
- boxes_4d[:, 2] = boxes_5d[:, 0] + boxes_5d[:, 2] / 2.0
- boxes_4d[:, 3] = boxes_5d[:, 1] + boxes_5d[:, 3] / 2.0
-
- image_size = (500, 600)
- test_boxes_4d = Boxes(boxes_4d)
- test_boxes_5d = RotatedBoxes(boxes_5d)
- # Before clip
- areas_4d = test_boxes_4d.area()
- areas_5d = test_boxes_5d.area()
- assert torch.allclose(areas_4d, areas_5d, atol=1e-1, rtol=1e-5)
- # After clip
- test_boxes_4d.clip(image_size)
- test_boxes_5d.clip(image_size)
- areas_4d = test_boxes_4d.area()
- areas_5d = test_boxes_5d.area()
- assert torch.allclose(areas_4d, areas_5d, atol=1e-1, rtol=1e-5)
-
- def test_clip_area_arbitrary_angle(self):
- num_boxes = 100
- boxes_5d = torch.zeros(num_boxes, 5)
- boxes_5d[:, 0] = torch.FloatTensor(num_boxes).uniform_(-100, 500)
- boxes_5d[:, 1] = torch.FloatTensor(num_boxes).uniform_(-100, 500)
- boxes_5d[:, 2] = torch.FloatTensor(num_boxes).uniform_(0, 500)
- boxes_5d[:, 3] = torch.FloatTensor(num_boxes).uniform_(0, 500)
- boxes_5d[:, 4] = torch.FloatTensor(num_boxes).uniform_(-1800, 1800)
- clip_angle_threshold = random.uniform(0, 180)
-
- image_size = (500, 600)
- test_boxes_5d = RotatedBoxes(boxes_5d)
- # Before clip
- areas_before = test_boxes_5d.area()
- # After clip
- test_boxes_5d.clip(image_size, clip_angle_threshold)
- areas_diff = test_boxes_5d.area() - areas_before
-
- # the areas should only decrease after clipping
- assert torch.all(areas_diff <= 0)
- # whenever the box is clipped (thus the area shrinks),
- # the angle for the box must be within the clip_angle_threshold
- # Note that the clip function will normalize the angle range
- # to be within (-180, 180]
- assert torch.all(
- torch.abs(boxes_5d[:, 4][torch.where(areas_diff < 0)]) < clip_angle_threshold
- )
-
- def test_normalize_angles(self):
- # torch.manual_seed(0)
- for _ in range(50):
- num_boxes = 100
- boxes_5d = torch.zeros(num_boxes, 5)
- boxes_5d[:, 0] = torch.FloatTensor(num_boxes).uniform_(-100, 500)
- boxes_5d[:, 1] = torch.FloatTensor(num_boxes).uniform_(-100, 500)
- boxes_5d[:, 2] = torch.FloatTensor(num_boxes).uniform_(0, 500)
- boxes_5d[:, 3] = torch.FloatTensor(num_boxes).uniform_(0, 500)
- boxes_5d[:, 4] = torch.FloatTensor(num_boxes).uniform_(-1800, 1800)
- rotated_boxes = RotatedBoxes(boxes_5d)
- normalized_boxes = rotated_boxes.clone()
- normalized_boxes.normalize_angles()
- assert torch.all(normalized_boxes.tensor[:, 4] > -180)
- assert torch.all(normalized_boxes.tensor[:, 4] <= 180)
- # x, y, w, h should not change
- assert torch.allclose(boxes_5d[:, :4], normalized_boxes.tensor[:, :4])
- # the cos/sin values of the angles should stay the same
-
- assert torch.allclose(
- torch.cos(boxes_5d[:, 4] * math.pi / 180),
- torch.cos(normalized_boxes.tensor[:, 4] * math.pi / 180),
- atol=1e-5,
- )
-
- assert torch.allclose(
- torch.sin(boxes_5d[:, 4] * math.pi / 180),
- torch.sin(normalized_boxes.tensor[:, 4] * math.pi / 180),
- atol=1e-5,
- )
-
- def test_pairwise_iou_0_degree_cpu(self):
- device = torch.device("cpu")
- boxes1 = torch.tensor(
- [[0.5, 0.5, 1.0, 1.0, 0.0], [0.5, 0.5, 1.0, 1.0, 0.0]],
- dtype=torch.float32,
- device=device,
- )
- boxes2 = torch.tensor(
- [
- [0.5, 0.5, 1.0, 1.0, 0.0],
- [0.25, 0.5, 0.5, 1.0, 0.0],
- [0.5, 0.25, 1.0, 0.5, 0.0],
- [0.25, 0.25, 0.5, 0.5, 0.0],
- [0.75, 0.75, 0.5, 0.5, 0.0],
- [1.0, 1.0, 1.0, 1.0, 0.0],
- ],
- dtype=torch.float32,
- device=device,
- )
- expected_ious = torch.tensor(
- [
- [1.0, 0.5, 0.5, 0.25, 0.25, 0.25 / (2 - 0.25)],
- [1.0, 0.5, 0.5, 0.25, 0.25, 0.25 / (2 - 0.25)],
- ],
- dtype=torch.float32,
- device=device,
- )
- ious = pairwise_iou(RotatedBoxes(boxes1), RotatedBoxes(boxes2))
- assert torch.allclose(ious, expected_ious)
-
- @unittest.skipIf(not torch.cuda.is_available(), "CUDA not available")
- def test_pairwise_iou_0_degree_cuda(self):
- device = torch.device("cuda")
- boxes1 = torch.tensor(
- [[0.5, 0.5, 1.0, 1.0, 0.0], [0.5, 0.5, 1.0, 1.0, 0.0]],
- dtype=torch.float32,
- device=device,
- )
- boxes2 = torch.tensor(
- [
- [0.5, 0.5, 1.0, 1.0, 0.0],
- [0.25, 0.5, 0.5, 1.0, 0.0],
- [0.5, 0.25, 1.0, 0.5, 0.0],
- [0.25, 0.25, 0.5, 0.5, 0.0],
- [0.75, 0.75, 0.5, 0.5, 0.0],
- [1.0, 1.0, 1.0, 1.0, 0.0],
- ],
- dtype=torch.float32,
- device=device,
- )
- expected_ious = torch.tensor(
- [
- [1.0, 0.5, 0.5, 0.25, 0.25, 0.25 / (2 - 0.25)],
- [1.0, 0.5, 0.5, 0.25, 0.25, 0.25 / (2 - 0.25)],
- ],
- dtype=torch.float32,
- device=device,
- )
- ious = pairwise_iou(RotatedBoxes(boxes1), RotatedBoxes(boxes2))
- assert torch.allclose(ious, expected_ious)
-
- def test_pairwise_iou_45_degrees_cpu(self):
- device = torch.device("cpu")
- boxes1 = torch.tensor(
- [
- [1, 1, math.sqrt(2), math.sqrt(2), 45],
- [1, 1, 2 * math.sqrt(2), 2 * math.sqrt(2), -45],
- ],
- dtype=torch.float32,
- device=device,
- )
- boxes2 = torch.tensor([[1, 1, 2, 2, 0]], dtype=torch.float32, device=device)
- expected_ious = torch.tensor([[0.5], [0.5]], dtype=torch.float32, device=device)
- ious = pairwise_iou(RotatedBoxes(boxes1), RotatedBoxes(boxes2))
- assert torch.allclose(ious, expected_ious)
-
- @unittest.skipIf(not torch.cuda.is_available(), "CUDA not available")
- def test_pairwise_iou_45_degrees_cuda(self):
- device = torch.device("cuda")
- boxes1 = torch.tensor(
- [
- [1, 1, math.sqrt(2), math.sqrt(2), 45],
- [1, 1, 2 * math.sqrt(2), 2 * math.sqrt(2), -45],
- ],
- dtype=torch.float32,
- device=device,
- )
- boxes2 = torch.tensor([[1, 1, 2, 2, 0]], dtype=torch.float32, device=device)
- expected_ious = torch.tensor([[0.5], [0.5]], dtype=torch.float32, device=device)
- ious = pairwise_iou(RotatedBoxes(boxes1), RotatedBoxes(boxes2))
- assert torch.allclose(ious, expected_ious)
-
- def test_pairwise_iou_orthogonal_cpu(self):
- device = torch.device("cpu")
- boxes1 = torch.tensor([[5, 5, 10, 6, 55]], dtype=torch.float32, device=device)
- boxes2 = torch.tensor([[5, 5, 10, 6, -35]], dtype=torch.float32, device=device)
- iou = (6.0 * 6.0) / (6.0 * 6.0 + 4.0 * 6.0 + 4.0 * 6.0)
- expected_ious = torch.tensor([[iou]], dtype=torch.float32, device=device)
- ious = pairwise_iou(RotatedBoxes(boxes1), RotatedBoxes(boxes2))
- assert torch.allclose(ious, expected_ious)
-
- @unittest.skipIf(not torch.cuda.is_available(), "CUDA not available")
- def test_pairwise_iou_orthogonal_cuda(self):
- device = torch.device("cuda")
- boxes1 = torch.tensor([[5, 5, 10, 6, 55]], dtype=torch.float32, device=device)
- boxes2 = torch.tensor([[5, 5, 10, 6, -35]], dtype=torch.float32, device=device)
- iou = (6.0 * 6.0) / (6.0 * 6.0 + 4.0 * 6.0 + 4.0 * 6.0)
- expected_ious = torch.tensor([[iou]], dtype=torch.float32, device=device)
- ious = pairwise_iou(RotatedBoxes(boxes1), RotatedBoxes(boxes2))
- assert torch.allclose(ious, expected_ious)
-
- def test_pairwise_iou_large_close_boxes_cpu(self):
- device = torch.device("cpu")
- boxes1 = torch.tensor(
- [[299.500000, 417.370422, 600.000000, 364.259186, 27.1828]],
- dtype=torch.float32,
- device=device,
- )
- boxes2 = torch.tensor(
- [[299.500000, 417.370422, 600.000000, 364.259155, 27.1828]],
- dtype=torch.float32,
- device=device,
- )
- iou = 364.259155 / 364.259186
- expected_ious = torch.tensor([[iou]], dtype=torch.float32, device=device)
- ious = pairwise_iou(RotatedBoxes(boxes1), RotatedBoxes(boxes2))
- assert torch.allclose(ious, expected_ious)
-
- @unittest.skipIf(not torch.cuda.is_available(), "CUDA not available")
- def test_pairwise_iou_large_close_boxes_cuda(self):
- device = torch.device("cuda")
- boxes1 = torch.tensor(
- [[299.500000, 417.370422, 600.000000, 364.259186, 27.1828]],
- dtype=torch.float32,
- device=device,
- )
- boxes2 = torch.tensor(
- [[299.500000, 417.370422, 600.000000, 364.259155, 27.1828]],
- dtype=torch.float32,
- device=device,
- )
- iou = 364.259155 / 364.259186
- expected_ious = torch.tensor([[iou]], dtype=torch.float32, device=device)
- ious = pairwise_iou(RotatedBoxes(boxes1), RotatedBoxes(boxes2))
- assert torch.allclose(ious, expected_ious)
-
- def test_pairwise_iou_many_boxes_cpu(self):
- device = torch.device("cpu")
- num_boxes1 = 100
- num_boxes2 = 200
- boxes1 = torch.stack(
- [
- torch.tensor(
- [5 + 20 * i, 5 + 20 * i, 10, 10, 0], dtype=torch.float32, device=device
- )
- for i in range(num_boxes1)
- ]
- )
- boxes2 = torch.stack(
- [
- torch.tensor(
- [5 + 20 * i, 5 + 20 * i, 10, 1 + 9 * i / num_boxes2, 0],
- dtype=torch.float32,
- device=device,
- )
- for i in range(num_boxes2)
- ]
- )
- expected_ious = torch.zeros(num_boxes1, num_boxes2, dtype=torch.float32, device=device)
- for i in range(min(num_boxes1, num_boxes2)):
- expected_ious[i][i] = (1 + 9 * i / num_boxes2) / 10.0
- ious = pairwise_iou(RotatedBoxes(boxes1), RotatedBoxes(boxes2))
- assert torch.allclose(ious, expected_ious)
-
- @unittest.skipIf(not torch.cuda.is_available(), "CUDA not available")
- def test_pairwise_iou_many_boxes_cuda(self):
- device = torch.device("cuda")
- num_boxes1 = 100
- num_boxes2 = 200
- boxes1 = torch.stack(
- [
- torch.tensor(
- [5 + 20 * i, 5 + 20 * i, 10, 10, 0], dtype=torch.float32, device=device
- )
- for i in range(num_boxes1)
- ]
- )
- boxes2 = torch.stack(
- [
- torch.tensor(
- [5 + 20 * i, 5 + 20 * i, 10, 1 + 9 * i / num_boxes2, 0],
- dtype=torch.float32,
- device=device,
- )
- for i in range(num_boxes2)
- ]
- )
- expected_ious = torch.zeros(num_boxes1, num_boxes2, dtype=torch.float32, device=device)
- for i in range(min(num_boxes1, num_boxes2)):
- expected_ious[i][i] = (1 + 9 * i / num_boxes2) / 10.0
- ious = pairwise_iou(RotatedBoxes(boxes1), RotatedBoxes(boxes2))
- assert torch.allclose(ious, expected_ious)
-
-
- def benchmark_rotated_iou():
- num_boxes1 = 200
- num_boxes2 = 500
- boxes1 = torch.stack(
- [
- torch.tensor([5 + 20 * i, 5 + 20 * i, 10, 10, 0], dtype=torch.float32)
- for i in range(num_boxes1)
- ]
- )
- boxes2 = torch.stack(
- [
- torch.tensor(
- [5 + 20 * i, 5 + 20 * i, 10, 1 + 9 * i / num_boxes2, 0], dtype=torch.float32
- )
- for i in range(num_boxes2)
- ]
- )
-
- def func(dev, n=1):
- b1 = boxes1.to(device=dev)
- b2 = boxes2.to(device=dev)
-
- def bench():
- for _ in range(n):
- pairwise_iou_rotated(b1, b2)
- if dev.type == "cuda":
- torch.cuda.synchronize()
-
- return bench
-
- # only run it once per timed loop, since it's slow
- args = [{"dev": torch.device("cpu"), "n": 1}]
- if torch.cuda.is_available():
- args.append({"dev": torch.device("cuda"), "n": 10})
-
- benchmark(func, "rotated_iou", args, warmup_iters=3)
-
-
- if __name__ == "__main__":
- unittest.main()
- benchmark_rotated_iou()
|