small fix for iou3d

Summary:
A small numerical fix for IoU for 3D boxes, fixes GH #992

* Adds a check for boxes with zero side areas (invalid boxes)
* Fixes numerical issue when two boxes have coplanar sides

Reviewed By: nikhilaravi

Differential Revision: D33195691

fbshipit-source-id: 8a34b4d1f1e5ec2edb6d54143930da44bdde0906
This commit is contained in:
Georgia Gkioxari
2021-12-17 16:12:51 -08:00
committed by Facebook GitHub Bot
parent 069c9fd759
commit ccfb72cc50
6 changed files with 202 additions and 4 deletions

View File

@@ -90,7 +90,8 @@ __global__ void IoUBox3DKernel(
for (int b2 = 0; b2 < box2_count; ++b2) {
const bool is_coplanar =
IsCoplanarFace(box1_intersect[b1], box2_intersect[b2]);
if (is_coplanar) {
const float area = FaceArea(box1_intersect[b1]);
if ((is_coplanar) && (area > kEpsilon)) {
tri2_keep[b2].keep = false;
}
}

View File

@@ -81,7 +81,8 @@ std::tuple<at::Tensor, at::Tensor> IoUBox3DCpu(
for (int b2 = 0; b2 < box2_intersect.size(); ++b2) {
const bool is_coplanar =
IsCoplanarFace(box1_intersect[b1], box2_intersect[b2]);
if (is_coplanar) {
const float area = FaceArea(box1_intersect[b1]);
if ((is_coplanar) && (area > kEpsilon)) {
tri2_keep[b2] = 0;
}
}

View File

@@ -138,6 +138,26 @@ FaceNormal(const float3 v0, const float3 v1, const float3 v2) {
return n;
}
// The area of the face defined by vertices (v0, v1, v2)
// Define e0 to be the edge connecting (v1, v0)
// Define e1 to be the edge connecting (v2, v0)
// Area is the norm of the cross product of e0, e1 divided by 2.0
//
// Args
// tri: FaceVerts of float3 coordinates of the vertices of the face
//
// Returns
// float: area for the face
//
__device__ inline float FaceArea(const FaceVerts& tri) {
// Get verts for face 1
const float3 v0 = tri.v0;
const float3 v1 = tri.v1;
const float3 v2 = tri.v2;
const float3 n = cross(v1 - v0, v2 - v0);
return norm(n) / 2.0;
}
// The normal of a box plane defined by the verts in `plane` with
// the centroid of the box given by `center`.
// Args

View File

@@ -145,6 +145,26 @@ inline vec3<float> FaceNormal(vec3<float> v0, vec3<float> v1, vec3<float> v2) {
return n;
}
// The area of the face defined by vertices (v0, v1, v2)
// Define e0 to be the edge connecting (v1, v0)
// Define e1 to be the edge connecting (v2, v0)
// Area is the norm of the cross product of e0, e1 divided by 2.0
//
// Args
// tri: vec3 coordinates of the vertices of the face
//
// Returns
// float: area for the face
//
inline float FaceArea(const std::vector<vec3<float>>& tri) {
// Get verts for face
const vec3<float> v0 = tri[0];
const vec3<float> v1 = tri[1];
const vec3<float> v2 = tri[2];
const vec3<float> n = cross(v1 - v0, v2 - v0);
return norm(n) / 2.0;
}
// The normal of a box plane defined by the verts in `plane` with
// the centroid of the box given by `center`.
// Args

View File

@@ -69,6 +69,28 @@ def _check_coplanar(boxes: torch.Tensor, eps: float = 1e-4) -> None:
return
def _check_nonzero(boxes: torch.Tensor, eps: float = 1e-4) -> None:
"""
Checks that the sides of the box have a non zero area
"""
faces = torch.tensor(_box_triangles, dtype=torch.int64, device=boxes.device)
# pyre-fixme[16]: `boxes` has no attribute `index_select`.
verts = boxes.index_select(index=faces.view(-1), dim=1)
B = boxes.shape[0]
T, V = faces.shape
# (B, T, 3, 3) -> (B, T, 3)
v0, v1, v2 = verts.reshape(B, T, V, 3).unbind(2)
normals = torch.cross(v1 - v0, v2 - v0, dim=-1) # (B, T, 3)
face_areas = normals.norm(dim=-1) / 2
if (face_areas < eps).any().item():
msg = "Planes have zero areas"
raise ValueError(msg)
return
class _box3d_overlap(Function):
"""
Torch autograd Function wrapper for box3d_overlap C++/CUDA implementations.
@@ -138,6 +160,8 @@ def box3d_overlap(
_check_coplanar(boxes1, eps)
_check_coplanar(boxes2, eps)
_check_nonzero(boxes1, eps)
_check_nonzero(boxes2, eps)
# pyre-fixme[16]: `_box3d_overlap` has no attribute `apply`.
vol, iou = _box3d_overlap.apply(boxes1, boxes2)