def test_pairwise_iou_many_boxes(self): for device in ["cpu" ] + (["cuda"] if torch.cuda.is_available() else []): 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)) self.assertTrue(torch.allclose(ious, expected_ious))
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)) self.assertTrue(torch.allclose(ious, expected_ious))
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() self.assertTrue(torch.all(normalized_boxes.tensor[:, 4] >= -180)) self.assertTrue(torch.all(normalized_boxes.tensor[:, 4] < 180)) # x, y, w, h should not change self.assertTrue(torch.allclose(boxes_5d[:, :4], normalized_boxes.tensor[:, :4])) # the cos/sin values of the angles should stay the same self.assertTrue( torch.allclose( torch.cos(boxes_5d[:, 4] * math.pi / 180), torch.cos(normalized_boxes.tensor[:, 4] * math.pi / 180), atol=1e-5, ) ) self.assertTrue( 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(self): for device in ["cpu" ] + (["cuda"] if torch.cuda.is_available() else []): 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)) self.assertTrue(torch.allclose(ious, expected_ious))
def func_cat(x: torch.Tensor): boxes1 = RotatedBoxes(x) boxes2 = RotatedBoxes(x) # this is not supported by torchscript for now. # boxes3 = RotatedBoxes.cat([boxes1, boxes2]) boxes3 = boxes1.cat([boxes1, boxes2]) return boxes3
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)) self.assertTrue(torch.allclose(ious, expected_ious))
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)) self.assertTrue(torch.allclose(ious, expected_ious))
def test_pairwise_iou_issue1207_simplified(self): for device in ["cpu"] + ["cuda"] if torch.cuda.is_available() else []: # Simplified test case of D2-issue-1207 boxes1 = torch.tensor([[3, 3, 8, 2, -45.0]], device=device) boxes2 = torch.tensor([[6, 0, 8, 2, -45.0]], device=device) iou = 0.0 expected_ious = torch.tensor([[iou]], dtype=torch.float32, device=device) ious = pairwise_iou(RotatedBoxes(boxes1), RotatedBoxes(boxes2)) self.assertTrue(torch.allclose(ious, expected_ious))
def test_pairwise_iou_issue1207_simplified_cuda(self): device = torch.device("cuda") # Simplified test case of D2-issue-1207 boxes1 = torch.tensor([[3, 3, 8, 2, -45.0]], device=device) boxes2 = torch.tensor([[6, 0, 8, 2, -45.0]], device=device) iou = 0.0 expected_ious = torch.tensor([[iou]], dtype=torch.float32, device=device) ious = pairwise_iou(RotatedBoxes(boxes1), RotatedBoxes(boxes2)) self.assertTrue(torch.allclose(ious, expected_ious))
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)) self.assertTrue(torch.allclose(ious, expected_ious))
def test_pairwise_iou_orthogonal(self): for device in ["cpu"] + ["cuda"] if torch.cuda.is_available() else []: 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)) self.assertTrue(torch.allclose(ious, expected_ious))
def test_pairwise_iou_issue1207(self): for device in ["cpu"] + ["cuda"] if torch.cuda.is_available() else []: # The original test case in D2-issue-1207 boxes1 = torch.tensor([[160.0, 153.0, 230.0, 23.0, -37.0]], device=device) boxes2 = torch.tensor([[190.0, 127.0, 80.0, 21.0, -46.0]], device=device) iou = 0.0 expected_ious = torch.tensor([[iou]], dtype=torch.float32, device=device) ious = pairwise_iou(RotatedBoxes(boxes1), RotatedBoxes(boxes2)) self.assertTrue(torch.allclose(ious, expected_ious))
def test_pairwise_iou_issue1207_cuda(self): device = torch.device("cuda") # The original test case in D2-issue-1207 boxes1 = torch.tensor([[160.0, 153.0, 230.0, 23.0, -37.0]], device=device) boxes2 = torch.tensor([[190.0, 127.0, 80.0, 21.0, -46.0]], device=device) iou = 0.0 expected_ious = torch.tensor([[iou]], dtype=torch.float32, device=device) ious = pairwise_iou(RotatedBoxes(boxes1), RotatedBoxes(boxes2)) self.assertTrue(torch.allclose(ious, expected_ious))
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)) self.assertTrue(torch.allclose(ious, expected_ious))
def test_pairwise_iou_large_close_boxes(self): for device in ["cpu"] + ["cuda"] if torch.cuda.is_available() else []: 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)) self.assertTrue(torch.allclose(ious, expected_ious))
def test_pairwise_iou_45_degrees(self): for device in ["cpu"] + ["cuda"] if torch.cuda.is_available() else []: 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)) self.assertTrue(torch.allclose(ious, expected_ious))
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() self.assertTrue(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() self.assertTrue(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 self.assertTrue(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] self.assertTrue( torch.all(torch.abs(boxes_5d[:, 4][torch.where(areas_diff < 0)]) < clip_angle_threshold) )
def test_empty_cat(self): x = RotatedBoxes.cat([]) self.assertTrue(x.tensor.shape, (0, 5))
def func(x): boxes = RotatedBoxes(x) test = boxes.to(torch.device("cpu")).tensor return boxes.area(), test
def rotated_mapper(original_dataset_dict): # Implement a mapper, similar to the default DatasetMapper, but with our own customizations dataset_dict = copy.deepcopy( original_dataset_dict) # it will be modified by code below original_gsd = dataset_dict["gsd"] target_gsd = np.random.uniform(0.09, 0.13) # randomize target gsd scale = original_gsd / target_gsd target_size = 400 target_crop = int(target_size / scale) target_crop = (target_crop, target_crop) image_np = detection_utils.read_image(dataset_dict["file_name"], format="BGR") boxes = np.asarray([anno['bbox'] for anno in dataset_dict['annotations']]) # select anno at random # draw random center # h, w = image_np.shape[:2] # rand_box = boxes[np.random.randint(len(boxes))] # ch, cw = rand_box[:2] # xmin = np.min() # xmax = np.max() # ymin = 3 # ymax = 4 # h0 = np.random.randint(min(h, ymin), min(h, ymax) + 1) # w0 = np.random.randint(min(w, xmin), min(w, xmax) + 1) # assert h >= target_crop[1] and w >= target_crop[0], "Shape computation has bugs." # crop = T.CropTransform(w0, h0, target_crop) # make sure random crop contains annotations i = 0 while True: random_crop = T.RandomCrop('absolute', target_crop).get_transform(image_np) cropped_boxes = RotatedBoxes( random_crop.apply_coords(copy.deepcopy(boxes))) inside_ind = cropped_boxes.inside_box(target_crop) if 1 < sum(inside_ind) <= 100: break i += 1 if i > 150: return None image, transforms = T.apply_transform_gens([ random_crop, T.Resize((target_size, target_size)), ], image_np) dataset_dict["image"] = torch.as_tensor( image.transpose(2, 0, 1).astype("float32")) annos = [ rotated_transform_instance_annotations(obj, transforms, image.shape[:2]) for obj in dataset_dict.pop("annotations") if obj.get("iscrowd", 0) == 0 ] instances = detection_utils.annotations_to_instances_rotated( annos, image.shape[:2]) instances = detection_utils.filter_empty_instances(instances) inside_ind = instances.gt_boxes.inside_box(image.shape[:2]) instances = instances[inside_ind] assert ((instances.gt_boxes.tensor.numpy()[:, 2] > 0).all().item() ), "width not > 0\n\n" + str(instances.gt_boxes.tensor.numpy()) dataset_dict["instances"] = instances return dataset_dict