7e4ef6f98b
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1509 lines
50 KiB
Plaintext
1509 lines
50 KiB
Plaintext
/**
|
|
* This file includes code originally from the DROID-SLAM repository:
|
|
* https://github.com/princeton-vl/DROID-SLAM
|
|
* Licensed under the BSD-3 License. See THIRD_PARTY_LICENSES.md for details.
|
|
*/
|
|
|
|
#include <torch/extension.h>
|
|
#include <cuda.h>
|
|
#include <cuda_runtime.h>
|
|
#include <cuda_runtime.h>
|
|
|
|
#include <vector>
|
|
#include <iostream>
|
|
|
|
#include <ATen/ATen.h>
|
|
#include <ATen/NativeFunctions.h>
|
|
#include <ATen/Parallel.h>
|
|
|
|
// #include "utils.cuh"
|
|
|
|
#include <eigen3/Eigen/Sparse>
|
|
#include <eigen3/Eigen/SparseCore>
|
|
#include <eigen3/Eigen/SparseCholesky>
|
|
|
|
typedef Eigen::SparseMatrix<double> SpMat;
|
|
typedef Eigen::Triplet<double> T;
|
|
typedef std::vector<std::vector<long>> graph_t;
|
|
typedef std::vector<torch::Tensor> tensor_list_t;
|
|
|
|
#define CHECK_CONTIGUOUS(x) TORCH_CHECK(x.is_contiguous(), #x " must be contiguous")
|
|
#define CHECK_INPUT(x) CHECK_CONTIGUOUS(x)
|
|
|
|
#define MIN_DEPTH 0.25
|
|
|
|
#define THREADS 256
|
|
#define NUM_BLOCKS(batch_size) ((batch_size + THREADS - 1) / THREADS)
|
|
|
|
#define GPU_1D_KERNEL_LOOP(k, n) for (size_t k = threadIdx.x; k < n; k += blockDim.x)
|
|
|
|
namespace slam_ext {
|
|
|
|
__device__ void warpReduce(volatile float *sdata, unsigned int tid) {
|
|
sdata[tid] += sdata[tid + 32];
|
|
sdata[tid] += sdata[tid + 16];
|
|
sdata[tid] += sdata[tid + 8];
|
|
sdata[tid] += sdata[tid + 4];
|
|
sdata[tid] += sdata[tid + 2];
|
|
sdata[tid] += sdata[tid + 1];
|
|
}
|
|
|
|
__device__ void blockReduce(volatile float *sdata) {
|
|
unsigned int tid = threadIdx.x;
|
|
__syncthreads();
|
|
|
|
// if (threadIdx.x < 256) {sdata[tid] += sdata[tid + 256]; } __syncthreads();
|
|
if (threadIdx.x < 128) {
|
|
sdata[tid] += sdata[tid + 128];
|
|
}
|
|
__syncthreads();
|
|
if (threadIdx.x < 64) {
|
|
sdata[tid] += sdata[tid + 64];
|
|
}
|
|
__syncthreads();
|
|
|
|
if (tid < 32) warpReduce(sdata, tid);
|
|
__syncthreads();
|
|
}
|
|
|
|
__device__ void actSO3(const float *q, const float *X, float *Y) {
|
|
float uv[3];
|
|
uv[0] = 2.0 * (q[1] * X[2] - q[2] * X[1]);
|
|
uv[1] = 2.0 * (q[2] * X[0] - q[0] * X[2]);
|
|
uv[2] = 2.0 * (q[0] * X[1] - q[1] * X[0]);
|
|
|
|
Y[0] = X[0] + q[3] * uv[0] + (q[1] * uv[2] - q[2] * uv[1]);
|
|
Y[1] = X[1] + q[3] * uv[1] + (q[2] * uv[0] - q[0] * uv[2]);
|
|
Y[2] = X[2] + q[3] * uv[2] + (q[0] * uv[1] - q[1] * uv[0]);
|
|
}
|
|
|
|
__device__ void actSE3(const float *t, const float *q, const float *X, float *Y) {
|
|
actSO3(q, X, Y);
|
|
Y[3] = X[3];
|
|
Y[0] += X[3] * t[0];
|
|
Y[1] += X[3] * t[1];
|
|
Y[2] += X[3] * t[2];
|
|
}
|
|
|
|
__device__ void adjSE3(const float *t, const float *q, const float *X, float *Y) {
|
|
float qinv[4] = {-q[0], -q[1], -q[2], q[3]};
|
|
actSO3(qinv, &X[0], &Y[0]);
|
|
actSO3(qinv, &X[3], &Y[3]);
|
|
|
|
float u[3], v[3];
|
|
u[0] = t[2] * X[1] - t[1] * X[2];
|
|
u[1] = t[0] * X[2] - t[2] * X[0];
|
|
u[2] = t[1] * X[0] - t[0] * X[1];
|
|
|
|
actSO3(qinv, u, v);
|
|
Y[3] += v[0];
|
|
Y[4] += v[1];
|
|
Y[5] += v[2];
|
|
}
|
|
|
|
__device__ void relSE3(const float *ti, const float *qi, const float *tj, const float *qj, float *tij, float *qij) {
|
|
qij[0] = -qj[3] * qi[0] + qj[0] * qi[3] - qj[1] * qi[2] + qj[2] * qi[1],
|
|
qij[1] = -qj[3] * qi[1] + qj[1] * qi[3] - qj[2] * qi[0] + qj[0] * qi[2],
|
|
qij[2] = -qj[3] * qi[2] + qj[2] * qi[3] - qj[0] * qi[1] + qj[1] * qi[0],
|
|
qij[3] = qj[3] * qi[3] + qj[0] * qi[0] + qj[1] * qi[1] + qj[2] * qi[2],
|
|
|
|
actSO3(qij, ti, tij);
|
|
tij[0] = tj[0] - tij[0];
|
|
tij[1] = tj[1] - tij[1];
|
|
tij[2] = tj[2] - tij[2];
|
|
}
|
|
|
|
__device__ void expSO3(const float *phi, float *q) {
|
|
// SO3 exponential map
|
|
float theta_sq = phi[0] * phi[0] + phi[1] * phi[1] + phi[2] * phi[2];
|
|
float theta_p4 = theta_sq * theta_sq;
|
|
|
|
float theta = sqrtf(theta_sq);
|
|
float imag, real;
|
|
|
|
if (theta_sq < 1e-8) {
|
|
imag = 0.5 - (1.0 / 48.0) * theta_sq + (1.0 / 3840.0) * theta_p4;
|
|
real = 1.0 - (1.0 / 8.0) * theta_sq + (1.0 / 384.0) * theta_p4;
|
|
} else {
|
|
imag = sinf(0.5 * theta) / theta;
|
|
real = cosf(0.5 * theta);
|
|
}
|
|
|
|
q[0] = imag * phi[0];
|
|
q[1] = imag * phi[1];
|
|
q[2] = imag * phi[2];
|
|
q[3] = real;
|
|
}
|
|
|
|
__device__ void crossInplace(const float *a, float *b) {
|
|
float x[3] = {
|
|
a[1] * b[2] - a[2] * b[1],
|
|
a[2] * b[0] - a[0] * b[2],
|
|
a[0] * b[1] - a[1] * b[0],
|
|
};
|
|
|
|
b[0] = x[0];
|
|
b[1] = x[1];
|
|
b[2] = x[2];
|
|
}
|
|
|
|
__device__ void expSE3(const float *xi, float *t, float *q) {
|
|
// SE3 exponential map
|
|
|
|
expSO3(xi + 3, q);
|
|
float tau[3] = {xi[0], xi[1], xi[2]};
|
|
float phi[3] = {xi[3], xi[4], xi[5]};
|
|
|
|
float theta_sq = phi[0] * phi[0] + phi[1] * phi[1] + phi[2] * phi[2];
|
|
float theta = sqrtf(theta_sq);
|
|
|
|
t[0] = tau[0];
|
|
t[1] = tau[1];
|
|
t[2] = tau[2];
|
|
|
|
if (theta > 1e-4) {
|
|
float a = (1 - cosf(theta)) / theta_sq;
|
|
crossInplace(phi, tau);
|
|
t[0] += a * tau[0];
|
|
t[1] += a * tau[1];
|
|
t[2] += a * tau[2];
|
|
|
|
float b = (theta - sinf(theta)) / (theta * theta_sq);
|
|
crossInplace(phi, tau);
|
|
t[0] += b * tau[0];
|
|
t[1] += b * tau[1];
|
|
t[2] += b * tau[2];
|
|
}
|
|
}
|
|
__global__ void projective_transform_kernel(
|
|
const torch::PackedTensorAccessor32<float, 4, torch::RestrictPtrTraits> target,
|
|
const torch::PackedTensorAccessor32<float, 4, torch::RestrictPtrTraits> weight,
|
|
const torch::PackedTensorAccessor32<float, 2, torch::RestrictPtrTraits> poses,
|
|
const torch::PackedTensorAccessor32<float, 3, torch::RestrictPtrTraits> disps,
|
|
const torch::PackedTensorAccessor32<float, 1, torch::RestrictPtrTraits> intrinsics,
|
|
const torch::PackedTensorAccessor32<long, 1, torch::RestrictPtrTraits> ii,
|
|
const torch::PackedTensorAccessor32<long, 1, torch::RestrictPtrTraits> jj,
|
|
torch::PackedTensorAccessor32<float, 4, torch::RestrictPtrTraits> Hs,
|
|
torch::PackedTensorAccessor32<float, 3, torch::RestrictPtrTraits> vs,
|
|
torch::PackedTensorAccessor32<float, 3, torch::RestrictPtrTraits> Eii,
|
|
torch::PackedTensorAccessor32<float, 3, torch::RestrictPtrTraits> Eij,
|
|
torch::PackedTensorAccessor32<float, 2, torch::RestrictPtrTraits> Cii,
|
|
torch::PackedTensorAccessor32<float, 2, torch::RestrictPtrTraits> bz) {
|
|
const int block_id = blockIdx.x;
|
|
const int thread_id = threadIdx.x;
|
|
|
|
const int ht = disps.size(1);
|
|
const int wd = disps.size(2);
|
|
|
|
int ix = static_cast<int>(ii[block_id]);
|
|
int jx = static_cast<int>(jj[block_id]);
|
|
|
|
__shared__ float fx;
|
|
__shared__ float fy;
|
|
__shared__ float cx;
|
|
__shared__ float cy;
|
|
|
|
__shared__ float ti[3], tj[3], tij[3];
|
|
__shared__ float qi[4], qj[4], qij[4];
|
|
|
|
// load intrinsics from global memory
|
|
if (thread_id == 0) {
|
|
fx = intrinsics[0];
|
|
fy = intrinsics[1];
|
|
cx = intrinsics[2];
|
|
cy = intrinsics[3];
|
|
}
|
|
|
|
__syncthreads();
|
|
|
|
// stereo frames
|
|
if (ix == jx) {
|
|
if (thread_id == 0) {
|
|
tij[0] = -0.1;
|
|
tij[1] = 0;
|
|
tij[2] = 0;
|
|
qij[0] = 0;
|
|
qij[1] = 0;
|
|
qij[2] = 0;
|
|
qij[3] = 1;
|
|
}
|
|
}
|
|
|
|
else {
|
|
// load poses from global memory
|
|
if (thread_id < 3) {
|
|
ti[thread_id] = poses[ix][thread_id];
|
|
tj[thread_id] = poses[jx][thread_id];
|
|
}
|
|
|
|
if (thread_id < 4) {
|
|
qi[thread_id] = poses[ix][thread_id + 3];
|
|
qj[thread_id] = poses[jx][thread_id + 3];
|
|
}
|
|
|
|
__syncthreads();
|
|
|
|
if (thread_id == 0) {
|
|
relSE3(ti, qi, tj, qj, tij, qij);
|
|
}
|
|
}
|
|
|
|
__syncthreads();
|
|
|
|
// points
|
|
float Xi[4];
|
|
float Xj[4];
|
|
|
|
// jacobians
|
|
float Jx[12];
|
|
float Jz;
|
|
|
|
float *Ji = &Jx[0];
|
|
float *Jj = &Jx[6];
|
|
|
|
// hessians
|
|
float hij[12 * (12 + 1) / 2];
|
|
|
|
float vi[6], vj[6];
|
|
|
|
int l;
|
|
for (l = 0; l < 12 * (12 + 1) / 2; l++) {
|
|
hij[l] = 0;
|
|
}
|
|
|
|
for (int n = 0; n < 6; n++) {
|
|
vi[n] = 0;
|
|
vj[n] = 0;
|
|
}
|
|
|
|
__syncthreads();
|
|
|
|
GPU_1D_KERNEL_LOOP(k, ht * wd) {
|
|
const int i = k / wd;
|
|
const int j = k % wd;
|
|
|
|
const float u = static_cast<float>(j);
|
|
const float v = static_cast<float>(i);
|
|
|
|
// homogenous coordinates
|
|
Xi[0] = (u - cx) / fx;
|
|
Xi[1] = (v - cy) / fy;
|
|
Xi[2] = 1;
|
|
Xi[3] = disps[ix][i][j];
|
|
|
|
// transform homogenous point
|
|
actSE3(tij, qij, Xi, Xj);
|
|
|
|
const float x = Xj[0];
|
|
const float y = Xj[1];
|
|
const float h = Xj[3];
|
|
|
|
const float d = (Xj[2] < MIN_DEPTH) ? 0.0 : 1.0 / Xj[2];
|
|
const float d2 = d * d;
|
|
|
|
float wu = (Xj[2] < MIN_DEPTH) ? 0.0 : .001 * weight[block_id][0][i][j];
|
|
float wv = (Xj[2] < MIN_DEPTH) ? 0.0 : .001 * weight[block_id][1][i][j];
|
|
|
|
// Residuals
|
|
const float ru = target[block_id][0][i][j] - (fx * d * x + cx);
|
|
const float rv = target[block_id][1][i][j] - (fy * d * y + cy);
|
|
|
|
// x - coordinate
|
|
|
|
// Eq.(11) dl_dTj
|
|
Jj[0] = fx * (h * d);
|
|
Jj[1] = fx * 0;
|
|
Jj[2] = fx * (-x * h * d2);
|
|
Jj[3] = fx * (-x * y * d2);
|
|
Jj[4] = fx * (1 + x * x * d2);
|
|
Jj[5] = fx * (-y * d);
|
|
|
|
// dl_dd
|
|
Jz = fx * (tij[0] * d - tij[2] * (x * d2));
|
|
|
|
// LHS and RHS of depth!
|
|
Cii[block_id][k] = wu * Jz * Jz;
|
|
bz[block_id][k] = wu * ru * Jz;
|
|
|
|
// If stereo don't add related terms.
|
|
if (ix == jx) wu = 0;
|
|
|
|
// Apply Eq.(12) to get dl_dTi
|
|
adjSE3(tij, qij, Jj, Ji);
|
|
for (int n = 0; n < 6; n++) Ji[n] *= -1;
|
|
|
|
// Compute the 'ij' half of the JTJ.
|
|
l = 0;
|
|
for (int n = 0; n < 12; n++) {
|
|
for (int m = 0; m <= n; m++) {
|
|
hij[l] += wu * Jx[n] * Jx[m];
|
|
l++;
|
|
}
|
|
}
|
|
|
|
// Compute RHS of pose, and two parts of the LHS of pose-depth.
|
|
for (int n = 0; n < 6; n++) {
|
|
vi[n] += wu * ru * Ji[n];
|
|
vj[n] += wu * ru * Jj[n];
|
|
|
|
// block_id is the edge idx, k is pixel idx
|
|
Eii[block_id][n][k] = wu * Jz * Ji[n];
|
|
Eij[block_id][n][k] = wu * Jz * Jj[n];
|
|
}
|
|
|
|
// y - coordinate
|
|
|
|
Jj[0] = fy * 0;
|
|
Jj[1] = fy * (h * d);
|
|
Jj[2] = fy * (-y * h * d2);
|
|
Jj[3] = fy * (-1 - y * y * d2);
|
|
Jj[4] = fy * (x * y * d2);
|
|
Jj[5] = fy * (x * d);
|
|
|
|
Jz = fy * (tij[1] * d - tij[2] * (y * d2));
|
|
Cii[block_id][k] += wv * Jz * Jz;
|
|
bz[block_id][k] += wv * rv * Jz;
|
|
|
|
if (ix == jx) wv = 0;
|
|
|
|
adjSE3(tij, qij, Jj, Ji);
|
|
for (int n = 0; n < 6; n++) Ji[n] *= -1;
|
|
|
|
l = 0;
|
|
for (int n = 0; n < 12; n++) {
|
|
for (int m = 0; m <= n; m++) {
|
|
hij[l] += wv * Jx[n] * Jx[m];
|
|
l++;
|
|
}
|
|
}
|
|
|
|
for (int n = 0; n < 6; n++) {
|
|
vi[n] += wv * rv * Ji[n];
|
|
vj[n] += wv * rv * Jj[n];
|
|
|
|
Eii[block_id][n][k] += wv * Jz * Ji[n];
|
|
Eij[block_id][n][k] += wv * Jz * Jj[n];
|
|
}
|
|
}
|
|
|
|
__syncthreads();
|
|
|
|
// As we parallel over the pixels using 256 threads, we need to reduce the results.
|
|
|
|
__shared__ float sdata[THREADS];
|
|
for (int n = 0; n < 6; n++) {
|
|
sdata[threadIdx.x] = vi[n];
|
|
blockReduce(sdata);
|
|
if (threadIdx.x == 0) {
|
|
vs[0][block_id][n] = sdata[0];
|
|
}
|
|
|
|
__syncthreads();
|
|
|
|
sdata[threadIdx.x] = vj[n];
|
|
blockReduce(sdata);
|
|
if (threadIdx.x == 0) {
|
|
vs[1][block_id][n] = sdata[0];
|
|
}
|
|
}
|
|
|
|
l = 0;
|
|
for (int n = 0; n < 12; n++) {
|
|
for (int m = 0; m <= n; m++) {
|
|
sdata[threadIdx.x] = hij[l];
|
|
blockReduce(sdata);
|
|
|
|
if (threadIdx.x == 0) {
|
|
if (n < 6 && m < 6) {
|
|
Hs[0][block_id][n][m] = sdata[0];
|
|
Hs[0][block_id][m][n] = sdata[0];
|
|
} else if (n >= 6 && m < 6) {
|
|
Hs[1][block_id][m][n - 6] = sdata[0];
|
|
Hs[2][block_id][n - 6][m] = sdata[0];
|
|
} else {
|
|
Hs[3][block_id][n - 6][m - 6] = sdata[0];
|
|
Hs[3][block_id][m - 6][n - 6] = sdata[0];
|
|
}
|
|
}
|
|
|
|
l++;
|
|
}
|
|
}
|
|
}
|
|
|
|
__global__ void projmap_kernel(const torch::PackedTensorAccessor32<float, 2, torch::RestrictPtrTraits> poses,
|
|
const torch::PackedTensorAccessor32<float, 3, torch::RestrictPtrTraits> disps,
|
|
const torch::PackedTensorAccessor32<float, 1, torch::RestrictPtrTraits> intrinsics,
|
|
const torch::PackedTensorAccessor32<long, 1, torch::RestrictPtrTraits> ii,
|
|
const torch::PackedTensorAccessor32<long, 1, torch::RestrictPtrTraits> jj,
|
|
torch::PackedTensorAccessor32<float, 4, torch::RestrictPtrTraits> coords,
|
|
torch::PackedTensorAccessor32<float, 4, torch::RestrictPtrTraits> valid) {
|
|
const int block_id = blockIdx.x;
|
|
const int thread_id = threadIdx.x;
|
|
|
|
const int ht = disps.size(1);
|
|
const int wd = disps.size(2);
|
|
|
|
__shared__ int ix;
|
|
__shared__ int jx;
|
|
|
|
__shared__ float fx;
|
|
__shared__ float fy;
|
|
__shared__ float cx;
|
|
__shared__ float cy;
|
|
|
|
__shared__ float ti[3], tj[3], tij[3];
|
|
__shared__ float qi[4], qj[4], qij[4];
|
|
|
|
// load intrinsics from global memory
|
|
if (thread_id == 0) {
|
|
ix = static_cast<int>(ii[block_id]);
|
|
jx = static_cast<int>(jj[block_id]);
|
|
fx = intrinsics[0];
|
|
fy = intrinsics[1];
|
|
cx = intrinsics[2];
|
|
cy = intrinsics[3];
|
|
}
|
|
|
|
__syncthreads();
|
|
|
|
// load poses from global memory
|
|
if (thread_id < 3) {
|
|
ti[thread_id] = poses[ix][thread_id];
|
|
tj[thread_id] = poses[jx][thread_id];
|
|
}
|
|
|
|
if (thread_id < 4) {
|
|
qi[thread_id] = poses[ix][thread_id + 3];
|
|
qj[thread_id] = poses[jx][thread_id + 3];
|
|
}
|
|
|
|
__syncthreads();
|
|
|
|
if (thread_id == 0) {
|
|
relSE3(ti, qi, tj, qj, tij, qij);
|
|
}
|
|
|
|
// points
|
|
float Xi[4];
|
|
float Xj[4];
|
|
|
|
__syncthreads();
|
|
|
|
GPU_1D_KERNEL_LOOP(k, ht * wd) {
|
|
const int i = k / wd;
|
|
const int j = k % wd;
|
|
|
|
const float u = static_cast<float>(j);
|
|
const float v = static_cast<float>(i);
|
|
|
|
// homogenous coordinates
|
|
Xi[0] = (u - cx) / fx;
|
|
Xi[1] = (v - cy) / fy;
|
|
Xi[2] = 1;
|
|
Xi[3] = disps[ix][i][j];
|
|
|
|
// transform homogenous point
|
|
actSE3(tij, qij, Xi, Xj);
|
|
|
|
coords[block_id][i][j][0] = u;
|
|
coords[block_id][i][j][1] = v;
|
|
|
|
if (Xj[2] > 0.01) {
|
|
coords[block_id][i][j][0] = fx * (Xj[0] / Xj[2]) + cx;
|
|
coords[block_id][i][j][1] = fy * (Xj[1] / Xj[2]) + cy;
|
|
}
|
|
|
|
valid[block_id][i][j][0] = (Xj[2] > MIN_DEPTH) ? 1.0 : 0.0;
|
|
}
|
|
}
|
|
|
|
__global__ void frame_distance_kernel(
|
|
const torch::PackedTensorAccessor32<float, 2, torch::RestrictPtrTraits> poses,
|
|
const torch::PackedTensorAccessor32<float, 3, torch::RestrictPtrTraits> disps,
|
|
const torch::PackedTensorAccessor32<float, 2, torch::RestrictPtrTraits> intrinsics,
|
|
const torch::PackedTensorAccessor32<long, 1, torch::RestrictPtrTraits> pi,
|
|
const torch::PackedTensorAccessor32<long, 1, torch::RestrictPtrTraits> pj,
|
|
const torch::PackedTensorAccessor32<long, 1, torch::RestrictPtrTraits> ri,
|
|
const torch::PackedTensorAccessor32<long, 1, torch::RestrictPtrTraits> rj,
|
|
const torch::PackedTensorAccessor32<long, 1, torch::RestrictPtrTraits> di,
|
|
torch::PackedTensorAccessor32<float, 1, torch::RestrictPtrTraits> dist, const float beta) {
|
|
const int block_id = blockIdx.x;
|
|
const int thread_id = threadIdx.x;
|
|
|
|
const int ht = disps.size(1);
|
|
const int wd = disps.size(2);
|
|
|
|
__shared__ int ix;
|
|
__shared__ int jx;
|
|
__shared__ int dix;
|
|
__shared__ int rix;
|
|
__shared__ int rjx;
|
|
|
|
__shared__ float fxi;
|
|
__shared__ float fyi;
|
|
__shared__ float cxi;
|
|
__shared__ float cyi;
|
|
__shared__ float fxj;
|
|
__shared__ float fyj;
|
|
__shared__ float cxj;
|
|
__shared__ float cyj;
|
|
|
|
__shared__ float ti[3], tj[3], tij[3];
|
|
__shared__ float qi[4], qj[4], qij[4];
|
|
|
|
// load intrinsics from global memory
|
|
if (thread_id == 0) {
|
|
ix = static_cast<int>(pi[block_id]);
|
|
jx = static_cast<int>(pj[block_id]);
|
|
dix = static_cast<int>(di[block_id]);
|
|
rix = static_cast<int>(ri[block_id]);
|
|
rjx = static_cast<int>(rj[block_id]);
|
|
fxi = intrinsics[rix][0];
|
|
fyi = intrinsics[rix][1];
|
|
cxi = intrinsics[rix][2];
|
|
cyi = intrinsics[rix][3];
|
|
fxj = intrinsics[rjx][0];
|
|
fyj = intrinsics[rjx][1];
|
|
cxj = intrinsics[rjx][2];
|
|
cyj = intrinsics[rjx][3];
|
|
}
|
|
|
|
__syncthreads();
|
|
|
|
// points
|
|
float Xi[4];
|
|
float Xj[4];
|
|
|
|
__shared__ float accum[THREADS];
|
|
accum[thread_id] = 0;
|
|
__shared__ float valid[THREADS];
|
|
valid[thread_id] = 0;
|
|
__shared__ float total[THREADS];
|
|
total[thread_id] = 0;
|
|
|
|
__syncthreads();
|
|
|
|
for (int n = 0; n < 1; n++) {
|
|
if (thread_id < 3) {
|
|
ti[thread_id] = poses[ix][thread_id];
|
|
tj[thread_id] = poses[jx][thread_id];
|
|
}
|
|
|
|
if (thread_id < 4) {
|
|
qi[thread_id] = poses[ix][thread_id + 3];
|
|
qj[thread_id] = poses[jx][thread_id + 3];
|
|
}
|
|
|
|
__syncthreads();
|
|
|
|
relSE3(ti, qi, tj, qj, tij, qij);
|
|
|
|
float d, du, dv;
|
|
|
|
GPU_1D_KERNEL_LOOP(k, ht * wd) {
|
|
const int i = k / wd;
|
|
const int j = k % wd;
|
|
|
|
const float u = static_cast<float>(j);
|
|
const float v = static_cast<float>(i);
|
|
|
|
// if (disps[ix][i][j] < 0.01) {
|
|
// continue;
|
|
// }
|
|
|
|
// homogenous coordinates
|
|
Xi[0] = (u - cxi) / fxi;
|
|
Xi[1] = (v - cyi) / fyi;
|
|
Xi[2] = 1;
|
|
Xi[3] = disps[dix][i][j];
|
|
|
|
// transform homogenous point
|
|
actSE3(tij, qij, Xi, Xj);
|
|
|
|
du = fxj * (Xj[0] / Xj[2]) + cxj - u;
|
|
dv = fyj * (Xj[1] / Xj[2]) + cyj - v;
|
|
d = sqrtf(du * du + dv * dv);
|
|
|
|
total[threadIdx.x] += beta;
|
|
|
|
if (Xj[2] > MIN_DEPTH) {
|
|
accum[threadIdx.x] += beta * d;
|
|
valid[threadIdx.x] += beta;
|
|
}
|
|
|
|
Xi[0] = (u - cxi) / fxi;
|
|
Xi[1] = (v - cyi) / fyi;
|
|
Xi[2] = 1;
|
|
Xi[3] = disps[dix][i][j];
|
|
|
|
Xj[0] = Xi[0] + Xi[3] * tij[0];
|
|
Xj[1] = Xi[1] + Xi[3] * tij[1];
|
|
Xj[2] = Xi[2] + Xi[3] * tij[2];
|
|
|
|
du = fxj * (Xj[0] / Xj[2]) + cxj - u;
|
|
dv = fyj * (Xj[1] / Xj[2]) + cyj - v;
|
|
d = sqrtf(du * du + dv * dv);
|
|
|
|
total[threadIdx.x] += (1 - beta);
|
|
|
|
if (Xj[2] > MIN_DEPTH) {
|
|
accum[threadIdx.x] += (1 - beta) * d;
|
|
valid[threadIdx.x] += (1 - beta);
|
|
}
|
|
}
|
|
|
|
if (threadIdx.x == 0) {
|
|
int tmp = ix;
|
|
ix = jx;
|
|
jx = tmp;
|
|
}
|
|
|
|
__syncthreads();
|
|
}
|
|
__syncthreads();
|
|
blockReduce(accum);
|
|
__syncthreads();
|
|
blockReduce(total);
|
|
__syncthreads();
|
|
blockReduce(valid);
|
|
|
|
__syncthreads();
|
|
|
|
if (thread_id == 0) {
|
|
dist[block_id] = (valid[0] / (total[0] + 1e-8) < 0.75) ? 1000.0 : accum[0] / valid[0];
|
|
}
|
|
}
|
|
|
|
__global__ void depth_filter_kernel(const torch::PackedTensorAccessor32<float, 2, torch::RestrictPtrTraits> poses,
|
|
const torch::PackedTensorAccessor32<float, 3, torch::RestrictPtrTraits> disps,
|
|
const torch::PackedTensorAccessor32<float, 1, torch::RestrictPtrTraits> intrinsics,
|
|
const torch::PackedTensorAccessor32<long, 1, torch::RestrictPtrTraits> inds,
|
|
const torch::PackedTensorAccessor32<float, 1, torch::RestrictPtrTraits> thresh,
|
|
torch::PackedTensorAccessor32<float, 3, torch::RestrictPtrTraits> counter) {
|
|
const int block_id = blockIdx.x;
|
|
const int neigh_id = blockIdx.y;
|
|
const int index = blockIdx.z * blockDim.x + threadIdx.x;
|
|
|
|
// if (threadIdx.x == 0) {
|
|
// printf("%d %d %d %d\n", blockIdx.x, blockIdx.y, blockDim.x, threadIdx.x);
|
|
// }
|
|
|
|
const int num = disps.size(0);
|
|
const int ht = disps.size(1);
|
|
const int wd = disps.size(2);
|
|
|
|
__shared__ int ix;
|
|
__shared__ int jx;
|
|
|
|
__shared__ float fx;
|
|
__shared__ float fy;
|
|
__shared__ float cx;
|
|
__shared__ float cy;
|
|
|
|
__shared__ float ti[3], tj[3], tij[3];
|
|
__shared__ float qi[4], qj[4], qij[4];
|
|
|
|
if (threadIdx.x == 0) {
|
|
ix = static_cast<int>(inds[block_id]);
|
|
jx = (neigh_id < 3) ? ix - neigh_id - 1 : ix + neigh_id - 2;
|
|
fx = intrinsics[0];
|
|
fy = intrinsics[1];
|
|
cx = intrinsics[2];
|
|
cy = intrinsics[3];
|
|
}
|
|
|
|
__syncthreads();
|
|
|
|
if (jx < 0 || jx >= num) {
|
|
return;
|
|
}
|
|
|
|
const float t = thresh[block_id];
|
|
|
|
// load poses from global memory
|
|
if (threadIdx.x < 3) {
|
|
ti[threadIdx.x] = poses[ix][threadIdx.x];
|
|
tj[threadIdx.x] = poses[jx][threadIdx.x];
|
|
}
|
|
|
|
if (threadIdx.x < 4) {
|
|
qi[threadIdx.x] = poses[ix][threadIdx.x + 3];
|
|
qj[threadIdx.x] = poses[jx][threadIdx.x + 3];
|
|
}
|
|
|
|
__syncthreads();
|
|
|
|
if (threadIdx.x == 0) {
|
|
relSE3(ti, qi, tj, qj, tij, qij);
|
|
}
|
|
|
|
// points
|
|
float Xi[4];
|
|
float Xj[4];
|
|
|
|
__syncthreads();
|
|
|
|
if (index < ht * wd) {
|
|
const int i = index / wd;
|
|
const int j = index % wd;
|
|
|
|
const float ui = static_cast<float>(j);
|
|
const float vi = static_cast<float>(i);
|
|
const float di = disps[ix][i][j];
|
|
|
|
// homogenous coordinates
|
|
Xi[0] = (ui - cx) / fx;
|
|
Xi[1] = (vi - cy) / fy;
|
|
Xi[2] = 1;
|
|
Xi[3] = di;
|
|
|
|
// transform homogenous point
|
|
actSE3(tij, qij, Xi, Xj);
|
|
|
|
const float uj = fx * (Xj[0] / Xj[2]) + cx;
|
|
const float vj = fy * (Xj[1] / Xj[2]) + cy;
|
|
const float dj = Xj[3] / Xj[2];
|
|
|
|
const int u0 = static_cast<int>(floor(uj));
|
|
const int v0 = static_cast<int>(floor(vj));
|
|
|
|
if (u0 >= 0 && v0 >= 0 && u0 < wd - 1 && v0 < ht - 1) {
|
|
const float wx = ceil(uj) - uj;
|
|
const float wy = ceil(vj) - vj;
|
|
|
|
const float d00 = disps[jx][v0 + 0][u0 + 0];
|
|
const float d01 = disps[jx][v0 + 0][u0 + 1];
|
|
const float d10 = disps[jx][v0 + 1][u0 + 0];
|
|
const float d11 = disps[jx][v0 + 1][u0 + 1];
|
|
|
|
const float dj_hat = wy * wx * d00 + wy * (1 - wx) * d01 + (1 - wy) * wx * d10 + (1 - wy) * (1 - wx) * d11;
|
|
|
|
const float err = abs(1.0 / dj - 1.0 / dj_hat);
|
|
if (abs(1.0 / dj - 1.0 / d00) < t)
|
|
atomicAdd(&counter[block_id][i][j], 1.0f);
|
|
else if (abs(1.0 / dj - 1.0 / d01) < t)
|
|
atomicAdd(&counter[block_id][i][j], 1.0f);
|
|
else if (abs(1.0 / dj - 1.0 / d10) < t)
|
|
atomicAdd(&counter[block_id][i][j], 1.0f);
|
|
else if (abs(1.0 / dj - 1.0 / d11) < t)
|
|
atomicAdd(&counter[block_id][i][j], 1.0f);
|
|
}
|
|
}
|
|
}
|
|
|
|
__global__ void iproj_kernel(const torch::PackedTensorAccessor32<float, 2, torch::RestrictPtrTraits> poses,
|
|
const torch::PackedTensorAccessor32<float, 3, torch::RestrictPtrTraits> disps,
|
|
const torch::PackedTensorAccessor32<float, 1, torch::RestrictPtrTraits> intrinsics,
|
|
torch::PackedTensorAccessor32<float, 4, torch::RestrictPtrTraits> points)
|
|
|
|
{
|
|
const int block_id = blockIdx.x;
|
|
const int index = blockIdx.y * blockDim.x + threadIdx.x;
|
|
|
|
const int num = disps.size(0);
|
|
const int ht = disps.size(1);
|
|
const int wd = disps.size(2);
|
|
|
|
__shared__ float fx;
|
|
__shared__ float fy;
|
|
__shared__ float cx;
|
|
__shared__ float cy;
|
|
|
|
__shared__ float t[3];
|
|
__shared__ float q[4];
|
|
|
|
if (threadIdx.x == 0) {
|
|
fx = intrinsics[0];
|
|
fy = intrinsics[1];
|
|
cx = intrinsics[2];
|
|
cy = intrinsics[3];
|
|
}
|
|
|
|
__syncthreads();
|
|
|
|
// load poses from global memory
|
|
if (threadIdx.x < 3) {
|
|
t[threadIdx.x] = poses[block_id][threadIdx.x];
|
|
}
|
|
|
|
if (threadIdx.x < 4) {
|
|
q[threadIdx.x] = poses[block_id][threadIdx.x + 3];
|
|
}
|
|
|
|
__syncthreads();
|
|
|
|
// points
|
|
float Xi[4];
|
|
float Xj[4];
|
|
|
|
if (index < ht * wd) {
|
|
const int i = index / wd;
|
|
const int j = index % wd;
|
|
|
|
const float ui = static_cast<float>(j);
|
|
const float vi = static_cast<float>(i);
|
|
const float di = disps[block_id][i][j];
|
|
|
|
// homogenous coordinates
|
|
Xi[0] = (ui - cx) / fx;
|
|
Xi[1] = (vi - cy) / fy;
|
|
Xi[2] = 1;
|
|
Xi[3] = di;
|
|
|
|
// transform homogenous point
|
|
actSE3(t, q, Xi, Xj);
|
|
|
|
points[block_id][i][j][0] = Xj[0] / Xj[3];
|
|
points[block_id][i][j][1] = Xj[1] / Xj[3];
|
|
points[block_id][i][j][2] = Xj[2] / Xj[3];
|
|
}
|
|
}
|
|
|
|
__global__ void accum_kernel(const torch::PackedTensorAccessor32<float, 2, torch::RestrictPtrTraits> inps,
|
|
const torch::PackedTensorAccessor32<long, 1, torch::RestrictPtrTraits> ptrs,
|
|
const torch::PackedTensorAccessor32<long, 1, torch::RestrictPtrTraits> idxs,
|
|
torch::PackedTensorAccessor32<float, 2, torch::RestrictPtrTraits> outs) {
|
|
const int block_id = blockIdx.x;
|
|
const int D = inps.size(2);
|
|
|
|
const int start = ptrs[block_id];
|
|
const int end = ptrs[block_id + 1];
|
|
|
|
for (int k = threadIdx.x; k < D; k += blockDim.x) {
|
|
float x = 0;
|
|
for (int i = start; i < end; i++) {
|
|
x += inps[idxs[i]][k];
|
|
}
|
|
outs[block_id][k] = x;
|
|
}
|
|
}
|
|
|
|
__device__ void retrSE3(const float *xi, const float *t, const float *q, float *t1, float *q1) {
|
|
// retraction on SE3 manifold
|
|
|
|
float dt[3] = {0, 0, 0};
|
|
float dq[4] = {0, 0, 0, 1};
|
|
|
|
expSE3(xi, dt, dq);
|
|
|
|
q1[0] = dq[3] * q[0] + dq[0] * q[3] + dq[1] * q[2] - dq[2] * q[1];
|
|
q1[1] = dq[3] * q[1] + dq[1] * q[3] + dq[2] * q[0] - dq[0] * q[2];
|
|
q1[2] = dq[3] * q[2] + dq[2] * q[3] + dq[0] * q[1] - dq[1] * q[0];
|
|
q1[3] = dq[3] * q[3] - dq[0] * q[0] - dq[1] * q[1] - dq[2] * q[2];
|
|
|
|
actSO3(dq, t, t1);
|
|
t1[0] += dt[0];
|
|
t1[1] += dt[1];
|
|
t1[2] += dt[2];
|
|
}
|
|
|
|
__global__ void pose_retr_kernel(torch::PackedTensorAccessor32<float, 2, torch::RestrictPtrTraits> poses,
|
|
const torch::PackedTensorAccessor32<float, 2, torch::RestrictPtrTraits> dx,
|
|
const int t0, const int t1) {
|
|
for (int k = t0 + threadIdx.x; k < t1; k += blockDim.x) {
|
|
float xi[6], q[4], q1[4], t[3], t1[3];
|
|
|
|
t[0] = poses[k][0];
|
|
t[1] = poses[k][1];
|
|
t[2] = poses[k][2];
|
|
|
|
q[0] = poses[k][3];
|
|
q[1] = poses[k][4];
|
|
q[2] = poses[k][5];
|
|
q[3] = poses[k][6];
|
|
|
|
for (int n = 0; n < 6; n++) {
|
|
xi[n] = dx[k - t0][n];
|
|
}
|
|
|
|
retrSE3(xi, t, q, t1, q1);
|
|
|
|
poses[k][0] = t1[0];
|
|
poses[k][1] = t1[1];
|
|
poses[k][2] = t1[2];
|
|
|
|
poses[k][3] = q1[0];
|
|
poses[k][4] = q1[1];
|
|
poses[k][5] = q1[2];
|
|
poses[k][6] = q1[3];
|
|
}
|
|
}
|
|
|
|
__global__ void disp_retr_kernel(torch::PackedTensorAccessor32<float, 3, torch::RestrictPtrTraits> disps,
|
|
const torch::PackedTensorAccessor32<float, 2, torch::RestrictPtrTraits> dz,
|
|
const torch::PackedTensorAccessor32<long, 1, torch::RestrictPtrTraits> inds) {
|
|
const int i = inds[blockIdx.x];
|
|
const int ht = disps.size(1);
|
|
const int wd = disps.size(2);
|
|
|
|
for (int k = threadIdx.x; k < ht * wd; k += blockDim.x) {
|
|
float d = disps[i][k / wd][k % wd] + dz[blockIdx.x][k];
|
|
disps[i][k / wd][k % wd] = d;
|
|
}
|
|
}
|
|
|
|
torch::Tensor accum_cuda(torch::Tensor data, torch::Tensor ix, torch::Tensor jx) {
|
|
torch::Tensor ix_cpu = ix.to(torch::kCPU);
|
|
torch::Tensor jx_cpu = jx.to(torch::kCPU);
|
|
torch::Tensor inds = torch::argsort(ix_cpu);
|
|
|
|
long *ix_data = ix_cpu.data_ptr<long>();
|
|
long *jx_data = jx_cpu.data_ptr<long>();
|
|
long *kx_data = inds.data_ptr<long>();
|
|
|
|
int count = jx.size(0);
|
|
std::vector<int> cols;
|
|
|
|
torch::Tensor ptrs_cpu = torch::zeros({count + 1}, torch::TensorOptions().dtype(torch::kInt64));
|
|
|
|
long *ptrs_data = ptrs_cpu.data_ptr<long>();
|
|
ptrs_data[0] = 0;
|
|
|
|
int i = 0;
|
|
for (int j = 0; j < count; j++) {
|
|
while (i < ix.size(0) && ix_data[kx_data[i]] <= jx_data[j]) {
|
|
if (ix_data[kx_data[i]] == jx_data[j]) cols.push_back(kx_data[i]);
|
|
i++;
|
|
}
|
|
ptrs_data[j + 1] = cols.size();
|
|
}
|
|
|
|
torch::Tensor idxs_cpu = torch::zeros({long(cols.size())}, torch::TensorOptions().dtype(torch::kInt64));
|
|
|
|
long *idxs_data = idxs_cpu.data_ptr<long>();
|
|
|
|
for (int i = 0; i < cols.size(); i++) {
|
|
idxs_data[i] = cols[i];
|
|
}
|
|
|
|
torch::Tensor ptrs = ptrs_cpu.to(torch::kCUDA);
|
|
torch::Tensor idxs = idxs_cpu.to(torch::kCUDA);
|
|
|
|
torch::Tensor out =
|
|
torch::zeros({jx.size(0), data.size(1)}, torch::TensorOptions().dtype(torch::kFloat32).device(torch::kCUDA));
|
|
|
|
accum_kernel<<<count, THREADS>>>(data.packed_accessor32<float, 2, torch::RestrictPtrTraits>(),
|
|
ptrs.packed_accessor32<long, 1, torch::RestrictPtrTraits>(),
|
|
idxs.packed_accessor32<long, 1, torch::RestrictPtrTraits>(),
|
|
out.packed_accessor32<float, 2, torch::RestrictPtrTraits>());
|
|
|
|
return out;
|
|
}
|
|
|
|
__global__ void EEt6x6_kernel(const torch::PackedTensorAccessor32<float, 3, torch::RestrictPtrTraits> E,
|
|
const torch::PackedTensorAccessor32<float, 2, torch::RestrictPtrTraits> Q,
|
|
const torch::PackedTensorAccessor32<long, 2, torch::RestrictPtrTraits> idx,
|
|
torch::PackedTensorAccessor32<float, 3, torch::RestrictPtrTraits> S) {
|
|
// indicices
|
|
const int ix = idx[blockIdx.x][0];
|
|
const int jx = idx[blockIdx.x][1];
|
|
const int kx = idx[blockIdx.x][2];
|
|
|
|
const int D = E.size(2);
|
|
|
|
float dS[6][6];
|
|
float ei[6];
|
|
float ej[6];
|
|
|
|
for (int i = 0; i < 6; i++) {
|
|
for (int j = 0; j < 6; j++) {
|
|
dS[i][j] = 0;
|
|
}
|
|
}
|
|
|
|
for (int k = threadIdx.x; k < D; k += blockDim.x) {
|
|
const float q = Q[kx][k];
|
|
|
|
// coalesced memory read
|
|
for (int n = 0; n < 6; n++) {
|
|
ei[n] = E[ix][n][k] * q;
|
|
ej[n] = E[jx][n][k];
|
|
}
|
|
|
|
// block EEt
|
|
for (int n = 0; n < 6; n++) {
|
|
for (int m = 0; m < 6; m++) {
|
|
dS[n][m] += ei[n] * ej[m];
|
|
}
|
|
}
|
|
}
|
|
|
|
__syncthreads();
|
|
__shared__ float sdata[THREADS];
|
|
|
|
for (int n = 0; n < 6; n++) {
|
|
for (int m = 0; m < 6; m++) {
|
|
sdata[threadIdx.x] = dS[n][m];
|
|
|
|
blockReduce(sdata);
|
|
|
|
if (threadIdx.x == 0) {
|
|
S[blockIdx.x][n][m] = sdata[0];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
__global__ void Ev6x1_kernel(const torch::PackedTensorAccessor32<float, 3, torch::RestrictPtrTraits> E,
|
|
const torch::PackedTensorAccessor32<float, 2, torch::RestrictPtrTraits> Q,
|
|
const torch::PackedTensorAccessor32<float, 2, torch::RestrictPtrTraits> w,
|
|
const torch::PackedTensorAccessor32<long, 2, torch::RestrictPtrTraits> idx,
|
|
torch::PackedTensorAccessor32<float, 2, torch::RestrictPtrTraits> v) {
|
|
const int D = E.size(2);
|
|
const int kx = idx[blockIdx.x][0];
|
|
|
|
float b[6];
|
|
for (int n = 0; n < 6; n++) {
|
|
b[n] = 0.0;
|
|
}
|
|
|
|
for (int k = threadIdx.x; k < D; k += blockDim.x) {
|
|
const float q_w = Q[kx][k] * w[kx][k];
|
|
|
|
for (int n = 0; n < 6; n++) {
|
|
b[n] += q_w * E[blockIdx.x][n][k];
|
|
}
|
|
}
|
|
|
|
__syncthreads();
|
|
__shared__ float sdata[THREADS];
|
|
|
|
for (int n = 0; n < 6; n++) {
|
|
sdata[threadIdx.x] = b[n];
|
|
blockReduce(sdata);
|
|
|
|
if (threadIdx.x == 0) {
|
|
v[blockIdx.x][n] += sdata[0];
|
|
}
|
|
}
|
|
}
|
|
|
|
__global__ void EvT6x1_kernel(const torch::PackedTensorAccessor32<float, 3, torch::RestrictPtrTraits> E,
|
|
const torch::PackedTensorAccessor32<float, 2, torch::RestrictPtrTraits> x,
|
|
const torch::PackedTensorAccessor32<long, 1, torch::RestrictPtrTraits> idx,
|
|
torch::PackedTensorAccessor32<float, 2, torch::RestrictPtrTraits> w) {
|
|
const int D = E.size(2);
|
|
const int ix = idx[blockIdx.x];
|
|
|
|
if (idx[blockIdx.x] <= 0 || idx[blockIdx.x] >= x.size(0)) return;
|
|
|
|
for (int k = threadIdx.x; k < D; k += blockDim.x) {
|
|
float dw = 0;
|
|
for (int n = 0; n < 6; n++) {
|
|
dw += E[blockIdx.x][n][k] * x[ix][n];
|
|
}
|
|
w[blockIdx.x][k] = dw;
|
|
}
|
|
}
|
|
|
|
class SparseBlock {
|
|
public:
|
|
Eigen::SparseMatrix<double> A;
|
|
Eigen::VectorX<double> b;
|
|
|
|
SparseBlock(int N, int M) : N(N), M(M) {
|
|
A = Eigen::SparseMatrix<double>(N * M, N * M);
|
|
b = Eigen::VectorXd::Zero(N * M);
|
|
}
|
|
|
|
SparseBlock(Eigen::SparseMatrix<double> const &A, Eigen::VectorX<double> const &b, int N, int M)
|
|
: A(A), b(b), N(N), M(M) {}
|
|
|
|
void update_lhs(torch::Tensor As, torch::Tensor ii, torch::Tensor jj) {
|
|
auto As_cpu = As.to(torch::kCPU).to(torch::kFloat64);
|
|
auto ii_cpu = ii.to(torch::kCPU).to(torch::kInt64);
|
|
auto jj_cpu = jj.to(torch::kCPU).to(torch::kInt64);
|
|
|
|
auto As_acc = As_cpu.accessor<double, 3>();
|
|
auto ii_acc = ii_cpu.accessor<long, 1>();
|
|
auto jj_acc = jj_cpu.accessor<long, 1>();
|
|
|
|
std::vector<T> tripletList;
|
|
for (int n = 0; n < ii.size(0); n++) {
|
|
const int i = ii_acc[n];
|
|
const int j = jj_acc[n];
|
|
|
|
if (i >= 0 && j >= 0) {
|
|
for (int k = 0; k < M; k++) {
|
|
for (int l = 0; l < M; l++) {
|
|
double val = As_acc[n][k][l];
|
|
tripletList.push_back(T(M * i + k, M * j + l, val));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
A.setFromTriplets(tripletList.begin(), tripletList.end());
|
|
}
|
|
|
|
void update_rhs(torch::Tensor bs, torch::Tensor ii) {
|
|
auto bs_cpu = bs.to(torch::kCPU).to(torch::kFloat64);
|
|
auto ii_cpu = ii.to(torch::kCPU).to(torch::kInt64);
|
|
|
|
auto bs_acc = bs_cpu.accessor<double, 2>();
|
|
auto ii_acc = ii_cpu.accessor<long, 1>();
|
|
|
|
for (int n = 0; n < ii.size(0); n++) {
|
|
const int i = ii_acc[n];
|
|
if (i >= 0) {
|
|
for (int j = 0; j < M; j++) {
|
|
b(i * M + j) += bs_acc[n][j];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SparseBlock operator-(const SparseBlock &S) { return SparseBlock(A - S.A, b - S.b, N, M); }
|
|
|
|
std::tuple<torch::Tensor, torch::Tensor> get_dense() {
|
|
Eigen::MatrixXd Ad = Eigen::MatrixXd(A);
|
|
|
|
torch::Tensor H = torch::from_blob(Ad.data(), {N * M, N * M}, torch::TensorOptions().dtype(torch::kFloat64))
|
|
.to(torch::kCUDA)
|
|
.to(torch::kFloat32);
|
|
|
|
torch::Tensor v = torch::from_blob(b.data(), {N * M, 1}, torch::TensorOptions().dtype(torch::kFloat64))
|
|
.to(torch::kCUDA)
|
|
.to(torch::kFloat32);
|
|
|
|
return std::make_tuple(H, v);
|
|
}
|
|
|
|
torch::Tensor solve(const float lm = 0.0001, const float ep = 0.1) {
|
|
torch::Tensor dx;
|
|
|
|
Eigen::SparseMatrix<double> L(A);
|
|
L.diagonal().array() += ep + lm * L.diagonal().array();
|
|
|
|
Eigen::SimplicialLLT<Eigen::SparseMatrix<double>> solver;
|
|
solver.compute(L);
|
|
|
|
if (solver.info() == Eigen::Success) {
|
|
Eigen::VectorXd x = solver.solve(b);
|
|
dx = torch::from_blob(x.data(), {N, M}, torch::TensorOptions().dtype(torch::kFloat64))
|
|
.to(torch::kCUDA)
|
|
.to(torch::kFloat32);
|
|
} else {
|
|
dx = torch::zeros({N, M}, torch::TensorOptions().device(torch::kCUDA).dtype(torch::kFloat32));
|
|
}
|
|
|
|
return dx;
|
|
}
|
|
|
|
private:
|
|
const int N;
|
|
const int M;
|
|
};
|
|
|
|
SparseBlock schur_block(torch::Tensor E, torch::Tensor Q, torch::Tensor w, torch::Tensor ii, torch::Tensor jj,
|
|
torch::Tensor kk, const int t0, const int t1) {
|
|
torch::Tensor ii_cpu = ii.to(torch::kCPU);
|
|
torch::Tensor jj_cpu = jj.to(torch::kCPU);
|
|
torch::Tensor kk_cpu = kk.to(torch::kCPU);
|
|
|
|
const int P = t1 - t0;
|
|
const long *ii_data = ii_cpu.data_ptr<long>();
|
|
const long *jj_data = jj_cpu.data_ptr<long>();
|
|
const long *kk_data = kk_cpu.data_ptr<long>();
|
|
|
|
std::vector<std::vector<long>> graph(P);
|
|
std::vector<std::vector<long>> index(P);
|
|
|
|
for (int n = 0; n < ii_cpu.size(0); n++) {
|
|
const int j = jj_data[n];
|
|
const int k = kk_data[n];
|
|
|
|
if (j >= t0 && j <= t1) {
|
|
const int t = j - t0;
|
|
graph[t].push_back(k);
|
|
index[t].push_back(n);
|
|
}
|
|
}
|
|
|
|
std::vector<long> ii_list, jj_list, idx, jdx;
|
|
|
|
for (int i = 0; i < P; i++) {
|
|
for (int j = 0; j < P; j++) {
|
|
for (int k = 0; k < graph[i].size(); k++) {
|
|
for (int l = 0; l < graph[j].size(); l++) {
|
|
if (graph[i][k] == graph[j][l]) {
|
|
ii_list.push_back(i);
|
|
jj_list.push_back(j);
|
|
|
|
idx.push_back(index[i][k]);
|
|
idx.push_back(index[j][l]);
|
|
idx.push_back(graph[i][k]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
torch::Tensor ix_cuda =
|
|
torch::from_blob(idx.data(), {long(idx.size())}, torch::TensorOptions().dtype(torch::kInt64))
|
|
.to(torch::kCUDA)
|
|
.view({-1, 3});
|
|
|
|
torch::Tensor jx_cuda = torch::stack({kk_cpu}, -1).to(torch::kCUDA).to(torch::kInt64);
|
|
|
|
torch::Tensor ii2_cpu =
|
|
torch::from_blob(ii_list.data(), {long(ii_list.size())}, torch::TensorOptions().dtype(torch::kInt64))
|
|
.view({-1});
|
|
|
|
torch::Tensor jj2_cpu =
|
|
torch::from_blob(jj_list.data(), {long(jj_list.size())}, torch::TensorOptions().dtype(torch::kInt64))
|
|
.view({-1});
|
|
|
|
torch::Tensor S =
|
|
torch::zeros({ix_cuda.size(0), 6, 6}, torch::TensorOptions().dtype(torch::kFloat32).device(torch::kCUDA));
|
|
|
|
torch::Tensor v =
|
|
torch::zeros({jx_cuda.size(0), 6}, torch::TensorOptions().dtype(torch::kFloat32).device(torch::kCUDA));
|
|
|
|
EEt6x6_kernel<<<ix_cuda.size(0), THREADS>>>(E.packed_accessor32<float, 3, torch::RestrictPtrTraits>(),
|
|
Q.packed_accessor32<float, 2, torch::RestrictPtrTraits>(),
|
|
ix_cuda.packed_accessor32<long, 2, torch::RestrictPtrTraits>(),
|
|
S.packed_accessor32<float, 3, torch::RestrictPtrTraits>());
|
|
|
|
Ev6x1_kernel<<<jx_cuda.size(0), THREADS>>>(E.packed_accessor32<float, 3, torch::RestrictPtrTraits>(),
|
|
Q.packed_accessor32<float, 2, torch::RestrictPtrTraits>(),
|
|
w.packed_accessor32<float, 2, torch::RestrictPtrTraits>(),
|
|
jx_cuda.packed_accessor32<long, 2, torch::RestrictPtrTraits>(),
|
|
v.packed_accessor32<float, 2, torch::RestrictPtrTraits>());
|
|
|
|
// schur block
|
|
// LHS = S = E Q E^T; RHS = v = E (Q) w
|
|
SparseBlock A(P, 6);
|
|
A.update_lhs(S, ii2_cpu, jj2_cpu);
|
|
A.update_rhs(v, jj_cpu - t0);
|
|
|
|
return A;
|
|
}
|
|
|
|
std::vector<torch::Tensor> ba_cuda(torch::Tensor poses, torch::Tensor disps, torch::Tensor intrinsics,
|
|
torch::Tensor disps_sens, torch::Tensor targets, torch::Tensor weights,
|
|
torch::Tensor eta, torch::Tensor ii, torch::Tensor jj, const int t0, const int t1,
|
|
const int iterations, const float lm, const float ep, const bool motion_only) {
|
|
CHECK_INPUT(targets);
|
|
CHECK_INPUT(weights);
|
|
CHECK_INPUT(poses);
|
|
CHECK_INPUT(disps);
|
|
CHECK_INPUT(intrinsics);
|
|
CHECK_INPUT(disps_sens);
|
|
CHECK_INPUT(ii);
|
|
CHECK_INPUT(jj);
|
|
|
|
auto opts = poses.options();
|
|
const int num = ii.size(0);
|
|
const int ht = disps.size(1);
|
|
const int wd = disps.size(2);
|
|
|
|
torch::Tensor ts = torch::arange(t0, t1).to(torch::kCUDA);
|
|
torch::Tensor ii_exp = torch::cat({ts, ii}, 0);
|
|
torch::Tensor jj_exp = torch::cat({ts, jj}, 0);
|
|
|
|
std::tuple<torch::Tensor, torch::Tensor> kuniq = torch::_unique(ii_exp, true, true);
|
|
|
|
torch::Tensor kx = std::get<0>(kuniq);
|
|
torch::Tensor kk_exp = std::get<1>(kuniq);
|
|
|
|
torch::Tensor dx;
|
|
torch::Tensor dz;
|
|
|
|
// jtj of pose (LHS and RHS)
|
|
torch::Tensor Hs = torch::zeros({4, num, 6, 6}, opts); // Real JTJ = Hs + Hs.T.
|
|
torch::Tensor vs = torch::zeros({2, num, 6}, opts); // This is the real vs.
|
|
// Cross-jtj-term of pose and depth. Actual JTJ_ii = sum_j E_ij + E_ii, JTJ_ij = E_ij.
|
|
torch::Tensor Eii = torch::zeros({num, 6, ht * wd}, opts);
|
|
torch::Tensor Eij = torch::zeros({num, 6, ht * wd}, opts);
|
|
// jtj of depth
|
|
torch::Tensor Cii = torch::zeros({num, ht * wd}, opts);
|
|
// RHS of depth
|
|
torch::Tensor wi = torch::zeros({num, ht * wd}, opts);
|
|
|
|
for (int itr = 0; itr < iterations; itr++) {
|
|
projective_transform_kernel<<<num, THREADS>>>(
|
|
// Inputs
|
|
targets.packed_accessor32<float, 4, torch::RestrictPtrTraits>(),
|
|
weights.packed_accessor32<float, 4, torch::RestrictPtrTraits>(),
|
|
poses.packed_accessor32<float, 2, torch::RestrictPtrTraits>(),
|
|
disps.packed_accessor32<float, 3, torch::RestrictPtrTraits>(),
|
|
intrinsics.packed_accessor32<float, 1, torch::RestrictPtrTraits>(),
|
|
ii.packed_accessor32<long, 1, torch::RestrictPtrTraits>(),
|
|
jj.packed_accessor32<long, 1, torch::RestrictPtrTraits>(),
|
|
// Outputs
|
|
Hs.packed_accessor32<float, 4, torch::RestrictPtrTraits>(),
|
|
vs.packed_accessor32<float, 3, torch::RestrictPtrTraits>(),
|
|
Eii.packed_accessor32<float, 3, torch::RestrictPtrTraits>(),
|
|
Eij.packed_accessor32<float, 3, torch::RestrictPtrTraits>(),
|
|
Cii.packed_accessor32<float, 2, torch::RestrictPtrTraits>(),
|
|
wi.packed_accessor32<float, 2, torch::RestrictPtrTraits>());
|
|
|
|
// pose x pose block
|
|
SparseBlock A(t1 - t0, 6);
|
|
|
|
A.update_lhs(Hs.reshape({-1, 6, 6}), torch::cat({ii, ii, jj, jj}) - t0, torch::cat({ii, jj, ii, jj}) - t0);
|
|
|
|
A.update_rhs(vs.reshape({-1, 6}), torch::cat({ii, jj}) - t0);
|
|
|
|
if (motion_only) {
|
|
dx = A.solve(lm, ep);
|
|
|
|
// update poses
|
|
pose_retr_kernel<<<1, THREADS>>>(poses.packed_accessor32<float, 2, torch::RestrictPtrTraits>(),
|
|
dx.packed_accessor32<float, 2, torch::RestrictPtrTraits>(), t0, t1);
|
|
}
|
|
|
|
else {
|
|
// add depth residual if there are depth sensor measurements
|
|
const float alpha = 0.05;
|
|
// m = (disps_sens[kx] > 0).float().view(-1, ht*wd)
|
|
torch::Tensor m = (disps_sens.index({kx, "..."}) > 0)
|
|
.to(torch::TensorOptions().dtype(torch::kFloat32))
|
|
.view({-1, ht * wd});
|
|
// LHS: C = C[kx] + alpha (if disps_sens exists) / eta (otherwise)
|
|
torch::Tensor C = accum_cuda(Cii, ii, kx) + m * alpha + (1 - m) * eta.view({-1, ht * wd});
|
|
// RHS: w = w[kx] - alpha * (disps - disps_sens) (residual)
|
|
torch::Tensor w =
|
|
accum_cuda(wi, ii, kx) -
|
|
m * alpha * (disps.index({kx, "..."}) - disps_sens.index({kx, "..."})).view({-1, ht * wd});
|
|
torch::Tensor Q = 1.0 / C;
|
|
|
|
// Compute Real-E (JTJ of pose-depth)
|
|
torch::Tensor Ei = accum_cuda(Eii.view({num, 6 * ht * wd}), ii, ts).view({t1 - t0, 6, ht * wd});
|
|
torch::Tensor E = torch::cat({Ei, Eij}, 0);
|
|
|
|
// (A - E Q E^T) dx = v - E (Q) w
|
|
SparseBlock S = schur_block(E, Q, w, ii_exp, jj_exp, kk_exp, t0, t1);
|
|
dx = (A - S).solve(lm, ep);
|
|
|
|
torch::Tensor ix = jj_exp - t0;
|
|
|
|
// dw = E^T dx
|
|
torch::Tensor dw = torch::zeros({ix.size(0), ht * wd}, opts);
|
|
EvT6x1_kernel<<<ix.size(0), THREADS>>>(E.packed_accessor32<float, 3, torch::RestrictPtrTraits>(),
|
|
dx.packed_accessor32<float, 2, torch::RestrictPtrTraits>(),
|
|
ix.packed_accessor32<long, 1, torch::RestrictPtrTraits>(),
|
|
dw.packed_accessor32<float, 2, torch::RestrictPtrTraits>());
|
|
|
|
// dz = (1.0/C) * (w - E^T dx)
|
|
dz = Q * (w - accum_cuda(dw, ii_exp, kx));
|
|
|
|
// update poses
|
|
pose_retr_kernel<<<1, THREADS>>>(poses.packed_accessor32<float, 2, torch::RestrictPtrTraits>(),
|
|
dx.packed_accessor32<float, 2, torch::RestrictPtrTraits>(), t0, t1);
|
|
|
|
// update disparity maps
|
|
disp_retr_kernel<<<kx.size(0), THREADS>>>(disps.packed_accessor32<float, 3, torch::RestrictPtrTraits>(),
|
|
dz.packed_accessor32<float, 2, torch::RestrictPtrTraits>(),
|
|
kx.packed_accessor32<long, 1, torch::RestrictPtrTraits>());
|
|
}
|
|
}
|
|
|
|
return {dx, dz};
|
|
}
|
|
|
|
torch::Tensor frame_distance_cuda(torch::Tensor poses, torch::Tensor disps, torch::Tensor intrinsics, torch::Tensor pi,
|
|
torch::Tensor pj, torch::Tensor qi, torch::Tensor qj, torch::Tensor di,
|
|
const float beta) {
|
|
CHECK_INPUT(poses);
|
|
CHECK_INPUT(disps);
|
|
CHECK_INPUT(intrinsics);
|
|
CHECK_INPUT(pi);
|
|
CHECK_INPUT(pj);
|
|
CHECK_INPUT(qi);
|
|
CHECK_INPUT(qj);
|
|
CHECK_INPUT(di);
|
|
|
|
auto opts = poses.options();
|
|
const int num = pi.size(0);
|
|
|
|
torch::Tensor dist = torch::zeros({num}, opts);
|
|
|
|
frame_distance_kernel<<<num, THREADS>>>(poses.packed_accessor32<float, 2, torch::RestrictPtrTraits>(),
|
|
disps.packed_accessor32<float, 3, torch::RestrictPtrTraits>(),
|
|
intrinsics.packed_accessor32<float, 2, torch::RestrictPtrTraits>(),
|
|
pi.packed_accessor32<long, 1, torch::RestrictPtrTraits>(),
|
|
pj.packed_accessor32<long, 1, torch::RestrictPtrTraits>(),
|
|
qi.packed_accessor32<long, 1, torch::RestrictPtrTraits>(),
|
|
qj.packed_accessor32<long, 1, torch::RestrictPtrTraits>(),
|
|
di.packed_accessor32<long, 1, torch::RestrictPtrTraits>(),
|
|
dist.packed_accessor32<float, 1, torch::RestrictPtrTraits>(), beta);
|
|
|
|
return dist;
|
|
}
|
|
|
|
std::vector<torch::Tensor> projmap_cuda(torch::Tensor poses, torch::Tensor disps, torch::Tensor intrinsics,
|
|
torch::Tensor ii, torch::Tensor jj) {
|
|
CHECK_INPUT(poses);
|
|
CHECK_INPUT(disps);
|
|
CHECK_INPUT(intrinsics);
|
|
CHECK_INPUT(ii);
|
|
CHECK_INPUT(jj);
|
|
auto opts = poses.options();
|
|
const int num = ii.size(0);
|
|
const int ht = disps.size(1);
|
|
const int wd = disps.size(2);
|
|
|
|
torch::Tensor coords = torch::zeros({num, ht, wd, 3}, opts);
|
|
torch::Tensor valid = torch::zeros({num, ht, wd, 1}, opts);
|
|
|
|
projmap_kernel<<<num, THREADS>>>(poses.packed_accessor32<float, 2, torch::RestrictPtrTraits>(),
|
|
disps.packed_accessor32<float, 3, torch::RestrictPtrTraits>(),
|
|
intrinsics.packed_accessor32<float, 1, torch::RestrictPtrTraits>(),
|
|
ii.packed_accessor32<long, 1, torch::RestrictPtrTraits>(),
|
|
jj.packed_accessor32<long, 1, torch::RestrictPtrTraits>(),
|
|
coords.packed_accessor32<float, 4, torch::RestrictPtrTraits>(),
|
|
valid.packed_accessor32<float, 4, torch::RestrictPtrTraits>());
|
|
|
|
return {coords, valid};
|
|
}
|
|
|
|
torch::Tensor depth_filter_cuda(torch::Tensor poses, torch::Tensor disps, torch::Tensor intrinsics, torch::Tensor ix,
|
|
torch::Tensor thresh) {
|
|
CHECK_INPUT(poses);
|
|
CHECK_INPUT(disps);
|
|
CHECK_INPUT(intrinsics);
|
|
CHECK_INPUT(ix);
|
|
CHECK_INPUT(thresh);
|
|
|
|
const int num = ix.size(0);
|
|
const int ht = disps.size(1);
|
|
const int wd = disps.size(2);
|
|
|
|
torch::Tensor counter = torch::zeros({num, ht, wd}, disps.options());
|
|
|
|
dim3 blocks(num, 6, NUM_BLOCKS(ht * wd));
|
|
|
|
depth_filter_kernel<<<blocks, THREADS>>>(poses.packed_accessor32<float, 2, torch::RestrictPtrTraits>(),
|
|
disps.packed_accessor32<float, 3, torch::RestrictPtrTraits>(),
|
|
intrinsics.packed_accessor32<float, 1, torch::RestrictPtrTraits>(),
|
|
ix.packed_accessor32<long, 1, torch::RestrictPtrTraits>(),
|
|
thresh.packed_accessor32<float, 1, torch::RestrictPtrTraits>(),
|
|
counter.packed_accessor32<float, 3, torch::RestrictPtrTraits>());
|
|
|
|
return counter;
|
|
}
|
|
|
|
torch::Tensor iproj_cuda(torch::Tensor poses, torch::Tensor disps, torch::Tensor intrinsics) {
|
|
CHECK_INPUT(poses);
|
|
CHECK_INPUT(disps);
|
|
CHECK_INPUT(intrinsics);
|
|
const int nm = disps.size(0);
|
|
const int ht = disps.size(1);
|
|
const int wd = disps.size(2);
|
|
|
|
auto opts = disps.options();
|
|
torch::Tensor points = torch::zeros({nm, ht, wd, 3}, opts);
|
|
|
|
dim3 blocks(nm, NUM_BLOCKS(ht * wd));
|
|
|
|
iproj_kernel<<<blocks, THREADS>>>(poses.packed_accessor32<float, 2, torch::RestrictPtrTraits>(),
|
|
disps.packed_accessor32<float, 3, torch::RestrictPtrTraits>(),
|
|
intrinsics.packed_accessor32<float, 1, torch::RestrictPtrTraits>(),
|
|
points.packed_accessor32<float, 4, torch::RestrictPtrTraits>());
|
|
|
|
return points;
|
|
}
|
|
|
|
} // namespace slam_ext |