@@ -111,6 +111,11 @@ def _test_iou(self, overlap_fn, device):
111
111
self .assertClose (
112
112
vol , torch .tensor ([[1 - dd ]], device = vol .device , dtype = vol .dtype )
113
113
)
114
+ # symmetry
115
+ vol , iou = overlap_fn (box2 [None ], box1 [None ])
116
+ self .assertClose (
117
+ vol , torch .tensor ([[1 - dd ]], device = vol .device , dtype = vol .dtype )
118
+ )
114
119
115
120
# 3rd test
116
121
dd = random .random ()
@@ -119,6 +124,11 @@ def _test_iou(self, overlap_fn, device):
119
124
self .assertClose (
120
125
vol , torch .tensor ([[1 - dd ]], device = vol .device , dtype = vol .dtype )
121
126
)
127
+ # symmetry
128
+ vol , _ = overlap_fn (box2 [None ], box1 [None ])
129
+ self .assertClose (
130
+ vol , torch .tensor ([[1 - dd ]], device = vol .device , dtype = vol .dtype )
131
+ )
122
132
123
133
# 4th test
124
134
ddx , ddy , ddz = random .random (), random .random (), random .random ()
@@ -132,6 +142,16 @@ def _test_iou(self, overlap_fn, device):
132
142
dtype = vol .dtype ,
133
143
),
134
144
)
145
+ # symmetry
146
+ vol , _ = overlap_fn (box2 [None ], box1 [None ])
147
+ self .assertClose (
148
+ vol ,
149
+ torch .tensor (
150
+ [[(1 - ddx ) * (1 - ddy ) * (1 - ddz )]],
151
+ device = vol .device ,
152
+ dtype = vol .dtype ,
153
+ ),
154
+ )
135
155
136
156
# Also check IoU is 1 when computing overlap with the same shifted box
137
157
vol , iou = overlap_fn (box2 [None ], box2 [None ])
@@ -152,6 +172,16 @@ def _test_iou(self, overlap_fn, device):
152
172
dtype = vol .dtype ,
153
173
),
154
174
)
175
+ # symmetry
176
+ vol , _ = overlap_fn (box2r [None ], box1r [None ])
177
+ self .assertClose (
178
+ vol ,
179
+ torch .tensor (
180
+ [[(1 - ddx ) * (1 - ddy ) * (1 - ddz )]],
181
+ device = vol .device ,
182
+ dtype = vol .dtype ,
183
+ ),
184
+ )
155
185
156
186
# 6th test
157
187
ddx , ddy , ddz = random .random (), random .random (), random .random ()
@@ -170,6 +200,17 @@ def _test_iou(self, overlap_fn, device):
170
200
),
171
201
atol = 1e-7 ,
172
202
)
203
+ # symmetry
204
+ vol , _ = overlap_fn (box2r [None ], box1r [None ])
205
+ self .assertClose (
206
+ vol ,
207
+ torch .tensor (
208
+ [[(1 - ddx ) * (1 - ddy ) * (1 - ddz )]],
209
+ device = vol .device ,
210
+ dtype = vol .dtype ,
211
+ ),
212
+ atol = 1e-7 ,
213
+ )
173
214
174
215
# 7th test: hand coded example and test with meshlab output
175
216
@@ -214,6 +255,10 @@ def _test_iou(self, overlap_fn, device):
214
255
vol , iou = overlap_fn (box1r [None ], box2r [None ])
215
256
self .assertClose (vol , torch .tensor ([[vol_inters ]], device = device ), atol = 1e-1 )
216
257
self .assertClose (iou , torch .tensor ([[iou_mesh ]], device = device ), atol = 1e-1 )
258
+ # symmetry
259
+ vol , iou = overlap_fn (box2r [None ], box1r [None ])
260
+ self .assertClose (vol , torch .tensor ([[vol_inters ]], device = device ), atol = 1e-1 )
261
+ self .assertClose (iou , torch .tensor ([[iou_mesh ]], device = device ), atol = 1e-1 )
217
262
218
263
# 8th test: compare with sampling
219
264
# create box1
@@ -232,14 +277,20 @@ def _test_iou(self, overlap_fn, device):
232
277
iou_sampling = self ._box3d_overlap_sampling_batched (
233
278
box1r [None ], box2r [None ], num_samples = 10000
234
279
)
235
-
280
+ self .assertClose (iou , iou_sampling , atol = 1e-2 )
281
+ # symmetry
282
+ vol , iou = overlap_fn (box2r [None ], box1r [None ])
236
283
self .assertClose (iou , iou_sampling , atol = 1e-2 )
237
284
238
285
# 9th test: non overlapping boxes, iou = 0.0
239
286
box2 = box1 + torch .tensor ([[0.0 , 100.0 , 0.0 ]], device = device )
240
287
vol , iou = overlap_fn (box1 [None ], box2 [None ])
241
288
self .assertClose (vol , torch .tensor ([[0.0 ]], device = vol .device , dtype = vol .dtype ))
242
289
self .assertClose (iou , torch .tensor ([[0.0 ]], device = vol .device , dtype = vol .dtype ))
290
+ # symmetry
291
+ vol , iou = overlap_fn (box2 [None ], box1 [None ])
292
+ self .assertClose (vol , torch .tensor ([[0.0 ]], device = vol .device , dtype = vol .dtype ))
293
+ self .assertClose (iou , torch .tensor ([[0.0 ]], device = vol .device , dtype = vol .dtype ))
243
294
244
295
# 10th test: Non coplanar verts in a plane
245
296
box10 = box1 + torch .rand ((8 , 3 ), dtype = torch .float32 , device = device )
@@ -284,6 +335,56 @@ def _test_iou(self, overlap_fn, device):
284
335
vols , ious = overlap_fn (box_skew_1 [None ], box_skew_2 [None ])
285
336
self .assertClose (vols , torch .tensor ([[vol_inters ]], device = device ), atol = 1e-1 )
286
337
self .assertClose (ious , torch .tensor ([[iou ]], device = device ), atol = 1e-1 )
338
+ # symmetry
339
+ vols , ious = overlap_fn (box_skew_2 [None ], box_skew_1 [None ])
340
+ self .assertClose (vols , torch .tensor ([[vol_inters ]], device = device ), atol = 1e-1 )
341
+ self .assertClose (ious , torch .tensor ([[iou ]], device = device ), atol = 1e-1 )
342
+
343
+ # 12th test: Zero area bounding box (from GH issue #992)
344
+ box12a = torch .tensor (
345
+ [
346
+ [- 1.0000 , - 1.0000 , - 0.5000 ],
347
+ [1.0000 , - 1.0000 , - 0.5000 ],
348
+ [1.0000 , 1.0000 , - 0.5000 ],
349
+ [- 1.0000 , 1.0000 , - 0.5000 ],
350
+ [- 1.0000 , - 1.0000 , 0.5000 ],
351
+ [1.0000 , - 1.0000 , 0.5000 ],
352
+ [1.0000 , 1.0000 , 0.5000 ],
353
+ [- 1.0000 , 1.0000 , 0.5000 ],
354
+ ],
355
+ device = device ,
356
+ dtype = torch .float32 ,
357
+ )
358
+
359
+ box12b = torch .tensor (
360
+ [
361
+ [0.0 , 0.0 , 0.0 ],
362
+ [0.0 , 0.0 , 0.0 ],
363
+ [0.0 , 0.0 , 0.0 ],
364
+ [0.0 , 0.0 , 0.0 ],
365
+ [0.0 , 0.0 , 0.0 ],
366
+ [0.0 , 0.0 , 0.0 ],
367
+ [0.0 , 0.0 , 0.0 ],
368
+ [0.0 , 0.0 , 0.0 ],
369
+ ],
370
+ device = device ,
371
+ dtype = torch .float32 ,
372
+ )
373
+ msg = "Planes have zero areas"
374
+ with self .assertRaisesRegex (ValueError , msg ):
375
+ overlap_fn (box12a [None ], box12b [None ])
376
+ # symmetry
377
+ with self .assertRaisesRegex (ValueError , msg ):
378
+ overlap_fn (box12b [None ], box12a [None ])
379
+
380
+ # 13th test: From GH issue #992
381
+ # Zero area coplanar face after intersection
382
+ ctrs = torch .tensor ([[0.0 , 0.0 , 0.0 ], [- 1.0 , 1.0 , 0.0 ]])
383
+ whl = torch .tensor ([[2.0 , 2.0 , 2.0 ], [2.0 , 2 , 2 ]])
384
+ box13a = TestIoU3D .create_box (ctrs [0 ], whl [0 ])
385
+ box13b = TestIoU3D .create_box (ctrs [1 ], whl [1 ])
386
+ vol , iou = overlap_fn (box13a [None ], box13b [None ])
387
+ self .assertClose (vol , torch .tensor ([[2.0 ]], device = vol .device , dtype = vol .dtype ))
287
388
288
389
def _test_real_boxes (self , overlap_fn , device ):
289
390
data_filename = "./real_boxes.pkl"
@@ -577,6 +678,13 @@ def box_planar_dir(box: torch.Tensor, eps=1e-4) -> torch.Tensor:
577
678
msg = "Plane vertices are not coplanar"
578
679
raise ValueError (msg )
579
680
681
+ # Check all faces have non zero area
682
+ area1 = torch .cross (v1 - v0 , v2 - v0 , dim = - 1 ).norm (dim = - 1 ) / 2
683
+ area2 = torch .cross (v3 - v0 , v2 - v0 , dim = - 1 ).norm (dim = - 1 ) / 2
684
+ if (area1 < eps ).any ().item () or (area2 < eps ).any ().item ():
685
+ msg = "Planes have zero areas"
686
+ raise ValueError (msg )
687
+
580
688
# We can write: `ctr = v0 + a * e0 + b * e1 + c * n`, (1).
581
689
# With <e0, n> = 0 and <e1, n> = 0, where <.,.> refers to the dot product,
582
690
# since that e0 is orthogonal to n. Same for e1.
@@ -607,6 +715,27 @@ def box_planar_dir(box: torch.Tensor, eps=1e-4) -> torch.Tensor:
607
715
return n
608
716
609
717
718
+ def tri_verts_area (tri_verts : torch .Tensor ) -> torch .Tensor :
719
+ """
720
+ Computes the area of the triangle faces in tri_verts
721
+ Args:
722
+ tri_verts: tensor of shape (T, 3, 3)
723
+ Returns:
724
+ areas: the area of the triangles (T, 1)
725
+ """
726
+ add_dim = False
727
+ if tri_verts .ndim == 2 :
728
+ tri_verts = tri_verts .unsqueeze (0 )
729
+ add_dim = True
730
+
731
+ v0 , v1 , v2 = tri_verts .unbind (1 )
732
+ areas = torch .cross (v1 - v0 , v2 - v0 , dim = - 1 ).norm (dim = - 1 ) / 2.0
733
+
734
+ if add_dim :
735
+ areas = areas [0 ]
736
+ return areas
737
+
738
+
610
739
def box_volume (box : torch .Tensor ) -> torch .Tensor :
611
740
"""
612
741
Computes the volume of each box in boxes.
@@ -988,7 +1117,10 @@ def box3d_overlap_naive(box1: torch.Tensor, box2: torch.Tensor):
988
1117
keep2 = torch .ones ((tri_verts2 .shape [0 ],), device = device , dtype = torch .bool )
989
1118
for i1 in range (tri_verts1 .shape [0 ]):
990
1119
for i2 in range (tri_verts2 .shape [0 ]):
991
- if coplanar_tri_faces (tri_verts1 [i1 ], tri_verts2 [i2 ]):
1120
+ if (
1121
+ coplanar_tri_faces (tri_verts1 [i1 ], tri_verts2 [i2 ])
1122
+ and tri_verts_area (tri_verts1 [i1 ]) > 1e-4
1123
+ ):
992
1124
keep2 [i2 ] = 0
993
1125
keep2 = keep2 .nonzero ()[:, 0 ]
994
1126
tri_verts2 = tri_verts2 [keep2 ]
0 commit comments