RoyYang0714 Claude Opus 5 (1M context) commited on
Commit
7266be4
·
1 Parent(s): 791f52a

fix: Compile the vis4d kernels here instead of installing them.

Browse files

pip 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 CHANGED
@@ -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` is compiled from source during the build; it provides the
46
- multi-scale deformable attention and 3D IoU kernels. The Space needs the
47
- variable **`PIP_NO_BUILD_ISOLATION=1`** set in its settings, otherwise pip
48
- compiles it against a newer torch than the one the Space runs and the
49
- extension fails to import. See the comment at the top of `requirements.txt`.
 
 
 
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
requirements.txt CHANGED
@@ -1,13 +1,11 @@
1
- # NOTE: torch stays unpinned on purpose, and the Space sets the build variable
2
- # PIP_NO_BUILD_ISOLATION=1. vis4d_cuda_ops declares torch as a build
3
- # requirement, so with build isolation pip resolves a second, newest torch
4
- # (2.13.0) to compile the extension against, while the Space caps the runtime
5
- # torch at 2.11.0. The two ABIs do not match and importing the extension then
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
vis4d_cuda_ops/__init__.py ADDED
@@ -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
+ ]
vis4d_cuda_ops/src/box_iou_rotated/box_iou_rotated.h ADDED
@@ -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
+ }
vis4d_cuda_ops/src/box_iou_rotated/box_iou_rotated_cpu.cpp ADDED
@@ -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
+ }
vis4d_cuda_ops/src/box_iou_rotated/box_iou_rotated_utils.h ADDED
@@ -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
+ }
vis4d_cuda_ops/src/deform_conv/deform_conv.h ADDED
@@ -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
+ }
vis4d_cuda_ops/src/iou_box3d/iou_box3d.h ADDED
@@ -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
+ }
vis4d_cuda_ops/src/iou_box3d/iou_box3d_cpu.cpp ADDED
@@ -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
+ }
vis4d_cuda_ops/src/iou_box3d/iou_utils.h ADDED
@@ -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
+ }
vis4d_cuda_ops/src/ms_deform_attn/ms_deform_attn.h ADDED
@@ -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
+
vis4d_cuda_ops/src/ms_deform_attn/ms_deform_attn_cuda.h ADDED
@@ -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
+
vis4d_cuda_ops/src/nms_rotated/nms_rotated.h ADDED
@@ -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
+ }
vis4d_cuda_ops/src/nms_rotated/nms_rotated_cpu.cpp ADDED
@@ -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
+ }
vis4d_cuda_ops/src/util/pytorch3d_cutils.h ADDED
@@ -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)
vis4d_cuda_ops/src/util/vec3.h ADDED
@@ -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
+ }
vis4d_cuda_ops/src/vision.cpp ADDED
@@ -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
+ }