Spaces:
Running on Zero
fix: Compile the vis4d kernels here instead of installing them.
Browse filespip cannot install vis4d_cuda_ops on a Space. It publishes no wheels, and pip
compiles it in an isolated build environment that resolves its own newest torch
(2.13.0) while the Space caps the runtime torch at 2.11.0, so the extension
imports with `undefined symbol: c10::NotImplementedError`. Setting
PIP_NO_BUILD_ISOLATION as a Space variable does not help, the build does not
see it.
A local vis4d_cuda_ops package now shadows the real one and compiles only the
upstream CPU translation units against the torch the Space runs, which cannot
drift. iou_box3d runs on CPU for the few boxes the track graph compares, and
deformable attention runs the grid-sample path already in mapdet3d, on the GPU.
Verified against the CUDA kernels on a 4090: same 19 tracks over the example
scene, same 5.5 s for 21 frames, and ~10 s to compile on first import.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- README.md +8 -5
- requirements.txt +6 -11
- vis4d_cuda_ops/__init__.py +110 -0
- vis4d_cuda_ops/src/box_iou_rotated/box_iou_rotated.h +33 -0
- vis4d_cuda_ops/src/box_iou_rotated/box_iou_rotated_cpu.cpp +37 -0
- vis4d_cuda_ops/src/box_iou_rotated/box_iou_rotated_utils.h +368 -0
- vis4d_cuda_ops/src/deform_conv/deform_conv.h +375 -0
- vis4d_cuda_ops/src/iou_box3d/iou_box3d.h +48 -0
- vis4d_cuda_ops/src/iou_box3d/iou_box3d_cpu.cpp +122 -0
- vis4d_cuda_ops/src/iou_box3d/iou_utils.h +733 -0
- vis4d_cuda_ops/src/ms_deform_attn/ms_deform_attn.h +60 -0
- vis4d_cuda_ops/src/ms_deform_attn/ms_deform_attn_cuda.h +30 -0
- vis4d_cuda_ops/src/nms_rotated/nms_rotated.h +37 -0
- vis4d_cuda_ops/src/nms_rotated/nms_rotated_cpu.cpp +73 -0
- vis4d_cuda_ops/src/util/pytorch3d_cutils.h +17 -0
- vis4d_cuda_ops/src/util/vec3.h +74 -0
- vis4d_cuda_ops/src/vision.cpp +38 -0
|
@@ -42,11 +42,14 @@ python app.py
|
|
| 42 |
- `sdk_version` is pinned to a Gradio 5 release because `gradio_rerun==0.26.0`
|
| 43 |
is a Gradio 5 custom component. It has to stay on the same version as the
|
| 44 |
`rerun-sdk` the model code logs with, so bumping one means bumping all three.
|
| 45 |
-
- `vis4d_cuda_ops`
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
|
|
|
|
|
|
|
|
|
| 50 |
- `mapdet3d/` is vendored from the research repository so the Space does not
|
| 51 |
depend on a published package.
|
| 52 |
- The model weights are pulled from
|
|
|
|
| 42 |
- `sdk_version` is pinned to a Gradio 5 release because `gradio_rerun==0.26.0`
|
| 43 |
is a Gradio 5 custom component. It has to stay on the same version as the
|
| 44 |
`rerun-sdk` the model code logs with, so bumping one means bumping all three.
|
| 45 |
+
- `vis4d_cuda_ops/` shadows the package of the same name, because pip cannot
|
| 46 |
+
install the real one here: it has no wheels, and pip compiles it in an
|
| 47 |
+
isolated build environment that resolves its own newest torch while the Space
|
| 48 |
+
caps the runtime torch lower, so the extension fails to import with
|
| 49 |
+
`undefined symbol: c10::NotImplementedError`. The local package compiles only
|
| 50 |
+
the upstream CPU sources, against the torch the Space runs, and routes
|
| 51 |
+
deformable attention through the grid-sample implementation already in
|
| 52 |
+
`mapdet3d`. Detections and tracks come out identical; see its docstring.
|
| 53 |
- `mapdet3d/` is vendored from the research repository so the Space does not
|
| 54 |
depend on a published package.
|
| 55 |
- The model weights are pulled from
|
|
@@ -1,13 +1,11 @@
|
|
| 1 |
-
# NOTE: torch stays unpinned
|
| 2 |
-
#
|
| 3 |
-
#
|
| 4 |
-
#
|
| 5 |
-
#
|
| 6 |
-
# dies on `undefined symbol: c10::NotImplementedError`. Without isolation the
|
| 7 |
-
# extension compiles against the torch already in the image, and leaving the
|
| 8 |
-
# bound loose here keeps pip from swapping that torch out underneath it.
|
| 9 |
torch>=2.8.0
|
| 10 |
torchvision
|
|
|
|
| 11 |
|
| 12 |
# Map-Det3D
|
| 13 |
numpy
|
|
@@ -27,9 +25,6 @@ tqdm
|
|
| 27 |
huggingface_hub
|
| 28 |
safetensors
|
| 29 |
|
| 30 |
-
# CUDA ops for multi-scale deformable attention and 3D IoU
|
| 31 |
-
git+https://github.com/SysCV/vis4d_cuda_ops.git
|
| 32 |
-
|
| 33 |
# Demo
|
| 34 |
spaces
|
| 35 |
rerun-sdk==0.26.0
|
|
|
|
| 1 |
+
# NOTE: torch stays unpinned so it keeps whatever the Space image ships. The
|
| 2 |
+
# kernels in vis4d_cuda_ops/ are compiled against it on first import, so the two
|
| 3 |
+
# cannot drift apart. Do not add vis4d_cuda_ops here: pip builds it in an
|
| 4 |
+
# isolated environment that resolves its own, newest torch, and the extension
|
| 5 |
+
# then fails to import against the older torch the Space runs.
|
|
|
|
|
|
|
|
|
|
| 6 |
torch>=2.8.0
|
| 7 |
torchvision
|
| 8 |
+
ninja
|
| 9 |
|
| 10 |
# Map-Det3D
|
| 11 |
numpy
|
|
|
|
| 25 |
huggingface_hub
|
| 26 |
safetensors
|
| 27 |
|
|
|
|
|
|
|
|
|
|
| 28 |
# Demo
|
| 29 |
spaces
|
| 30 |
rerun-sdk==0.26.0
|
|
@@ -0,0 +1,110 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""CPU build of vis4d_cuda_ops, standing in for the published package.
|
| 2 |
+
|
| 3 |
+
Map-Det3D imports ``iou_box3d`` and ``ms_deform_attn_forward`` from
|
| 4 |
+
``vis4d_cuda_ops``. Installing that package on a Space does not work: it has no
|
| 5 |
+
wheels, and pip compiles it inside an isolated build environment that resolves
|
| 6 |
+
its own, newest torch, while the Space caps the runtime torch several releases
|
| 7 |
+
lower. The two ABIs disagree and importing the result dies on
|
| 8 |
+
``undefined symbol: c10::NotImplementedError``.
|
| 9 |
+
|
| 10 |
+
Since ``/home/user/app`` comes first on ``sys.path``, this package answers those
|
| 11 |
+
imports instead. It compiles only the CPU translation units of the upstream
|
| 12 |
+
sources, against the torch the Space actually runs, so no ABI can drift:
|
| 13 |
+
|
| 14 |
+
* ``iou_box3d`` runs the upstream CPU kernel, on the handful of boxes the track
|
| 15 |
+
graph compares per frame.
|
| 16 |
+
* ``ms_deform_attn_forward`` runs the grid-sample implementation that ships in
|
| 17 |
+
``mapdet3d.op.layer.ms_deform_attn`` and is used whenever the fused kernel is
|
| 18 |
+
unavailable. It is the same computation and still runs on the GPU, only
|
| 19 |
+
without the fused kernel.
|
| 20 |
+
|
| 21 |
+
The upstream sources live under ``src/`` unmodified; see ``LICENSE``.
|
| 22 |
+
"""
|
| 23 |
+
|
| 24 |
+
from __future__ import annotations
|
| 25 |
+
|
| 26 |
+
import os
|
| 27 |
+
|
| 28 |
+
from torch import Tensor
|
| 29 |
+
from torch.utils.cpp_extension import load
|
| 30 |
+
|
| 31 |
+
_SRC = os.path.join(os.path.dirname(os.path.abspath(__file__)), "src")
|
| 32 |
+
|
| 33 |
+
_CPU_SOURCES = [
|
| 34 |
+
os.path.join(_SRC, "vision.cpp"),
|
| 35 |
+
os.path.join(_SRC, "iou_box3d", "iou_box3d_cpu.cpp"),
|
| 36 |
+
os.path.join(_SRC, "box_iou_rotated", "box_iou_rotated_cpu.cpp"),
|
| 37 |
+
os.path.join(_SRC, "nms_rotated", "nms_rotated_cpu.cpp"),
|
| 38 |
+
]
|
| 39 |
+
|
| 40 |
+
# Compiled on first import and cached in ~/.cache/torch_extensions afterwards.
|
| 41 |
+
_ops = load(
|
| 42 |
+
name="vis4d_cpu_ops",
|
| 43 |
+
sources=_CPU_SOURCES,
|
| 44 |
+
extra_include_paths=[_SRC],
|
| 45 |
+
extra_cflags=["-O2"],
|
| 46 |
+
with_cuda=False,
|
| 47 |
+
verbose=False,
|
| 48 |
+
)
|
| 49 |
+
|
| 50 |
+
box_iou_rotated = _ops.box_iou_rotated
|
| 51 |
+
nms_rotated = _ops.nms_rotated
|
| 52 |
+
deform_conv_forward = _ops.deform_conv_forward
|
| 53 |
+
deform_conv_backward_input = _ops.deform_conv_backward_input
|
| 54 |
+
deform_conv_backward_filter = _ops.deform_conv_backward_filter
|
| 55 |
+
modulated_deform_conv_forward = _ops.modulated_deform_conv_forward
|
| 56 |
+
modulated_deform_conv_backward = _ops.modulated_deform_conv_backward
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def iou_box3d(
|
| 60 |
+
boxes1: Tensor, boxes2: Tensor
|
| 61 |
+
) -> tuple[Tensor, Tensor]: # pragma: no cover
|
| 62 |
+
"""Intersection volume and IoU of two sets of 3D box corners."""
|
| 63 |
+
device = boxes1.device
|
| 64 |
+
volume, iou = _ops.iou_box3d(boxes1.cpu(), boxes2.cpu())
|
| 65 |
+
|
| 66 |
+
return volume.to(device), iou.to(device)
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
def ms_deform_attn_forward(
|
| 70 |
+
value: Tensor,
|
| 71 |
+
spatial_shapes: Tensor,
|
| 72 |
+
level_start_index: Tensor,
|
| 73 |
+
sampling_loc: Tensor,
|
| 74 |
+
attn_weight: Tensor,
|
| 75 |
+
im2col_step: int,
|
| 76 |
+
) -> Tensor: # pragma: no cover
|
| 77 |
+
"""Multi-scale deformable attention, via grid sampling.
|
| 78 |
+
|
| 79 |
+
``level_start_index`` and ``im2col_step`` only exist to drive the fused
|
| 80 |
+
kernel; the grid-sample path splits ``value`` by ``spatial_shapes`` itself.
|
| 81 |
+
"""
|
| 82 |
+
# Imported here because mapdet3d imports this module while it is still
|
| 83 |
+
# setting up its own.
|
| 84 |
+
from mapdet3d.op.layer.ms_deform_attn import ms_deformable_attention_cpu
|
| 85 |
+
|
| 86 |
+
return ms_deformable_attention_cpu(
|
| 87 |
+
value, spatial_shapes, sampling_loc, attn_weight
|
| 88 |
+
)
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
def ms_deform_attn_backward(*args: object, **kwargs: object) -> None:
|
| 92 |
+
"""Not built: the demo only ever runs under ``torch.no_grad()``."""
|
| 93 |
+
raise NotImplementedError(
|
| 94 |
+
"vis4d_cuda_ops was built without the deformable attention backward "
|
| 95 |
+
"kernel. Install the upstream package to train."
|
| 96 |
+
)
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
__all__ = [
|
| 100 |
+
"box_iou_rotated",
|
| 101 |
+
"deform_conv_backward_filter",
|
| 102 |
+
"deform_conv_backward_input",
|
| 103 |
+
"deform_conv_forward",
|
| 104 |
+
"iou_box3d",
|
| 105 |
+
"modulated_deform_conv_backward",
|
| 106 |
+
"modulated_deform_conv_forward",
|
| 107 |
+
"ms_deform_attn_backward",
|
| 108 |
+
"ms_deform_attn_forward",
|
| 109 |
+
"nms_rotated",
|
| 110 |
+
]
|
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
// modified from
|
| 3 |
+
// https://github.com/facebookresearch/detectron2/blob/main/detectron2/layers/csrc/box_iou_rotated/box_iou_rotated.h
|
| 4 |
+
#pragma once
|
| 5 |
+
#include <torch/types.h>
|
| 6 |
+
|
| 7 |
+
at::Tensor box_iou_rotated_cpu(
|
| 8 |
+
const at::Tensor& boxes1,
|
| 9 |
+
const at::Tensor& boxes2);
|
| 10 |
+
|
| 11 |
+
#if defined(WITH_CUDA) || defined(WITH_HIP)
|
| 12 |
+
at::Tensor box_iou_rotated_cuda(
|
| 13 |
+
const at::Tensor& boxes1,
|
| 14 |
+
const at::Tensor& boxes2);
|
| 15 |
+
#endif
|
| 16 |
+
|
| 17 |
+
// Interface for Python
|
| 18 |
+
// inline is needed to prevent multiple function definitions when this header is
|
| 19 |
+
// included by different cpps
|
| 20 |
+
inline at::Tensor box_iou_rotated(
|
| 21 |
+
const at::Tensor& boxes1,
|
| 22 |
+
const at::Tensor& boxes2) {
|
| 23 |
+
assert(boxes1.device().is_cuda() == boxes2.device().is_cuda());
|
| 24 |
+
if (boxes1.device().is_cuda()) {
|
| 25 |
+
#if defined(WITH_CUDA) || defined(WITH_HIP)
|
| 26 |
+
return box_iou_rotated_cuda(boxes1.contiguous(), boxes2.contiguous());
|
| 27 |
+
#else
|
| 28 |
+
AT_ERROR("Not compiled with GPU support!");
|
| 29 |
+
#endif
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
return box_iou_rotated_cpu(boxes1.contiguous(), boxes2.contiguous());
|
| 33 |
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
// modified from
|
| 3 |
+
// https://github.com/facebookresearch/detectron2/blob/main/detectron2/layers/csrc/box_iou_rotated/box_iou_rotated_cpu.cpp
|
| 4 |
+
#include "box_iou_rotated.h"
|
| 5 |
+
#include "box_iou_rotated_utils.h"
|
| 6 |
+
|
| 7 |
+
template <typename T>
|
| 8 |
+
void box_iou_rotated_cpu_kernel(
|
| 9 |
+
const at::Tensor& boxes1,
|
| 10 |
+
const at::Tensor& boxes2,
|
| 11 |
+
at::Tensor& ious) {
|
| 12 |
+
auto num_boxes1 = boxes1.size(0);
|
| 13 |
+
auto num_boxes2 = boxes2.size(0);
|
| 14 |
+
|
| 15 |
+
for (int i = 0; i < num_boxes1; i++) {
|
| 16 |
+
for (int j = 0; j < num_boxes2; j++) {
|
| 17 |
+
ious[i * num_boxes2 + j] = single_box_iou_rotated<T>(
|
| 18 |
+
boxes1[i].data_ptr<T>(), boxes2[j].data_ptr<T>());
|
| 19 |
+
}
|
| 20 |
+
}
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
at::Tensor box_iou_rotated_cpu(
|
| 24 |
+
// input must be contiguous:
|
| 25 |
+
const at::Tensor& boxes1,
|
| 26 |
+
const at::Tensor& boxes2) {
|
| 27 |
+
auto num_boxes1 = boxes1.size(0);
|
| 28 |
+
auto num_boxes2 = boxes2.size(0);
|
| 29 |
+
at::Tensor ious =
|
| 30 |
+
at::empty({num_boxes1 * num_boxes2}, boxes1.options().dtype(at::kFloat));
|
| 31 |
+
|
| 32 |
+
box_iou_rotated_cpu_kernel<float>(boxes1, boxes2, ious);
|
| 33 |
+
|
| 34 |
+
// reshape from 1d array to 2d array
|
| 35 |
+
auto shape = std::vector<int64_t>{num_boxes1, num_boxes2};
|
| 36 |
+
return ious.reshape(shape);
|
| 37 |
+
}
|
|
@@ -0,0 +1,368 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
// modified from
|
| 3 |
+
// https://github.com/facebookresearch/detectron2/blob/main/detectron2/layers/csrc/box_iou_rotated/box_iou_rotated_utils.h
|
| 4 |
+
#pragma once
|
| 5 |
+
|
| 6 |
+
#include <cassert>
|
| 7 |
+
#include <cmath>
|
| 8 |
+
|
| 9 |
+
#if defined(__CUDACC__) || __HCC__ == 1 || __HIP__ == 1
|
| 10 |
+
// Designates functions callable from the host (CPU) and the device (GPU)
|
| 11 |
+
#define HOST_DEVICE __host__ __device__
|
| 12 |
+
#define HOST_DEVICE_INLINE HOST_DEVICE __forceinline__
|
| 13 |
+
#else
|
| 14 |
+
#include <algorithm>
|
| 15 |
+
#define HOST_DEVICE
|
| 16 |
+
#define HOST_DEVICE_INLINE HOST_DEVICE inline
|
| 17 |
+
#endif
|
| 18 |
+
|
| 19 |
+
namespace {
|
| 20 |
+
|
| 21 |
+
template <typename T>
|
| 22 |
+
struct RotatedBox {
|
| 23 |
+
T x_ctr, y_ctr, w, h, a;
|
| 24 |
+
};
|
| 25 |
+
|
| 26 |
+
template <typename T>
|
| 27 |
+
struct Point {
|
| 28 |
+
T x, y;
|
| 29 |
+
HOST_DEVICE_INLINE Point(const T& px = 0, const T& py = 0) : x(px), y(py) {}
|
| 30 |
+
HOST_DEVICE_INLINE Point operator+(const Point& p) const {
|
| 31 |
+
return Point(x + p.x, y + p.y);
|
| 32 |
+
}
|
| 33 |
+
HOST_DEVICE_INLINE Point& operator+=(const Point& p) {
|
| 34 |
+
x += p.x;
|
| 35 |
+
y += p.y;
|
| 36 |
+
return *this;
|
| 37 |
+
}
|
| 38 |
+
HOST_DEVICE_INLINE Point operator-(const Point& p) const {
|
| 39 |
+
return Point(x - p.x, y - p.y);
|
| 40 |
+
}
|
| 41 |
+
HOST_DEVICE_INLINE Point operator*(const T coeff) const {
|
| 42 |
+
return Point(x * coeff, y * coeff);
|
| 43 |
+
}
|
| 44 |
+
};
|
| 45 |
+
|
| 46 |
+
template <typename T>
|
| 47 |
+
HOST_DEVICE_INLINE T dot_2d(const Point<T>& A, const Point<T>& B) {
|
| 48 |
+
return A.x * B.x + A.y * B.y;
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
// R: result type. can be different from input type
|
| 52 |
+
template <typename T, typename R = T>
|
| 53 |
+
HOST_DEVICE_INLINE R cross_2d(const Point<T>& A, const Point<T>& B) {
|
| 54 |
+
return static_cast<R>(A.x) * static_cast<R>(B.y) -
|
| 55 |
+
static_cast<R>(B.x) * static_cast<R>(A.y);
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
template <typename T>
|
| 59 |
+
HOST_DEVICE_INLINE void get_rotated_vertices(
|
| 60 |
+
const RotatedBox<T>& box,
|
| 61 |
+
Point<T> (&pts)[4]) {
|
| 62 |
+
// M_PI / 180. == 0.01745329251
|
| 63 |
+
double theta = box.a * 0.01745329251;
|
| 64 |
+
T cosTheta2 = (T)cos(theta) * 0.5f;
|
| 65 |
+
T sinTheta2 = (T)sin(theta) * 0.5f;
|
| 66 |
+
|
| 67 |
+
// y: top --> down; x: left --> right
|
| 68 |
+
pts[0].x = box.x_ctr + sinTheta2 * box.h + cosTheta2 * box.w;
|
| 69 |
+
pts[0].y = box.y_ctr + cosTheta2 * box.h - sinTheta2 * box.w;
|
| 70 |
+
pts[1].x = box.x_ctr - sinTheta2 * box.h + cosTheta2 * box.w;
|
| 71 |
+
pts[1].y = box.y_ctr - cosTheta2 * box.h - sinTheta2 * box.w;
|
| 72 |
+
pts[2].x = 2 * box.x_ctr - pts[0].x;
|
| 73 |
+
pts[2].y = 2 * box.y_ctr - pts[0].y;
|
| 74 |
+
pts[3].x = 2 * box.x_ctr - pts[1].x;
|
| 75 |
+
pts[3].y = 2 * box.y_ctr - pts[1].y;
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
template <typename T>
|
| 79 |
+
HOST_DEVICE_INLINE int get_intersection_points(
|
| 80 |
+
const Point<T> (&pts1)[4],
|
| 81 |
+
const Point<T> (&pts2)[4],
|
| 82 |
+
Point<T> (&intersections)[24]) {
|
| 83 |
+
// Line vector
|
| 84 |
+
// A line from p1 to p2 is: p1 + (p2-p1)*t, t=[0,1]
|
| 85 |
+
Point<T> vec1[4], vec2[4];
|
| 86 |
+
for (int i = 0; i < 4; i++) {
|
| 87 |
+
vec1[i] = pts1[(i + 1) % 4] - pts1[i];
|
| 88 |
+
vec2[i] = pts2[(i + 1) % 4] - pts2[i];
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
// When computing the intersection area, it doesn't hurt if we have
|
| 92 |
+
// more (duplicated/approximate) intersections/vertices than needed,
|
| 93 |
+
// while it can cause drastic difference if we miss an intersection/vertex.
|
| 94 |
+
// Therefore, we add an epsilon to relax the comparisons between
|
| 95 |
+
// the float point numbers that decide the intersection points.
|
| 96 |
+
double EPS = 1e-5;
|
| 97 |
+
|
| 98 |
+
// Line test - test all line combos for intersection
|
| 99 |
+
int num = 0; // number of intersections
|
| 100 |
+
for (int i = 0; i < 4; i++) {
|
| 101 |
+
for (int j = 0; j < 4; j++) {
|
| 102 |
+
// Solve for 2x2 Ax=b
|
| 103 |
+
T det = cross_2d<T>(vec2[j], vec1[i]);
|
| 104 |
+
|
| 105 |
+
// This takes care of parallel lines
|
| 106 |
+
if (fabs(det) <= 1e-14) {
|
| 107 |
+
continue;
|
| 108 |
+
}
|
| 109 |
+
|
| 110 |
+
auto vec12 = pts2[j] - pts1[i];
|
| 111 |
+
|
| 112 |
+
T t1 = cross_2d<T>(vec2[j], vec12) / det;
|
| 113 |
+
T t2 = cross_2d<T>(vec1[i], vec12) / det;
|
| 114 |
+
|
| 115 |
+
if (t1 > -EPS && t1 < 1.0f + EPS && t2 > -EPS && t2 < 1.0f + EPS) {
|
| 116 |
+
intersections[num++] = pts1[i] + vec1[i] * t1;
|
| 117 |
+
}
|
| 118 |
+
}
|
| 119 |
+
}
|
| 120 |
+
|
| 121 |
+
// Check for vertices of rect1 inside rect2
|
| 122 |
+
{
|
| 123 |
+
const auto& AB = vec2[0];
|
| 124 |
+
const auto& DA = vec2[3];
|
| 125 |
+
auto ABdotAB = dot_2d<T>(AB, AB);
|
| 126 |
+
auto ADdotAD = dot_2d<T>(DA, DA);
|
| 127 |
+
for (int i = 0; i < 4; i++) {
|
| 128 |
+
// assume ABCD is the rectangle, and P is the point to be judged
|
| 129 |
+
// P is inside ABCD iff. P's projection on AB lies within AB
|
| 130 |
+
// and P's projection on AD lies within AD
|
| 131 |
+
|
| 132 |
+
auto AP = pts1[i] - pts2[0];
|
| 133 |
+
|
| 134 |
+
auto APdotAB = dot_2d<T>(AP, AB);
|
| 135 |
+
auto APdotAD = -dot_2d<T>(AP, DA);
|
| 136 |
+
|
| 137 |
+
if ((APdotAB > -EPS) && (APdotAD > -EPS) && (APdotAB < ABdotAB + EPS) &&
|
| 138 |
+
(APdotAD < ADdotAD + EPS)) {
|
| 139 |
+
intersections[num++] = pts1[i];
|
| 140 |
+
}
|
| 141 |
+
}
|
| 142 |
+
}
|
| 143 |
+
|
| 144 |
+
// Reverse the check - check for vertices of rect2 inside rect1
|
| 145 |
+
{
|
| 146 |
+
const auto& AB = vec1[0];
|
| 147 |
+
const auto& DA = vec1[3];
|
| 148 |
+
auto ABdotAB = dot_2d<T>(AB, AB);
|
| 149 |
+
auto ADdotAD = dot_2d<T>(DA, DA);
|
| 150 |
+
for (int i = 0; i < 4; i++) {
|
| 151 |
+
auto AP = pts2[i] - pts1[0];
|
| 152 |
+
|
| 153 |
+
auto APdotAB = dot_2d<T>(AP, AB);
|
| 154 |
+
auto APdotAD = -dot_2d<T>(AP, DA);
|
| 155 |
+
|
| 156 |
+
if ((APdotAB > -EPS) && (APdotAD > -EPS) && (APdotAB < ABdotAB + EPS) &&
|
| 157 |
+
(APdotAD < ADdotAD + EPS)) {
|
| 158 |
+
intersections[num++] = pts2[i];
|
| 159 |
+
}
|
| 160 |
+
}
|
| 161 |
+
}
|
| 162 |
+
|
| 163 |
+
return num;
|
| 164 |
+
}
|
| 165 |
+
|
| 166 |
+
template <typename T>
|
| 167 |
+
HOST_DEVICE_INLINE int convex_hull_graham(
|
| 168 |
+
const Point<T> (&p)[24],
|
| 169 |
+
const int& num_in,
|
| 170 |
+
Point<T> (&q)[24],
|
| 171 |
+
bool shift_to_zero = false) {
|
| 172 |
+
assert(num_in >= 2);
|
| 173 |
+
|
| 174 |
+
// Step 1:
|
| 175 |
+
// Find point with minimum y
|
| 176 |
+
// if more than 1 points have the same minimum y,
|
| 177 |
+
// pick the one with the minimum x.
|
| 178 |
+
int t = 0;
|
| 179 |
+
for (int i = 1; i < num_in; i++) {
|
| 180 |
+
if (p[i].y < p[t].y || (p[i].y == p[t].y && p[i].x < p[t].x)) {
|
| 181 |
+
t = i;
|
| 182 |
+
}
|
| 183 |
+
}
|
| 184 |
+
auto& start = p[t]; // starting point
|
| 185 |
+
|
| 186 |
+
// Step 2:
|
| 187 |
+
// Subtract starting point from every points (for sorting in the next step)
|
| 188 |
+
for (int i = 0; i < num_in; i++) {
|
| 189 |
+
q[i] = p[i] - start;
|
| 190 |
+
}
|
| 191 |
+
|
| 192 |
+
// Swap the starting point to position 0
|
| 193 |
+
auto tmp = q[0];
|
| 194 |
+
q[0] = q[t];
|
| 195 |
+
q[t] = tmp;
|
| 196 |
+
|
| 197 |
+
// Step 3:
|
| 198 |
+
// Sort point 1 ~ num_in according to their relative cross-product values
|
| 199 |
+
// (essentially sorting according to angles)
|
| 200 |
+
// If the angles are the same, sort according to their distance to origin
|
| 201 |
+
T dist[24];
|
| 202 |
+
#if defined(__CUDACC__) || __HCC__ == 1 || __HIP__ == 1
|
| 203 |
+
// compute distance to origin before sort, and sort them together with the
|
| 204 |
+
// points
|
| 205 |
+
for (int i = 0; i < num_in; i++) {
|
| 206 |
+
dist[i] = dot_2d<T>(q[i], q[i]);
|
| 207 |
+
}
|
| 208 |
+
|
| 209 |
+
// CUDA version
|
| 210 |
+
// In the future, we can potentially use thrust
|
| 211 |
+
// for sorting here to improve speed (though not guaranteed)
|
| 212 |
+
for (int i = 1; i < num_in - 1; i++) {
|
| 213 |
+
for (int j = i + 1; j < num_in; j++) {
|
| 214 |
+
T crossProduct = cross_2d<T>(q[i], q[j]);
|
| 215 |
+
if ((crossProduct < -1e-6) ||
|
| 216 |
+
(fabs(crossProduct) < 1e-6 && dist[i] > dist[j])) {
|
| 217 |
+
auto q_tmp = q[i];
|
| 218 |
+
q[i] = q[j];
|
| 219 |
+
q[j] = q_tmp;
|
| 220 |
+
auto dist_tmp = dist[i];
|
| 221 |
+
dist[i] = dist[j];
|
| 222 |
+
dist[j] = dist_tmp;
|
| 223 |
+
}
|
| 224 |
+
}
|
| 225 |
+
}
|
| 226 |
+
#else
|
| 227 |
+
// CPU version
|
| 228 |
+
std::sort(
|
| 229 |
+
q + 1, q + num_in, [](const Point<T>& A, const Point<T>& B) -> bool {
|
| 230 |
+
T temp = cross_2d<T>(A, B);
|
| 231 |
+
if (fabs(temp) < 1e-6) {
|
| 232 |
+
return dot_2d<T>(A, A) < dot_2d<T>(B, B);
|
| 233 |
+
} else {
|
| 234 |
+
return temp > 0;
|
| 235 |
+
}
|
| 236 |
+
});
|
| 237 |
+
// compute distance to origin after sort, since the points are now different.
|
| 238 |
+
for (int i = 0; i < num_in; i++) {
|
| 239 |
+
dist[i] = dot_2d<T>(q[i], q[i]);
|
| 240 |
+
}
|
| 241 |
+
#endif
|
| 242 |
+
|
| 243 |
+
// Step 4:
|
| 244 |
+
// Make sure there are at least 2 points (that don't overlap with each other)
|
| 245 |
+
// in the stack
|
| 246 |
+
int k; // index of the non-overlapped second point
|
| 247 |
+
for (k = 1; k < num_in; k++) {
|
| 248 |
+
if (dist[k] > 1e-8) {
|
| 249 |
+
break;
|
| 250 |
+
}
|
| 251 |
+
}
|
| 252 |
+
if (k == num_in) {
|
| 253 |
+
// We reach the end, which means the convex hull is just one point
|
| 254 |
+
q[0] = p[t];
|
| 255 |
+
return 1;
|
| 256 |
+
}
|
| 257 |
+
q[1] = q[k];
|
| 258 |
+
int m = 2; // 2 points in the stack
|
| 259 |
+
// Step 5:
|
| 260 |
+
// Finally we can start the scanning process.
|
| 261 |
+
// When a non-convex relationship between the 3 points is found
|
| 262 |
+
// (either concave shape or duplicated points),
|
| 263 |
+
// we pop the previous point from the stack
|
| 264 |
+
// until the 3-point relationship is convex again, or
|
| 265 |
+
// until the stack only contains two points
|
| 266 |
+
for (int i = k + 1; i < num_in; i++) {
|
| 267 |
+
while (m > 1) {
|
| 268 |
+
auto q1 = q[i] - q[m - 2], q2 = q[m - 1] - q[m - 2];
|
| 269 |
+
// cross_2d() uses FMA and therefore computes round(round(q1.x*q2.y) -
|
| 270 |
+
// q2.x*q1.y) So it may not return 0 even when q1==q2. Therefore we
|
| 271 |
+
// compare round(q1.x*q2.y) and round(q2.x*q1.y) directly. (round means
|
| 272 |
+
// round to nearest floating point).
|
| 273 |
+
if (q1.x * q2.y >= q2.x * q1.y)
|
| 274 |
+
m--;
|
| 275 |
+
else
|
| 276 |
+
break;
|
| 277 |
+
}
|
| 278 |
+
// Using double also helps, but float can solve the issue for now.
|
| 279 |
+
// while (m > 1 && cross_2d<T, double>(q[i] - q[m - 2], q[m - 1] - q[m - 2])
|
| 280 |
+
// >= 0) {
|
| 281 |
+
// m--;
|
| 282 |
+
// }
|
| 283 |
+
q[m++] = q[i];
|
| 284 |
+
}
|
| 285 |
+
|
| 286 |
+
// Step 6 (Optional):
|
| 287 |
+
// In general sense we need the original coordinates, so we
|
| 288 |
+
// need to shift the points back (reverting Step 2)
|
| 289 |
+
// But if we're only interested in getting the area/perimeter of the shape
|
| 290 |
+
// We can simply return.
|
| 291 |
+
if (!shift_to_zero) {
|
| 292 |
+
for (int i = 0; i < m; i++) {
|
| 293 |
+
q[i] += start;
|
| 294 |
+
}
|
| 295 |
+
}
|
| 296 |
+
|
| 297 |
+
return m;
|
| 298 |
+
}
|
| 299 |
+
|
| 300 |
+
template <typename T>
|
| 301 |
+
HOST_DEVICE_INLINE T polygon_area(const Point<T> (&q)[24], const int& m) {
|
| 302 |
+
if (m <= 2) {
|
| 303 |
+
return 0;
|
| 304 |
+
}
|
| 305 |
+
|
| 306 |
+
T area = 0;
|
| 307 |
+
for (int i = 1; i < m - 1; i++) {
|
| 308 |
+
area += fabs(cross_2d<T>(q[i] - q[0], q[i + 1] - q[0]));
|
| 309 |
+
}
|
| 310 |
+
|
| 311 |
+
return area / 2.0;
|
| 312 |
+
}
|
| 313 |
+
|
| 314 |
+
template <typename T>
|
| 315 |
+
HOST_DEVICE_INLINE T rotated_boxes_intersection(
|
| 316 |
+
const RotatedBox<T>& box1,
|
| 317 |
+
const RotatedBox<T>& box2) {
|
| 318 |
+
// There are up to 4 x 4 + 4 + 4 = 24 intersections (including dups) returned
|
| 319 |
+
// from rotated_rect_intersection_pts
|
| 320 |
+
Point<T> intersectPts[24], orderedPts[24];
|
| 321 |
+
|
| 322 |
+
Point<T> pts1[4];
|
| 323 |
+
Point<T> pts2[4];
|
| 324 |
+
get_rotated_vertices<T>(box1, pts1);
|
| 325 |
+
get_rotated_vertices<T>(box2, pts2);
|
| 326 |
+
|
| 327 |
+
int num = get_intersection_points<T>(pts1, pts2, intersectPts);
|
| 328 |
+
|
| 329 |
+
if (num <= 2) {
|
| 330 |
+
return 0.0;
|
| 331 |
+
}
|
| 332 |
+
|
| 333 |
+
// Convex Hull to order the intersection points in clockwise order and find
|
| 334 |
+
// the contour area.
|
| 335 |
+
int num_convex = convex_hull_graham<T>(intersectPts, num, orderedPts, true);
|
| 336 |
+
return polygon_area<T>(orderedPts, num_convex);
|
| 337 |
+
}
|
| 338 |
+
|
| 339 |
+
} // namespace
|
| 340 |
+
|
| 341 |
+
template <typename T>
|
| 342 |
+
HOST_DEVICE_INLINE T
|
| 343 |
+
single_box_iou_rotated(T const* const box1_raw, T const* const box2_raw) {
|
| 344 |
+
// shift center to the middle point to achieve higher precision in result
|
| 345 |
+
RotatedBox<T> box1, box2;
|
| 346 |
+
auto center_shift_x = (box1_raw[0] + box2_raw[0]) / 2.0;
|
| 347 |
+
auto center_shift_y = (box1_raw[1] + box2_raw[1]) / 2.0;
|
| 348 |
+
box1.x_ctr = box1_raw[0] - center_shift_x;
|
| 349 |
+
box1.y_ctr = box1_raw[1] - center_shift_y;
|
| 350 |
+
box1.w = box1_raw[2];
|
| 351 |
+
box1.h = box1_raw[3];
|
| 352 |
+
box1.a = box1_raw[4];
|
| 353 |
+
box2.x_ctr = box2_raw[0] - center_shift_x;
|
| 354 |
+
box2.y_ctr = box2_raw[1] - center_shift_y;
|
| 355 |
+
box2.w = box2_raw[2];
|
| 356 |
+
box2.h = box2_raw[3];
|
| 357 |
+
box2.a = box2_raw[4];
|
| 358 |
+
|
| 359 |
+
T area1 = box1.w * box1.h;
|
| 360 |
+
T area2 = box2.w * box2.h;
|
| 361 |
+
if (area1 < 1e-14 || area2 < 1e-14) {
|
| 362 |
+
return 0.f;
|
| 363 |
+
}
|
| 364 |
+
|
| 365 |
+
T intersection = rotated_boxes_intersection<T>(box1, box2);
|
| 366 |
+
T iou = intersection / (area1 + area2 - intersection);
|
| 367 |
+
return iou;
|
| 368 |
+
}
|
|
@@ -0,0 +1,375 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
// modified from
|
| 3 |
+
// https://github.com/facebookresearch/detectron2/blob/main/detectron2/layers/csrc/deformable/deform_conv.h
|
| 4 |
+
#pragma once
|
| 5 |
+
#include <torch/types.h>
|
| 6 |
+
|
| 7 |
+
#if defined(WITH_CUDA) || defined(WITH_HIP)
|
| 8 |
+
int deform_conv_forward_cuda(
|
| 9 |
+
at::Tensor input,
|
| 10 |
+
at::Tensor weight,
|
| 11 |
+
at::Tensor offset,
|
| 12 |
+
at::Tensor output,
|
| 13 |
+
at::Tensor columns,
|
| 14 |
+
at::Tensor ones,
|
| 15 |
+
int kW,
|
| 16 |
+
int kH,
|
| 17 |
+
int dW,
|
| 18 |
+
int dH,
|
| 19 |
+
int padW,
|
| 20 |
+
int padH,
|
| 21 |
+
int dilationW,
|
| 22 |
+
int dilationH,
|
| 23 |
+
int group,
|
| 24 |
+
int deformable_group,
|
| 25 |
+
int im2col_step);
|
| 26 |
+
|
| 27 |
+
int deform_conv_backward_input_cuda(
|
| 28 |
+
at::Tensor input,
|
| 29 |
+
at::Tensor offset,
|
| 30 |
+
at::Tensor gradOutput,
|
| 31 |
+
at::Tensor gradInput,
|
| 32 |
+
at::Tensor gradOffset,
|
| 33 |
+
at::Tensor weight,
|
| 34 |
+
at::Tensor columns,
|
| 35 |
+
int kW,
|
| 36 |
+
int kH,
|
| 37 |
+
int dW,
|
| 38 |
+
int dH,
|
| 39 |
+
int padW,
|
| 40 |
+
int padH,
|
| 41 |
+
int dilationW,
|
| 42 |
+
int dilationH,
|
| 43 |
+
int group,
|
| 44 |
+
int deformable_group,
|
| 45 |
+
int im2col_step);
|
| 46 |
+
|
| 47 |
+
int deform_conv_backward_parameters_cuda(
|
| 48 |
+
at::Tensor input,
|
| 49 |
+
at::Tensor offset,
|
| 50 |
+
at::Tensor gradOutput,
|
| 51 |
+
at::Tensor gradWeight, // at::Tensor gradBias,
|
| 52 |
+
at::Tensor columns,
|
| 53 |
+
at::Tensor ones,
|
| 54 |
+
int kW,
|
| 55 |
+
int kH,
|
| 56 |
+
int dW,
|
| 57 |
+
int dH,
|
| 58 |
+
int padW,
|
| 59 |
+
int padH,
|
| 60 |
+
int dilationW,
|
| 61 |
+
int dilationH,
|
| 62 |
+
int group,
|
| 63 |
+
int deformable_group,
|
| 64 |
+
float scale,
|
| 65 |
+
int im2col_step);
|
| 66 |
+
|
| 67 |
+
void modulated_deform_conv_cuda_forward(
|
| 68 |
+
at::Tensor input,
|
| 69 |
+
at::Tensor weight,
|
| 70 |
+
at::Tensor bias,
|
| 71 |
+
at::Tensor ones,
|
| 72 |
+
at::Tensor offset,
|
| 73 |
+
at::Tensor mask,
|
| 74 |
+
at::Tensor output,
|
| 75 |
+
at::Tensor columns,
|
| 76 |
+
int kernel_h,
|
| 77 |
+
int kernel_w,
|
| 78 |
+
const int stride_h,
|
| 79 |
+
const int stride_w,
|
| 80 |
+
const int pad_h,
|
| 81 |
+
const int pad_w,
|
| 82 |
+
const int dilation_h,
|
| 83 |
+
const int dilation_w,
|
| 84 |
+
const int group,
|
| 85 |
+
const int deformable_group,
|
| 86 |
+
const bool with_bias);
|
| 87 |
+
|
| 88 |
+
void modulated_deform_conv_cuda_backward(
|
| 89 |
+
at::Tensor input,
|
| 90 |
+
at::Tensor weight,
|
| 91 |
+
at::Tensor bias,
|
| 92 |
+
at::Tensor ones,
|
| 93 |
+
at::Tensor offset,
|
| 94 |
+
at::Tensor mask,
|
| 95 |
+
at::Tensor columns,
|
| 96 |
+
at::Tensor grad_input,
|
| 97 |
+
at::Tensor grad_weight,
|
| 98 |
+
at::Tensor grad_bias,
|
| 99 |
+
at::Tensor grad_offset,
|
| 100 |
+
at::Tensor grad_mask,
|
| 101 |
+
at::Tensor grad_output,
|
| 102 |
+
int kernel_h,
|
| 103 |
+
int kernel_w,
|
| 104 |
+
int stride_h,
|
| 105 |
+
int stride_w,
|
| 106 |
+
int pad_h,
|
| 107 |
+
int pad_w,
|
| 108 |
+
int dilation_h,
|
| 109 |
+
int dilation_w,
|
| 110 |
+
int group,
|
| 111 |
+
int deformable_group,
|
| 112 |
+
const bool with_bias);
|
| 113 |
+
|
| 114 |
+
#endif
|
| 115 |
+
|
| 116 |
+
inline int deform_conv_forward(
|
| 117 |
+
at::Tensor input,
|
| 118 |
+
at::Tensor weight,
|
| 119 |
+
at::Tensor offset,
|
| 120 |
+
at::Tensor output,
|
| 121 |
+
at::Tensor columns,
|
| 122 |
+
at::Tensor ones,
|
| 123 |
+
int kW,
|
| 124 |
+
int kH,
|
| 125 |
+
int dW,
|
| 126 |
+
int dH,
|
| 127 |
+
int padW,
|
| 128 |
+
int padH,
|
| 129 |
+
int dilationW,
|
| 130 |
+
int dilationH,
|
| 131 |
+
int group,
|
| 132 |
+
int deformable_group,
|
| 133 |
+
int im2col_step) {
|
| 134 |
+
if (input.is_cuda()) {
|
| 135 |
+
#if defined(WITH_CUDA) || defined(WITH_HIP)
|
| 136 |
+
TORCH_CHECK(weight.is_cuda(), "weight tensor is not on GPU!");
|
| 137 |
+
TORCH_CHECK(offset.is_cuda(), "offset tensor is not on GPU!");
|
| 138 |
+
return deform_conv_forward_cuda(
|
| 139 |
+
input,
|
| 140 |
+
weight,
|
| 141 |
+
offset,
|
| 142 |
+
output,
|
| 143 |
+
columns,
|
| 144 |
+
ones,
|
| 145 |
+
kW,
|
| 146 |
+
kH,
|
| 147 |
+
dW,
|
| 148 |
+
dH,
|
| 149 |
+
padW,
|
| 150 |
+
padH,
|
| 151 |
+
dilationW,
|
| 152 |
+
dilationH,
|
| 153 |
+
group,
|
| 154 |
+
deformable_group,
|
| 155 |
+
im2col_step);
|
| 156 |
+
#else
|
| 157 |
+
AT_ERROR("Not compiled with GPU support!");
|
| 158 |
+
#endif
|
| 159 |
+
}
|
| 160 |
+
AT_ERROR("This operator is not implemented on CPU");
|
| 161 |
+
}
|
| 162 |
+
|
| 163 |
+
inline int deform_conv_backward_input(
|
| 164 |
+
at::Tensor input,
|
| 165 |
+
at::Tensor offset,
|
| 166 |
+
at::Tensor gradOutput,
|
| 167 |
+
at::Tensor gradInput,
|
| 168 |
+
at::Tensor gradOffset,
|
| 169 |
+
at::Tensor weight,
|
| 170 |
+
at::Tensor columns,
|
| 171 |
+
int kW,
|
| 172 |
+
int kH,
|
| 173 |
+
int dW,
|
| 174 |
+
int dH,
|
| 175 |
+
int padW,
|
| 176 |
+
int padH,
|
| 177 |
+
int dilationW,
|
| 178 |
+
int dilationH,
|
| 179 |
+
int group,
|
| 180 |
+
int deformable_group,
|
| 181 |
+
int im2col_step) {
|
| 182 |
+
if (gradOutput.is_cuda()) {
|
| 183 |
+
#if defined(WITH_CUDA) || defined(WITH_HIP)
|
| 184 |
+
TORCH_CHECK(input.is_cuda(), "input tensor is not on GPU!");
|
| 185 |
+
TORCH_CHECK(weight.is_cuda(), "weight tensor is not on GPU!");
|
| 186 |
+
TORCH_CHECK(offset.is_cuda(), "offset tensor is not on GPU!");
|
| 187 |
+
return deform_conv_backward_input_cuda(
|
| 188 |
+
input,
|
| 189 |
+
offset,
|
| 190 |
+
gradOutput,
|
| 191 |
+
gradInput,
|
| 192 |
+
gradOffset,
|
| 193 |
+
weight,
|
| 194 |
+
columns,
|
| 195 |
+
kW,
|
| 196 |
+
kH,
|
| 197 |
+
dW,
|
| 198 |
+
dH,
|
| 199 |
+
padW,
|
| 200 |
+
padH,
|
| 201 |
+
dilationW,
|
| 202 |
+
dilationH,
|
| 203 |
+
group,
|
| 204 |
+
deformable_group,
|
| 205 |
+
im2col_step);
|
| 206 |
+
#else
|
| 207 |
+
AT_ERROR("Not compiled with GPU support!");
|
| 208 |
+
#endif
|
| 209 |
+
}
|
| 210 |
+
AT_ERROR("This operator is not implemented on CPU");
|
| 211 |
+
}
|
| 212 |
+
|
| 213 |
+
inline int deform_conv_backward_filter(
|
| 214 |
+
at::Tensor input,
|
| 215 |
+
at::Tensor offset,
|
| 216 |
+
at::Tensor gradOutput,
|
| 217 |
+
at::Tensor gradWeight, // at::Tensor gradBias,
|
| 218 |
+
at::Tensor columns,
|
| 219 |
+
at::Tensor ones,
|
| 220 |
+
int kW,
|
| 221 |
+
int kH,
|
| 222 |
+
int dW,
|
| 223 |
+
int dH,
|
| 224 |
+
int padW,
|
| 225 |
+
int padH,
|
| 226 |
+
int dilationW,
|
| 227 |
+
int dilationH,
|
| 228 |
+
int group,
|
| 229 |
+
int deformable_group,
|
| 230 |
+
float scale,
|
| 231 |
+
int im2col_step) {
|
| 232 |
+
if (gradOutput.is_cuda()) {
|
| 233 |
+
#if defined(WITH_CUDA) || defined(WITH_HIP)
|
| 234 |
+
TORCH_CHECK(input.is_cuda(), "input tensor is not on GPU!");
|
| 235 |
+
TORCH_CHECK(offset.is_cuda(), "offset tensor is not on GPU!");
|
| 236 |
+
return deform_conv_backward_parameters_cuda(
|
| 237 |
+
input,
|
| 238 |
+
offset,
|
| 239 |
+
gradOutput,
|
| 240 |
+
gradWeight,
|
| 241 |
+
columns,
|
| 242 |
+
ones,
|
| 243 |
+
kW,
|
| 244 |
+
kH,
|
| 245 |
+
dW,
|
| 246 |
+
dH,
|
| 247 |
+
padW,
|
| 248 |
+
padH,
|
| 249 |
+
dilationW,
|
| 250 |
+
dilationH,
|
| 251 |
+
group,
|
| 252 |
+
deformable_group,
|
| 253 |
+
scale,
|
| 254 |
+
im2col_step);
|
| 255 |
+
#else
|
| 256 |
+
AT_ERROR("Not compiled with GPU support!");
|
| 257 |
+
#endif
|
| 258 |
+
}
|
| 259 |
+
AT_ERROR("This operator is not implemented on CPU");
|
| 260 |
+
}
|
| 261 |
+
|
| 262 |
+
inline void modulated_deform_conv_forward(
|
| 263 |
+
at::Tensor input,
|
| 264 |
+
at::Tensor weight,
|
| 265 |
+
at::Tensor bias,
|
| 266 |
+
at::Tensor ones,
|
| 267 |
+
at::Tensor offset,
|
| 268 |
+
at::Tensor mask,
|
| 269 |
+
at::Tensor output,
|
| 270 |
+
at::Tensor columns,
|
| 271 |
+
int kernel_h,
|
| 272 |
+
int kernel_w,
|
| 273 |
+
const int stride_h,
|
| 274 |
+
const int stride_w,
|
| 275 |
+
const int pad_h,
|
| 276 |
+
const int pad_w,
|
| 277 |
+
const int dilation_h,
|
| 278 |
+
const int dilation_w,
|
| 279 |
+
const int group,
|
| 280 |
+
const int deformable_group,
|
| 281 |
+
const bool with_bias) {
|
| 282 |
+
if (input.is_cuda()) {
|
| 283 |
+
#if defined(WITH_CUDA) || defined(WITH_HIP)
|
| 284 |
+
TORCH_CHECK(weight.is_cuda(), "weight tensor is not on GPU!");
|
| 285 |
+
TORCH_CHECK(bias.is_cuda(), "bias tensor is not on GPU!");
|
| 286 |
+
TORCH_CHECK(offset.is_cuda(), "offset tensor is not on GPU!");
|
| 287 |
+
return modulated_deform_conv_cuda_forward(
|
| 288 |
+
input,
|
| 289 |
+
weight,
|
| 290 |
+
bias,
|
| 291 |
+
ones,
|
| 292 |
+
offset,
|
| 293 |
+
mask,
|
| 294 |
+
output,
|
| 295 |
+
columns,
|
| 296 |
+
kernel_h,
|
| 297 |
+
kernel_w,
|
| 298 |
+
stride_h,
|
| 299 |
+
stride_w,
|
| 300 |
+
pad_h,
|
| 301 |
+
pad_w,
|
| 302 |
+
dilation_h,
|
| 303 |
+
dilation_w,
|
| 304 |
+
group,
|
| 305 |
+
deformable_group,
|
| 306 |
+
with_bias);
|
| 307 |
+
#else
|
| 308 |
+
AT_ERROR("Not compiled with GPU support!");
|
| 309 |
+
#endif
|
| 310 |
+
}
|
| 311 |
+
AT_ERROR("This operator is not implemented on CPU");
|
| 312 |
+
}
|
| 313 |
+
|
| 314 |
+
inline void modulated_deform_conv_backward(
|
| 315 |
+
at::Tensor input,
|
| 316 |
+
at::Tensor weight,
|
| 317 |
+
at::Tensor bias,
|
| 318 |
+
at::Tensor ones,
|
| 319 |
+
at::Tensor offset,
|
| 320 |
+
at::Tensor mask,
|
| 321 |
+
at::Tensor columns,
|
| 322 |
+
at::Tensor grad_input,
|
| 323 |
+
at::Tensor grad_weight,
|
| 324 |
+
at::Tensor grad_bias,
|
| 325 |
+
at::Tensor grad_offset,
|
| 326 |
+
at::Tensor grad_mask,
|
| 327 |
+
at::Tensor grad_output,
|
| 328 |
+
int kernel_h,
|
| 329 |
+
int kernel_w,
|
| 330 |
+
int stride_h,
|
| 331 |
+
int stride_w,
|
| 332 |
+
int pad_h,
|
| 333 |
+
int pad_w,
|
| 334 |
+
int dilation_h,
|
| 335 |
+
int dilation_w,
|
| 336 |
+
int group,
|
| 337 |
+
int deformable_group,
|
| 338 |
+
const bool with_bias) {
|
| 339 |
+
if (grad_output.is_cuda()) {
|
| 340 |
+
#if defined(WITH_CUDA) || defined(WITH_HIP)
|
| 341 |
+
TORCH_CHECK(input.is_cuda(), "input tensor is not on GPU!");
|
| 342 |
+
TORCH_CHECK(weight.is_cuda(), "weight tensor is not on GPU!");
|
| 343 |
+
TORCH_CHECK(bias.is_cuda(), "bias tensor is not on GPU!");
|
| 344 |
+
TORCH_CHECK(offset.is_cuda(), "offset tensor is not on GPU!");
|
| 345 |
+
return modulated_deform_conv_cuda_backward(
|
| 346 |
+
input,
|
| 347 |
+
weight,
|
| 348 |
+
bias,
|
| 349 |
+
ones,
|
| 350 |
+
offset,
|
| 351 |
+
mask,
|
| 352 |
+
columns,
|
| 353 |
+
grad_input,
|
| 354 |
+
grad_weight,
|
| 355 |
+
grad_bias,
|
| 356 |
+
grad_offset,
|
| 357 |
+
grad_mask,
|
| 358 |
+
grad_output,
|
| 359 |
+
kernel_h,
|
| 360 |
+
kernel_w,
|
| 361 |
+
stride_h,
|
| 362 |
+
stride_w,
|
| 363 |
+
pad_h,
|
| 364 |
+
pad_w,
|
| 365 |
+
dilation_h,
|
| 366 |
+
dilation_w,
|
| 367 |
+
group,
|
| 368 |
+
deformable_group,
|
| 369 |
+
with_bias);
|
| 370 |
+
#else
|
| 371 |
+
AT_ERROR("Not compiled with GPU support!");
|
| 372 |
+
#endif
|
| 373 |
+
}
|
| 374 |
+
AT_ERROR("This operator is not implemented on CPU");
|
| 375 |
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/*
|
| 2 |
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 3 |
+
* All rights reserved.
|
| 4 |
+
*
|
| 5 |
+
* This source code is licensed under the BSD-style license found in the
|
| 6 |
+
* LICENSE file in the root directory of this source tree.
|
| 7 |
+
*/
|
| 8 |
+
|
| 9 |
+
#pragma once
|
| 10 |
+
#include <torch/extension.h>
|
| 11 |
+
#include <tuple>
|
| 12 |
+
#include "../util/pytorch3d_cutils.h"
|
| 13 |
+
|
| 14 |
+
// Calculate the intersection volume and IoU metric for two batches of boxes
|
| 15 |
+
//
|
| 16 |
+
// Args:
|
| 17 |
+
// boxes1: tensor of shape (N, 8, 3) of the coordinates of the 1st boxes
|
| 18 |
+
// boxes2: tensor of shape (M, 8, 3) of the coordinates of the 2nd boxes
|
| 19 |
+
// Returns:
|
| 20 |
+
// vol: (N, M) tensor of the volume of the intersecting convex shapes
|
| 21 |
+
// iou: (N, M) tensor of the intersection over union which is
|
| 22 |
+
// defined as: `iou = vol / (vol1 + vol2 - vol)`
|
| 23 |
+
|
| 24 |
+
// CPU implementation
|
| 25 |
+
std::tuple<at::Tensor, at::Tensor> IoUBox3DCpu(
|
| 26 |
+
const at::Tensor& boxes1,
|
| 27 |
+
const at::Tensor& boxes2);
|
| 28 |
+
|
| 29 |
+
// CUDA implementation
|
| 30 |
+
std::tuple<at::Tensor, at::Tensor> IoUBox3DCuda(
|
| 31 |
+
const at::Tensor& boxes1,
|
| 32 |
+
const at::Tensor& boxes2);
|
| 33 |
+
|
| 34 |
+
// Implementation which is exposed
|
| 35 |
+
inline std::tuple<at::Tensor, at::Tensor> IoUBox3D(
|
| 36 |
+
const at::Tensor& boxes1,
|
| 37 |
+
const at::Tensor& boxes2) {
|
| 38 |
+
if (boxes1.is_cuda() || boxes2.is_cuda()) {
|
| 39 |
+
#ifdef WITH_CUDA
|
| 40 |
+
CHECK_CUDA(boxes1);
|
| 41 |
+
CHECK_CUDA(boxes2);
|
| 42 |
+
return IoUBox3DCuda(boxes1.contiguous(), boxes2.contiguous());
|
| 43 |
+
#else
|
| 44 |
+
AT_ERROR("Not compiled with GPU support.");
|
| 45 |
+
#endif
|
| 46 |
+
}
|
| 47 |
+
return IoUBox3DCpu(boxes1.contiguous(), boxes2.contiguous());
|
| 48 |
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/*
|
| 2 |
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 3 |
+
* All rights reserved.
|
| 4 |
+
*
|
| 5 |
+
* This source code is licensed under the BSD-style license found in the
|
| 6 |
+
* LICENSE file in the root directory of this source tree.
|
| 7 |
+
*/
|
| 8 |
+
|
| 9 |
+
#include <torch/extension.h>
|
| 10 |
+
#include <torch/torch.h>
|
| 11 |
+
#include <list>
|
| 12 |
+
#include <numeric>
|
| 13 |
+
#include <queue>
|
| 14 |
+
#include <tuple>
|
| 15 |
+
#include "iou_utils.h"
|
| 16 |
+
|
| 17 |
+
std::tuple<at::Tensor, at::Tensor> IoUBox3DCpu(
|
| 18 |
+
const at::Tensor& boxes1,
|
| 19 |
+
const at::Tensor& boxes2) {
|
| 20 |
+
const int N = boxes1.size(0);
|
| 21 |
+
const int M = boxes2.size(0);
|
| 22 |
+
auto float_opts = boxes1.options().dtype(torch::kFloat32);
|
| 23 |
+
torch::Tensor vols = torch::zeros({N, M}, float_opts);
|
| 24 |
+
torch::Tensor ious = torch::zeros({N, M}, float_opts);
|
| 25 |
+
|
| 26 |
+
// Create tensor accessors
|
| 27 |
+
auto boxes1_a = boxes1.accessor<float, 3>();
|
| 28 |
+
auto boxes2_a = boxes2.accessor<float, 3>();
|
| 29 |
+
auto vols_a = vols.accessor<float, 2>();
|
| 30 |
+
auto ious_a = ious.accessor<float, 2>();
|
| 31 |
+
|
| 32 |
+
// Iterate through the N boxes in boxes1
|
| 33 |
+
for (int n = 0; n < N; ++n) {
|
| 34 |
+
const auto& box1 = boxes1_a[n];
|
| 35 |
+
// Convert to vector of face vertices i.e. effectively (F, 3, 3)
|
| 36 |
+
// face_verts is a data type defined in iou_utils.h
|
| 37 |
+
const face_verts box1_tris = GetBoxTris(box1);
|
| 38 |
+
|
| 39 |
+
// Calculate the position of the center of the box which is used in
|
| 40 |
+
// several calculations. This requires a tensor as input.
|
| 41 |
+
const vec3<float> box1_center = BoxCenter(boxes1[n]);
|
| 42 |
+
|
| 43 |
+
// Convert to vector of face vertices i.e. effectively (P, 4, 3)
|
| 44 |
+
const face_verts box1_planes = GetBoxPlanes(box1);
|
| 45 |
+
|
| 46 |
+
// Get Box Volumes
|
| 47 |
+
const float box1_vol = BoxVolume(box1_tris, box1_center);
|
| 48 |
+
|
| 49 |
+
// Iterate through the M boxes in boxes2
|
| 50 |
+
for (int m = 0; m < M; ++m) {
|
| 51 |
+
// Repeat above steps for box2
|
| 52 |
+
// TODO: check if caching these value helps performance.
|
| 53 |
+
const auto& box2 = boxes2_a[m];
|
| 54 |
+
const face_verts box2_tris = GetBoxTris(box2);
|
| 55 |
+
const vec3<float> box2_center = BoxCenter(boxes2[m]);
|
| 56 |
+
const face_verts box2_planes = GetBoxPlanes(box2);
|
| 57 |
+
const float box2_vol = BoxVolume(box2_tris, box2_center);
|
| 58 |
+
|
| 59 |
+
// Every triangle in one box will be compared to each plane in the other
|
| 60 |
+
// box. There are 3 possible outcomes:
|
| 61 |
+
// 1. If the triangle is fully inside, then it will
|
| 62 |
+
// remain as is.
|
| 63 |
+
// 2. If the triagnle it is fully outside, it will be removed.
|
| 64 |
+
// 3. If the triangle intersects with the (infinite) plane, it
|
| 65 |
+
// will be broken into subtriangles such that each subtriangle is full
|
| 66 |
+
// inside the plane and part of the intersecting tetrahedron.
|
| 67 |
+
|
| 68 |
+
// Tris in Box1 -> Planes in Box2
|
| 69 |
+
face_verts box1_intersect =
|
| 70 |
+
BoxIntersections(box1_tris, box2_planes, box2_center);
|
| 71 |
+
// Tris in Box2 -> Planes in Box1
|
| 72 |
+
face_verts box2_intersect =
|
| 73 |
+
BoxIntersections(box2_tris, box1_planes, box1_center);
|
| 74 |
+
|
| 75 |
+
// If there are overlapping regions in Box2, remove any coplanar faces
|
| 76 |
+
if (box2_intersect.size() > 0) {
|
| 77 |
+
// Identify if any triangles in Box2 are coplanar with Box1
|
| 78 |
+
std::vector<int> tri2_keep(box2_intersect.size());
|
| 79 |
+
std::fill(tri2_keep.begin(), tri2_keep.end(), 1);
|
| 80 |
+
for (int b1 = 0; b1 < box1_intersect.size(); ++b1) {
|
| 81 |
+
for (int b2 = 0; b2 < box2_intersect.size(); ++b2) {
|
| 82 |
+
const bool is_coplanar =
|
| 83 |
+
IsCoplanarTriTri(box1_intersect[b1], box2_intersect[b2]);
|
| 84 |
+
const float area = FaceArea(box1_intersect[b1]);
|
| 85 |
+
if ((is_coplanar) && (area > aEpsilon)) {
|
| 86 |
+
tri2_keep[b2] = 0;
|
| 87 |
+
}
|
| 88 |
+
}
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
// Keep only the non coplanar triangles in Box2 - add them to the
|
| 92 |
+
// Box1 triangles.
|
| 93 |
+
for (int b2 = 0; b2 < box2_intersect.size(); ++b2) {
|
| 94 |
+
if (tri2_keep[b2] == 1) {
|
| 95 |
+
box1_intersect.push_back((box2_intersect[b2]));
|
| 96 |
+
}
|
| 97 |
+
}
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
// Initialize the vol and iou to 0.0 in case there are no triangles
|
| 101 |
+
// in the intersecting shape.
|
| 102 |
+
float vol = 0.0;
|
| 103 |
+
float iou = 0.0;
|
| 104 |
+
|
| 105 |
+
// If there are triangles in the intersecting shape
|
| 106 |
+
if (box1_intersect.size() > 0) {
|
| 107 |
+
// The intersecting shape is a polyhedron made up of the
|
| 108 |
+
// triangular faces that are all now in box1_intersect.
|
| 109 |
+
// Calculate the polyhedron center
|
| 110 |
+
const vec3<float> polyhedron_center = PolyhedronCenter(box1_intersect);
|
| 111 |
+
// Compute intersecting polyhedron volume
|
| 112 |
+
vol = BoxVolume(box1_intersect, polyhedron_center);
|
| 113 |
+
// Compute IoU
|
| 114 |
+
iou = vol / (box1_vol + box2_vol - vol);
|
| 115 |
+
}
|
| 116 |
+
// Save out volume and IoU
|
| 117 |
+
vols_a[n][m] = vol;
|
| 118 |
+
ious_a[n][m] = iou;
|
| 119 |
+
}
|
| 120 |
+
}
|
| 121 |
+
return std::make_tuple(vols, ious);
|
| 122 |
+
}
|
|
@@ -0,0 +1,733 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/*
|
| 2 |
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 3 |
+
* All rights reserved.
|
| 4 |
+
*
|
| 5 |
+
* This source code is licensed under the BSD-style license found in the
|
| 6 |
+
* LICENSE file in the root directory of this source tree.
|
| 7 |
+
*/
|
| 8 |
+
|
| 9 |
+
#include <ATen/ATen.h>
|
| 10 |
+
#include <assert.h>
|
| 11 |
+
#include <torch/extension.h>
|
| 12 |
+
#include <torch/torch.h>
|
| 13 |
+
#include <algorithm>
|
| 14 |
+
#include <list>
|
| 15 |
+
#include <numeric>
|
| 16 |
+
#include <queue>
|
| 17 |
+
#include <tuple>
|
| 18 |
+
#include <type_traits>
|
| 19 |
+
#include "../util/vec3.h"
|
| 20 |
+
|
| 21 |
+
// dEpsilon: Used in dot products and is used to assess whether two unit vectors
|
| 22 |
+
// are orthogonal (or coplanar). It's an epsilon on cos(θ).
|
| 23 |
+
// With dEpsilon = 0.001, two unit vectors are considered co-planar
|
| 24 |
+
// if their θ = 2.5 deg.
|
| 25 |
+
const auto dEpsilon = 1e-3;
|
| 26 |
+
// aEpsilon: Used once in main function to check for small face areas
|
| 27 |
+
const auto aEpsilon = 1e-4;
|
| 28 |
+
// kEpsilon: Used only for norm(u) = u/max(||u||, kEpsilon)
|
| 29 |
+
const auto kEpsilon = 1e-8;
|
| 30 |
+
|
| 31 |
+
/*
|
| 32 |
+
_PLANES and _TRIS define the 4- and 3-connectivity
|
| 33 |
+
of the 8 box corners.
|
| 34 |
+
_PLANES gives the quad faces of the 3D box
|
| 35 |
+
_TRIS gives the triangle faces of the 3D box
|
| 36 |
+
*/
|
| 37 |
+
const int NUM_PLANES = 6;
|
| 38 |
+
const int NUM_TRIS = 12;
|
| 39 |
+
const int _PLANES[6][4] = {
|
| 40 |
+
{0, 1, 2, 3},
|
| 41 |
+
{3, 2, 6, 7},
|
| 42 |
+
{0, 1, 5, 4},
|
| 43 |
+
{0, 3, 7, 4},
|
| 44 |
+
{1, 5, 6, 2},
|
| 45 |
+
{4, 5, 6, 7},
|
| 46 |
+
};
|
| 47 |
+
const int _TRIS[12][3] = {
|
| 48 |
+
{0, 1, 2},
|
| 49 |
+
{0, 3, 2},
|
| 50 |
+
{4, 5, 6},
|
| 51 |
+
{4, 6, 7},
|
| 52 |
+
{1, 5, 6},
|
| 53 |
+
{1, 6, 2},
|
| 54 |
+
{0, 4, 7},
|
| 55 |
+
{0, 7, 3},
|
| 56 |
+
{3, 2, 6},
|
| 57 |
+
{3, 6, 7},
|
| 58 |
+
{0, 1, 5},
|
| 59 |
+
{0, 4, 5},
|
| 60 |
+
};
|
| 61 |
+
|
| 62 |
+
// Create a new data type for representing the
|
| 63 |
+
// verts for each face which can be triangle or plane.
|
| 64 |
+
// This helps make the code more readable.
|
| 65 |
+
using face_verts = std::vector<std::vector<vec3<float>>>;
|
| 66 |
+
|
| 67 |
+
// Args
|
| 68 |
+
// box: (8, 3) tensor accessor for the box vertices
|
| 69 |
+
// plane_idx: index of the plane in the box
|
| 70 |
+
// vert_idx: index of the vertex in the plane
|
| 71 |
+
//
|
| 72 |
+
// Returns
|
| 73 |
+
// vec3<T> (x, y, x) vertex coordinates
|
| 74 |
+
//
|
| 75 |
+
template <typename Box>
|
| 76 |
+
inline vec3<float>
|
| 77 |
+
ExtractVertsPlane(const Box& box, const int plane_idx, const int vert_idx) {
|
| 78 |
+
return vec3<float>(
|
| 79 |
+
box[_PLANES[plane_idx][vert_idx]][0],
|
| 80 |
+
box[_PLANES[plane_idx][vert_idx]][1],
|
| 81 |
+
box[_PLANES[plane_idx][vert_idx]][2]);
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
// Args
|
| 85 |
+
// box: (8, 3) tensor accessor for the box vertices
|
| 86 |
+
// tri_idx: index of the triangle face in the box
|
| 87 |
+
// vert_idx: index of the vertex in the triangle
|
| 88 |
+
//
|
| 89 |
+
// Returns
|
| 90 |
+
// vec3<T> (x, y, x) vertex coordinates
|
| 91 |
+
//
|
| 92 |
+
template <typename Box>
|
| 93 |
+
inline vec3<float>
|
| 94 |
+
ExtractVertsTri(const Box& box, const int tri_idx, const int vert_idx) {
|
| 95 |
+
return vec3<float>(
|
| 96 |
+
box[_TRIS[tri_idx][vert_idx]][0],
|
| 97 |
+
box[_TRIS[tri_idx][vert_idx]][1],
|
| 98 |
+
box[_TRIS[tri_idx][vert_idx]][2]);
|
| 99 |
+
}
|
| 100 |
+
|
| 101 |
+
// Args
|
| 102 |
+
// box: (8, 3) tensor accessor for the box vertices
|
| 103 |
+
//
|
| 104 |
+
// Returns
|
| 105 |
+
// std::vector<std::vector<vec3<T>>> effectively (F, 3, 3)
|
| 106 |
+
// coordinates of the verts for each face
|
| 107 |
+
//
|
| 108 |
+
template <typename Box>
|
| 109 |
+
inline face_verts GetBoxTris(const Box& box) {
|
| 110 |
+
face_verts box_tris;
|
| 111 |
+
for (int t = 0; t < NUM_TRIS; ++t) {
|
| 112 |
+
vec3<float> v0 = ExtractVertsTri(box, t, 0);
|
| 113 |
+
vec3<float> v1 = ExtractVertsTri(box, t, 1);
|
| 114 |
+
vec3<float> v2 = ExtractVertsTri(box, t, 2);
|
| 115 |
+
box_tris.push_back({v0, v1, v2});
|
| 116 |
+
}
|
| 117 |
+
return box_tris;
|
| 118 |
+
}
|
| 119 |
+
|
| 120 |
+
// Args
|
| 121 |
+
// box: (8, 3) tensor accessor for the box vertices
|
| 122 |
+
//
|
| 123 |
+
// Returns
|
| 124 |
+
// std::vector<std::vector<vec3<T>>> effectively (P, 3, 3)
|
| 125 |
+
// coordinates of the 4 verts for each plane
|
| 126 |
+
//
|
| 127 |
+
template <typename Box>
|
| 128 |
+
inline face_verts GetBoxPlanes(const Box& box) {
|
| 129 |
+
face_verts box_planes;
|
| 130 |
+
for (int t = 0; t < NUM_PLANES; ++t) {
|
| 131 |
+
vec3<float> v0 = ExtractVertsPlane(box, t, 0);
|
| 132 |
+
vec3<float> v1 = ExtractVertsPlane(box, t, 1);
|
| 133 |
+
vec3<float> v2 = ExtractVertsPlane(box, t, 2);
|
| 134 |
+
vec3<float> v3 = ExtractVertsPlane(box, t, 3);
|
| 135 |
+
box_planes.push_back({v0, v1, v2, v3});
|
| 136 |
+
}
|
| 137 |
+
return box_planes;
|
| 138 |
+
}
|
| 139 |
+
|
| 140 |
+
// The normal of a plane spanned by vectors e0 and e1
|
| 141 |
+
//
|
| 142 |
+
// Args
|
| 143 |
+
// e0, e1: vec3 vectors defining a plane
|
| 144 |
+
//
|
| 145 |
+
// Returns
|
| 146 |
+
// vec3: normal of the plane
|
| 147 |
+
//
|
| 148 |
+
inline vec3<float> GetNormal(const vec3<float> e0, const vec3<float> e1) {
|
| 149 |
+
vec3<float> n = cross(e0, e1);
|
| 150 |
+
n = n / std::fmaxf(norm(n), kEpsilon);
|
| 151 |
+
return n;
|
| 152 |
+
}
|
| 153 |
+
|
| 154 |
+
// The center of a triangle tri
|
| 155 |
+
//
|
| 156 |
+
// Args
|
| 157 |
+
// tri: vec3 coordinates of the vertices of the triangle
|
| 158 |
+
//
|
| 159 |
+
// Returns
|
| 160 |
+
// vec3: center of the triangle
|
| 161 |
+
//
|
| 162 |
+
inline vec3<float> TriCenter(const std::vector<vec3<float>>& tri) {
|
| 163 |
+
// Vertices of the triangle
|
| 164 |
+
const vec3<float> v0 = tri[0];
|
| 165 |
+
const vec3<float> v1 = tri[1];
|
| 166 |
+
const vec3<float> v2 = tri[2];
|
| 167 |
+
|
| 168 |
+
return (v0 + v1 + v2) / 3.0f;
|
| 169 |
+
}
|
| 170 |
+
|
| 171 |
+
// The normal of the triangle defined by vertices (v0, v1, v2)
|
| 172 |
+
// We find the "best" edges connecting the face center to the vertices,
|
| 173 |
+
// such that the cross product between the edges is maximized.
|
| 174 |
+
//
|
| 175 |
+
// Args
|
| 176 |
+
// tri: vec3 coordinates of the vertices of the face
|
| 177 |
+
//
|
| 178 |
+
// Returns
|
| 179 |
+
// vec3: normal for the face
|
| 180 |
+
//
|
| 181 |
+
inline vec3<float> TriNormal(const std::vector<vec3<float>>& tri) {
|
| 182 |
+
// Get center of triangle
|
| 183 |
+
const vec3<float> ctr = TriCenter(tri);
|
| 184 |
+
|
| 185 |
+
// find the "best" normal as cross product of edges from center
|
| 186 |
+
float max_dist = -1.0f;
|
| 187 |
+
vec3<float> n = {0.0f, 0.0f, 0.0f};
|
| 188 |
+
for (int i = 0; i < 2; ++i) {
|
| 189 |
+
for (int j = i + 1; j < 3; ++j) {
|
| 190 |
+
const float dist = norm(cross(tri[i] - ctr, tri[j] - ctr));
|
| 191 |
+
if (dist > max_dist) {
|
| 192 |
+
n = GetNormal(tri[i] - ctr, tri[j] - ctr);
|
| 193 |
+
}
|
| 194 |
+
}
|
| 195 |
+
}
|
| 196 |
+
return n;
|
| 197 |
+
}
|
| 198 |
+
|
| 199 |
+
// The center of a plane
|
| 200 |
+
//
|
| 201 |
+
// Args
|
| 202 |
+
// plane: vec3 coordinates of the vertices of the plane
|
| 203 |
+
//
|
| 204 |
+
// Returns
|
| 205 |
+
// vec3: center of the plane
|
| 206 |
+
//
|
| 207 |
+
inline vec3<float> PlaneCenter(const std::vector<vec3<float>>& plane) {
|
| 208 |
+
// Vertices of the plane
|
| 209 |
+
const vec3<float> v0 = plane[0];
|
| 210 |
+
const vec3<float> v1 = plane[1];
|
| 211 |
+
const vec3<float> v2 = plane[2];
|
| 212 |
+
const vec3<float> v3 = plane[3];
|
| 213 |
+
|
| 214 |
+
return (v0 + v1 + v2 + v3) / 4.0f;
|
| 215 |
+
}
|
| 216 |
+
|
| 217 |
+
// The normal of a planar face with vertices (v0, v1, v2, v3)
|
| 218 |
+
// We find the "best" edges connecting the face center to the vertices,
|
| 219 |
+
// such that the cross product between the edges is maximized.
|
| 220 |
+
//
|
| 221 |
+
// Args
|
| 222 |
+
// plane: vec3 coordinates of the vertices of the planar face
|
| 223 |
+
//
|
| 224 |
+
// Returns
|
| 225 |
+
// vec3: normal of the planar face
|
| 226 |
+
//
|
| 227 |
+
inline vec3<float> PlaneNormal(const std::vector<vec3<float>>& plane) {
|
| 228 |
+
// Get center of planar face
|
| 229 |
+
vec3<float> ctr = PlaneCenter(plane);
|
| 230 |
+
|
| 231 |
+
// find the "best" normal as cross product of edges from center
|
| 232 |
+
float max_dist = -1.0f;
|
| 233 |
+
vec3<float> n = {0.0f, 0.0f, 0.0f};
|
| 234 |
+
for (int i = 0; i < 3; ++i) {
|
| 235 |
+
for (int j = i + 1; j < 4; ++j) {
|
| 236 |
+
const float dist = norm(cross(plane[i] - ctr, plane[j] - ctr));
|
| 237 |
+
if (dist > max_dist) {
|
| 238 |
+
n = GetNormal(plane[i] - ctr, plane[j] - ctr);
|
| 239 |
+
}
|
| 240 |
+
}
|
| 241 |
+
}
|
| 242 |
+
return n;
|
| 243 |
+
}
|
| 244 |
+
|
| 245 |
+
// The area of the face defined by vertices (v0, v1, v2)
|
| 246 |
+
// Define e0 to be the edge connecting (v1, v0)
|
| 247 |
+
// Define e1 to be the edge connecting (v2, v0)
|
| 248 |
+
// Area is the norm of the cross product of e0, e1 divided by 2.0
|
| 249 |
+
//
|
| 250 |
+
// Args
|
| 251 |
+
// tri: vec3 coordinates of the vertices of the face
|
| 252 |
+
//
|
| 253 |
+
// Returns
|
| 254 |
+
// float: area for the face
|
| 255 |
+
//
|
| 256 |
+
inline float FaceArea(const std::vector<vec3<float>>& tri) {
|
| 257 |
+
// Get verts for face
|
| 258 |
+
const vec3<float> v0 = tri[0];
|
| 259 |
+
const vec3<float> v1 = tri[1];
|
| 260 |
+
const vec3<float> v2 = tri[2];
|
| 261 |
+
const vec3<float> n = cross(v1 - v0, v2 - v0);
|
| 262 |
+
return norm(n) / 2.0;
|
| 263 |
+
}
|
| 264 |
+
|
| 265 |
+
// The normal of a box plane defined by the verts in `plane` such that it
|
| 266 |
+
// points toward the centroid of the box given by `center`.
|
| 267 |
+
//
|
| 268 |
+
// Args
|
| 269 |
+
// plane: vec3 coordinates of the vertices of the plane
|
| 270 |
+
// center: vec3 coordinates of the center of the box from
|
| 271 |
+
// which the plane originated
|
| 272 |
+
//
|
| 273 |
+
// Returns
|
| 274 |
+
// vec3: normal for the plane such that it points towards
|
| 275 |
+
// the center of the box
|
| 276 |
+
//
|
| 277 |
+
inline vec3<float> PlaneNormalDirection(
|
| 278 |
+
const std::vector<vec3<float>>& plane,
|
| 279 |
+
const vec3<float>& center) {
|
| 280 |
+
// The plane's center & normal
|
| 281 |
+
const vec3<float> plane_center = PlaneCenter(plane);
|
| 282 |
+
vec3<float> n = PlaneNormal(plane);
|
| 283 |
+
|
| 284 |
+
// We project the center on the plane defined by (v0, v1, v2, v3)
|
| 285 |
+
// We can write center = plane_center + a * e0 + b * e1 + c * n
|
| 286 |
+
// We know that <e0, n> = 0 and <e1, n> = 0 and
|
| 287 |
+
// <a, b> is the dot product between a and b.
|
| 288 |
+
// This means we can solve for c as:
|
| 289 |
+
// c = <center - plane_center - a * e0 - b * e1, n>
|
| 290 |
+
// = <center - plane_center, n>
|
| 291 |
+
const float c = dot((center - plane_center), n);
|
| 292 |
+
|
| 293 |
+
// If c is negative, then we revert the direction of n such that n
|
| 294 |
+
// points "inside"
|
| 295 |
+
if (c < 0.0f) {
|
| 296 |
+
n = -1.0f * n;
|
| 297 |
+
}
|
| 298 |
+
|
| 299 |
+
return n;
|
| 300 |
+
}
|
| 301 |
+
|
| 302 |
+
// Calculate the volume of the box by summing the volume of
|
| 303 |
+
// each of the tetrahedrons formed with a triangle face and
|
| 304 |
+
// the box centroid.
|
| 305 |
+
//
|
| 306 |
+
// Args
|
| 307 |
+
// box_tris: vector of vec3 coordinates of the vertices of each
|
| 308 |
+
// of the triangles in the box
|
| 309 |
+
// box_center: vec3 coordinates of the center of the box
|
| 310 |
+
//
|
| 311 |
+
// Returns
|
| 312 |
+
// float: volume of the box
|
| 313 |
+
//
|
| 314 |
+
inline float BoxVolume(
|
| 315 |
+
const face_verts& box_tris,
|
| 316 |
+
const vec3<float>& box_center) {
|
| 317 |
+
float box_vol = 0.0;
|
| 318 |
+
// Iterate through each triange, calculate the area of the
|
| 319 |
+
// tetrahedron formed with the box_center and sum them
|
| 320 |
+
for (int t = 0; t < box_tris.size(); ++t) {
|
| 321 |
+
// Subtract the center:
|
| 322 |
+
const vec3<float> v0 = box_tris[t][0] - box_center;
|
| 323 |
+
const vec3<float> v1 = box_tris[t][1] - box_center;
|
| 324 |
+
const vec3<float> v2 = box_tris[t][2] - box_center;
|
| 325 |
+
|
| 326 |
+
// Compute the area
|
| 327 |
+
const float area = dot(v0, cross(v1, v2));
|
| 328 |
+
const float vol = std::abs(area) / 6.0;
|
| 329 |
+
box_vol = box_vol + vol;
|
| 330 |
+
}
|
| 331 |
+
return box_vol;
|
| 332 |
+
}
|
| 333 |
+
|
| 334 |
+
// Compute the box center as the mean of the verts
|
| 335 |
+
//
|
| 336 |
+
// Args
|
| 337 |
+
// box_verts: (8, 3) tensor of the corner vertices of the box
|
| 338 |
+
//
|
| 339 |
+
// Returns
|
| 340 |
+
// vec3: coordinates of the center of the box
|
| 341 |
+
//
|
| 342 |
+
inline vec3<float> BoxCenter(const at::Tensor& box_verts) {
|
| 343 |
+
const auto& box_center_t = at::mean(box_verts, 0);
|
| 344 |
+
const vec3<float> box_center(
|
| 345 |
+
box_center_t[0].item<float>(),
|
| 346 |
+
box_center_t[1].item<float>(),
|
| 347 |
+
box_center_t[2].item<float>());
|
| 348 |
+
return box_center;
|
| 349 |
+
}
|
| 350 |
+
|
| 351 |
+
// Compute the polyhedron center as the mean of the face centers
|
| 352 |
+
// of the triangle faces
|
| 353 |
+
//
|
| 354 |
+
// Args
|
| 355 |
+
// tris: vector of vec3 coordinates of the
|
| 356 |
+
// vertices of each of the triangles in the polyhedron
|
| 357 |
+
//
|
| 358 |
+
// Returns
|
| 359 |
+
// vec3: coordinates of the center of the polyhedron
|
| 360 |
+
//
|
| 361 |
+
inline vec3<float> PolyhedronCenter(const face_verts& tris) {
|
| 362 |
+
float x = 0.0;
|
| 363 |
+
float y = 0.0;
|
| 364 |
+
float z = 0.0;
|
| 365 |
+
const int num_tris = tris.size();
|
| 366 |
+
|
| 367 |
+
// Find the center point of each face
|
| 368 |
+
for (int t = 0; t < num_tris; ++t) {
|
| 369 |
+
const vec3<float> v0 = tris[t][0];
|
| 370 |
+
const vec3<float> v1 = tris[t][1];
|
| 371 |
+
const vec3<float> v2 = tris[t][2];
|
| 372 |
+
const float x_face = (v0.x + v1.x + v2.x) / 3.0;
|
| 373 |
+
const float y_face = (v0.y + v1.y + v2.y) / 3.0;
|
| 374 |
+
const float z_face = (v0.z + v1.z + v2.z) / 3.0;
|
| 375 |
+
x = x + x_face;
|
| 376 |
+
y = y + y_face;
|
| 377 |
+
z = z + z_face;
|
| 378 |
+
}
|
| 379 |
+
|
| 380 |
+
// Take the mean of the centers of all faces
|
| 381 |
+
x = x / num_tris;
|
| 382 |
+
y = y / num_tris;
|
| 383 |
+
z = z / num_tris;
|
| 384 |
+
|
| 385 |
+
const vec3<float> center(x, y, z);
|
| 386 |
+
return center;
|
| 387 |
+
}
|
| 388 |
+
|
| 389 |
+
// Compute a boolean indicator for whether a point
|
| 390 |
+
// is inside a plane, where inside refers to whether
|
| 391 |
+
// or not the point has a component in the
|
| 392 |
+
// normal direction of the plane.
|
| 393 |
+
//
|
| 394 |
+
// Args
|
| 395 |
+
// plane: vector of vec3 coordinates of the
|
| 396 |
+
// vertices of each of the triangles in the box
|
| 397 |
+
// normal: vec3 of the direction of the plane normal
|
| 398 |
+
// point: vec3 of the position of the point of interest
|
| 399 |
+
//
|
| 400 |
+
// Returns
|
| 401 |
+
// bool: whether or not the point is inside the plane
|
| 402 |
+
//
|
| 403 |
+
inline bool IsInside(
|
| 404 |
+
const std::vector<vec3<float>>& plane,
|
| 405 |
+
const vec3<float>& normal,
|
| 406 |
+
const vec3<float>& point) {
|
| 407 |
+
// The center of the plane
|
| 408 |
+
const vec3<float> plane_ctr = PlaneCenter(plane);
|
| 409 |
+
|
| 410 |
+
// Every point p can be written as p = plane_ctr + a e0 + b e1 + c n
|
| 411 |
+
// Solving for c:
|
| 412 |
+
// c = (point - plane_ctr - a * e0 - b * e1).dot(n)
|
| 413 |
+
// We know that <e0, n> = 0 and <e1, n> = 0
|
| 414 |
+
// So the calculation can be simplified as:
|
| 415 |
+
const float c = dot((point - plane_ctr), normal);
|
| 416 |
+
const bool inside = c >= 0.0f;
|
| 417 |
+
return inside;
|
| 418 |
+
}
|
| 419 |
+
|
| 420 |
+
// Find the point of intersection between a plane
|
| 421 |
+
// and a line given by the end points (p0, p1)
|
| 422 |
+
//
|
| 423 |
+
// Args
|
| 424 |
+
// plane: vector of vec3 coordinates of the
|
| 425 |
+
// vertices of each of the triangles in the box
|
| 426 |
+
// normal: vec3 of the direction of the plane normal
|
| 427 |
+
// p0, p1: vec3 of the start and end point of the line
|
| 428 |
+
//
|
| 429 |
+
// Returns
|
| 430 |
+
// vec3: position of the intersection point
|
| 431 |
+
//
|
| 432 |
+
inline vec3<float> PlaneEdgeIntersection(
|
| 433 |
+
const std::vector<vec3<float>>& plane,
|
| 434 |
+
const vec3<float>& normal,
|
| 435 |
+
const vec3<float>& p0,
|
| 436 |
+
const vec3<float>& p1) {
|
| 437 |
+
// The center of the plane
|
| 438 |
+
const vec3<float> plane_ctr = PlaneCenter(plane);
|
| 439 |
+
|
| 440 |
+
// The point of intersection can be parametrized
|
| 441 |
+
// p = p0 + a (p1 - p0) where a in [0, 1]
|
| 442 |
+
// We want to find a such that p is on plane
|
| 443 |
+
// <p - ctr, n> = 0
|
| 444 |
+
|
| 445 |
+
vec3<float> direc = p1 - p0;
|
| 446 |
+
direc = direc / std::fmaxf(norm(direc), kEpsilon);
|
| 447 |
+
|
| 448 |
+
vec3<float> p = (p1 + p0) / 2.0f;
|
| 449 |
+
|
| 450 |
+
if (std::abs(dot(direc, normal)) >= dEpsilon) {
|
| 451 |
+
const float top = -1.0f * dot(p0 - plane_ctr, normal);
|
| 452 |
+
const float bot = dot(p1 - p0, normal);
|
| 453 |
+
const float a = top / bot;
|
| 454 |
+
p = p0 + a * (p1 - p0);
|
| 455 |
+
}
|
| 456 |
+
return p;
|
| 457 |
+
}
|
| 458 |
+
|
| 459 |
+
// Compute the most distant points between two sets of vertices
|
| 460 |
+
//
|
| 461 |
+
// Args
|
| 462 |
+
// verts1, verts2: vec3 defining the list of vertices
|
| 463 |
+
//
|
| 464 |
+
// Returns
|
| 465 |
+
// v1m, v2m: vec3 vectors of the most distant points
|
| 466 |
+
// in verts1 and verts2 respectively
|
| 467 |
+
//
|
| 468 |
+
inline std::tuple<vec3<float>, vec3<float>> ArgMaxVerts(
|
| 469 |
+
const std::vector<vec3<float>>& verts1,
|
| 470 |
+
const std::vector<vec3<float>>& verts2) {
|
| 471 |
+
vec3<float> v1m = {0.0f, 0.0f, 0.0f};
|
| 472 |
+
vec3<float> v2m = {0.0f, 0.0f, 0.0f};
|
| 473 |
+
float maxdist = -1.0f;
|
| 474 |
+
|
| 475 |
+
for (const auto& v1 : verts1) {
|
| 476 |
+
for (const auto& v2 : verts2) {
|
| 477 |
+
if (norm(v1 - v2) > maxdist) {
|
| 478 |
+
v1m = v1;
|
| 479 |
+
v2m = v2;
|
| 480 |
+
maxdist = norm(v1 - v2);
|
| 481 |
+
}
|
| 482 |
+
}
|
| 483 |
+
}
|
| 484 |
+
return std::make_tuple(v1m, v2m);
|
| 485 |
+
}
|
| 486 |
+
|
| 487 |
+
// Compute a boolean indicator for whether or not two faces
|
| 488 |
+
// are coplanar
|
| 489 |
+
//
|
| 490 |
+
// Args
|
| 491 |
+
// tri1, tri2: std:vector<vec3> of the vertex coordinates of
|
| 492 |
+
// triangle faces
|
| 493 |
+
//
|
| 494 |
+
// Returns
|
| 495 |
+
// bool: whether or not the two faces are coplanar
|
| 496 |
+
//
|
| 497 |
+
inline bool IsCoplanarTriTri(
|
| 498 |
+
const std::vector<vec3<float>>& tri1,
|
| 499 |
+
const std::vector<vec3<float>>& tri2) {
|
| 500 |
+
// Get normal for tri 1
|
| 501 |
+
const vec3<float> n1 = TriNormal(tri1);
|
| 502 |
+
|
| 503 |
+
// Get normal for tri 2
|
| 504 |
+
const vec3<float> n2 = TriNormal(tri2);
|
| 505 |
+
|
| 506 |
+
// Check if parallel
|
| 507 |
+
const bool check1 = std::abs(dot(n1, n2)) > 1 - dEpsilon;
|
| 508 |
+
|
| 509 |
+
// Compute most distant points
|
| 510 |
+
auto argvs = ArgMaxVerts(tri1, tri2);
|
| 511 |
+
const auto v1m = std::get<0>(argvs);
|
| 512 |
+
const auto v2m = std::get<1>(argvs);
|
| 513 |
+
|
| 514 |
+
vec3<float> n12m = v1m - v2m;
|
| 515 |
+
n12m = n12m / std::fmaxf(norm(n12m), kEpsilon);
|
| 516 |
+
|
| 517 |
+
const bool check2 = (std::abs(dot(n12m, n1)) < dEpsilon) ||
|
| 518 |
+
(std::abs(dot(n12m, n2)) < dEpsilon);
|
| 519 |
+
|
| 520 |
+
return (check1 && check2);
|
| 521 |
+
}
|
| 522 |
+
|
| 523 |
+
// Compute a boolean indicator for whether or not a triangular and a planar
|
| 524 |
+
// face are coplanar
|
| 525 |
+
//
|
| 526 |
+
// Args
|
| 527 |
+
// tri, plane: std:vector<vec3> of the vertex coordinates of
|
| 528 |
+
// triangular face and planar face
|
| 529 |
+
// normal: the normal direction of the plane pointing "inside"
|
| 530 |
+
//
|
| 531 |
+
// Returns
|
| 532 |
+
// bool: whether or not the two faces are coplanar
|
| 533 |
+
//
|
| 534 |
+
inline bool IsCoplanarTriPlane(
|
| 535 |
+
const std::vector<vec3<float>>& tri,
|
| 536 |
+
const std::vector<vec3<float>>& plane,
|
| 537 |
+
const vec3<float>& normal) {
|
| 538 |
+
// Get normal for tri
|
| 539 |
+
const vec3<float> nt = TriNormal(tri);
|
| 540 |
+
|
| 541 |
+
// check if parallel
|
| 542 |
+
const bool check1 = std::abs(dot(nt, normal)) > 1 - dEpsilon;
|
| 543 |
+
|
| 544 |
+
// Compute most distant points
|
| 545 |
+
auto argvs = ArgMaxVerts(tri, plane);
|
| 546 |
+
const auto v1m = std::get<0>(argvs);
|
| 547 |
+
const auto v2m = std::get<1>(argvs);
|
| 548 |
+
|
| 549 |
+
vec3<float> n12m = v1m - v2m;
|
| 550 |
+
n12m = n12m / std::fmaxf(norm(n12m), kEpsilon);
|
| 551 |
+
|
| 552 |
+
const bool check2 = std::abs(dot(n12m, normal)) < dEpsilon;
|
| 553 |
+
|
| 554 |
+
return (check1 && check2);
|
| 555 |
+
}
|
| 556 |
+
|
| 557 |
+
// Triangle is clipped into a quadrilateral
|
| 558 |
+
// based on the intersection points with the plane.
|
| 559 |
+
// Then the quadrilateral is divided into two triangles.
|
| 560 |
+
//
|
| 561 |
+
// Args
|
| 562 |
+
// plane: vector of vec3 coordinates of the
|
| 563 |
+
// vertices of each of the triangles in the box
|
| 564 |
+
// normal: vec3 of the direction of the plane normal
|
| 565 |
+
// vout: vec3 of the point in the triangle which is outside
|
| 566 |
+
// the plane
|
| 567 |
+
// vin1, vin2: vec3 of the points in the triangle which are
|
| 568 |
+
// inside the plane
|
| 569 |
+
//
|
| 570 |
+
// Returns
|
| 571 |
+
// std::vector<std::vector<vec3>>: vector of vertex coordinates
|
| 572 |
+
// of the new triangle faces
|
| 573 |
+
//
|
| 574 |
+
inline face_verts ClipTriByPlaneOneOut(
|
| 575 |
+
const std::vector<vec3<float>>& plane,
|
| 576 |
+
const vec3<float>& normal,
|
| 577 |
+
const vec3<float>& vout,
|
| 578 |
+
const vec3<float>& vin1,
|
| 579 |
+
const vec3<float>& vin2) {
|
| 580 |
+
// point of intersection between plane and (vin1, vout)
|
| 581 |
+
const vec3<float> pint1 = PlaneEdgeIntersection(plane, normal, vin1, vout);
|
| 582 |
+
// point of intersection between plane and (vin2, vout)
|
| 583 |
+
const vec3<float> pint2 = PlaneEdgeIntersection(plane, normal, vin2, vout);
|
| 584 |
+
const face_verts face_verts = {{vin1, pint1, pint2}, {vin1, pint2, vin2}};
|
| 585 |
+
return face_verts;
|
| 586 |
+
}
|
| 587 |
+
|
| 588 |
+
// Triangle is clipped into a smaller triangle based
|
| 589 |
+
// on the intersection points with the plane.
|
| 590 |
+
//
|
| 591 |
+
// Args
|
| 592 |
+
// plane: vector of vec3 coordinates of the
|
| 593 |
+
// vertices of each of the triangles in the box
|
| 594 |
+
// normal: vec3 of the direction of the plane normal
|
| 595 |
+
// vout1, vout2: vec3 of the points in the triangle which are
|
| 596 |
+
// outside the plane
|
| 597 |
+
// vin: vec3 of the point in the triangle which is inside
|
| 598 |
+
// the plane
|
| 599 |
+
// Returns
|
| 600 |
+
// std::vector<std::vector<vec3>>: vector of vertex coordinates
|
| 601 |
+
// of the new triangle face
|
| 602 |
+
//
|
| 603 |
+
inline face_verts ClipTriByPlaneTwoOut(
|
| 604 |
+
const std::vector<vec3<float>>& plane,
|
| 605 |
+
const vec3<float>& normal,
|
| 606 |
+
const vec3<float>& vout1,
|
| 607 |
+
const vec3<float>& vout2,
|
| 608 |
+
const vec3<float>& vin) {
|
| 609 |
+
// point of intersection between plane and (vin, vout1)
|
| 610 |
+
const vec3<float> pint1 = PlaneEdgeIntersection(plane, normal, vin, vout1);
|
| 611 |
+
// point of intersection between plane and (vin, vout2)
|
| 612 |
+
const vec3<float> pint2 = PlaneEdgeIntersection(plane, normal, vin, vout2);
|
| 613 |
+
const face_verts face_verts = {{vin, pint1, pint2}};
|
| 614 |
+
return face_verts;
|
| 615 |
+
}
|
| 616 |
+
|
| 617 |
+
// Clip the triangle faces so that they lie within the
|
| 618 |
+
// plane, creating new triangle faces where necessary.
|
| 619 |
+
//
|
| 620 |
+
// Args
|
| 621 |
+
// plane: vector of vec3 coordinates of the
|
| 622 |
+
// vertices of each of the triangles in the box
|
| 623 |
+
// tri: std:vector<vec3> of the vertex coordinates of the
|
| 624 |
+
// triangle faces
|
| 625 |
+
// normal: vec3 of the direction of the plane normal
|
| 626 |
+
//
|
| 627 |
+
// Returns
|
| 628 |
+
// std::vector<std::vector<vec3>>: vector of vertex coordinates
|
| 629 |
+
// of the new triangle faces formed after clipping.
|
| 630 |
+
// All triangles are now "inside" the plane.
|
| 631 |
+
//
|
| 632 |
+
inline face_verts ClipTriByPlane(
|
| 633 |
+
const std::vector<vec3<float>>& plane,
|
| 634 |
+
const std::vector<vec3<float>>& tri,
|
| 635 |
+
const vec3<float>& normal) {
|
| 636 |
+
// Get Triangle vertices
|
| 637 |
+
const vec3<float> v0 = tri[0];
|
| 638 |
+
const vec3<float> v1 = tri[1];
|
| 639 |
+
const vec3<float> v2 = tri[2];
|
| 640 |
+
|
| 641 |
+
// Check coplanar
|
| 642 |
+
const bool iscoplanar = IsCoplanarTriPlane(tri, plane, normal);
|
| 643 |
+
if (iscoplanar) {
|
| 644 |
+
// Return input vertices
|
| 645 |
+
face_verts tris = {{v0, v1, v2}};
|
| 646 |
+
return tris;
|
| 647 |
+
}
|
| 648 |
+
|
| 649 |
+
// Check each of the triangle vertices to see if it is inside the plane
|
| 650 |
+
const bool isin0 = IsInside(plane, normal, v0);
|
| 651 |
+
const bool isin1 = IsInside(plane, normal, v1);
|
| 652 |
+
const bool isin2 = IsInside(plane, normal, v2);
|
| 653 |
+
|
| 654 |
+
// All in
|
| 655 |
+
if (isin0 && isin1 && isin2) {
|
| 656 |
+
// Return input vertices
|
| 657 |
+
face_verts tris = {{v0, v1, v2}};
|
| 658 |
+
return tris;
|
| 659 |
+
}
|
| 660 |
+
|
| 661 |
+
face_verts empty_tris = {};
|
| 662 |
+
// All out
|
| 663 |
+
if (!isin0 && !isin1 && !isin2) {
|
| 664 |
+
return empty_tris;
|
| 665 |
+
}
|
| 666 |
+
|
| 667 |
+
// One vert out
|
| 668 |
+
if (isin0 && isin1 && !isin2) {
|
| 669 |
+
return ClipTriByPlaneOneOut(plane, normal, v2, v0, v1);
|
| 670 |
+
}
|
| 671 |
+
if (isin0 && !isin1 && isin2) {
|
| 672 |
+
return ClipTriByPlaneOneOut(plane, normal, v1, v0, v2);
|
| 673 |
+
}
|
| 674 |
+
if (!isin0 && isin1 && isin2) {
|
| 675 |
+
return ClipTriByPlaneOneOut(plane, normal, v0, v1, v2);
|
| 676 |
+
}
|
| 677 |
+
|
| 678 |
+
// Two verts out
|
| 679 |
+
if (isin0 && !isin1 && !isin2) {
|
| 680 |
+
return ClipTriByPlaneTwoOut(plane, normal, v1, v2, v0);
|
| 681 |
+
}
|
| 682 |
+
if (!isin0 && !isin1 && isin2) {
|
| 683 |
+
return ClipTriByPlaneTwoOut(plane, normal, v0, v1, v2);
|
| 684 |
+
}
|
| 685 |
+
if (!isin0 && isin1 && !isin2) {
|
| 686 |
+
return ClipTriByPlaneTwoOut(plane, normal, v0, v2, v1);
|
| 687 |
+
}
|
| 688 |
+
|
| 689 |
+
// Else return empty (should not be reached)
|
| 690 |
+
return empty_tris;
|
| 691 |
+
}
|
| 692 |
+
|
| 693 |
+
// Get the triangles from each box which are part of the
|
| 694 |
+
// intersecting polyhedron by computing the intersection
|
| 695 |
+
// points with each of the planes.
|
| 696 |
+
//
|
| 697 |
+
// Args
|
| 698 |
+
// tris: vertex coordinates of all the triangle faces
|
| 699 |
+
// in the box
|
| 700 |
+
// planes: vertex coordinates of all the planes in the box
|
| 701 |
+
// center: vec3 coordinates of the center of the box from which
|
| 702 |
+
// the planes originate
|
| 703 |
+
//
|
| 704 |
+
// Returns
|
| 705 |
+
// std::vector<std::vector<vec3>>> vector of vertex coordinates
|
| 706 |
+
// of the new triangle faces formed after clipping.
|
| 707 |
+
// All triangles are now "inside" the planes.
|
| 708 |
+
//
|
| 709 |
+
inline face_verts BoxIntersections(
|
| 710 |
+
const face_verts& tris,
|
| 711 |
+
const face_verts& planes,
|
| 712 |
+
const vec3<float>& center) {
|
| 713 |
+
// Create a new vector to avoid modifying in place
|
| 714 |
+
face_verts out_tris = tris;
|
| 715 |
+
for (int p = 0; p < NUM_PLANES; ++p) {
|
| 716 |
+
// Get plane normal direction
|
| 717 |
+
const vec3<float> n2 = PlaneNormalDirection(planes[p], center);
|
| 718 |
+
// Iterate through triangles in tris
|
| 719 |
+
// Create intermediate vector to store the updated tris
|
| 720 |
+
face_verts tri_verts_updated;
|
| 721 |
+
for (int t = 0; t < out_tris.size(); ++t) {
|
| 722 |
+
// Clip tri by plane
|
| 723 |
+
const face_verts tri_updated = ClipTriByPlane(planes[p], out_tris[t], n2);
|
| 724 |
+
// Add to the tri_verts_updated output if not empty
|
| 725 |
+
for (int v = 0; v < tri_updated.size(); ++v) {
|
| 726 |
+
tri_verts_updated.push_back(tri_updated[v]);
|
| 727 |
+
}
|
| 728 |
+
}
|
| 729 |
+
// Update the tris
|
| 730 |
+
out_tris = tri_verts_updated;
|
| 731 |
+
}
|
| 732 |
+
return out_tris;
|
| 733 |
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/*!
|
| 2 |
+
**************************************************************************************************
|
| 3 |
+
* Deformable DETR
|
| 4 |
+
* Copyright (c) 2020 SenseTime. All Rights Reserved.
|
| 5 |
+
* Licensed under the Apache License, Version 2.0 [see LICENSE for details]
|
| 6 |
+
**************************************************************************************************
|
| 7 |
+
* Modified from https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/tree/pytorch_1.0.0
|
| 8 |
+
**************************************************************************************************
|
| 9 |
+
*/
|
| 10 |
+
|
| 11 |
+
#pragma once
|
| 12 |
+
|
| 13 |
+
#ifdef WITH_CUDA
|
| 14 |
+
#include "ms_deform_attn_cuda.h"
|
| 15 |
+
#endif
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
at::Tensor
|
| 19 |
+
ms_deform_attn_forward(
|
| 20 |
+
const at::Tensor &value,
|
| 21 |
+
const at::Tensor &spatial_shapes,
|
| 22 |
+
const at::Tensor &level_start_index,
|
| 23 |
+
const at::Tensor &sampling_loc,
|
| 24 |
+
const at::Tensor &attn_weight,
|
| 25 |
+
const int im2col_step)
|
| 26 |
+
{
|
| 27 |
+
if (value.is_cuda())
|
| 28 |
+
{
|
| 29 |
+
#ifdef WITH_CUDA
|
| 30 |
+
return ms_deform_attn_cuda_forward(
|
| 31 |
+
value, spatial_shapes, level_start_index, sampling_loc, attn_weight, im2col_step);
|
| 32 |
+
#else
|
| 33 |
+
AT_ERROR("Not compiled with GPU support");
|
| 34 |
+
#endif
|
| 35 |
+
}
|
| 36 |
+
AT_ERROR("Not implemented on the CPU");
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
std::vector<at::Tensor>
|
| 40 |
+
ms_deform_attn_backward(
|
| 41 |
+
const at::Tensor &value,
|
| 42 |
+
const at::Tensor &spatial_shapes,
|
| 43 |
+
const at::Tensor &level_start_index,
|
| 44 |
+
const at::Tensor &sampling_loc,
|
| 45 |
+
const at::Tensor &attn_weight,
|
| 46 |
+
const at::Tensor &grad_output,
|
| 47 |
+
const int im2col_step)
|
| 48 |
+
{
|
| 49 |
+
if (value.is_cuda())
|
| 50 |
+
{
|
| 51 |
+
#ifdef WITH_CUDA
|
| 52 |
+
return ms_deform_attn_cuda_backward(
|
| 53 |
+
value, spatial_shapes, level_start_index, sampling_loc, attn_weight, grad_output, im2col_step);
|
| 54 |
+
#else
|
| 55 |
+
AT_ERROR("Not compiled with GPU support");
|
| 56 |
+
#endif
|
| 57 |
+
}
|
| 58 |
+
AT_ERROR("Not implemented on the CPU");
|
| 59 |
+
}
|
| 60 |
+
|
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/*!
|
| 2 |
+
**************************************************************************************************
|
| 3 |
+
* Deformable DETR
|
| 4 |
+
* Copyright (c) 2020 SenseTime. All Rights Reserved.
|
| 5 |
+
* Licensed under the Apache License, Version 2.0 [see LICENSE for details]
|
| 6 |
+
**************************************************************************************************
|
| 7 |
+
* Modified from https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/tree/pytorch_1.0.0
|
| 8 |
+
**************************************************************************************************
|
| 9 |
+
*/
|
| 10 |
+
|
| 11 |
+
#pragma once
|
| 12 |
+
#include <torch/extension.h>
|
| 13 |
+
|
| 14 |
+
at::Tensor ms_deform_attn_cuda_forward(
|
| 15 |
+
const at::Tensor &value,
|
| 16 |
+
const at::Tensor &spatial_shapes,
|
| 17 |
+
const at::Tensor &level_start_index,
|
| 18 |
+
const at::Tensor &sampling_loc,
|
| 19 |
+
const at::Tensor &attn_weight,
|
| 20 |
+
const int im2col_step);
|
| 21 |
+
|
| 22 |
+
std::vector<at::Tensor> ms_deform_attn_cuda_backward(
|
| 23 |
+
const at::Tensor &value,
|
| 24 |
+
const at::Tensor &spatial_shapes,
|
| 25 |
+
const at::Tensor &level_start_index,
|
| 26 |
+
const at::Tensor &sampling_loc,
|
| 27 |
+
const at::Tensor &attn_weight,
|
| 28 |
+
const at::Tensor &grad_output,
|
| 29 |
+
const int im2col_step);
|
| 30 |
+
|
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
// modified from
|
| 3 |
+
// https://github.com/facebookresearch/detectron2/blob/master/detectron2/layers/csrc/nms_rotated/nms_rotated.h
|
| 4 |
+
#pragma once
|
| 5 |
+
#include <torch/types.h>
|
| 6 |
+
|
| 7 |
+
at::Tensor nms_rotated_cpu(
|
| 8 |
+
const at::Tensor& dets,
|
| 9 |
+
const at::Tensor& scores,
|
| 10 |
+
const double iou_threshold);
|
| 11 |
+
|
| 12 |
+
#if defined(WITH_CUDA) || defined(WITH_HIP)
|
| 13 |
+
at::Tensor nms_rotated_cuda(
|
| 14 |
+
const at::Tensor& dets,
|
| 15 |
+
const at::Tensor& scores,
|
| 16 |
+
const double iou_threshold);
|
| 17 |
+
#endif
|
| 18 |
+
|
| 19 |
+
// Interface for Python
|
| 20 |
+
// inline is needed to prevent multiple function definitions when this header is
|
| 21 |
+
// included by different cpps
|
| 22 |
+
inline at::Tensor nms_rotated(
|
| 23 |
+
const at::Tensor& dets,
|
| 24 |
+
const at::Tensor& scores,
|
| 25 |
+
const double iou_threshold) {
|
| 26 |
+
assert(dets.device().is_cuda() == scores.device().is_cuda());
|
| 27 |
+
if (dets.device().is_cuda()) {
|
| 28 |
+
#if defined(WITH_CUDA) || defined(WITH_HIP)
|
| 29 |
+
return nms_rotated_cuda(
|
| 30 |
+
dets.contiguous(), scores.contiguous(), iou_threshold);
|
| 31 |
+
#else
|
| 32 |
+
AT_ERROR("Not compiled with GPU support!");
|
| 33 |
+
#endif
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
return nms_rotated_cpu(dets.contiguous(), scores.contiguous(), iou_threshold);
|
| 37 |
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright (c) Facebook, Inc. and its affiliates.
|
| 2 |
+
// modified from
|
| 3 |
+
// https://github.com/facebookresearch/detectron2/blob/main/detectron2/layers/csrc/nms_rotated/nms_rotated_cpu.cpp
|
| 4 |
+
#include "../box_iou_rotated/box_iou_rotated_utils.h"
|
| 5 |
+
#include "nms_rotated.h"
|
| 6 |
+
|
| 7 |
+
template <typename scalar_t>
|
| 8 |
+
at::Tensor nms_rotated_cpu_kernel(
|
| 9 |
+
const at::Tensor& dets,
|
| 10 |
+
const at::Tensor& scores,
|
| 11 |
+
const double iou_threshold) {
|
| 12 |
+
// nms_rotated_cpu_kernel is modified from torchvision's nms_cpu_kernel,
|
| 13 |
+
// however, the code in this function is much shorter because
|
| 14 |
+
// we delegate the IoU computation for rotated boxes to
|
| 15 |
+
// the single_box_iou_rotated function in box_iou_rotated_utils.h
|
| 16 |
+
AT_ASSERTM(dets.device().is_cpu(), "dets must be a CPU tensor");
|
| 17 |
+
AT_ASSERTM(scores.device().is_cpu(), "scores must be a CPU tensor");
|
| 18 |
+
AT_ASSERTM(
|
| 19 |
+
dets.scalar_type() == scores.scalar_type(),
|
| 20 |
+
"dets should have the same type as scores");
|
| 21 |
+
|
| 22 |
+
if (dets.numel() == 0) {
|
| 23 |
+
return at::empty({0}, dets.options().dtype(at::kLong));
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
auto order_t = std::get<1>(scores.sort(0, /* descending=*/true));
|
| 27 |
+
|
| 28 |
+
auto ndets = dets.size(0);
|
| 29 |
+
at::Tensor suppressed_t = at::zeros({ndets}, dets.options().dtype(at::kByte));
|
| 30 |
+
at::Tensor keep_t = at::zeros({ndets}, dets.options().dtype(at::kLong));
|
| 31 |
+
|
| 32 |
+
auto suppressed = suppressed_t.data_ptr<uint8_t>();
|
| 33 |
+
auto keep = keep_t.data_ptr<int64_t>();
|
| 34 |
+
auto order = order_t.data_ptr<int64_t>();
|
| 35 |
+
|
| 36 |
+
int64_t num_to_keep = 0;
|
| 37 |
+
|
| 38 |
+
for (int64_t _i = 0; _i < ndets; _i++) {
|
| 39 |
+
auto i = order[_i];
|
| 40 |
+
if (suppressed[i] == 1) {
|
| 41 |
+
continue;
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
keep[num_to_keep++] = i;
|
| 45 |
+
|
| 46 |
+
for (int64_t _j = _i + 1; _j < ndets; _j++) {
|
| 47 |
+
auto j = order[_j];
|
| 48 |
+
if (suppressed[j] == 1) {
|
| 49 |
+
continue;
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
auto ovr = single_box_iou_rotated<scalar_t>(
|
| 53 |
+
dets[i].data_ptr<scalar_t>(), dets[j].data_ptr<scalar_t>());
|
| 54 |
+
if (ovr >= iou_threshold) {
|
| 55 |
+
suppressed[j] = 1;
|
| 56 |
+
}
|
| 57 |
+
}
|
| 58 |
+
}
|
| 59 |
+
return keep_t.narrow(/*dim=*/0, /*start=*/0, /*length=*/num_to_keep);
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
at::Tensor nms_rotated_cpu(
|
| 63 |
+
// input must be contiguous
|
| 64 |
+
const at::Tensor& dets,
|
| 65 |
+
const at::Tensor& scores,
|
| 66 |
+
const double iou_threshold) {
|
| 67 |
+
auto result = at::empty({0}, dets.options());
|
| 68 |
+
|
| 69 |
+
AT_DISPATCH_FLOATING_TYPES(dets.scalar_type(), "nms_rotated", [&] {
|
| 70 |
+
result = nms_rotated_cpu_kernel<scalar_t>(dets, scores, iou_threshold);
|
| 71 |
+
});
|
| 72 |
+
return result;
|
| 73 |
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/*
|
| 2 |
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 3 |
+
* All rights reserved.
|
| 4 |
+
*
|
| 5 |
+
* This source code is licensed under the BSD-style license found in the
|
| 6 |
+
* LICENSE file in the root directory of this source tree.
|
| 7 |
+
*/
|
| 8 |
+
|
| 9 |
+
#pragma once
|
| 10 |
+
#include <torch/extension.h>
|
| 11 |
+
|
| 12 |
+
#define CHECK_CUDA(x) TORCH_CHECK(x.is_cuda(), #x " must be a CUDA tensor.")
|
| 13 |
+
#define CHECK_CONTIGUOUS(x) \
|
| 14 |
+
TORCH_CHECK(x.is_contiguous(), #x " must be contiguous.")
|
| 15 |
+
#define CHECK_CONTIGUOUS_CUDA(x) \
|
| 16 |
+
CHECK_CUDA(x); \
|
| 17 |
+
CHECK_CONTIGUOUS(x)
|
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/*
|
| 2 |
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 3 |
+
* All rights reserved.
|
| 4 |
+
*
|
| 5 |
+
* This source code is licensed under the BSD-style license found in the
|
| 6 |
+
* LICENSE file in the root directory of this source tree.
|
| 7 |
+
*/
|
| 8 |
+
|
| 9 |
+
#pragma once
|
| 10 |
+
|
| 11 |
+
// A fixed-sized vector with basic arithmetic operators useful for
|
| 12 |
+
// representing 3D coordinates.
|
| 13 |
+
// TODO: switch to Eigen if more functionality is needed.
|
| 14 |
+
|
| 15 |
+
template <
|
| 16 |
+
typename T,
|
| 17 |
+
typename = std::enable_if_t<
|
| 18 |
+
std::is_same<T, double>::value || std::is_same<T, float>::value>>
|
| 19 |
+
struct vec3 {
|
| 20 |
+
T x, y, z;
|
| 21 |
+
typedef T scalar_t;
|
| 22 |
+
vec3(T x, T y, T z) : x(x), y(y), z(z) {}
|
| 23 |
+
};
|
| 24 |
+
|
| 25 |
+
template <typename T>
|
| 26 |
+
inline vec3<T> operator+(const vec3<T>& a, const vec3<T>& b) {
|
| 27 |
+
return vec3<T>(a.x + b.x, a.y + b.y, a.z + b.z);
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
template <typename T>
|
| 31 |
+
inline vec3<T> operator-(const vec3<T>& a, const vec3<T>& b) {
|
| 32 |
+
return vec3<T>(a.x - b.x, a.y - b.y, a.z - b.z);
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
template <typename T>
|
| 36 |
+
inline vec3<T> operator/(const vec3<T>& a, const T b) {
|
| 37 |
+
if (b == 0.0) {
|
| 38 |
+
AT_ERROR(
|
| 39 |
+
"denominator in vec3 division is 0"); // prevent divide by 0 errors.
|
| 40 |
+
}
|
| 41 |
+
return vec3<T>(a.x / b, a.y / b, a.z / b);
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
template <typename T>
|
| 45 |
+
inline vec3<T> operator*(const T a, const vec3<T>& b) {
|
| 46 |
+
return vec3<T>(a * b.x, a * b.y, a * b.z);
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
template <typename T>
|
| 50 |
+
inline vec3<T> operator*(const vec3<T>& a, const vec3<T>& b) {
|
| 51 |
+
return vec3<T>(a.x * b.x, a.y * b.y, a.z * b.z);
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
template <typename T>
|
| 55 |
+
inline T dot(const vec3<T>& a, const vec3<T>& b) {
|
| 56 |
+
return a.x * b.x + a.y * b.y + a.z * b.z;
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
template <typename T>
|
| 60 |
+
inline vec3<T> cross(const vec3<T>& a, const vec3<T>& b) {
|
| 61 |
+
return vec3<T>(
|
| 62 |
+
a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x);
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
template <typename T>
|
| 66 |
+
inline T norm(const vec3<T>& a) {
|
| 67 |
+
return sqrt(dot(a, a));
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
+
template <typename T>
|
| 71 |
+
std::ostream& operator<<(std::ostream& os, const vec3<T>& v) {
|
| 72 |
+
os << "vec3(" << v.x << ", " << v.y << ", " << v.z << ")";
|
| 73 |
+
return os;
|
| 74 |
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/*!
|
| 2 |
+
**************************************************************************************************
|
| 3 |
+
* Vis4D CUDA Operations
|
| 4 |
+
**************************************************************************************************
|
| 5 |
+
* Modified from https://github.com/fundamentalvision/Deformable-DETR/blob/main/models/ops/src/vision.cpp
|
| 6 |
+
**************************************************************************************************
|
| 7 |
+
*/
|
| 8 |
+
#include <torch/extension.h>
|
| 9 |
+
#include "nms_rotated/nms_rotated.h"
|
| 10 |
+
#include "box_iou_rotated/box_iou_rotated.h"
|
| 11 |
+
#include "ms_deform_attn/ms_deform_attn.h"
|
| 12 |
+
#include "deform_conv/deform_conv.h"
|
| 13 |
+
#include "iou_box3d/iou_box3d.h"
|
| 14 |
+
|
| 15 |
+
PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
|
| 16 |
+
m.def("nms_rotated", &nms_rotated);
|
| 17 |
+
m.def("box_iou_rotated", &box_iou_rotated);
|
| 18 |
+
m.def("deform_conv_forward", &deform_conv_forward, "deform_conv_forward");
|
| 19 |
+
m.def(
|
| 20 |
+
"deform_conv_backward_input",
|
| 21 |
+
&deform_conv_backward_input,
|
| 22 |
+
"deform_conv_backward_input");
|
| 23 |
+
m.def(
|
| 24 |
+
"deform_conv_backward_filter",
|
| 25 |
+
&deform_conv_backward_filter,
|
| 26 |
+
"deform_conv_backward_filter");
|
| 27 |
+
m.def(
|
| 28 |
+
"modulated_deform_conv_forward",
|
| 29 |
+
&modulated_deform_conv_forward,
|
| 30 |
+
"modulated_deform_conv_forward");
|
| 31 |
+
m.def(
|
| 32 |
+
"modulated_deform_conv_backward",
|
| 33 |
+
&modulated_deform_conv_backward,
|
| 34 |
+
"modulated_deform_conv_backward");
|
| 35 |
+
m.def("ms_deform_attn_forward", &ms_deform_attn_forward, "ms_deform_attn_forward");
|
| 36 |
+
m.def("ms_deform_attn_backward", &ms_deform_attn_backward, "ms_deform_attn_backward");
|
| 37 |
+
m.def("iou_box3d", &IoUBox3D, "iou_box3d");
|
| 38 |
+
}
|