Spaces:
Running
Running
init
Browse files- lidm/eval/README.md +0 -95
- lidm/eval/__init__.py +0 -62
- lidm/eval/compile.sh +0 -9
- lidm/eval/eval_utils.py +0 -138
- lidm/eval/fid_score.py +0 -191
- lidm/eval/metric_utils.py +0 -458
- lidm/eval/models/__init__.py +0 -0
- lidm/eval/models/minkowskinet/__init__.py +0 -0
- lidm/eval/models/minkowskinet/model.py +0 -141
- lidm/eval/models/rangenet/__init__.py +0 -0
- lidm/eval/models/rangenet/model.py +0 -372
- lidm/eval/models/spvcnn/__init__.py +0 -0
- lidm/eval/models/spvcnn/model.py +0 -179
- lidm/eval/models/ts/__init__.py +0 -0
- lidm/eval/models/ts/basic_blocks.py +0 -79
- lidm/eval/models/ts/utils.py +0 -90
- lidm/eval/modules/__init__.py +0 -0
- lidm/eval/modules/chamfer2D/__init__.py +0 -0
- lidm/eval/modules/chamfer2D/chamfer2D.cu +0 -182
- lidm/eval/modules/chamfer2D/chamfer_cuda.cpp +0 -33
- lidm/eval/modules/chamfer2D/dist_chamfer_2D.py +0 -84
- lidm/eval/modules/chamfer2D/setup.py +0 -14
- lidm/eval/modules/chamfer3D/__init__.py +0 -0
- lidm/eval/modules/chamfer3D/chamfer3D.cu +0 -196
- lidm/eval/modules/chamfer3D/chamfer_cuda.cpp +0 -33
- lidm/eval/modules/chamfer3D/dist_chamfer_3D.py +0 -76
- lidm/eval/modules/chamfer3D/setup.py +0 -14
- lidm/eval/modules/emd/__init__.py +0 -0
- lidm/eval/modules/emd/emd.cpp +0 -31
- lidm/eval/modules/emd/emd_cuda.cu +0 -316
- lidm/eval/modules/emd/emd_module.py +0 -112
- lidm/eval/modules/emd/setup.py +0 -14
- lidm/models/diffusion/ddim.py +1 -1
- lidm/models/diffusion/plms.py +1 -1
- sample_cond.py +1 -1
lidm/eval/README.md
DELETED
@@ -1,95 +0,0 @@
|
|
1 |
-
# Evaluation Toolbox for LiDAR Generation
|
2 |
-
|
3 |
-
This directory is a **self-contained**, **memory-friendly** and mostly **CUDA-accelerated** toolbox of multiple evaluation metrics for LiDAR generative models, including:
|
4 |
-
* Perceptual metrics (our proposed):
|
5 |
-
* Fréchet Range Image Distance (**FRID**)
|
6 |
-
* Fréchet Sparse Volume Distance (**FSVD**)
|
7 |
-
* Fréchet Point-based Volume Distance (**FPVD**)
|
8 |
-
* Statistical metrics (proposed in [Learning Representations and Generative Models for 3D Point Clouds](https://arxiv.org/abs/1707.02392)):
|
9 |
-
* Minimum Matching Distance (**MMD**)
|
10 |
-
* Jensen-Shannon Divergence (**JSD**)
|
11 |
-
* Statistical pairwise metrics (for reconstruction only):
|
12 |
-
* Chamfer Distance (**CD**)
|
13 |
-
* Earth Mover's Distance (**EMD**)
|
14 |
-
|
15 |
-
## Citation
|
16 |
-
|
17 |
-
If you find this project useful in your research, please consider citing:
|
18 |
-
```
|
19 |
-
@article{ran2024towards,
|
20 |
-
title={Towards Realistic Scene Generation with LiDAR Diffusion Models},
|
21 |
-
author={Ran, Haoxi and Guizilini, Vitor and Wang, Yue},
|
22 |
-
journal={arXiv preprint arXiv:2404.00815},
|
23 |
-
year={2024}
|
24 |
-
}
|
25 |
-
```
|
26 |
-
|
27 |
-
|
28 |
-
## Dependencies
|
29 |
-
|
30 |
-
### Basic (install through **pip**):
|
31 |
-
* scipy
|
32 |
-
* numpy
|
33 |
-
* torch
|
34 |
-
* pyyaml
|
35 |
-
|
36 |
-
### Required by FSVD and FPVD:
|
37 |
-
* [Torchsparse v1.4.0](https://github.com/mit-han-lab/torchsparse/tree/v1.4.0) (pip install git+https://github.com/mit-han-lab/torchsparse.git@v1.4.0)
|
38 |
-
* [Google Sparse Hash library](https://github.com/sparsehash/sparsehash) (apt-get install libsparsehash-dev **or** compile locally and update variable CPLUS_INCLUDE_PATH with directory path)
|
39 |
-
|
40 |
-
|
41 |
-
## Model Zoo
|
42 |
-
|
43 |
-
To evaluate with perceptual metrics on different types of LiDAR data, you can download all models through:
|
44 |
-
* this [google drive link](https://drive.google.com/file/d/1Ml4p4_nMlwLkSp7JB528GJv2_HxO8v1i/view?usp=drive_link) in the .zip file
|
45 |
-
|
46 |
-
or
|
47 |
-
* the **full directory** of one specific model:
|
48 |
-
|
49 |
-
### 64-beam LiDAR (trained on [SemanticKITTI](http://semantic-kitti.org/dataset.html)):
|
50 |
-
|
51 |
-
| Metric | Model | Arch | Link | Code | Comments |
|
52 |
-
|:------:|:-------------------------------------------------------------------------------------------:|:-----------------------:|:-------------------------------------------------------------------------------------------------------:|:-----------------------------------------------------------------|---------------------------------------------------------------------------|
|
53 |
-
| FRID | [RangeNet++](https://www.ipb.uni-bonn.de/wp-content/papercite-data/pdf/milioto2019iros.pdf) | DarkNet21-based UNet | [Google Drive](https://drive.google.com/drive/folders/1ZS8KOoxB9hjB6kwKbH5Zfc8O5qJlKsbl?usp=drive_link) | [./models/rangenet/model.py](./models/rangenet/model.py) | range image input (our trained model without the need of remission input) |
|
54 |
-
| FSVD | [MinkowskiNet](https://arxiv.org/abs/1904.08755) | Sparse UNet | [Google Drive](https://drive.google.com/drive/folders/1zN12ZEvjIvo4PCjAsncgC22yvtRrCCMe?usp=drive_link) | [./models/minkowskinet/model.py](./models/minkowskinet/model.py) | point cloud input |
|
55 |
-
| FPVD | [SPVCNN](https://arxiv.org/abs/2007.16100) | Point-Voxel Sparse UNet | [Google Drive](https://drive.google.com/drive/folders/1oEm3qpxfGetiVAfXIvecawEiFqW79M6B?usp=drive_link) | [./models/spvcnn/model.py](./models/spvcnn/model.py) | point cloud input |
|
56 |
-
|
57 |
-
|
58 |
-
### 32-beam LiDAR (trained on [nuScenes](https://www.nuscenes.org/nuscenes)):
|
59 |
-
|
60 |
-
| Metric | Model | Arch | Link | Code | Comments |
|
61 |
-
|:------:|:------------------------------------------------:|:-----------------------:|:-------------------------------------------------------------------------------------------------------:|:-----------------------------------------------------------------|-------------------|
|
62 |
-
| FSVD | [MinkowskiNet](https://arxiv.org/abs/1904.08755) | Sparse UNet | [Google Drive](https://drive.google.com/drive/folders/1oZIS9FlklCQ6dlh3TZ8Junir7QwgT-Me?usp=drive_link) | [./models/minkowskinet/model.py](./models/minkowskinet/model.py) | point cloud input |
|
63 |
-
| FPVD | [SPVCNN](https://arxiv.org/abs/2007.16100) | Point-Voxel Sparse UNet | [Google Drive](https://drive.google.com/drive/folders/1F69RbprAoT6MOJ7iI0KHjxuq-tbeqGiR?usp=drive_link) | [./models/spvcnn/model.py](./models/spvcnn/model.py) | point cloud input |
|
64 |
-
|
65 |
-
|
66 |
-
## Usage
|
67 |
-
|
68 |
-
1. Place the unzipped `pretrained_weights` folder under the root python directory **or** modify the `DEFAULT_ROOT` variable in the `__init__.py`.
|
69 |
-
2. Prepare input data, including the synthesized samples and the reference dataset. **Note**: The reference data should be the **point clouds projected back from range images** instead of raw point clouds.
|
70 |
-
3. Specify the data type (`32` or `64`) and the metrics to evaluate. Options: `mmd`, `jsd`, `frid`, `fsvd`, `fpvd`, `cd`, `emd`.
|
71 |
-
4. (Optional) If you want to compute `frid`, `fsvd` or `fpvd` metric, adjust the corresponding batch size through the `MODAL2BATCHSIZE` in file `__init__.py` according to your max GPU memory (default: ~24GB).
|
72 |
-
5. Start evaluation and all results will print out!
|
73 |
-
|
74 |
-
### Example:
|
75 |
-
|
76 |
-
```
|
77 |
-
from .eval_utils import evaluate
|
78 |
-
|
79 |
-
data = '64' # specify data type to evaluate
|
80 |
-
metrics = ['mmd', 'jsd', 'frid', 'fsvd', 'fpvd'] # specify metrics to evaluate
|
81 |
-
|
82 |
-
# list of np.float32 array
|
83 |
-
# shape of each array: (#points, #dim=3), #dim: xyz coordinate (NOTE: no need to input remission)
|
84 |
-
reference = ...
|
85 |
-
samples = ...
|
86 |
-
|
87 |
-
evaluate(reference, samples, metrics, data)
|
88 |
-
```
|
89 |
-
|
90 |
-
|
91 |
-
## Acknowledgement
|
92 |
-
|
93 |
-
- The implementation of MinkowskiNet and SPVCNN is borrowed from [2DPASS](https://github.com/yanx27/2DPASS).
|
94 |
-
- The implementation of RangeNet++ is borrowed from [the official RangeNet++ codebase](https://github.com/PRBonn/lidar-bonnetal).
|
95 |
-
- The implementation of Chamfer Distance is adapted from [CD Pytorch Implementation](https://github.com/ThibaultGROUEIX/ChamferDistancePytorch) and Earth Mover's Distance from [MSN official repo](https://github.com/Colin97/MSN-Point-Cloud-Completion).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lidm/eval/__init__.py
DELETED
@@ -1,62 +0,0 @@
|
|
1 |
-
"""
|
2 |
-
@Author: Haoxi Ran
|
3 |
-
@Date: 01/03/2024
|
4 |
-
@Citation: Towards Realistic Scene Generation with LiDAR Diffusion Models
|
5 |
-
|
6 |
-
"""
|
7 |
-
|
8 |
-
import os
|
9 |
-
|
10 |
-
import torch
|
11 |
-
import yaml
|
12 |
-
|
13 |
-
from lidm.utils.misc_utils import dict2namespace
|
14 |
-
from ..modules.rangenet.model import Model as rangenet
|
15 |
-
|
16 |
-
try:
|
17 |
-
from ..modules.spvcnn.model import Model as spvcnn
|
18 |
-
from ..modules.minkowskinet.model import Model as minkowskinet
|
19 |
-
except:
|
20 |
-
print('To install torchsparse 1.4.0, please refer to https://github.com/mit-han-lab/torchsparse/tree/74099d10a51c71c14318bce63d6421f698b24f24')
|
21 |
-
|
22 |
-
# user settings
|
23 |
-
DEFAULT_ROOT = './pretrained_weights'
|
24 |
-
MODAL2BATCHSIZE = {'range': 100, 'voxel': 50, 'point_voxel': 25}
|
25 |
-
OUTPUT_TEMPLATE = 50 * '-' + '\n|' + 16 * ' ' + '{}:{:.4E}' + 17 * ' ' + '|\n' + 50 * '-'
|
26 |
-
|
27 |
-
# eval settings (do not modify)
|
28 |
-
VOXEL_SIZE = 0.05
|
29 |
-
NUM_SECTORS = 16
|
30 |
-
AGG_TYPE = 'depth'
|
31 |
-
TYPE2DATASET = {'32': 'nuscenes', '64': 'kitti'}
|
32 |
-
DATA_CONFIG = {'64': {'x': [-50, 50], 'y': [-50, 50], 'z': [-3, 1]},
|
33 |
-
'32': {'x': [-30, 30], 'y': [-30, 30], 'z': [-3, 6]}}
|
34 |
-
MODALITY2MODEL = {'range': 'rangenet', 'voxel': 'minkowskinet', 'point_voxel': 'spvcnn'}
|
35 |
-
DATASET_CONFIG = {'kitti': {'size': [64, 1024], 'fov': [3, -25], 'depth_range': [1.0, 56.0], 'depth_scale': 6},
|
36 |
-
'nuscenes': {'size': [32, 1024], 'fov': [10, -30], 'depth_range': [1.0, 45.0]}}
|
37 |
-
|
38 |
-
|
39 |
-
def build_model(dataset_name, model_name, device='cpu'):
|
40 |
-
# config
|
41 |
-
model_folder = os.path.join(DEFAULT_ROOT, dataset_name, model_name)
|
42 |
-
|
43 |
-
if not os.path.isdir(model_folder):
|
44 |
-
raise Exception('Not Available Pretrained Weights!')
|
45 |
-
|
46 |
-
config = yaml.safe_load(open(os.path.join(model_folder, 'config.yaml'), 'r'))
|
47 |
-
if model_name != 'rangenet':
|
48 |
-
config = dict2namespace(config)
|
49 |
-
|
50 |
-
# build model
|
51 |
-
model = eval(model_name)(config)
|
52 |
-
|
53 |
-
# load checkpoint
|
54 |
-
if model_name == 'rangenet':
|
55 |
-
model.load_pretrained_weights(model_folder)
|
56 |
-
else:
|
57 |
-
ckpt = torch.load(os.path.join(model_folder, 'model.ckpt'), map_location="cpu")
|
58 |
-
model.load_state_dict(ckpt['state_dict'], strict=False)
|
59 |
-
model.to(device)
|
60 |
-
model.eval()
|
61 |
-
|
62 |
-
return model
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lidm/eval/compile.sh
DELETED
@@ -1,9 +0,0 @@
|
|
1 |
-
#!/bin/sh
|
2 |
-
|
3 |
-
cd modules/chamfer
|
4 |
-
python setup.py build_ext --inplace
|
5 |
-
|
6 |
-
cd ../emd
|
7 |
-
python setup.py build_ext --inplace
|
8 |
-
|
9 |
-
cd ..
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lidm/eval/eval_utils.py
DELETED
@@ -1,138 +0,0 @@
|
|
1 |
-
"""
|
2 |
-
@Author: Haoxi Ran
|
3 |
-
@Date: 01/03/2024
|
4 |
-
@Citation: Towards Realistic Scene Generation with LiDAR Diffusion Models
|
5 |
-
|
6 |
-
"""
|
7 |
-
import multiprocessing
|
8 |
-
from functools import partial
|
9 |
-
|
10 |
-
import numpy as np
|
11 |
-
from scipy.spatial.distance import jensenshannon
|
12 |
-
from tqdm import tqdm
|
13 |
-
|
14 |
-
from . import OUTPUT_TEMPLATE
|
15 |
-
from .metric_utils import compute_logits, compute_pairwise_cd, \
|
16 |
-
compute_pairwise_emd, pcd2bev_sum, compute_pairwise_cd_batch, pcd2bev_bin
|
17 |
-
from .fid_score import calculate_frechet_distance
|
18 |
-
|
19 |
-
|
20 |
-
def evaluate(reference, samples, metrics, data):
|
21 |
-
# perceptual
|
22 |
-
if 'frid' in metrics:
|
23 |
-
compute_frid(reference, samples, data)
|
24 |
-
if 'fsvd' in metrics:
|
25 |
-
compute_fsvd(reference, samples, data)
|
26 |
-
if 'fpvd' in metrics:
|
27 |
-
compute_fpvd(reference, samples, data)
|
28 |
-
|
29 |
-
# reconstruction
|
30 |
-
if 'cd' in metrics:
|
31 |
-
compute_cd(reference, samples)
|
32 |
-
if 'emd' in metrics:
|
33 |
-
compute_emd(reference, samples)
|
34 |
-
|
35 |
-
# statistical
|
36 |
-
if 'jsd' in metrics:
|
37 |
-
compute_jsd(reference, samples, data)
|
38 |
-
if 'mmd' in metrics:
|
39 |
-
compute_mmd(reference, samples, data)
|
40 |
-
|
41 |
-
|
42 |
-
def compute_cd(reference, samples):
|
43 |
-
"""
|
44 |
-
Calculate score of Chamfer Distance (CD)
|
45 |
-
|
46 |
-
"""
|
47 |
-
print('Evaluating (CD) ...')
|
48 |
-
results = []
|
49 |
-
for x, y in zip(reference, samples):
|
50 |
-
d = compute_pairwise_cd(x, y)
|
51 |
-
results.append(d)
|
52 |
-
score = sum(results) / len(results)
|
53 |
-
print(OUTPUT_TEMPLATE.format('CD ', score))
|
54 |
-
|
55 |
-
|
56 |
-
def compute_emd(reference, samples):
|
57 |
-
"""
|
58 |
-
Calculate score of Earth Mover's Distance (EMD)
|
59 |
-
|
60 |
-
"""
|
61 |
-
print('Evaluating (EMD) ...')
|
62 |
-
results = []
|
63 |
-
for x, y in zip(reference, samples):
|
64 |
-
d = compute_pairwise_emd(x, y)
|
65 |
-
results.append(d)
|
66 |
-
score = sum(results) / len(results)
|
67 |
-
print(OUTPUT_TEMPLATE.format('EMD ', score))
|
68 |
-
|
69 |
-
|
70 |
-
def compute_mmd(reference, samples, data, dist='cd', verbose=True):
|
71 |
-
"""
|
72 |
-
Calculate the score of Minimum Matching Distance (MMD)
|
73 |
-
|
74 |
-
"""
|
75 |
-
print('Evaluating (MMD) ...')
|
76 |
-
assert dist in ['cd', 'emd']
|
77 |
-
reference, samples = pcd2bev_bin(data, reference, samples)
|
78 |
-
compute_dist_func = compute_pairwise_cd_batch if dist == 'cd' else compute_pairwise_emd
|
79 |
-
results = []
|
80 |
-
for r in tqdm(reference, disable=not verbose):
|
81 |
-
dists = compute_dist_func(r, samples)
|
82 |
-
results.append(min(dists))
|
83 |
-
score = sum(results) / len(results)
|
84 |
-
print(OUTPUT_TEMPLATE.format('MMD ', score))
|
85 |
-
|
86 |
-
|
87 |
-
def compute_jsd(reference, samples, data):
|
88 |
-
"""
|
89 |
-
Calculate the score of Jensen-Shannon Divergence (JSD)
|
90 |
-
|
91 |
-
"""
|
92 |
-
print('Evaluating (JSD) ...')
|
93 |
-
reference, samples = pcd2bev_sum(data, reference, samples)
|
94 |
-
reference = (reference / np.sum(reference)).flatten()
|
95 |
-
samples = (samples / np.sum(samples)).flatten()
|
96 |
-
score = jensenshannon(reference, samples)
|
97 |
-
print(OUTPUT_TEMPLATE.format('JSD ', score))
|
98 |
-
|
99 |
-
|
100 |
-
def compute_fd(reference, samples):
|
101 |
-
mu1, mu2 = np.mean(reference, axis=0), np.mean(samples, axis=0)
|
102 |
-
sigma1, sigma2 = np.cov(reference, rowvar=False), np.cov(samples, rowvar=False)
|
103 |
-
distance = calculate_frechet_distance(mu1, sigma1, mu2, sigma2)
|
104 |
-
return distance
|
105 |
-
|
106 |
-
|
107 |
-
def compute_frid(reference, samples, data):
|
108 |
-
"""
|
109 |
-
Calculate the score of Fréchet Range Image Distance (FRID)
|
110 |
-
|
111 |
-
"""
|
112 |
-
print('Evaluating (FRID) ...')
|
113 |
-
gt_logits, samples_logits = compute_logits(data, 'range', reference, samples)
|
114 |
-
score = compute_fd(gt_logits, samples_logits)
|
115 |
-
print(OUTPUT_TEMPLATE.format('FRID', score))
|
116 |
-
|
117 |
-
|
118 |
-
def compute_fsvd(reference, samples, data):
|
119 |
-
"""
|
120 |
-
Calculate the score of Fréchet Sparse Volume Distance (FSVD)
|
121 |
-
|
122 |
-
"""
|
123 |
-
print('Evaluating (FSVD) ...')
|
124 |
-
gt_logits, samples_logits = compute_logits(data, 'voxel', reference, samples)
|
125 |
-
score = compute_fd(gt_logits, samples_logits)
|
126 |
-
print(OUTPUT_TEMPLATE.format('FSVD', score))
|
127 |
-
|
128 |
-
|
129 |
-
def compute_fpvd(reference, samples, data):
|
130 |
-
"""
|
131 |
-
Calculate the score of Fréchet Point-based Volume Distance (FPVD)
|
132 |
-
|
133 |
-
"""
|
134 |
-
print('Evaluating (FPVD) ...')
|
135 |
-
gt_logits, samples_logits = compute_logits(data, 'point_voxel', reference, samples)
|
136 |
-
score = compute_fd(gt_logits, samples_logits)
|
137 |
-
print(OUTPUT_TEMPLATE.format('FPVD', score))
|
138 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lidm/eval/fid_score.py
DELETED
@@ -1,191 +0,0 @@
|
|
1 |
-
"""Calculates the Frechet Inception Distance (FID) to evalulate GANs
|
2 |
-
The FID metric calculates the distance between two distributions of images.
|
3 |
-
Typically, we have summary statistics (mean & covariance matrix) of one
|
4 |
-
of these distributions, while the 2nd distribution is given by a GAN.
|
5 |
-
When run as a stand-alone program, it compares the distribution of
|
6 |
-
images that are stored as PNG/JPEG at a specified location with a
|
7 |
-
distribution given by summary statistics (in pickle format).
|
8 |
-
The FID is calculated by assuming that X_1 and X_2 are the activations of
|
9 |
-
the pool_3 layer of the inception net for generated samples and real world
|
10 |
-
samples respectively.
|
11 |
-
See --help to see further details.
|
12 |
-
Code adapted from https://github.com/bioinf-jku/TTUR to use PyTorch instead
|
13 |
-
of Tensorflow
|
14 |
-
Copyright 2018 Institute of Bioinformatics, JKU Linz
|
15 |
-
Licensed under the Apache License, Version 2.0 (the "License");
|
16 |
-
you may not use this file except in compliance with the License.
|
17 |
-
You may obtain a copy of the License at
|
18 |
-
http://www.apache.org/licenses/LICENSE-2.0
|
19 |
-
Unless required by applicable law or agreed to in writing, software
|
20 |
-
distributed under the License is distributed on an "AS IS" BASIS,
|
21 |
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
22 |
-
See the License for the specific language governing permissions and
|
23 |
-
limitations under the License.
|
24 |
-
"""
|
25 |
-
import os
|
26 |
-
import pathlib
|
27 |
-
from argparse import ArgumentDefaultsHelpFormatter, ArgumentParser
|
28 |
-
|
29 |
-
import numpy as np
|
30 |
-
import torch
|
31 |
-
import torchvision.transforms as TF
|
32 |
-
from PIL import Image
|
33 |
-
from scipy import linalg
|
34 |
-
from torch.nn.functional import adaptive_avg_pool2d
|
35 |
-
|
36 |
-
try:
|
37 |
-
from tqdm import tqdm
|
38 |
-
except ImportError:
|
39 |
-
# If tqdm is not available, provide a mock version of it
|
40 |
-
def tqdm(x):
|
41 |
-
return x
|
42 |
-
|
43 |
-
class ImagePathDataset(torch.utils.data.Dataset):
|
44 |
-
def __init__(self, files, transforms=None):
|
45 |
-
self.files = files
|
46 |
-
self.transforms = transforms
|
47 |
-
|
48 |
-
def __len__(self):
|
49 |
-
return len(self.files)
|
50 |
-
|
51 |
-
def __getitem__(self, i):
|
52 |
-
path = self.files[i]
|
53 |
-
img = Image.open(path).convert('RGB')
|
54 |
-
if self.transforms is not None:
|
55 |
-
img = self.transforms(img)
|
56 |
-
return img
|
57 |
-
|
58 |
-
|
59 |
-
def get_activations(files, model, batch_size=50, dims=2048, device='cpu',
|
60 |
-
num_workers=1):
|
61 |
-
"""Calculates the activations of the pool_3 layer for all images.
|
62 |
-
Params:
|
63 |
-
-- files : List of image files paths
|
64 |
-
-- model : Instance of inception model
|
65 |
-
-- batch_size : Batch size of images for the model to process at once.
|
66 |
-
Make sure that the number of samples is a multiple of
|
67 |
-
the batch size, otherwise some samples are ignored. This
|
68 |
-
behavior is retained to match the original FID score
|
69 |
-
implementation.
|
70 |
-
-- dims : Dimensionality of features returned by Inception
|
71 |
-
-- device : Device to run calculations
|
72 |
-
-- num_workers : Number of parallel dataloader workers
|
73 |
-
Returns:
|
74 |
-
-- A numpy array of dimension (num images, dims) that contains the
|
75 |
-
activations of the given tensor when feeding inception with the
|
76 |
-
query tensor.
|
77 |
-
"""
|
78 |
-
model.eval()
|
79 |
-
|
80 |
-
if batch_size > len(files):
|
81 |
-
print(('Warning: batch size is bigger than the data size. '
|
82 |
-
'Setting batch size to data size'))
|
83 |
-
batch_size = len(files)
|
84 |
-
|
85 |
-
dataset = ImagePathDataset(files, transforms=TF.ToTensor())
|
86 |
-
dataloader = torch.utils.data.DataLoader(dataset,
|
87 |
-
batch_size=batch_size,
|
88 |
-
shuffle=False,
|
89 |
-
drop_last=False,
|
90 |
-
num_workers=num_workers)
|
91 |
-
|
92 |
-
pred_arr = np.empty((len(files), dims))
|
93 |
-
|
94 |
-
start_idx = 0
|
95 |
-
|
96 |
-
for batch in tqdm(dataloader):
|
97 |
-
batch = batch.to(device)
|
98 |
-
|
99 |
-
with torch.no_grad():
|
100 |
-
pred = model(batch)[0]
|
101 |
-
|
102 |
-
# If model output is not scalar, apply global spatial average pooling.
|
103 |
-
# This happens if you choose a dimensionality not equal 2048.
|
104 |
-
if pred.size(2) != 1 or pred.size(3) != 1:
|
105 |
-
pred = adaptive_avg_pool2d(pred, output_size=(1, 1))
|
106 |
-
|
107 |
-
pred = pred.squeeze(3).squeeze(2).cpu().numpy()
|
108 |
-
|
109 |
-
pred_arr[start_idx:start_idx + pred.shape[0]] = pred
|
110 |
-
|
111 |
-
start_idx = start_idx + pred.shape[0]
|
112 |
-
|
113 |
-
return pred_arr
|
114 |
-
|
115 |
-
|
116 |
-
def calculate_frechet_distance(mu1, sigma1, mu2, sigma2, eps=1e-6):
|
117 |
-
"""Numpy implementation of the Frechet Distance.
|
118 |
-
The Frechet distance between two multivariate Gaussians X_1 ~ N(mu_1, C_1)
|
119 |
-
and X_2 ~ N(mu_2, C_2) is
|
120 |
-
d^2 = ||mu_1 - mu_2||^2 + Tr(C_1 + C_2 - 2*sqrt(C_1*C_2)).
|
121 |
-
Stable version by Dougal J. Sutherland.
|
122 |
-
Params:
|
123 |
-
-- mu1 : Numpy array containing the activations of a layer of the
|
124 |
-
inception net (like returned by the function 'get_predictions')
|
125 |
-
for generated samples.
|
126 |
-
-- mu2 : The sample mean over activations, precalculated on an
|
127 |
-
representative data set.
|
128 |
-
-- sigma1: The covariance matrix over activations for generated samples.
|
129 |
-
-- sigma2: The covariance matrix over activations, precalculated on an
|
130 |
-
representative data set.
|
131 |
-
Returns:
|
132 |
-
-- : The Frechet Distance.
|
133 |
-
"""
|
134 |
-
|
135 |
-
mu1 = np.atleast_1d(mu1)
|
136 |
-
mu2 = np.atleast_1d(mu2)
|
137 |
-
|
138 |
-
sigma1 = np.atleast_2d(sigma1)
|
139 |
-
sigma2 = np.atleast_2d(sigma2)
|
140 |
-
|
141 |
-
assert mu1.shape == mu2.shape, \
|
142 |
-
'Training and test mean vectors have different lengths'
|
143 |
-
assert sigma1.shape == sigma2.shape, \
|
144 |
-
'Training and test covariances have different dimensions'
|
145 |
-
|
146 |
-
diff = mu1 - mu2
|
147 |
-
|
148 |
-
# Product might be almost singular
|
149 |
-
covmean, _ = linalg.sqrtm(sigma1.dot(sigma2), disp=False)
|
150 |
-
if not np.isfinite(covmean).all():
|
151 |
-
msg = ('fid calculation produces singular product; '
|
152 |
-
'adding %s to diagonal of cov estimates') % eps
|
153 |
-
print(msg)
|
154 |
-
offset = np.eye(sigma1.shape[0]) * eps
|
155 |
-
covmean = linalg.sqrtm((sigma1 + offset).dot(sigma2 + offset))
|
156 |
-
|
157 |
-
# Numerical error might give slight imaginary component
|
158 |
-
if np.iscomplexobj(covmean):
|
159 |
-
if not np.allclose(np.diagonal(covmean).imag, 0, atol=1e-3):
|
160 |
-
m = np.max(np.abs(covmean.imag))
|
161 |
-
raise ValueError('Imaginary component {}'.format(m))
|
162 |
-
covmean = covmean.real
|
163 |
-
|
164 |
-
tr_covmean = np.trace(covmean)
|
165 |
-
|
166 |
-
return (diff.dot(diff) + np.trace(sigma1)
|
167 |
-
+ np.trace(sigma2) - 2 * tr_covmean)
|
168 |
-
|
169 |
-
|
170 |
-
def calculate_activation_statistics(files, model, batch_size=50, dims=2048,
|
171 |
-
device='cpu', num_workers=1):
|
172 |
-
"""Calculation of the statistics used by the FID.
|
173 |
-
Params:
|
174 |
-
-- files : List of image files paths
|
175 |
-
-- model : Instance of inception model
|
176 |
-
-- batch_size : The images numpy array is split into batches with
|
177 |
-
batch size batch_size. A reasonable batch size
|
178 |
-
depends on the hardware.
|
179 |
-
-- dims : Dimensionality of features returned by Inception
|
180 |
-
-- device : Device to run calculations
|
181 |
-
-- num_workers : Number of parallel dataloader workers
|
182 |
-
Returns:
|
183 |
-
-- mu : The mean over samples of the activations of the pool_3 layer of
|
184 |
-
the inception model.
|
185 |
-
-- sigma : The covariance matrix of the activations of the pool_3 layer of
|
186 |
-
the inception model.
|
187 |
-
"""
|
188 |
-
act = get_activations(files, model, batch_size, dims, device, num_workers)
|
189 |
-
mu = np.mean(act, axis=0)
|
190 |
-
sigma = np.cov(act, rowvar=False)
|
191 |
-
return mu, sigma
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lidm/eval/metric_utils.py
DELETED
@@ -1,458 +0,0 @@
|
|
1 |
-
"""
|
2 |
-
@Author: Haoxi Ran
|
3 |
-
@Date: 01/03/2024
|
4 |
-
@Citation: Towards Realistic Scene Generation with LiDAR Diffusion Models
|
5 |
-
|
6 |
-
"""
|
7 |
-
|
8 |
-
import math
|
9 |
-
from itertools import repeat
|
10 |
-
from typing import List, Tuple, Union
|
11 |
-
import numpy as np
|
12 |
-
import torch
|
13 |
-
|
14 |
-
from . import build_model, VOXEL_SIZE, MODALITY2MODEL, MODAL2BATCHSIZE, DATASET_CONFIG, AGG_TYPE, NUM_SECTORS, \
|
15 |
-
TYPE2DATASET, DATA_CONFIG
|
16 |
-
|
17 |
-
try:
|
18 |
-
from torchsparse import SparseTensor, PointTensor
|
19 |
-
from torchsparse.utils.collate import sparse_collate_fn
|
20 |
-
from .modules.chamfer3D.dist_chamfer_3D import chamfer_3DDist
|
21 |
-
from .modules.chamfer2D.dist_chamfer_2D import chamfer_2DDist
|
22 |
-
from .modules.emd.emd_module import emdModule
|
23 |
-
except:
|
24 |
-
print(
|
25 |
-
'To install torchsparse 1.4.0, please refer to https://github.com/mit-han-lab/torchsparse/tree/74099d10a51c71c14318bce63d6421f698b24f24')
|
26 |
-
|
27 |
-
|
28 |
-
def ravel_hash(x: np.ndarray) -> np.ndarray:
|
29 |
-
assert x.ndim == 2, x.shape
|
30 |
-
|
31 |
-
x = x - np.min(x, axis=0)
|
32 |
-
x = x.astype(np.uint64, copy=False)
|
33 |
-
xmax = np.max(x, axis=0).astype(np.uint64) + 1
|
34 |
-
|
35 |
-
h = np.zeros(x.shape[0], dtype=np.uint64)
|
36 |
-
for k in range(x.shape[1] - 1):
|
37 |
-
h += x[:, k]
|
38 |
-
h *= xmax[k + 1]
|
39 |
-
h += x[:, -1]
|
40 |
-
return h
|
41 |
-
|
42 |
-
|
43 |
-
def sparse_quantize(coords, voxel_size: Union[float, Tuple[float, ...]] = 1, *, return_index: bool = False,
|
44 |
-
return_inverse: bool = False) -> List[np.ndarray]:
|
45 |
-
"""
|
46 |
-
Modified based on https://github.com/mit-han-lab/torchsparse/blob/462dea4a701f87a7545afb3616bf2cf53dd404f3/torchsparse/utils/quantize.py
|
47 |
-
|
48 |
-
"""
|
49 |
-
if isinstance(voxel_size, (float, int)):
|
50 |
-
voxel_size = tuple(repeat(voxel_size, coords.shape[1]))
|
51 |
-
assert isinstance(voxel_size, tuple) and len(voxel_size) in [2, 3] # support 2D and 3D coordinates only
|
52 |
-
|
53 |
-
voxel_size = np.array(voxel_size)
|
54 |
-
coords = np.floor(coords / voxel_size).astype(np.int32)
|
55 |
-
|
56 |
-
_, indices, inverse_indices = np.unique(
|
57 |
-
ravel_hash(coords), return_index=True, return_inverse=True
|
58 |
-
)
|
59 |
-
coords = coords[indices]
|
60 |
-
|
61 |
-
outputs = [coords]
|
62 |
-
if return_index:
|
63 |
-
outputs += [indices]
|
64 |
-
if return_inverse:
|
65 |
-
outputs += [inverse_indices]
|
66 |
-
return outputs[0] if len(outputs) == 1 else outputs
|
67 |
-
|
68 |
-
|
69 |
-
def pcd2range(pcd, size, fov, depth_range, remission=None, labels=None, **kwargs):
|
70 |
-
# laser parameters
|
71 |
-
fov_up = fov[0] / 180.0 * np.pi # field of view up in rad
|
72 |
-
fov_down = fov[1] / 180.0 * np.pi # field of view down in rad
|
73 |
-
fov_range = abs(fov_down) + abs(fov_up) # get field of view total in rad
|
74 |
-
|
75 |
-
# get depth (distance) of all points
|
76 |
-
depth = np.linalg.norm(pcd, 2, axis=1)
|
77 |
-
|
78 |
-
# mask points out of range
|
79 |
-
mask = np.logical_and(depth > depth_range[0], depth < depth_range[1])
|
80 |
-
depth, pcd = depth[mask], pcd[mask]
|
81 |
-
|
82 |
-
# get scan components
|
83 |
-
scan_x, scan_y, scan_z = pcd[:, 0], pcd[:, 1], pcd[:, 2]
|
84 |
-
|
85 |
-
# get angles of all points
|
86 |
-
yaw = -np.arctan2(scan_y, scan_x)
|
87 |
-
pitch = np.arcsin(scan_z / depth)
|
88 |
-
|
89 |
-
# get projections in image coords
|
90 |
-
proj_x = 0.5 * (yaw / np.pi + 1.0) # in [0.0, 1.0]
|
91 |
-
proj_y = 1.0 - (pitch + abs(fov_down)) / fov_range # in [0.0, 1.0]
|
92 |
-
|
93 |
-
# scale to image size using angular resolution
|
94 |
-
proj_x *= size[1] # in [0.0, W]
|
95 |
-
proj_y *= size[0] # in [0.0, H]
|
96 |
-
|
97 |
-
# round and clamp for use as index
|
98 |
-
proj_x = np.maximum(0, np.minimum(size[1] - 1, np.floor(proj_x))).astype(np.int32) # in [0,W-1]
|
99 |
-
proj_y = np.maximum(0, np.minimum(size[0] - 1, np.floor(proj_y))).astype(np.int32) # in [0,H-1]
|
100 |
-
|
101 |
-
# order in decreasing depth
|
102 |
-
order = np.argsort(depth)[::-1]
|
103 |
-
proj_x, proj_y = proj_x[order], proj_y[order]
|
104 |
-
|
105 |
-
# project depth
|
106 |
-
depth = depth[order]
|
107 |
-
proj_range = np.full(size, -1, dtype=np.float32)
|
108 |
-
proj_range[proj_y, proj_x] = depth
|
109 |
-
|
110 |
-
# project point feature
|
111 |
-
if remission is not None:
|
112 |
-
remission = remission[mask][order]
|
113 |
-
proj_feature = np.full(size, -1, dtype=np.float32)
|
114 |
-
proj_feature[proj_y, proj_x] = remission
|
115 |
-
elif labels is not None:
|
116 |
-
labels = labels[mask][order]
|
117 |
-
proj_feature = np.full(size, 0, dtype=np.float32)
|
118 |
-
proj_feature[proj_y, proj_x] = labels
|
119 |
-
else:
|
120 |
-
proj_feature = None
|
121 |
-
|
122 |
-
return proj_range, proj_feature
|
123 |
-
|
124 |
-
|
125 |
-
def range2xyz(range_img, fov, depth_range, depth_scale, log_scale=True, **kwargs):
|
126 |
-
# laser parameters
|
127 |
-
size = range_img.shape
|
128 |
-
fov_up = fov[0] / 180.0 * np.pi # field of view up in rad
|
129 |
-
fov_down = fov[1] / 180.0 * np.pi # field of view down in rad
|
130 |
-
fov_range = abs(fov_down) + abs(fov_up) # get field of view total in rad
|
131 |
-
|
132 |
-
# inverse transform from depth
|
133 |
-
if log_scale:
|
134 |
-
depth = (np.exp2(range_img * depth_scale) - 1)
|
135 |
-
else:
|
136 |
-
depth = range_img
|
137 |
-
|
138 |
-
scan_x, scan_y = np.meshgrid(np.arange(size[1]), np.arange(size[0]))
|
139 |
-
scan_x = scan_x.astype(np.float64) / size[1]
|
140 |
-
scan_y = scan_y.astype(np.float64) / size[0]
|
141 |
-
|
142 |
-
yaw = np.pi * (scan_x * 2 - 1)
|
143 |
-
pitch = (1.0 - scan_y) * fov_range - abs(fov_down)
|
144 |
-
|
145 |
-
xyz = -np.ones((3, *size))
|
146 |
-
xyz[0] = np.cos(yaw) * np.cos(pitch) * depth
|
147 |
-
xyz[1] = -np.sin(yaw) * np.cos(pitch) * depth
|
148 |
-
xyz[2] = np.sin(pitch) * depth
|
149 |
-
|
150 |
-
# mask out invalid points
|
151 |
-
mask = np.logical_and(depth > depth_range[0], depth < depth_range[1])
|
152 |
-
xyz[:, ~mask] = -1
|
153 |
-
|
154 |
-
return xyz
|
155 |
-
|
156 |
-
|
157 |
-
def pcd2voxel(pcd):
|
158 |
-
pcd_voxel = np.round(pcd / VOXEL_SIZE)
|
159 |
-
pcd_voxel = pcd_voxel - pcd_voxel.min(0, keepdims=1)
|
160 |
-
feat = np.concatenate((pcd, -np.ones((pcd.shape[0], 1))), axis=1) # -1 for remission placeholder
|
161 |
-
_, inds, inverse_map = sparse_quantize(pcd_voxel, 1, return_index=True, return_inverse=True)
|
162 |
-
|
163 |
-
feat = torch.FloatTensor(feat[inds])
|
164 |
-
pcd_voxel = torch.LongTensor(pcd_voxel[inds])
|
165 |
-
lidar = SparseTensor(feat, pcd_voxel)
|
166 |
-
output = {'lidar': lidar}
|
167 |
-
return output
|
168 |
-
|
169 |
-
|
170 |
-
def pcd2voxel_full(data_type, *args):
|
171 |
-
config = DATA_CONFIG[data_type]
|
172 |
-
x_range, y_range, z_range = config['x'], config['y'], config['z']
|
173 |
-
vol_shape = (math.ceil((x_range[1] - x_range[0]) / VOXEL_SIZE), math.ceil((y_range[1] - y_range[0]) / VOXEL_SIZE),
|
174 |
-
math.ceil((z_range[1] - z_range[0]) / VOXEL_SIZE))
|
175 |
-
min_bound = (math.ceil((x_range[0]) / VOXEL_SIZE), math.ceil((y_range[0]) / VOXEL_SIZE),
|
176 |
-
math.ceil((z_range[0]) / VOXEL_SIZE))
|
177 |
-
|
178 |
-
output = tuple()
|
179 |
-
for data in args:
|
180 |
-
volume_list = []
|
181 |
-
for pcd in data:
|
182 |
-
# mask out invalid points
|
183 |
-
mask_x = np.logical_and(pcd[:, 0] > x_range[0], pcd[:, 0] < x_range[1])
|
184 |
-
mask_y = np.logical_and(pcd[:, 1] > y_range[0], pcd[:, 1] < y_range[1])
|
185 |
-
mask_z = np.logical_and(pcd[:, 2] > z_range[0], pcd[:, 2] < z_range[1])
|
186 |
-
mask = mask_x & mask_y & mask_z
|
187 |
-
pcd = pcd[mask]
|
188 |
-
|
189 |
-
# voxelize
|
190 |
-
pcd_voxel = np.floor(pcd / VOXEL_SIZE)
|
191 |
-
_, indices, inverse_map = sparse_quantize(pcd_voxel, 1, return_index=True, return_inverse=True)
|
192 |
-
pcd_voxel = pcd_voxel[indices]
|
193 |
-
pcd_voxel = (pcd_voxel - min_bound).astype(np.int32)
|
194 |
-
|
195 |
-
# 2D bev grid
|
196 |
-
vol = np.zeros(vol_shape, dtype=np.float32)
|
197 |
-
vol[pcd_voxel[:, 0], pcd_voxel[:, 1], pcd_voxel[:, 2]] = 1
|
198 |
-
volume_list.append(vol)
|
199 |
-
output += (volume_list,)
|
200 |
-
return output
|
201 |
-
|
202 |
-
|
203 |
-
# def pcd2bev_full(data_type, *args, voxel_size=VOXEL_SIZE):
|
204 |
-
# config = DATA_CONFIG[data_type]
|
205 |
-
# x_range, y_range = config['x'], config['y']
|
206 |
-
# vol_shape = (math.ceil((x_range[1] - x_range[0]) / voxel_size), math.ceil((y_range[1] - y_range[0]) / voxel_size))
|
207 |
-
# min_bound = (math.ceil((x_range[0]) / voxel_size), math.ceil((y_range[0]) / voxel_size))
|
208 |
-
#
|
209 |
-
# output = tuple()
|
210 |
-
# for data in args:
|
211 |
-
# volume_list = []
|
212 |
-
# for pcd in data:
|
213 |
-
# # mask out invalid points
|
214 |
-
# mask_x = np.logical_and(pcd[:, 0] > x_range[0], pcd[:, 0] < x_range[1])
|
215 |
-
# mask_y = np.logical_and(pcd[:, 1] > y_range[0], pcd[:, 1] < y_range[1])
|
216 |
-
# mask = mask_x & mask_y
|
217 |
-
# pcd = pcd[mask][:, :2] # keep x,y coord
|
218 |
-
#
|
219 |
-
# # voxelize
|
220 |
-
# pcd_voxel = np.floor(pcd / voxel_size)
|
221 |
-
# _, indices, inverse_map = sparse_quantize(pcd_voxel, 1, return_index=True, return_inverse=True)
|
222 |
-
# pcd_voxel = pcd_voxel[indices]
|
223 |
-
# pcd_voxel = (pcd_voxel - min_bound).astype(np.int32)
|
224 |
-
#
|
225 |
-
# # 2D bev grid
|
226 |
-
# vol = np.zeros(vol_shape, dtype=np.float32)
|
227 |
-
# vol[pcd_voxel[:, 0], pcd_voxel[:, 1]] = 1
|
228 |
-
# volume_list.append(vol)
|
229 |
-
# output += (volume_list,)
|
230 |
-
# return output
|
231 |
-
|
232 |
-
|
233 |
-
def pcd2bev_sum(data_type, *args, voxel_size=VOXEL_SIZE):
|
234 |
-
config = DATA_CONFIG[data_type]
|
235 |
-
x_range, y_range = config['x'], config['y']
|
236 |
-
vol_shape = (math.ceil((x_range[1] - x_range[0]) / voxel_size), math.ceil((y_range[1] - y_range[0]) / voxel_size))
|
237 |
-
min_bound = (math.ceil((x_range[0]) / voxel_size), math.ceil((y_range[0]) / voxel_size))
|
238 |
-
|
239 |
-
output = tuple()
|
240 |
-
for data in args:
|
241 |
-
volume_sum = np.zeros(vol_shape, np.float32)
|
242 |
-
for pcd in data:
|
243 |
-
# mask out invalid points
|
244 |
-
mask_x = np.logical_and(pcd[:, 0] > x_range[0], pcd[:, 0] < x_range[1])
|
245 |
-
mask_y = np.logical_and(pcd[:, 1] > y_range[0], pcd[:, 1] < y_range[1])
|
246 |
-
mask = mask_x & mask_y
|
247 |
-
pcd = pcd[mask][:, :2] # keep x,y coord
|
248 |
-
|
249 |
-
# voxelize
|
250 |
-
pcd_voxel = np.floor(pcd / voxel_size)
|
251 |
-
_, indices, inverse_map = sparse_quantize(pcd_voxel, 1, return_index=True, return_inverse=True)
|
252 |
-
pcd_voxel = pcd_voxel[indices]
|
253 |
-
pcd_voxel = (pcd_voxel - min_bound).astype(np.int32)
|
254 |
-
|
255 |
-
# summation
|
256 |
-
volume_sum[pcd_voxel[:, 0], pcd_voxel[:, 1]] += 1.
|
257 |
-
output += (volume_sum,)
|
258 |
-
return output
|
259 |
-
|
260 |
-
|
261 |
-
def pcd2bev_bin(data_type, *args, voxel_size=0.5):
|
262 |
-
config = DATA_CONFIG[data_type]
|
263 |
-
x_range, y_range = config['x'], config['y']
|
264 |
-
vol_shape = (math.ceil((x_range[1] - x_range[0]) / voxel_size), math.ceil((y_range[1] - y_range[0]) / voxel_size))
|
265 |
-
min_bound = (math.ceil((x_range[0]) / voxel_size), math.ceil((y_range[0]) / voxel_size))
|
266 |
-
|
267 |
-
output = tuple()
|
268 |
-
for data in args:
|
269 |
-
pcd_list = []
|
270 |
-
for pcd in data:
|
271 |
-
# mask out invalid points
|
272 |
-
mask_x = np.logical_and(pcd[:, 0] > x_range[0], pcd[:, 0] < x_range[1])
|
273 |
-
mask_y = np.logical_and(pcd[:, 1] > y_range[0], pcd[:, 1] < y_range[1])
|
274 |
-
mask = mask_x & mask_y
|
275 |
-
pcd = pcd[mask][:, :2] # keep x,y coord
|
276 |
-
|
277 |
-
# voxelize
|
278 |
-
pcd_voxel = np.floor(pcd / voxel_size)
|
279 |
-
_, indices, inverse_map = sparse_quantize(pcd_voxel, 1, return_index=True, return_inverse=True)
|
280 |
-
pcd_voxel = pcd_voxel[indices]
|
281 |
-
pcd_voxel = ((pcd_voxel - min_bound) / vol_shape).astype(np.float32)
|
282 |
-
pcd_list.append(pcd_voxel)
|
283 |
-
output += (pcd_list,)
|
284 |
-
return output
|
285 |
-
|
286 |
-
|
287 |
-
def bev_sample(data_type, *args, voxel_size=0.5):
|
288 |
-
config = DATA_CONFIG[data_type]
|
289 |
-
x_range, y_range = config['x'], config['y']
|
290 |
-
|
291 |
-
output = tuple()
|
292 |
-
for data in args:
|
293 |
-
pcd_list = []
|
294 |
-
for pcd in data:
|
295 |
-
# mask out invalid points
|
296 |
-
mask_x = np.logical_and(pcd[:, 0] > x_range[0], pcd[:, 0] < x_range[1])
|
297 |
-
mask_y = np.logical_and(pcd[:, 1] > y_range[0], pcd[:, 1] < y_range[1])
|
298 |
-
mask = mask_x & mask_y
|
299 |
-
pcd = pcd[mask][:, :2] # keep x,y coord
|
300 |
-
|
301 |
-
# voxelize
|
302 |
-
pcd_voxel = np.floor(pcd / voxel_size)
|
303 |
-
_, indices, inverse_map = sparse_quantize(pcd_voxel, 1, return_index=True, return_inverse=True)
|
304 |
-
pcd = pcd[indices]
|
305 |
-
pcd_list.append(pcd)
|
306 |
-
output += (pcd_list,)
|
307 |
-
return output
|
308 |
-
|
309 |
-
|
310 |
-
def preprocess_pcd(pcd, **kwargs):
|
311 |
-
depth = np.linalg.norm(pcd, 2, axis=1)
|
312 |
-
mask = np.logical_and(depth > kwargs['depth_range'][0], depth < kwargs['depth_range'][1])
|
313 |
-
pcd = pcd[mask]
|
314 |
-
return pcd
|
315 |
-
|
316 |
-
|
317 |
-
def preprocess_range(pcd, **kwargs):
|
318 |
-
depth_img = pcd2range(pcd, **kwargs)[0]
|
319 |
-
xyz_img = range2xyz(depth_img, log_scale=False, **kwargs)
|
320 |
-
depth_img = depth_img[None]
|
321 |
-
img = np.vstack([depth_img, xyz_img])
|
322 |
-
return img
|
323 |
-
|
324 |
-
|
325 |
-
def batch2list(batch_dict, agg_type='depth', **kwargs):
|
326 |
-
"""
|
327 |
-
Aggregation Type: Default 'depth', ['all', 'sector', 'depth']
|
328 |
-
"""
|
329 |
-
output_list = []
|
330 |
-
batch_indices = batch_dict['batch_indices']
|
331 |
-
for b_idx in range(batch_indices.max() + 1):
|
332 |
-
# avg all
|
333 |
-
if agg_type == 'all':
|
334 |
-
logits = batch_dict['logits'][batch_indices == b_idx].mean(0)
|
335 |
-
|
336 |
-
# avg on sectors
|
337 |
-
elif agg_type == 'sector':
|
338 |
-
logits = batch_dict['logits'][batch_indices == b_idx]
|
339 |
-
coords = batch_dict['coords'][batch_indices == b_idx].float()
|
340 |
-
coords = coords - coords.mean(0)
|
341 |
-
angle = torch.atan2(coords[:, 1], coords[:, 0]) # [-pi, pi]
|
342 |
-
sector_range = torch.linspace(-np.pi - 1e-4, np.pi + 1e-4, NUM_SECTORS + 1)
|
343 |
-
logits_list = []
|
344 |
-
for i in range(NUM_SECTORS):
|
345 |
-
sector_indices = torch.where((angle >= sector_range[i]) & (angle < sector_range[i + 1]))[0]
|
346 |
-
sector_logits = logits[sector_indices].mean(0)
|
347 |
-
sector_logits = torch.nan_to_num(sector_logits, 0.)
|
348 |
-
logits_list.append(sector_logits)
|
349 |
-
logits = torch.cat(logits_list) # dim: 768
|
350 |
-
|
351 |
-
# avg by depth
|
352 |
-
elif agg_type == 'depth':
|
353 |
-
logits = batch_dict['logits'][batch_indices == b_idx]
|
354 |
-
coords = batch_dict['coords'][batch_indices == b_idx].float()
|
355 |
-
coords = coords - coords.mean(0)
|
356 |
-
bev_depth = torch.norm(coords, dim=-1) * VOXEL_SIZE
|
357 |
-
sector_range = torch.linspace(kwargs['depth_range'][0] + 3, kwargs['depth_range'][1], NUM_SECTORS + 1)
|
358 |
-
sector_range[0] = 0.
|
359 |
-
logits_list = []
|
360 |
-
for i in range(NUM_SECTORS):
|
361 |
-
sector_indices = torch.where((bev_depth >= sector_range[i]) & (bev_depth < sector_range[i + 1]))[0]
|
362 |
-
sector_logits = logits[sector_indices].mean(0)
|
363 |
-
sector_logits = torch.nan_to_num(sector_logits, 0.)
|
364 |
-
logits_list.append(sector_logits)
|
365 |
-
logits = torch.cat(logits_list) # dim: 768
|
366 |
-
|
367 |
-
else:
|
368 |
-
raise NotImplementedError
|
369 |
-
|
370 |
-
output_list.append(logits.detach().cpu().numpy())
|
371 |
-
return output_list
|
372 |
-
|
373 |
-
|
374 |
-
def compute_logits(data_type, modality, *args):
|
375 |
-
assert data_type in ['32', '64']
|
376 |
-
assert modality in ['range', 'voxel', 'point_voxel']
|
377 |
-
is_voxel = 'voxel' in modality
|
378 |
-
dataset_name = TYPE2DATASET[data_type]
|
379 |
-
dataset_config = DATASET_CONFIG[dataset_name]
|
380 |
-
bs = MODAL2BATCHSIZE[modality]
|
381 |
-
|
382 |
-
model = build_model(dataset_name, MODALITY2MODEL[modality], device='cuda')
|
383 |
-
|
384 |
-
output = tuple()
|
385 |
-
for data in args:
|
386 |
-
all_logits_list = []
|
387 |
-
for i in range(math.ceil(len(data) / bs)):
|
388 |
-
batch = data[i * bs:(i + 1) * bs]
|
389 |
-
if is_voxel:
|
390 |
-
batch = [pcd2voxel(preprocess_pcd(pcd, **dataset_config)) for pcd in batch]
|
391 |
-
batch = sparse_collate_fn(batch)
|
392 |
-
batch = {k: v.cuda() if isinstance(v, (torch.Tensor, SparseTensor, PointTensor)) else v for k, v in
|
393 |
-
batch.items()}
|
394 |
-
with torch.no_grad():
|
395 |
-
batch_out = model(batch, return_final_logits=True)
|
396 |
-
batch_out = batch2list(batch_out, AGG_TYPE, **dataset_config)
|
397 |
-
all_logits_list.extend(batch_out)
|
398 |
-
else:
|
399 |
-
batch = [preprocess_range(pcd, **dataset_config) for pcd in batch]
|
400 |
-
batch = torch.from_numpy(np.stack(batch)).float().cuda()
|
401 |
-
with torch.no_grad():
|
402 |
-
batch_out = model(batch, return_final_logits=True, agg_type=AGG_TYPE)
|
403 |
-
all_logits_list.append(batch_out)
|
404 |
-
if is_voxel:
|
405 |
-
all_logits = np.stack(all_logits_list)
|
406 |
-
else:
|
407 |
-
all_logits = np.vstack(all_logits_list)
|
408 |
-
output += (all_logits,)
|
409 |
-
|
410 |
-
del model, batch, batch_out
|
411 |
-
torch.cuda.empty_cache()
|
412 |
-
return output
|
413 |
-
|
414 |
-
|
415 |
-
def compute_pairwise_cd(x, y, module=None):
|
416 |
-
if module is None:
|
417 |
-
module = chamfer_3DDist()
|
418 |
-
if x.ndim == 2 and y.ndim == 2:
|
419 |
-
x, y = x[None], y[None]
|
420 |
-
x, y = torch.from_numpy(x).cuda(), torch.from_numpy(y).cuda()
|
421 |
-
dist1, dist2, _, _ = module(x, y)
|
422 |
-
dist = (dist1.mean() + dist2.mean()) / 2
|
423 |
-
return dist.item()
|
424 |
-
|
425 |
-
|
426 |
-
def compute_pairwise_cd_batch(reference, samples):
|
427 |
-
ndim = reference.ndim
|
428 |
-
assert ndim in [2, 3]
|
429 |
-
module = chamfer_3DDist() if ndim == 3 else chamfer_2DDist()
|
430 |
-
len_r, len_s = reference.shape[0], [s.shape[0] for s in samples]
|
431 |
-
max_len = max([len_r] + len_s)
|
432 |
-
reference = torch.from_numpy(
|
433 |
-
np.vstack([reference, np.ones((max_len - reference.shape[0], ndim), dtype=np.float32) * 1e6])).cuda()
|
434 |
-
samples = [np.vstack([s, np.ones((max_len - s.shape[0], ndim), dtype=np.float32) * 1e6]) for s in samples]
|
435 |
-
samples = torch.from_numpy(np.stack(samples)).cuda()
|
436 |
-
reference = reference.expand_as(samples)
|
437 |
-
dist_r, dist_s, _, _ = module(reference, samples)
|
438 |
-
|
439 |
-
results = []
|
440 |
-
for i in range(samples.shape[0]):
|
441 |
-
dist1, dist2, len1, len2 = dist_r[i], dist_s[i], len_r, len_s[i]
|
442 |
-
dist = (dist1[:len1].mean() + dist2[:len2].mean()) / 2.
|
443 |
-
results.append(dist.item())
|
444 |
-
return results
|
445 |
-
|
446 |
-
|
447 |
-
def compute_pairwise_emd(x, y, module=None):
|
448 |
-
if module is None:
|
449 |
-
module = emdModule()
|
450 |
-
n_points = min(x.shape[0], y.shape[0])
|
451 |
-
n_points = n_points - n_points % 1024
|
452 |
-
x, y = x[:n_points], y[:n_points]
|
453 |
-
if x.ndim == 2 and y.ndim == 2:
|
454 |
-
x, y = x[None], y[None]
|
455 |
-
x, y = torch.from_numpy(x).cuda(), torch.from_numpy(y).cuda()
|
456 |
-
dist, _ = module(x, y, 0.005, 50)
|
457 |
-
dist = torch.sqrt(dist).mean()
|
458 |
-
return dist.item()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lidm/eval/models/__init__.py
DELETED
File without changes
|
lidm/eval/models/minkowskinet/__init__.py
DELETED
File without changes
|
lidm/eval/models/minkowskinet/model.py
DELETED
@@ -1,141 +0,0 @@
|
|
1 |
-
import torch
|
2 |
-
import torch.nn as nn
|
3 |
-
|
4 |
-
try:
|
5 |
-
import torchsparse
|
6 |
-
import torchsparse.nn as spnn
|
7 |
-
from ..ts import basic_blocks
|
8 |
-
except ImportError:
|
9 |
-
raise Exception('Required ts lib. Reference: https://github.com/mit-han-lab/torchsparse/tree/v1.4.0')
|
10 |
-
|
11 |
-
|
12 |
-
class Model(nn.Module):
|
13 |
-
def __init__(self, config):
|
14 |
-
super().__init__()
|
15 |
-
|
16 |
-
cr = config.model_params.cr
|
17 |
-
cs = config.model_params.layer_num
|
18 |
-
cs = [int(cr * x) for x in cs]
|
19 |
-
|
20 |
-
self.pres = self.vres = config.model_params.voxel_size
|
21 |
-
self.num_classes = config.model_params.num_class
|
22 |
-
|
23 |
-
self.stem = nn.Sequential(
|
24 |
-
spnn.Conv3d(config.model_params.input_dims, cs[0], kernel_size=3, stride=1),
|
25 |
-
spnn.BatchNorm(cs[0]), spnn.ReLU(True),
|
26 |
-
spnn.Conv3d(cs[0], cs[0], kernel_size=3, stride=1),
|
27 |
-
spnn.BatchNorm(cs[0]), spnn.ReLU(True))
|
28 |
-
|
29 |
-
self.stage1 = nn.Sequential(
|
30 |
-
basic_blocks.BasicConvolutionBlock(cs[0], cs[0], ks=2, stride=2, dilation=1),
|
31 |
-
basic_blocks.ResidualBlock(cs[0], cs[1], ks=3, stride=1, dilation=1),
|
32 |
-
basic_blocks.ResidualBlock(cs[1], cs[1], ks=3, stride=1, dilation=1),
|
33 |
-
)
|
34 |
-
|
35 |
-
self.stage2 = nn.Sequential(
|
36 |
-
basic_blocks.BasicConvolutionBlock(cs[1], cs[1], ks=2, stride=2, dilation=1),
|
37 |
-
basic_blocks.ResidualBlock(cs[1], cs[2], ks=3, stride=1, dilation=1),
|
38 |
-
basic_blocks.ResidualBlock(cs[2], cs[2], ks=3, stride=1, dilation=1),
|
39 |
-
)
|
40 |
-
|
41 |
-
self.stage3 = nn.Sequential(
|
42 |
-
basic_blocks.BasicConvolutionBlock(cs[2], cs[2], ks=2, stride=2, dilation=1),
|
43 |
-
basic_blocks.ResidualBlock(cs[2], cs[3], ks=3, stride=1, dilation=1),
|
44 |
-
basic_blocks.ResidualBlock(cs[3], cs[3], ks=3, stride=1, dilation=1),
|
45 |
-
)
|
46 |
-
|
47 |
-
self.stage4 = nn.Sequential(
|
48 |
-
basic_blocks.BasicConvolutionBlock(cs[3], cs[3], ks=2, stride=2, dilation=1),
|
49 |
-
basic_blocks.ResidualBlock(cs[3], cs[4], ks=3, stride=1, dilation=1),
|
50 |
-
basic_blocks.ResidualBlock(cs[4], cs[4], ks=3, stride=1, dilation=1),
|
51 |
-
)
|
52 |
-
|
53 |
-
self.up1 = nn.ModuleList([
|
54 |
-
basic_blocks.BasicDeconvolutionBlock(cs[4], cs[5], ks=2, stride=2),
|
55 |
-
nn.Sequential(
|
56 |
-
basic_blocks.ResidualBlock(cs[5] + cs[3], cs[5], ks=3, stride=1,
|
57 |
-
dilation=1),
|
58 |
-
basic_blocks.ResidualBlock(cs[5], cs[5], ks=3, stride=1, dilation=1),
|
59 |
-
)
|
60 |
-
])
|
61 |
-
|
62 |
-
self.up2 = nn.ModuleList([
|
63 |
-
basic_blocks.BasicDeconvolutionBlock(cs[5], cs[6], ks=2, stride=2),
|
64 |
-
nn.Sequential(
|
65 |
-
basic_blocks.ResidualBlock(cs[6] + cs[2], cs[6], ks=3, stride=1,
|
66 |
-
dilation=1),
|
67 |
-
basic_blocks.ResidualBlock(cs[6], cs[6], ks=3, stride=1, dilation=1),
|
68 |
-
)
|
69 |
-
])
|
70 |
-
|
71 |
-
self.up3 = nn.ModuleList([
|
72 |
-
basic_blocks.BasicDeconvolutionBlock(cs[6], cs[7], ks=2, stride=2),
|
73 |
-
nn.Sequential(
|
74 |
-
basic_blocks.ResidualBlock(cs[7] + cs[1], cs[7], ks=3, stride=1,
|
75 |
-
dilation=1),
|
76 |
-
basic_blocks.ResidualBlock(cs[7], cs[7], ks=3, stride=1, dilation=1),
|
77 |
-
)
|
78 |
-
])
|
79 |
-
|
80 |
-
self.up4 = nn.ModuleList([
|
81 |
-
basic_blocks.BasicDeconvolutionBlock(cs[7], cs[8], ks=2, stride=2),
|
82 |
-
nn.Sequential(
|
83 |
-
basic_blocks.ResidualBlock(cs[8] + cs[0], cs[8], ks=3, stride=1,
|
84 |
-
dilation=1),
|
85 |
-
basic_blocks.ResidualBlock(cs[8], cs[8], ks=3, stride=1, dilation=1),
|
86 |
-
)
|
87 |
-
])
|
88 |
-
|
89 |
-
self.classifier = nn.Sequential(nn.Linear(cs[8], self.num_classes))
|
90 |
-
|
91 |
-
self.weight_initialization()
|
92 |
-
self.dropout = nn.Dropout(0.3, True)
|
93 |
-
|
94 |
-
def weight_initialization(self):
|
95 |
-
for m in self.modules():
|
96 |
-
if isinstance(m, nn.BatchNorm1d):
|
97 |
-
nn.init.constant_(m.weight, 1)
|
98 |
-
nn.init.constant_(m.bias, 0)
|
99 |
-
|
100 |
-
def forward(self, data_dict, return_logits=False, return_final_logits=False):
|
101 |
-
x = data_dict['lidar']
|
102 |
-
x.C = x.C.int()
|
103 |
-
|
104 |
-
x0 = self.stem(x)
|
105 |
-
x1 = self.stage1(x0)
|
106 |
-
x2 = self.stage2(x1)
|
107 |
-
x3 = self.stage3(x2)
|
108 |
-
x4 = self.stage4(x3)
|
109 |
-
|
110 |
-
if return_logits:
|
111 |
-
output_dict = dict()
|
112 |
-
output_dict['logits'] = x4.F
|
113 |
-
output_dict['batch_indices'] = x4.C[:, -1]
|
114 |
-
return output_dict
|
115 |
-
|
116 |
-
y1 = self.up1[0](x4)
|
117 |
-
y1 = torchsparse.cat([y1, x3])
|
118 |
-
y1 = self.up1[1](y1)
|
119 |
-
|
120 |
-
y2 = self.up2[0](y1)
|
121 |
-
y2 = torchsparse.cat([y2, x2])
|
122 |
-
y2 = self.up2[1](y2)
|
123 |
-
|
124 |
-
y3 = self.up3[0](y2)
|
125 |
-
y3 = torchsparse.cat([y3, x1])
|
126 |
-
y3 = self.up3[1](y3)
|
127 |
-
|
128 |
-
y4 = self.up4[0](y3)
|
129 |
-
y4 = torchsparse.cat([y4, x0])
|
130 |
-
y4 = self.up4[1](y4)
|
131 |
-
if return_final_logits:
|
132 |
-
output_dict = dict()
|
133 |
-
output_dict['logits'] = y4.F
|
134 |
-
output_dict['coords'] = y4.C[:, :3]
|
135 |
-
output_dict['batch_indices'] = y4.C[:, -1]
|
136 |
-
return output_dict
|
137 |
-
|
138 |
-
output = self.classifier(y4.F)
|
139 |
-
data_dict['output'] = output.F
|
140 |
-
|
141 |
-
return data_dict
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lidm/eval/models/rangenet/__init__.py
DELETED
File without changes
|
lidm/eval/models/rangenet/model.py
DELETED
@@ -1,372 +0,0 @@
|
|
1 |
-
#!/usr/bin/env python3
|
2 |
-
# This file is covered by the LICENSE file in the root of this project.
|
3 |
-
from collections import OrderedDict
|
4 |
-
|
5 |
-
import torch
|
6 |
-
import torch.nn as nn
|
7 |
-
import torch.nn.functional as F
|
8 |
-
|
9 |
-
|
10 |
-
class BasicBlock(nn.Module):
|
11 |
-
def __init__(self, inplanes, planes, bn_d=0.1):
|
12 |
-
super(BasicBlock, self).__init__()
|
13 |
-
self.conv1 = nn.Conv2d(inplanes, planes[0], kernel_size=1,
|
14 |
-
stride=1, padding=0, bias=False)
|
15 |
-
self.bn1 = nn.BatchNorm2d(planes[0], momentum=bn_d)
|
16 |
-
self.relu1 = nn.LeakyReLU(0.1)
|
17 |
-
self.conv2 = nn.Conv2d(planes[0], planes[1], kernel_size=3,
|
18 |
-
stride=1, padding=1, bias=False)
|
19 |
-
self.bn2 = nn.BatchNorm2d(planes[1], momentum=bn_d)
|
20 |
-
self.relu2 = nn.LeakyReLU(0.1)
|
21 |
-
|
22 |
-
def forward(self, x):
|
23 |
-
residual = x
|
24 |
-
|
25 |
-
out = self.conv1(x)
|
26 |
-
out = self.bn1(out)
|
27 |
-
out = self.relu1(out)
|
28 |
-
|
29 |
-
out = self.conv2(out)
|
30 |
-
out = self.bn2(out)
|
31 |
-
out = self.relu2(out)
|
32 |
-
|
33 |
-
out += residual
|
34 |
-
return out
|
35 |
-
|
36 |
-
|
37 |
-
# ******************************************************************************
|
38 |
-
|
39 |
-
# number of layers per model
|
40 |
-
model_blocks = {
|
41 |
-
21: [1, 1, 2, 2, 1],
|
42 |
-
53: [1, 2, 8, 8, 4],
|
43 |
-
}
|
44 |
-
|
45 |
-
|
46 |
-
class Backbone(nn.Module):
|
47 |
-
"""
|
48 |
-
Class for DarknetSeg. Subclasses PyTorch's own "nn" module
|
49 |
-
"""
|
50 |
-
|
51 |
-
def __init__(self, params):
|
52 |
-
super(Backbone, self).__init__()
|
53 |
-
self.use_range = params["input_depth"]["range"]
|
54 |
-
self.use_xyz = params["input_depth"]["xyz"]
|
55 |
-
self.use_remission = params["input_depth"]["remission"]
|
56 |
-
self.drop_prob = params["dropout"]
|
57 |
-
self.bn_d = params["bn_d"]
|
58 |
-
self.OS = params["OS"]
|
59 |
-
self.layers = params["extra"]["layers"]
|
60 |
-
|
61 |
-
# input depth calc
|
62 |
-
self.input_depth = 0
|
63 |
-
self.input_idxs = []
|
64 |
-
if self.use_range:
|
65 |
-
self.input_depth += 1
|
66 |
-
self.input_idxs.append(0)
|
67 |
-
if self.use_xyz:
|
68 |
-
self.input_depth += 3
|
69 |
-
self.input_idxs.extend([1, 2, 3])
|
70 |
-
if self.use_remission:
|
71 |
-
self.input_depth += 1
|
72 |
-
self.input_idxs.append(4)
|
73 |
-
|
74 |
-
# stride play
|
75 |
-
self.strides = [2, 2, 2, 2, 2]
|
76 |
-
# check current stride
|
77 |
-
current_os = 1
|
78 |
-
for s in self.strides:
|
79 |
-
current_os *= s
|
80 |
-
|
81 |
-
# make the new stride
|
82 |
-
if self.OS > current_os:
|
83 |
-
print("Can't do OS, ", self.OS,
|
84 |
-
" because it is bigger than original ", current_os)
|
85 |
-
else:
|
86 |
-
# redo strides according to needed stride
|
87 |
-
for i, stride in enumerate(reversed(self.strides), 0):
|
88 |
-
if int(current_os) != self.OS:
|
89 |
-
if stride == 2:
|
90 |
-
current_os /= 2
|
91 |
-
self.strides[-1 - i] = 1
|
92 |
-
if int(current_os) == self.OS:
|
93 |
-
break
|
94 |
-
|
95 |
-
# check that darknet exists
|
96 |
-
assert self.layers in model_blocks.keys()
|
97 |
-
|
98 |
-
# generate layers depending on darknet type
|
99 |
-
self.blocks = model_blocks[self.layers]
|
100 |
-
|
101 |
-
# input layer
|
102 |
-
self.conv1 = nn.Conv2d(self.input_depth, 32, kernel_size=3,
|
103 |
-
stride=1, padding=1, bias=False)
|
104 |
-
self.bn1 = nn.BatchNorm2d(32, momentum=self.bn_d)
|
105 |
-
self.relu1 = nn.LeakyReLU(0.1)
|
106 |
-
|
107 |
-
# encoder
|
108 |
-
self.enc1 = self._make_enc_layer(BasicBlock, [32, 64], self.blocks[0],
|
109 |
-
stride=self.strides[0], bn_d=self.bn_d)
|
110 |
-
self.enc2 = self._make_enc_layer(BasicBlock, [64, 128], self.blocks[1],
|
111 |
-
stride=self.strides[1], bn_d=self.bn_d)
|
112 |
-
self.enc3 = self._make_enc_layer(BasicBlock, [128, 256], self.blocks[2],
|
113 |
-
stride=self.strides[2], bn_d=self.bn_d)
|
114 |
-
self.enc4 = self._make_enc_layer(BasicBlock, [256, 512], self.blocks[3],
|
115 |
-
stride=self.strides[3], bn_d=self.bn_d)
|
116 |
-
self.enc5 = self._make_enc_layer(BasicBlock, [512, 1024], self.blocks[4],
|
117 |
-
stride=self.strides[4], bn_d=self.bn_d)
|
118 |
-
|
119 |
-
# for a bit of fun
|
120 |
-
self.dropout = nn.Dropout2d(self.drop_prob)
|
121 |
-
|
122 |
-
# last channels
|
123 |
-
self.last_channels = 1024
|
124 |
-
|
125 |
-
# make layer useful function
|
126 |
-
def _make_enc_layer(self, block, planes, blocks, stride, bn_d=0.1):
|
127 |
-
layers = []
|
128 |
-
|
129 |
-
# downsample
|
130 |
-
layers.append(("conv", nn.Conv2d(planes[0], planes[1],
|
131 |
-
kernel_size=3,
|
132 |
-
stride=[1, stride], dilation=1,
|
133 |
-
padding=1, bias=False)))
|
134 |
-
layers.append(("bn", nn.BatchNorm2d(planes[1], momentum=bn_d)))
|
135 |
-
layers.append(("relu", nn.LeakyReLU(0.1)))
|
136 |
-
|
137 |
-
# blocks
|
138 |
-
inplanes = planes[1]
|
139 |
-
for i in range(0, blocks):
|
140 |
-
layers.append(("residual_{}".format(i),
|
141 |
-
block(inplanes, planes, bn_d)))
|
142 |
-
|
143 |
-
return nn.Sequential(OrderedDict(layers))
|
144 |
-
|
145 |
-
def run_layer(self, x, layer, skips, os):
|
146 |
-
y = layer(x)
|
147 |
-
if y.shape[2] < x.shape[2] or y.shape[3] < x.shape[3]:
|
148 |
-
skips[os] = x.detach()
|
149 |
-
os *= 2
|
150 |
-
x = y
|
151 |
-
return x, skips, os
|
152 |
-
|
153 |
-
def forward(self, x, return_logits=False, return_list=None):
|
154 |
-
# filter input
|
155 |
-
x = x[:, self.input_idxs]
|
156 |
-
|
157 |
-
# run cnn
|
158 |
-
# store for skip connections
|
159 |
-
skips = {}
|
160 |
-
out_dict = {}
|
161 |
-
os = 1
|
162 |
-
|
163 |
-
# first layer
|
164 |
-
x, skips, os = self.run_layer(x, self.conv1, skips, os)
|
165 |
-
x, skips, os = self.run_layer(x, self.bn1, skips, os)
|
166 |
-
x, skips, os = self.run_layer(x, self.relu1, skips, os)
|
167 |
-
if return_list and 'enc_0' in return_list:
|
168 |
-
out_dict['enc_0'] = x.detach().cpu() # 32, 64, 1024
|
169 |
-
|
170 |
-
# all encoder blocks with intermediate dropouts
|
171 |
-
x, skips, os = self.run_layer(x, self.enc1, skips, os)
|
172 |
-
if return_list and 'enc_1' in return_list:
|
173 |
-
out_dict['enc_1'] = x.detach().cpu() # 64, 64, 512
|
174 |
-
x, skips, os = self.run_layer(x, self.dropout, skips, os)
|
175 |
-
|
176 |
-
x, skips, os = self.run_layer(x, self.enc2, skips, os)
|
177 |
-
if return_list and 'enc_2' in return_list:
|
178 |
-
out_dict['enc_2'] = x.detach().cpu() # 128, 64, 256
|
179 |
-
x, skips, os = self.run_layer(x, self.dropout, skips, os)
|
180 |
-
|
181 |
-
x, skips, os = self.run_layer(x, self.enc3, skips, os)
|
182 |
-
if return_list and 'enc_3' in return_list:
|
183 |
-
out_dict['enc_3'] = x.detach().cpu() # 256, 64, 128
|
184 |
-
x, skips, os = self.run_layer(x, self.dropout, skips, os)
|
185 |
-
|
186 |
-
x, skips, os = self.run_layer(x, self.enc4, skips, os)
|
187 |
-
if return_list and 'enc_4' in return_list:
|
188 |
-
out_dict['enc_4'] = x.detach().cpu() # 512, 64, 64
|
189 |
-
x, skips, os = self.run_layer(x, self.dropout, skips, os)
|
190 |
-
|
191 |
-
x, skips, os = self.run_layer(x, self.enc5, skips, os)
|
192 |
-
if return_list and 'enc_5' in return_list:
|
193 |
-
out_dict['enc_5'] = x.detach().cpu() # 1024, 64, 32
|
194 |
-
if return_logits:
|
195 |
-
return x
|
196 |
-
|
197 |
-
x, skips, os = self.run_layer(x, self.dropout, skips, os)
|
198 |
-
|
199 |
-
if return_list is not None:
|
200 |
-
return x, skips, out_dict
|
201 |
-
return x, skips
|
202 |
-
|
203 |
-
def get_last_depth(self):
|
204 |
-
return self.last_channels
|
205 |
-
|
206 |
-
def get_input_depth(self):
|
207 |
-
return self.input_depth
|
208 |
-
|
209 |
-
|
210 |
-
class Decoder(nn.Module):
|
211 |
-
"""
|
212 |
-
Class for DarknetSeg. Subclasses PyTorch's own "nn" module
|
213 |
-
"""
|
214 |
-
|
215 |
-
def __init__(self, params, OS=32, feature_depth=1024):
|
216 |
-
super(Decoder, self).__init__()
|
217 |
-
self.backbone_OS = OS
|
218 |
-
self.backbone_feature_depth = feature_depth
|
219 |
-
self.drop_prob = params["dropout"]
|
220 |
-
self.bn_d = params["bn_d"]
|
221 |
-
self.index = 0
|
222 |
-
|
223 |
-
# stride play
|
224 |
-
self.strides = [2, 2, 2, 2, 2]
|
225 |
-
# check current stride
|
226 |
-
current_os = 1
|
227 |
-
for s in self.strides:
|
228 |
-
current_os *= s
|
229 |
-
# redo strides according to needed stride
|
230 |
-
for i, stride in enumerate(self.strides):
|
231 |
-
if int(current_os) != self.backbone_OS:
|
232 |
-
if stride == 2:
|
233 |
-
current_os /= 2
|
234 |
-
self.strides[i] = 1
|
235 |
-
if int(current_os) == self.backbone_OS:
|
236 |
-
break
|
237 |
-
|
238 |
-
# decoder
|
239 |
-
self.dec5 = self._make_dec_layer(BasicBlock,
|
240 |
-
[self.backbone_feature_depth, 512],
|
241 |
-
bn_d=self.bn_d,
|
242 |
-
stride=self.strides[0])
|
243 |
-
self.dec4 = self._make_dec_layer(BasicBlock, [512, 256], bn_d=self.bn_d,
|
244 |
-
stride=self.strides[1])
|
245 |
-
self.dec3 = self._make_dec_layer(BasicBlock, [256, 128], bn_d=self.bn_d,
|
246 |
-
stride=self.strides[2])
|
247 |
-
self.dec2 = self._make_dec_layer(BasicBlock, [128, 64], bn_d=self.bn_d,
|
248 |
-
stride=self.strides[3])
|
249 |
-
self.dec1 = self._make_dec_layer(BasicBlock, [64, 32], bn_d=self.bn_d,
|
250 |
-
stride=self.strides[4])
|
251 |
-
|
252 |
-
# layer list to execute with skips
|
253 |
-
self.layers = [self.dec5, self.dec4, self.dec3, self.dec2, self.dec1]
|
254 |
-
|
255 |
-
# for a bit of fun
|
256 |
-
self.dropout = nn.Dropout2d(self.drop_prob)
|
257 |
-
|
258 |
-
# last channels
|
259 |
-
self.last_channels = 32
|
260 |
-
|
261 |
-
def _make_dec_layer(self, block, planes, bn_d=0.1, stride=2):
|
262 |
-
layers = []
|
263 |
-
|
264 |
-
# downsample
|
265 |
-
if stride == 2:
|
266 |
-
layers.append(("upconv", nn.ConvTranspose2d(planes[0], planes[1],
|
267 |
-
kernel_size=[1, 4], stride=[1, 2],
|
268 |
-
padding=[0, 1])))
|
269 |
-
else:
|
270 |
-
layers.append(("conv", nn.Conv2d(planes[0], planes[1],
|
271 |
-
kernel_size=3, padding=1)))
|
272 |
-
layers.append(("bn", nn.BatchNorm2d(planes[1], momentum=bn_d)))
|
273 |
-
layers.append(("relu", nn.LeakyReLU(0.1)))
|
274 |
-
|
275 |
-
# blocks
|
276 |
-
layers.append(("residual", block(planes[1], planes, bn_d)))
|
277 |
-
|
278 |
-
return nn.Sequential(OrderedDict(layers))
|
279 |
-
|
280 |
-
def run_layer(self, x, layer, skips, os):
|
281 |
-
feats = layer(x) # up
|
282 |
-
if feats.shape[-1] > x.shape[-1]:
|
283 |
-
os //= 2 # match skip
|
284 |
-
feats = feats + skips[os].detach() # add skip
|
285 |
-
x = feats
|
286 |
-
return x, skips, os
|
287 |
-
|
288 |
-
def forward(self, x, skips, return_logits=False, return_list=None):
|
289 |
-
os = self.backbone_OS
|
290 |
-
out_dict = {}
|
291 |
-
|
292 |
-
# run layers
|
293 |
-
x, skips, os = self.run_layer(x, self.dec5, skips, os)
|
294 |
-
if return_list and 'dec_4' in return_list:
|
295 |
-
out_dict['dec_4'] = x.detach().cpu() # 512, 64, 64
|
296 |
-
x, skips, os = self.run_layer(x, self.dec4, skips, os)
|
297 |
-
if return_list and 'dec_3' in return_list:
|
298 |
-
out_dict['dec_3'] = x.detach().cpu() # 256, 64, 128
|
299 |
-
x, skips, os = self.run_layer(x, self.dec3, skips, os)
|
300 |
-
if return_list and 'dec_2' in return_list:
|
301 |
-
out_dict['dec_2'] = x.detach().cpu() # 128, 64, 256
|
302 |
-
x, skips, os = self.run_layer(x, self.dec2, skips, os)
|
303 |
-
if return_list and 'dec_1' in return_list:
|
304 |
-
out_dict['dec_1'] = x.detach().cpu() # 64, 64, 512
|
305 |
-
x, skips, os = self.run_layer(x, self.dec1, skips, os)
|
306 |
-
if return_list and 'dec_0' in return_list:
|
307 |
-
out_dict['dec_0'] = x.detach().cpu() # 32, 64, 1024
|
308 |
-
|
309 |
-
logits = torch.clone(x).detach()
|
310 |
-
x = self.dropout(x)
|
311 |
-
|
312 |
-
if return_logits:
|
313 |
-
return x, logits
|
314 |
-
if return_list is not None:
|
315 |
-
return out_dict
|
316 |
-
return x
|
317 |
-
|
318 |
-
def get_last_depth(self):
|
319 |
-
return self.last_channels
|
320 |
-
|
321 |
-
|
322 |
-
class Model(nn.Module):
|
323 |
-
def __init__(self, config):
|
324 |
-
super().__init__()
|
325 |
-
self.config = config
|
326 |
-
self.backbone = Backbone(params=self.config["backbone"])
|
327 |
-
self.decoder = Decoder(params=self.config["decoder"], OS=self.config["backbone"]["OS"],
|
328 |
-
feature_depth=self.backbone.get_last_depth())
|
329 |
-
|
330 |
-
def load_pretrained_weights(self, path):
|
331 |
-
w_dict = torch.load(path + "/backbone",
|
332 |
-
map_location=lambda storage, loc: storage)
|
333 |
-
self.backbone.load_state_dict(w_dict, strict=True)
|
334 |
-
w_dict = torch.load(path + "/segmentation_decoder",
|
335 |
-
map_location=lambda storage, loc: storage)
|
336 |
-
self.decoder.load_state_dict(w_dict, strict=True)
|
337 |
-
|
338 |
-
def forward(self, x, return_logits=False, return_final_logits=False, return_list=None, agg_type='depth'):
|
339 |
-
if return_logits:
|
340 |
-
logits = self.backbone(x, return_logits)
|
341 |
-
logits = F.adaptive_avg_pool2d(logits, (1, 1)).squeeze()
|
342 |
-
logits = torch.clone(logits).detach().cpu().numpy()
|
343 |
-
return logits
|
344 |
-
elif return_list is not None:
|
345 |
-
x, skips, enc_dict = self.backbone(x, return_list=return_list)
|
346 |
-
dec_dict = self.decoder(x, skips, return_list=return_list)
|
347 |
-
out_dict = {**enc_dict, **dec_dict}
|
348 |
-
return out_dict
|
349 |
-
elif return_final_logits:
|
350 |
-
assert agg_type in ['all', 'sector', 'depth']
|
351 |
-
y, skips = self.backbone(x)
|
352 |
-
y, logits = self.decoder(y, skips, True)
|
353 |
-
|
354 |
-
B, C, H, W = logits.shape
|
355 |
-
N = 16
|
356 |
-
|
357 |
-
# avg all
|
358 |
-
if agg_type == 'all':
|
359 |
-
logits = logits.mean([2, 3])
|
360 |
-
# avg in patch
|
361 |
-
elif agg_type == 'sector':
|
362 |
-
logits = logits.view(B, C, H, N, W // N).mean([2, 4]).reshape(B, -1)
|
363 |
-
# avg in row
|
364 |
-
elif agg_type == 'depth':
|
365 |
-
logits = logits.view(B, C, N, H // N, W).mean([3, 4]).reshape(B, -1)
|
366 |
-
|
367 |
-
logits = torch.clone(logits).detach().cpu().numpy()
|
368 |
-
return logits
|
369 |
-
else:
|
370 |
-
y, skips = self.backbone(x)
|
371 |
-
y = self.decoder(y, skips, False)
|
372 |
-
return y
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lidm/eval/models/spvcnn/__init__.py
DELETED
File without changes
|
lidm/eval/models/spvcnn/model.py
DELETED
@@ -1,179 +0,0 @@
|
|
1 |
-
import torch.nn as nn
|
2 |
-
|
3 |
-
try:
|
4 |
-
import torchsparse
|
5 |
-
import torchsparse.nn as spnn
|
6 |
-
from torchsparse import PointTensor
|
7 |
-
from ..ts.utils import initial_voxelize, point_to_voxel, voxel_to_point
|
8 |
-
from ..ts import basic_blocks
|
9 |
-
except ImportError:
|
10 |
-
raise Exception('Required torchsparse lib. Reference: https://github.com/mit-han-lab/torchsparse/tree/v1.4.0')
|
11 |
-
|
12 |
-
|
13 |
-
class Model(nn.Module):
|
14 |
-
def __init__(self, config):
|
15 |
-
super().__init__()
|
16 |
-
cr = config.model_params.cr
|
17 |
-
cs = config.model_params.layer_num
|
18 |
-
cs = [int(cr * x) for x in cs]
|
19 |
-
|
20 |
-
self.pres = self.vres = config.model_params.voxel_size
|
21 |
-
self.num_classes = config.model_params.num_class
|
22 |
-
|
23 |
-
self.stem = nn.Sequential(
|
24 |
-
spnn.Conv3d(config.model_params.input_dims, cs[0], kernel_size=3, stride=1),
|
25 |
-
spnn.BatchNorm(cs[0]), spnn.ReLU(True),
|
26 |
-
spnn.Conv3d(cs[0], cs[0], kernel_size=3, stride=1),
|
27 |
-
spnn.BatchNorm(cs[0]), spnn.ReLU(True))
|
28 |
-
|
29 |
-
self.stage1 = nn.Sequential(
|
30 |
-
basic_blocks.BasicConvolutionBlock(cs[0], cs[0], ks=2, stride=2, dilation=1),
|
31 |
-
basic_blocks.ResidualBlock(cs[0], cs[1], ks=3, stride=1, dilation=1),
|
32 |
-
basic_blocks.ResidualBlock(cs[1], cs[1], ks=3, stride=1, dilation=1),
|
33 |
-
)
|
34 |
-
|
35 |
-
self.stage2 = nn.Sequential(
|
36 |
-
basic_blocks.BasicConvolutionBlock(cs[1], cs[1], ks=2, stride=2, dilation=1),
|
37 |
-
basic_blocks.ResidualBlock(cs[1], cs[2], ks=3, stride=1, dilation=1),
|
38 |
-
basic_blocks.ResidualBlock(cs[2], cs[2], ks=3, stride=1, dilation=1),
|
39 |
-
)
|
40 |
-
|
41 |
-
self.stage3 = nn.Sequential(
|
42 |
-
basic_blocks.BasicConvolutionBlock(cs[2], cs[2], ks=2, stride=2, dilation=1),
|
43 |
-
basic_blocks.ResidualBlock(cs[2], cs[3], ks=3, stride=1, dilation=1),
|
44 |
-
basic_blocks.ResidualBlock(cs[3], cs[3], ks=3, stride=1, dilation=1),
|
45 |
-
)
|
46 |
-
|
47 |
-
self.stage4 = nn.Sequential(
|
48 |
-
basic_blocks.BasicConvolutionBlock(cs[3], cs[3], ks=2, stride=2, dilation=1),
|
49 |
-
basic_blocks.ResidualBlock(cs[3], cs[4], ks=3, stride=1, dilation=1),
|
50 |
-
basic_blocks.ResidualBlock(cs[4], cs[4], ks=3, stride=1, dilation=1),
|
51 |
-
)
|
52 |
-
|
53 |
-
self.up1 = nn.ModuleList([
|
54 |
-
basic_blocks.BasicDeconvolutionBlock(cs[4], cs[5], ks=2, stride=2),
|
55 |
-
nn.Sequential(
|
56 |
-
basic_blocks.ResidualBlock(cs[5] + cs[3], cs[5], ks=3, stride=1,
|
57 |
-
dilation=1),
|
58 |
-
basic_blocks.ResidualBlock(cs[5], cs[5], ks=3, stride=1, dilation=1),
|
59 |
-
)
|
60 |
-
])
|
61 |
-
|
62 |
-
self.up2 = nn.ModuleList([
|
63 |
-
basic_blocks.BasicDeconvolutionBlock(cs[5], cs[6], ks=2, stride=2),
|
64 |
-
nn.Sequential(
|
65 |
-
basic_blocks.ResidualBlock(cs[6] + cs[2], cs[6], ks=3, stride=1,
|
66 |
-
dilation=1),
|
67 |
-
basic_blocks.ResidualBlock(cs[6], cs[6], ks=3, stride=1, dilation=1),
|
68 |
-
)
|
69 |
-
])
|
70 |
-
|
71 |
-
self.up3 = nn.ModuleList([
|
72 |
-
basic_blocks.BasicDeconvolutionBlock(cs[6], cs[7], ks=2, stride=2),
|
73 |
-
nn.Sequential(
|
74 |
-
basic_blocks.ResidualBlock(cs[7] + cs[1], cs[7], ks=3, stride=1,
|
75 |
-
dilation=1),
|
76 |
-
basic_blocks.ResidualBlock(cs[7], cs[7], ks=3, stride=1, dilation=1),
|
77 |
-
)
|
78 |
-
])
|
79 |
-
|
80 |
-
self.up4 = nn.ModuleList([
|
81 |
-
basic_blocks.BasicDeconvolutionBlock(cs[7], cs[8], ks=2, stride=2),
|
82 |
-
nn.Sequential(
|
83 |
-
basic_blocks.ResidualBlock(cs[8] + cs[0], cs[8], ks=3, stride=1,
|
84 |
-
dilation=1),
|
85 |
-
basic_blocks.ResidualBlock(cs[8], cs[8], ks=3, stride=1, dilation=1),
|
86 |
-
)
|
87 |
-
])
|
88 |
-
|
89 |
-
self.classifier = nn.Sequential(nn.Linear(cs[8], self.num_classes))
|
90 |
-
|
91 |
-
self.point_transforms = nn.ModuleList([
|
92 |
-
nn.Sequential(
|
93 |
-
nn.Linear(cs[0], cs[4]),
|
94 |
-
nn.BatchNorm1d(cs[4]),
|
95 |
-
nn.ReLU(True),
|
96 |
-
),
|
97 |
-
nn.Sequential(
|
98 |
-
nn.Linear(cs[4], cs[6]),
|
99 |
-
nn.BatchNorm1d(cs[6]),
|
100 |
-
nn.ReLU(True),
|
101 |
-
),
|
102 |
-
nn.Sequential(
|
103 |
-
nn.Linear(cs[6], cs[8]),
|
104 |
-
nn.BatchNorm1d(cs[8]),
|
105 |
-
nn.ReLU(True),
|
106 |
-
)
|
107 |
-
])
|
108 |
-
|
109 |
-
self.weight_initialization()
|
110 |
-
self.dropout = nn.Dropout(0.3, True)
|
111 |
-
|
112 |
-
def weight_initialization(self):
|
113 |
-
for m in self.modules():
|
114 |
-
if isinstance(m, nn.BatchNorm1d):
|
115 |
-
nn.init.constant_(m.weight, 1)
|
116 |
-
nn.init.constant_(m.bias, 0)
|
117 |
-
|
118 |
-
def forward(self, data_dict, return_logits=False, return_final_logits=False):
|
119 |
-
x = data_dict['lidar']
|
120 |
-
|
121 |
-
# x: SparseTensor z: PointTensor
|
122 |
-
z = PointTensor(x.F, x.C.float())
|
123 |
-
|
124 |
-
x0 = initial_voxelize(z, self.pres, self.vres)
|
125 |
-
|
126 |
-
x0 = self.stem(x0)
|
127 |
-
z0 = voxel_to_point(x0, z, nearest=False)
|
128 |
-
z0.F = z0.F
|
129 |
-
|
130 |
-
x1 = point_to_voxel(x0, z0)
|
131 |
-
x1 = self.stage1(x1)
|
132 |
-
x2 = self.stage2(x1)
|
133 |
-
x3 = self.stage3(x2)
|
134 |
-
x4 = self.stage4(x3)
|
135 |
-
z1 = voxel_to_point(x4, z0)
|
136 |
-
z1.F = z1.F + self.point_transforms[0](z0.F)
|
137 |
-
|
138 |
-
y1 = point_to_voxel(x4, z1)
|
139 |
-
|
140 |
-
if return_logits:
|
141 |
-
output_dict = dict()
|
142 |
-
output_dict['logits'] = y1.F
|
143 |
-
output_dict['batch_indices'] = y1.C[:, -1]
|
144 |
-
return output_dict
|
145 |
-
|
146 |
-
y1.F = self.dropout(y1.F)
|
147 |
-
y1 = self.up1[0](y1)
|
148 |
-
y1 = torchsparse.cat([y1, x3])
|
149 |
-
y1 = self.up1[1](y1)
|
150 |
-
|
151 |
-
y2 = self.up2[0](y1)
|
152 |
-
y2 = torchsparse.cat([y2, x2])
|
153 |
-
y2 = self.up2[1](y2)
|
154 |
-
z2 = voxel_to_point(y2, z1)
|
155 |
-
z2.F = z2.F + self.point_transforms[1](z1.F)
|
156 |
-
|
157 |
-
y3 = point_to_voxel(y2, z2)
|
158 |
-
y3.F = self.dropout(y3.F)
|
159 |
-
y3 = self.up3[0](y3)
|
160 |
-
y3 = torchsparse.cat([y3, x1])
|
161 |
-
y3 = self.up3[1](y3)
|
162 |
-
|
163 |
-
y4 = self.up4[0](y3)
|
164 |
-
y4 = torchsparse.cat([y4, x0])
|
165 |
-
y4 = self.up4[1](y4)
|
166 |
-
z3 = voxel_to_point(y4, z2)
|
167 |
-
z3.F = z3.F + self.point_transforms[2](z2.F)
|
168 |
-
|
169 |
-
if return_final_logits:
|
170 |
-
output_dict = dict()
|
171 |
-
output_dict['logits'] = z3.F
|
172 |
-
output_dict['coords'] = z3.C[:, :3]
|
173 |
-
output_dict['batch_indices'] = z3.C[:, -1].long()
|
174 |
-
return output_dict
|
175 |
-
|
176 |
-
# output = self.classifier(z3.F)
|
177 |
-
data_dict['logits'] = z3.F
|
178 |
-
|
179 |
-
return data_dict
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lidm/eval/models/ts/__init__.py
DELETED
File without changes
|
lidm/eval/models/ts/basic_blocks.py
DELETED
@@ -1,79 +0,0 @@
|
|
1 |
-
#!/usr/bin/env python
|
2 |
-
# encoding: utf-8
|
3 |
-
'''
|
4 |
-
@author: Xu Yan
|
5 |
-
@file: basic_blocks.py
|
6 |
-
@time: 2021/4/14 22:53
|
7 |
-
'''
|
8 |
-
import torch.nn as nn
|
9 |
-
|
10 |
-
try:
|
11 |
-
import torchsparse.nn as spnn
|
12 |
-
except:
|
13 |
-
print('To install torchsparse 1.4.0, please refer to https://github.com/mit-han-lab/torchsparse/tree/74099d10a51c71c14318bce63d6421f698b24f24')
|
14 |
-
|
15 |
-
|
16 |
-
class BasicConvolutionBlock(nn.Module):
|
17 |
-
def __init__(self, inc, outc, ks=3, stride=1, dilation=1):
|
18 |
-
super().__init__()
|
19 |
-
self.net = nn.Sequential(
|
20 |
-
spnn.Conv3d(
|
21 |
-
inc,
|
22 |
-
outc,
|
23 |
-
kernel_size=ks,
|
24 |
-
dilation=dilation,
|
25 |
-
stride=stride), spnn.BatchNorm(outc),
|
26 |
-
spnn.ReLU(True))
|
27 |
-
|
28 |
-
def forward(self, x):
|
29 |
-
out = self.net(x)
|
30 |
-
return out
|
31 |
-
|
32 |
-
|
33 |
-
class BasicDeconvolutionBlock(nn.Module):
|
34 |
-
def __init__(self, inc, outc, ks=3, stride=1):
|
35 |
-
super().__init__()
|
36 |
-
self.net = nn.Sequential(
|
37 |
-
spnn.Conv3d(
|
38 |
-
inc,
|
39 |
-
outc,
|
40 |
-
kernel_size=ks,
|
41 |
-
stride=stride,
|
42 |
-
transposed=True),
|
43 |
-
spnn.BatchNorm(outc),
|
44 |
-
spnn.ReLU(True))
|
45 |
-
|
46 |
-
def forward(self, x):
|
47 |
-
return self.net(x)
|
48 |
-
|
49 |
-
|
50 |
-
class ResidualBlock(nn.Module):
|
51 |
-
def __init__(self, inc, outc, ks=3, stride=1, dilation=1):
|
52 |
-
super().__init__()
|
53 |
-
self.net = nn.Sequential(
|
54 |
-
spnn.Conv3d(
|
55 |
-
inc,
|
56 |
-
outc,
|
57 |
-
kernel_size=ks,
|
58 |
-
dilation=dilation,
|
59 |
-
stride=stride), spnn.BatchNorm(outc),
|
60 |
-
spnn.ReLU(True),
|
61 |
-
spnn.Conv3d(
|
62 |
-
outc,
|
63 |
-
outc,
|
64 |
-
kernel_size=ks,
|
65 |
-
dilation=dilation,
|
66 |
-
stride=1),
|
67 |
-
spnn.BatchNorm(outc))
|
68 |
-
|
69 |
-
self.downsample = nn.Sequential() if (inc == outc and stride == 1) else \
|
70 |
-
nn.Sequential(
|
71 |
-
spnn.Conv3d(inc, outc, kernel_size=1, dilation=1, stride=stride),
|
72 |
-
spnn.BatchNorm(outc)
|
73 |
-
)
|
74 |
-
|
75 |
-
self.ReLU = spnn.ReLU(True)
|
76 |
-
|
77 |
-
def forward(self, x):
|
78 |
-
out = self.ReLU(self.net(x) + self.downsample(x))
|
79 |
-
return out
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lidm/eval/models/ts/utils.py
DELETED
@@ -1,90 +0,0 @@
|
|
1 |
-
import torch
|
2 |
-
|
3 |
-
try:
|
4 |
-
import torchsparse.nn.functional as F
|
5 |
-
from torchsparse import PointTensor, SparseTensor
|
6 |
-
from torchsparse.nn.utils import get_kernel_offsets
|
7 |
-
except:
|
8 |
-
print('To install torchsparse 1.4.0, please refer to https://github.com/mit-han-lab/torchsparse/tree/74099d10a51c71c14318bce63d6421f698b24f24')
|
9 |
-
|
10 |
-
__all__ = ['initial_voxelize', 'point_to_voxel', 'voxel_to_point']
|
11 |
-
|
12 |
-
|
13 |
-
# z: PointTensor
|
14 |
-
# return: SparseTensor
|
15 |
-
def initial_voxelize(z, init_res, after_res):
|
16 |
-
new_float_coord = torch.cat([(z.C[:, :3] * init_res) / after_res, z.C[:, -1].view(-1, 1)], 1)
|
17 |
-
|
18 |
-
pc_hash = F.sphash(torch.floor(new_float_coord).int())
|
19 |
-
sparse_hash = torch.unique(pc_hash)
|
20 |
-
idx_query = F.sphashquery(pc_hash, sparse_hash)
|
21 |
-
counts = F.spcount(idx_query.int(), len(sparse_hash))
|
22 |
-
|
23 |
-
inserted_coords = F.spvoxelize(torch.floor(new_float_coord), idx_query, counts)
|
24 |
-
inserted_coords = torch.round(inserted_coords).int()
|
25 |
-
inserted_feat = F.spvoxelize(z.F, idx_query, counts)
|
26 |
-
|
27 |
-
new_tensor = SparseTensor(inserted_feat, inserted_coords, 1)
|
28 |
-
new_tensor.cmaps.setdefault(new_tensor.stride, new_tensor.coords)
|
29 |
-
z.additional_features['idx_query'][1] = idx_query
|
30 |
-
z.additional_features['counts'][1] = counts
|
31 |
-
z.C = new_float_coord
|
32 |
-
|
33 |
-
return new_tensor
|
34 |
-
|
35 |
-
|
36 |
-
# x: SparseTensor, z: PointTensor
|
37 |
-
# return: SparseTensor
|
38 |
-
def point_to_voxel(x, z):
|
39 |
-
if z.additional_features is None or \
|
40 |
-
z.additional_features.get('idx_query') is None or \
|
41 |
-
z.additional_features['idx_query'].get(x.s) is None:
|
42 |
-
pc_hash = F.sphash(
|
43 |
-
torch.cat([torch.floor(z.C[:, :3] / x.s[0]).int() * x.s[0], z.C[:, -1].int().view(-1, 1)], 1))
|
44 |
-
sparse_hash = F.sphash(x.C)
|
45 |
-
idx_query = F.sphashquery(pc_hash, sparse_hash)
|
46 |
-
counts = F.spcount(idx_query.int(), x.C.shape[0])
|
47 |
-
z.additional_features['idx_query'][x.s] = idx_query
|
48 |
-
z.additional_features['counts'][x.s] = counts
|
49 |
-
else:
|
50 |
-
idx_query = z.additional_features['idx_query'][x.s]
|
51 |
-
counts = z.additional_features['counts'][x.s]
|
52 |
-
|
53 |
-
inserted_feat = F.spvoxelize(z.F, idx_query, counts)
|
54 |
-
new_tensor = SparseTensor(inserted_feat, x.C, x.s)
|
55 |
-
new_tensor.cmaps = x.cmaps
|
56 |
-
new_tensor.kmaps = x.kmaps
|
57 |
-
|
58 |
-
return new_tensor
|
59 |
-
|
60 |
-
|
61 |
-
# x: SparseTensor, z: PointTensor
|
62 |
-
# return: PointTensor
|
63 |
-
def voxel_to_point(x, z, nearest=False):
|
64 |
-
if z.idx_query is None or z.weights is None or z.idx_query.get(x.s) is None or z.weights.get(x.s) is None:
|
65 |
-
off = get_kernel_offsets(2, x.s, 1, device=z.F.device)
|
66 |
-
old_hash = F.sphash(
|
67 |
-
torch.cat([
|
68 |
-
torch.floor(z.C[:, :3] / x.s[0]).int() * x.s[0],
|
69 |
-
z.C[:, -1].int().view(-1, 1)], 1), off)
|
70 |
-
pc_hash = F.sphash(x.C.to(z.F.device))
|
71 |
-
idx_query = F.sphashquery(old_hash, pc_hash)
|
72 |
-
weights = F.calc_ti_weights(z.C, idx_query, scale=x.s[0]).transpose(0, 1).contiguous()
|
73 |
-
idx_query = idx_query.transpose(0, 1).contiguous()
|
74 |
-
if nearest:
|
75 |
-
weights[:, 1:] = 0.
|
76 |
-
idx_query[:, 1:] = -1
|
77 |
-
new_feat = F.spdevoxelize(x.F, idx_query, weights)
|
78 |
-
new_tensor = PointTensor(new_feat, z.C, idx_query=z.idx_query, weights=z.weights)
|
79 |
-
new_tensor.additional_features = z.additional_features
|
80 |
-
new_tensor.idx_query[x.s] = idx_query
|
81 |
-
new_tensor.weights[x.s] = weights
|
82 |
-
z.idx_query[x.s] = idx_query
|
83 |
-
z.weights[x.s] = weights
|
84 |
-
|
85 |
-
else:
|
86 |
-
new_feat = F.spdevoxelize(x.F, z.idx_query.get(x.s), z.weights.get(x.s))
|
87 |
-
new_tensor = PointTensor(new_feat, z.C, idx_query=z.idx_query, weights=z.weights)
|
88 |
-
new_tensor.additional_features = z.additional_features
|
89 |
-
|
90 |
-
return new_tensor
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lidm/eval/modules/__init__.py
DELETED
File without changes
|
lidm/eval/modules/chamfer2D/__init__.py
DELETED
File without changes
|
lidm/eval/modules/chamfer2D/chamfer2D.cu
DELETED
@@ -1,182 +0,0 @@
|
|
1 |
-
|
2 |
-
#include <stdio.h>
|
3 |
-
#include <ATen/ATen.h>
|
4 |
-
|
5 |
-
#include <cuda.h>
|
6 |
-
#include <cuda_runtime.h>
|
7 |
-
|
8 |
-
#include <vector>
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
__global__ void NmDistanceKernel(int b,int n,const float * xyz,int m,const float * xyz2,float * result,int * result_i){
|
13 |
-
const int batch=512;
|
14 |
-
__shared__ float buf[batch*2];
|
15 |
-
for (int i=blockIdx.x;i<b;i+=gridDim.x){
|
16 |
-
for (int k2=0;k2<m;k2+=batch){
|
17 |
-
int end_k=min(m,k2+batch)-k2;
|
18 |
-
for (int j=threadIdx.x;j<end_k*2;j+=blockDim.x){
|
19 |
-
buf[j]=xyz2[(i*m+k2)*2+j];
|
20 |
-
}
|
21 |
-
__syncthreads();
|
22 |
-
for (int j=threadIdx.x+blockIdx.y*blockDim.x;j<n;j+=blockDim.x*gridDim.y){
|
23 |
-
float x1=xyz[(i*n+j)*2+0];
|
24 |
-
float y1=xyz[(i*n+j)*2+1];
|
25 |
-
int best_i=0;
|
26 |
-
float best=0;
|
27 |
-
int end_ka=end_k-(end_k&2);
|
28 |
-
if (end_ka==batch){
|
29 |
-
for (int k=0;k<batch;k+=4){
|
30 |
-
{
|
31 |
-
float x2=buf[k*2+0]-x1;
|
32 |
-
float y2=buf[k*2+1]-y1;
|
33 |
-
float d=x2*x2+y2*y2;
|
34 |
-
if (k==0 || d<best){
|
35 |
-
best=d;
|
36 |
-
best_i=k+k2;
|
37 |
-
}
|
38 |
-
}
|
39 |
-
{
|
40 |
-
float x2=buf[k*2+2]-x1;
|
41 |
-
float y2=buf[k*2+3]-y1;
|
42 |
-
float d=x2*x2+y2*y2;
|
43 |
-
if (d<best){
|
44 |
-
best=d;
|
45 |
-
best_i=k+k2+1;
|
46 |
-
}
|
47 |
-
}
|
48 |
-
{
|
49 |
-
float x2=buf[k*2+4]-x1;
|
50 |
-
float y2=buf[k*2+5]-y1;
|
51 |
-
float d=x2*x2+y2*y2;
|
52 |
-
if (d<best){
|
53 |
-
best=d;
|
54 |
-
best_i=k+k2+2;
|
55 |
-
}
|
56 |
-
}
|
57 |
-
{
|
58 |
-
float x2=buf[k*2+6]-x1;
|
59 |
-
float y2=buf[k*2+7]-y1;
|
60 |
-
float d=x2*x2+y2*y2;
|
61 |
-
if (d<best){
|
62 |
-
best=d;
|
63 |
-
best_i=k+k2+3;
|
64 |
-
}
|
65 |
-
}
|
66 |
-
}
|
67 |
-
}else{
|
68 |
-
for (int k=0;k<end_ka;k+=4){
|
69 |
-
{
|
70 |
-
float x2=buf[k*2+0]-x1;
|
71 |
-
float y2=buf[k*2+1]-y1;
|
72 |
-
float d=x2*x2+y2*y2;
|
73 |
-
if (k==0 || d<best){
|
74 |
-
best=d;
|
75 |
-
best_i=k+k2;
|
76 |
-
}
|
77 |
-
}
|
78 |
-
{
|
79 |
-
float x2=buf[k*2+2]-x1;
|
80 |
-
float y2=buf[k*2+3]-y1;
|
81 |
-
float d=x2*x2+y2*y2;
|
82 |
-
if (d<best){
|
83 |
-
best=d;
|
84 |
-
best_i=k+k2+1;
|
85 |
-
}
|
86 |
-
}
|
87 |
-
{
|
88 |
-
float x2=buf[k*2+4]-x1;
|
89 |
-
float y2=buf[k*2+5]-y1;
|
90 |
-
float d=x2*x2+y2*y2;
|
91 |
-
if (d<best){
|
92 |
-
best=d;
|
93 |
-
best_i=k+k2+2;
|
94 |
-
}
|
95 |
-
}
|
96 |
-
{
|
97 |
-
float x2=buf[k*2+6]-x1;
|
98 |
-
float y2=buf[k*2+7]-y1;
|
99 |
-
float d=x2*x2+y2*y2;
|
100 |
-
if (d<best){
|
101 |
-
best=d;
|
102 |
-
best_i=k+k2+3;
|
103 |
-
}
|
104 |
-
}
|
105 |
-
}
|
106 |
-
}
|
107 |
-
for (int k=end_ka;k<end_k;k++){
|
108 |
-
float x2=buf[k*2+0]-x1;
|
109 |
-
float y2=buf[k*2+1]-y1;
|
110 |
-
float d=x2*x2+y2*y2;
|
111 |
-
if (k==0 || d<best){
|
112 |
-
best=d;
|
113 |
-
best_i=k+k2;
|
114 |
-
}
|
115 |
-
}
|
116 |
-
if (k2==0 || result[(i*n+j)]>best){
|
117 |
-
result[(i*n+j)]=best;
|
118 |
-
result_i[(i*n+j)]=best_i;
|
119 |
-
}
|
120 |
-
}
|
121 |
-
__syncthreads();
|
122 |
-
}
|
123 |
-
}
|
124 |
-
}
|
125 |
-
// int chamfer_cuda_forward(int b,int n,const float * xyz,int m,const float * xyz2,float * result,int * result_i,float * result2,int * result2_i, cudaStream_t stream){
|
126 |
-
int chamfer_cuda_forward(at::Tensor xyz1, at::Tensor xyz2, at::Tensor dist1, at::Tensor dist2, at::Tensor idx1, at::Tensor idx2){
|
127 |
-
|
128 |
-
const auto batch_size = xyz1.size(0);
|
129 |
-
const auto n = xyz1.size(1); //num_points point cloud A
|
130 |
-
const auto m = xyz2.size(1); //num_points point cloud B
|
131 |
-
|
132 |
-
NmDistanceKernel<<<dim3(32,16,1),512>>>(batch_size, n, xyz1.data<float>(), m, xyz2.data<float>(), dist1.data<float>(), idx1.data<int>());
|
133 |
-
NmDistanceKernel<<<dim3(32,16,1),512>>>(batch_size, m, xyz2.data<float>(), n, xyz1.data<float>(), dist2.data<float>(), idx2.data<int>());
|
134 |
-
|
135 |
-
cudaError_t err = cudaGetLastError();
|
136 |
-
if (err != cudaSuccess) {
|
137 |
-
printf("error in nnd updateOutput: %s\n", cudaGetErrorString(err));
|
138 |
-
//THError("aborting");
|
139 |
-
return 0;
|
140 |
-
}
|
141 |
-
return 1;
|
142 |
-
|
143 |
-
|
144 |
-
}
|
145 |
-
__global__ void NmDistanceGradKernel(int b,int n,const float * xyz1,int m,const float * xyz2,const float * grad_dist1,const int * idx1,float * grad_xyz1,float * grad_xyz2){
|
146 |
-
for (int i=blockIdx.x;i<b;i+=gridDim.x){
|
147 |
-
for (int j=threadIdx.x+blockIdx.y*blockDim.x;j<n;j+=blockDim.x*gridDim.y){
|
148 |
-
float x1=xyz1[(i*n+j)*2+0];
|
149 |
-
float y1=xyz1[(i*n+j)*2+1];
|
150 |
-
int j2=idx1[i*n+j];
|
151 |
-
float x2=xyz2[(i*m+j2)*2+0];
|
152 |
-
float y2=xyz2[(i*m+j2)*2+1];
|
153 |
-
float g=grad_dist1[i*n+j]*2;
|
154 |
-
atomicAdd(&(grad_xyz1[(i*n+j)*2+0]),g*(x1-x2));
|
155 |
-
atomicAdd(&(grad_xyz1[(i*n+j)*2+1]),g*(y1-y2));
|
156 |
-
atomicAdd(&(grad_xyz2[(i*m+j2)*2+0]),-(g*(x1-x2)));
|
157 |
-
atomicAdd(&(grad_xyz2[(i*m+j2)*2+1]),-(g*(y1-y2)));
|
158 |
-
}
|
159 |
-
}
|
160 |
-
}
|
161 |
-
// int chamfer_cuda_backward(int b,int n,const float * xyz1,int m,const float * xyz2,const float * grad_dist1,const int * idx1,const float * grad_dist2,const int * idx2,float * grad_xyz1,float * grad_xyz2, cudaStream_t stream){
|
162 |
-
int chamfer_cuda_backward(at::Tensor xyz1, at::Tensor xyz2, at::Tensor gradxyz1, at::Tensor gradxyz2, at::Tensor graddist1, at::Tensor graddist2, at::Tensor idx1, at::Tensor idx2){
|
163 |
-
// cudaMemset(grad_xyz1,0,b*n*3*4);
|
164 |
-
// cudaMemset(grad_xyz2,0,b*m*3*4);
|
165 |
-
|
166 |
-
const auto batch_size = xyz1.size(0);
|
167 |
-
const auto n = xyz1.size(1); //num_points point cloud A
|
168 |
-
const auto m = xyz2.size(1); //num_points point cloud B
|
169 |
-
|
170 |
-
NmDistanceGradKernel<<<dim3(1,16,1),256>>>(batch_size,n,xyz1.data<float>(),m,xyz2.data<float>(),graddist1.data<float>(),idx1.data<int>(),gradxyz1.data<float>(),gradxyz2.data<float>());
|
171 |
-
NmDistanceGradKernel<<<dim3(1,16,1),256>>>(batch_size,m,xyz2.data<float>(),n,xyz1.data<float>(),graddist2.data<float>(),idx2.data<int>(),gradxyz2.data<float>(),gradxyz1.data<float>());
|
172 |
-
|
173 |
-
cudaError_t err = cudaGetLastError();
|
174 |
-
if (err != cudaSuccess) {
|
175 |
-
printf("error in nnd get grad: %s\n", cudaGetErrorString(err));
|
176 |
-
//THError("aborting");
|
177 |
-
return 0;
|
178 |
-
}
|
179 |
-
return 1;
|
180 |
-
|
181 |
-
}
|
182 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lidm/eval/modules/chamfer2D/chamfer_cuda.cpp
DELETED
@@ -1,33 +0,0 @@
|
|
1 |
-
#include <torch/torch.h>
|
2 |
-
#include <vector>
|
3 |
-
|
4 |
-
///TMP
|
5 |
-
//#include "common.h"
|
6 |
-
/// NOT TMP
|
7 |
-
|
8 |
-
|
9 |
-
int chamfer_cuda_forward(at::Tensor xyz1, at::Tensor xyz2, at::Tensor dist1, at::Tensor dist2, at::Tensor idx1, at::Tensor idx2);
|
10 |
-
|
11 |
-
|
12 |
-
int chamfer_cuda_backward(at::Tensor xyz1, at::Tensor xyz2, at::Tensor gradxyz1, at::Tensor gradxyz2, at::Tensor graddist1, at::Tensor graddist2, at::Tensor idx1, at::Tensor idx2);
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
int chamfer_forward(at::Tensor xyz1, at::Tensor xyz2, at::Tensor dist1, at::Tensor dist2, at::Tensor idx1, at::Tensor idx2) {
|
18 |
-
return chamfer_cuda_forward(xyz1, xyz2, dist1, dist2, idx1, idx2);
|
19 |
-
}
|
20 |
-
|
21 |
-
|
22 |
-
int chamfer_backward(at::Tensor xyz1, at::Tensor xyz2, at::Tensor gradxyz1, at::Tensor gradxyz2, at::Tensor graddist1,
|
23 |
-
at::Tensor graddist2, at::Tensor idx1, at::Tensor idx2) {
|
24 |
-
|
25 |
-
return chamfer_cuda_backward(xyz1, xyz2, gradxyz1, gradxyz2, graddist1, graddist2, idx1, idx2);
|
26 |
-
}
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
|
31 |
-
m.def("forward", &chamfer_forward, "chamfer forward (CUDA)");
|
32 |
-
m.def("backward", &chamfer_backward, "chamfer backward (CUDA)");
|
33 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lidm/eval/modules/chamfer2D/dist_chamfer_2D.py
DELETED
@@ -1,84 +0,0 @@
|
|
1 |
-
from torch import nn
|
2 |
-
from torch.autograd import Function
|
3 |
-
import torch
|
4 |
-
import importlib
|
5 |
-
import os
|
6 |
-
|
7 |
-
chamfer_found = importlib.find_loader("chamfer_2D") is not None
|
8 |
-
if not chamfer_found:
|
9 |
-
## Cool trick from https://github.com/chrdiller
|
10 |
-
print("Jitting Chamfer 2D")
|
11 |
-
cur_path = os.path.dirname(os.path.abspath(__file__))
|
12 |
-
build_path = cur_path.replace('chamfer2D', 'tmp')
|
13 |
-
os.makedirs(build_path, exist_ok=True)
|
14 |
-
|
15 |
-
from torch.utils.cpp_extension import load
|
16 |
-
|
17 |
-
chamfer_2D = load(name="chamfer_2D",
|
18 |
-
sources=[
|
19 |
-
"/".join(os.path.abspath(__file__).split('/')[:-1] + ["chamfer_cuda.cpp"]),
|
20 |
-
"/".join(os.path.abspath(__file__).split('/')[:-1] + ["chamfer2D.cu"]),
|
21 |
-
], build_directory=build_path)
|
22 |
-
print("Loaded JIT 2D CUDA chamfer distance")
|
23 |
-
|
24 |
-
else:
|
25 |
-
import chamfer_2D
|
26 |
-
|
27 |
-
print("Loaded compiled 2D CUDA chamfer distance")
|
28 |
-
|
29 |
-
|
30 |
-
# Chamfer's distance module @thibaultgroueix
|
31 |
-
# GPU tensors only
|
32 |
-
class chamfer_2DFunction(Function):
|
33 |
-
@staticmethod
|
34 |
-
def forward(ctx, xyz1, xyz2):
|
35 |
-
batchsize, n, dim = xyz1.size()
|
36 |
-
assert dim == 2, "Wrong last dimension for the chamfer distance 's input! Check with .size()"
|
37 |
-
_, m, dim = xyz2.size()
|
38 |
-
assert dim == 2, "Wrong last dimension for the chamfer distance 's input! Check with .size()"
|
39 |
-
device = xyz1.device
|
40 |
-
|
41 |
-
device = xyz1.device
|
42 |
-
|
43 |
-
dist1 = torch.zeros(batchsize, n)
|
44 |
-
dist2 = torch.zeros(batchsize, m)
|
45 |
-
|
46 |
-
idx1 = torch.zeros(batchsize, n).type(torch.IntTensor)
|
47 |
-
idx2 = torch.zeros(batchsize, m).type(torch.IntTensor)
|
48 |
-
|
49 |
-
dist1 = dist1.to(device)
|
50 |
-
dist2 = dist2.to(device)
|
51 |
-
idx1 = idx1.to(device)
|
52 |
-
idx2 = idx2.to(device)
|
53 |
-
torch.cuda.set_device(device)
|
54 |
-
|
55 |
-
chamfer_2D.forward(xyz1, xyz2, dist1, dist2, idx1, idx2)
|
56 |
-
ctx.save_for_backward(xyz1, xyz2, idx1, idx2)
|
57 |
-
return dist1, dist2, idx1, idx2
|
58 |
-
|
59 |
-
@staticmethod
|
60 |
-
def backward(ctx, graddist1, graddist2, gradidx1, gradidx2):
|
61 |
-
xyz1, xyz2, idx1, idx2 = ctx.saved_tensors
|
62 |
-
graddist1 = graddist1.contiguous()
|
63 |
-
graddist2 = graddist2.contiguous()
|
64 |
-
device = graddist1.device
|
65 |
-
|
66 |
-
gradxyz1 = torch.zeros(xyz1.size())
|
67 |
-
gradxyz2 = torch.zeros(xyz2.size())
|
68 |
-
|
69 |
-
gradxyz1 = gradxyz1.to(device)
|
70 |
-
gradxyz2 = gradxyz2.to(device)
|
71 |
-
chamfer_2D.backward(
|
72 |
-
xyz1, xyz2, gradxyz1, gradxyz2, graddist1, graddist2, idx1, idx2
|
73 |
-
)
|
74 |
-
return gradxyz1, gradxyz2
|
75 |
-
|
76 |
-
|
77 |
-
class chamfer_2DDist(nn.Module):
|
78 |
-
def __init__(self):
|
79 |
-
super(chamfer_2DDist, self).__init__()
|
80 |
-
|
81 |
-
def forward(self, input1, input2):
|
82 |
-
input1 = input1.contiguous()
|
83 |
-
input2 = input2.contiguous()
|
84 |
-
return chamfer_2DFunction.apply(input1, input2)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lidm/eval/modules/chamfer2D/setup.py
DELETED
@@ -1,14 +0,0 @@
|
|
1 |
-
from setuptools import setup
|
2 |
-
from torch.utils.cpp_extension import BuildExtension, CUDAExtension
|
3 |
-
|
4 |
-
setup(
|
5 |
-
name='chamfer_2D',
|
6 |
-
ext_modules=[
|
7 |
-
CUDAExtension('chamfer_2D', [
|
8 |
-
"/".join(__file__.split('/')[:-1] + ['chamfer_cuda.cpp']),
|
9 |
-
"/".join(__file__.split('/')[:-1] + ['chamfer2D.cu']),
|
10 |
-
]),
|
11 |
-
],
|
12 |
-
cmdclass={
|
13 |
-
'build_ext': BuildExtension
|
14 |
-
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lidm/eval/modules/chamfer3D/__init__.py
DELETED
File without changes
|
lidm/eval/modules/chamfer3D/chamfer3D.cu
DELETED
@@ -1,196 +0,0 @@
|
|
1 |
-
|
2 |
-
#include <stdio.h>
|
3 |
-
#include <ATen/ATen.h>
|
4 |
-
|
5 |
-
#include <cuda.h>
|
6 |
-
#include <cuda_runtime.h>
|
7 |
-
|
8 |
-
#include <vector>
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
__global__ void NmDistanceKernel(int b,int n,const float * xyz,int m,const float * xyz2,float * result,int * result_i){
|
13 |
-
const int batch=512;
|
14 |
-
__shared__ float buf[batch*3];
|
15 |
-
for (int i=blockIdx.x;i<b;i+=gridDim.x){
|
16 |
-
for (int k2=0;k2<m;k2+=batch){
|
17 |
-
int end_k=min(m,k2+batch)-k2;
|
18 |
-
for (int j=threadIdx.x;j<end_k*3;j+=blockDim.x){
|
19 |
-
buf[j]=xyz2[(i*m+k2)*3+j];
|
20 |
-
}
|
21 |
-
__syncthreads();
|
22 |
-
for (int j=threadIdx.x+blockIdx.y*blockDim.x;j<n;j+=blockDim.x*gridDim.y){
|
23 |
-
float x1=xyz[(i*n+j)*3+0];
|
24 |
-
float y1=xyz[(i*n+j)*3+1];
|
25 |
-
float z1=xyz[(i*n+j)*3+2];
|
26 |
-
int best_i=0;
|
27 |
-
float best=0;
|
28 |
-
int end_ka=end_k-(end_k&3);
|
29 |
-
if (end_ka==batch){
|
30 |
-
for (int k=0;k<batch;k+=4){
|
31 |
-
{
|
32 |
-
float x2=buf[k*3+0]-x1;
|
33 |
-
float y2=buf[k*3+1]-y1;
|
34 |
-
float z2=buf[k*3+2]-z1;
|
35 |
-
float d=x2*x2+y2*y2+z2*z2;
|
36 |
-
if (k==0 || d<best){
|
37 |
-
best=d;
|
38 |
-
best_i=k+k2;
|
39 |
-
}
|
40 |
-
}
|
41 |
-
{
|
42 |
-
float x2=buf[k*3+3]-x1;
|
43 |
-
float y2=buf[k*3+4]-y1;
|
44 |
-
float z2=buf[k*3+5]-z1;
|
45 |
-
float d=x2*x2+y2*y2+z2*z2;
|
46 |
-
if (d<best){
|
47 |
-
best=d;
|
48 |
-
best_i=k+k2+1;
|
49 |
-
}
|
50 |
-
}
|
51 |
-
{
|
52 |
-
float x2=buf[k*3+6]-x1;
|
53 |
-
float y2=buf[k*3+7]-y1;
|
54 |
-
float z2=buf[k*3+8]-z1;
|
55 |
-
float d=x2*x2+y2*y2+z2*z2;
|
56 |
-
if (d<best){
|
57 |
-
best=d;
|
58 |
-
best_i=k+k2+2;
|
59 |
-
}
|
60 |
-
}
|
61 |
-
{
|
62 |
-
float x2=buf[k*3+9]-x1;
|
63 |
-
float y2=buf[k*3+10]-y1;
|
64 |
-
float z2=buf[k*3+11]-z1;
|
65 |
-
float d=x2*x2+y2*y2+z2*z2;
|
66 |
-
if (d<best){
|
67 |
-
best=d;
|
68 |
-
best_i=k+k2+3;
|
69 |
-
}
|
70 |
-
}
|
71 |
-
}
|
72 |
-
}else{
|
73 |
-
for (int k=0;k<end_ka;k+=4){
|
74 |
-
{
|
75 |
-
float x2=buf[k*3+0]-x1;
|
76 |
-
float y2=buf[k*3+1]-y1;
|
77 |
-
float z2=buf[k*3+2]-z1;
|
78 |
-
float d=x2*x2+y2*y2+z2*z2;
|
79 |
-
if (k==0 || d<best){
|
80 |
-
best=d;
|
81 |
-
best_i=k+k2;
|
82 |
-
}
|
83 |
-
}
|
84 |
-
{
|
85 |
-
float x2=buf[k*3+3]-x1;
|
86 |
-
float y2=buf[k*3+4]-y1;
|
87 |
-
float z2=buf[k*3+5]-z1;
|
88 |
-
float d=x2*x2+y2*y2+z2*z2;
|
89 |
-
if (d<best){
|
90 |
-
best=d;
|
91 |
-
best_i=k+k2+1;
|
92 |
-
}
|
93 |
-
}
|
94 |
-
{
|
95 |
-
float x2=buf[k*3+6]-x1;
|
96 |
-
float y2=buf[k*3+7]-y1;
|
97 |
-
float z2=buf[k*3+8]-z1;
|
98 |
-
float d=x2*x2+y2*y2+z2*z2;
|
99 |
-
if (d<best){
|
100 |
-
best=d;
|
101 |
-
best_i=k+k2+2;
|
102 |
-
}
|
103 |
-
}
|
104 |
-
{
|
105 |
-
float x2=buf[k*3+9]-x1;
|
106 |
-
float y2=buf[k*3+10]-y1;
|
107 |
-
float z2=buf[k*3+11]-z1;
|
108 |
-
float d=x2*x2+y2*y2+z2*z2;
|
109 |
-
if (d<best){
|
110 |
-
best=d;
|
111 |
-
best_i=k+k2+3;
|
112 |
-
}
|
113 |
-
}
|
114 |
-
}
|
115 |
-
}
|
116 |
-
for (int k=end_ka;k<end_k;k++){
|
117 |
-
float x2=buf[k*3+0]-x1;
|
118 |
-
float y2=buf[k*3+1]-y1;
|
119 |
-
float z2=buf[k*3+2]-z1;
|
120 |
-
float d=x2*x2+y2*y2+z2*z2;
|
121 |
-
if (k==0 || d<best){
|
122 |
-
best=d;
|
123 |
-
best_i=k+k2;
|
124 |
-
}
|
125 |
-
}
|
126 |
-
if (k2==0 || result[(i*n+j)]>best){
|
127 |
-
result[(i*n+j)]=best;
|
128 |
-
result_i[(i*n+j)]=best_i;
|
129 |
-
}
|
130 |
-
}
|
131 |
-
__syncthreads();
|
132 |
-
}
|
133 |
-
}
|
134 |
-
}
|
135 |
-
// int chamfer_cuda_forward(int b,int n,const float * xyz,int m,const float * xyz2,float * result,int * result_i,float * result2,int * result2_i, cudaStream_t stream){
|
136 |
-
int chamfer_cuda_forward(at::Tensor xyz1, at::Tensor xyz2, at::Tensor dist1, at::Tensor dist2, at::Tensor idx1, at::Tensor idx2){
|
137 |
-
|
138 |
-
const auto batch_size = xyz1.size(0);
|
139 |
-
const auto n = xyz1.size(1); //num_points point cloud A
|
140 |
-
const auto m = xyz2.size(1); //num_points point cloud B
|
141 |
-
|
142 |
-
NmDistanceKernel<<<dim3(32,16,1),512>>>(batch_size, n, xyz1.data<float>(), m, xyz2.data<float>(), dist1.data<float>(), idx1.data<int>());
|
143 |
-
NmDistanceKernel<<<dim3(32,16,1),512>>>(batch_size, m, xyz2.data<float>(), n, xyz1.data<float>(), dist2.data<float>(), idx2.data<int>());
|
144 |
-
|
145 |
-
cudaError_t err = cudaGetLastError();
|
146 |
-
if (err != cudaSuccess) {
|
147 |
-
printf("error in nnd updateOutput: %s\n", cudaGetErrorString(err));
|
148 |
-
//THError("aborting");
|
149 |
-
return 0;
|
150 |
-
}
|
151 |
-
return 1;
|
152 |
-
|
153 |
-
|
154 |
-
}
|
155 |
-
__global__ void NmDistanceGradKernel(int b,int n,const float * xyz1,int m,const float * xyz2,const float * grad_dist1,const int * idx1,float * grad_xyz1,float * grad_xyz2){
|
156 |
-
for (int i=blockIdx.x;i<b;i+=gridDim.x){
|
157 |
-
for (int j=threadIdx.x+blockIdx.y*blockDim.x;j<n;j+=blockDim.x*gridDim.y){
|
158 |
-
float x1=xyz1[(i*n+j)*3+0];
|
159 |
-
float y1=xyz1[(i*n+j)*3+1];
|
160 |
-
float z1=xyz1[(i*n+j)*3+2];
|
161 |
-
int j2=idx1[i*n+j];
|
162 |
-
float x2=xyz2[(i*m+j2)*3+0];
|
163 |
-
float y2=xyz2[(i*m+j2)*3+1];
|
164 |
-
float z2=xyz2[(i*m+j2)*3+2];
|
165 |
-
float g=grad_dist1[i*n+j]*2;
|
166 |
-
atomicAdd(&(grad_xyz1[(i*n+j)*3+0]),g*(x1-x2));
|
167 |
-
atomicAdd(&(grad_xyz1[(i*n+j)*3+1]),g*(y1-y2));
|
168 |
-
atomicAdd(&(grad_xyz1[(i*n+j)*3+2]),g*(z1-z2));
|
169 |
-
atomicAdd(&(grad_xyz2[(i*m+j2)*3+0]),-(g*(x1-x2)));
|
170 |
-
atomicAdd(&(grad_xyz2[(i*m+j2)*3+1]),-(g*(y1-y2)));
|
171 |
-
atomicAdd(&(grad_xyz2[(i*m+j2)*3+2]),-(g*(z1-z2)));
|
172 |
-
}
|
173 |
-
}
|
174 |
-
}
|
175 |
-
// int chamfer_cuda_backward(int b,int n,const float * xyz1,int m,const float * xyz2,const float * grad_dist1,const int * idx1,const float * grad_dist2,const int * idx2,float * grad_xyz1,float * grad_xyz2, cudaStream_t stream){
|
176 |
-
int chamfer_cuda_backward(at::Tensor xyz1, at::Tensor xyz2, at::Tensor gradxyz1, at::Tensor gradxyz2, at::Tensor graddist1, at::Tensor graddist2, at::Tensor idx1, at::Tensor idx2){
|
177 |
-
// cudaMemset(grad_xyz1,0,b*n*3*4);
|
178 |
-
// cudaMemset(grad_xyz2,0,b*m*3*4);
|
179 |
-
|
180 |
-
const auto batch_size = xyz1.size(0);
|
181 |
-
const auto n = xyz1.size(1); //num_points point cloud A
|
182 |
-
const auto m = xyz2.size(1); //num_points point cloud B
|
183 |
-
|
184 |
-
NmDistanceGradKernel<<<dim3(1,16,1),256>>>(batch_size,n,xyz1.data<float>(),m,xyz2.data<float>(),graddist1.data<float>(),idx1.data<int>(),gradxyz1.data<float>(),gradxyz2.data<float>());
|
185 |
-
NmDistanceGradKernel<<<dim3(1,16,1),256>>>(batch_size,m,xyz2.data<float>(),n,xyz1.data<float>(),graddist2.data<float>(),idx2.data<int>(),gradxyz2.data<float>(),gradxyz1.data<float>());
|
186 |
-
|
187 |
-
cudaError_t err = cudaGetLastError();
|
188 |
-
if (err != cudaSuccess) {
|
189 |
-
printf("error in nnd get grad: %s\n", cudaGetErrorString(err));
|
190 |
-
//THError("aborting");
|
191 |
-
return 0;
|
192 |
-
}
|
193 |
-
return 1;
|
194 |
-
|
195 |
-
}
|
196 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lidm/eval/modules/chamfer3D/chamfer_cuda.cpp
DELETED
@@ -1,33 +0,0 @@
|
|
1 |
-
#include <torch/torch.h>
|
2 |
-
#include <vector>
|
3 |
-
|
4 |
-
///TMP
|
5 |
-
//#include "common.h"
|
6 |
-
/// NOT TMP
|
7 |
-
|
8 |
-
|
9 |
-
int chamfer_cuda_forward(at::Tensor xyz1, at::Tensor xyz2, at::Tensor dist1, at::Tensor dist2, at::Tensor idx1, at::Tensor idx2);
|
10 |
-
|
11 |
-
|
12 |
-
int chamfer_cuda_backward(at::Tensor xyz1, at::Tensor xyz2, at::Tensor gradxyz1, at::Tensor gradxyz2, at::Tensor graddist1, at::Tensor graddist2, at::Tensor idx1, at::Tensor idx2);
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
int chamfer_forward(at::Tensor xyz1, at::Tensor xyz2, at::Tensor dist1, at::Tensor dist2, at::Tensor idx1, at::Tensor idx2) {
|
18 |
-
return chamfer_cuda_forward(xyz1, xyz2, dist1, dist2, idx1, idx2);
|
19 |
-
}
|
20 |
-
|
21 |
-
|
22 |
-
int chamfer_backward(at::Tensor xyz1, at::Tensor xyz2, at::Tensor gradxyz1, at::Tensor gradxyz2, at::Tensor graddist1,
|
23 |
-
at::Tensor graddist2, at::Tensor idx1, at::Tensor idx2) {
|
24 |
-
|
25 |
-
return chamfer_cuda_backward(xyz1, xyz2, gradxyz1, gradxyz2, graddist1, graddist2, idx1, idx2);
|
26 |
-
}
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
|
31 |
-
m.def("forward", &chamfer_forward, "chamfer forward (CUDA)");
|
32 |
-
m.def("backward", &chamfer_backward, "chamfer backward (CUDA)");
|
33 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lidm/eval/modules/chamfer3D/dist_chamfer_3D.py
DELETED
@@ -1,76 +0,0 @@
|
|
1 |
-
from torch import nn
|
2 |
-
from torch.autograd import Function
|
3 |
-
import torch
|
4 |
-
import importlib
|
5 |
-
import os
|
6 |
-
|
7 |
-
chamfer_found = importlib.find_loader("chamfer_3D") is not None
|
8 |
-
if not chamfer_found:
|
9 |
-
## Cool trick from https://github.com/chrdiller
|
10 |
-
print("Jitting Chamfer 3D")
|
11 |
-
|
12 |
-
from torch.utils.cpp_extension import load
|
13 |
-
|
14 |
-
chamfer_3D = load(name="chamfer_3D",
|
15 |
-
sources=[
|
16 |
-
"/".join(os.path.abspath(__file__).split('/')[:-1] + ["chamfer_cuda.cpp"]),
|
17 |
-
"/".join(os.path.abspath(__file__).split('/')[:-1] + ["chamfer3D.cu"]),
|
18 |
-
])
|
19 |
-
print("Loaded JIT 3D CUDA chamfer distance")
|
20 |
-
|
21 |
-
else:
|
22 |
-
import chamfer_3D
|
23 |
-
print("Loaded compiled 3D CUDA chamfer distance")
|
24 |
-
|
25 |
-
|
26 |
-
# Chamfer's distance module @thibaultgroueix
|
27 |
-
# GPU tensors only
|
28 |
-
class chamfer_3DFunction(Function):
|
29 |
-
@staticmethod
|
30 |
-
def forward(ctx, xyz1, xyz2):
|
31 |
-
batchsize, n, _ = xyz1.size()
|
32 |
-
_, m, _ = xyz2.size()
|
33 |
-
device = xyz1.device
|
34 |
-
|
35 |
-
dist1 = torch.zeros(batchsize, n)
|
36 |
-
dist2 = torch.zeros(batchsize, m)
|
37 |
-
|
38 |
-
idx1 = torch.zeros(batchsize, n).type(torch.IntTensor)
|
39 |
-
idx2 = torch.zeros(batchsize, m).type(torch.IntTensor)
|
40 |
-
|
41 |
-
dist1 = dist1.to(device)
|
42 |
-
dist2 = dist2.to(device)
|
43 |
-
idx1 = idx1.to(device)
|
44 |
-
idx2 = idx2.to(device)
|
45 |
-
torch.cuda.set_device(device)
|
46 |
-
|
47 |
-
chamfer_3D.forward(xyz1, xyz2, dist1, dist2, idx1, idx2)
|
48 |
-
ctx.save_for_backward(xyz1, xyz2, idx1, idx2)
|
49 |
-
return dist1, dist2, idx1, idx2
|
50 |
-
|
51 |
-
@staticmethod
|
52 |
-
def backward(ctx, graddist1, graddist2, gradidx1, gradidx2):
|
53 |
-
xyz1, xyz2, idx1, idx2 = ctx.saved_tensors
|
54 |
-
graddist1 = graddist1.contiguous()
|
55 |
-
graddist2 = graddist2.contiguous()
|
56 |
-
device = graddist1.device
|
57 |
-
|
58 |
-
gradxyz1 = torch.zeros(xyz1.size())
|
59 |
-
gradxyz2 = torch.zeros(xyz2.size())
|
60 |
-
|
61 |
-
gradxyz1 = gradxyz1.to(device)
|
62 |
-
gradxyz2 = gradxyz2.to(device)
|
63 |
-
chamfer_3D.backward(
|
64 |
-
xyz1, xyz2, gradxyz1, gradxyz2, graddist1, graddist2, idx1, idx2
|
65 |
-
)
|
66 |
-
return gradxyz1, gradxyz2
|
67 |
-
|
68 |
-
|
69 |
-
class chamfer_3DDist(nn.Module):
|
70 |
-
def __init__(self):
|
71 |
-
super(chamfer_3DDist, self).__init__()
|
72 |
-
|
73 |
-
def forward(self, input1, input2):
|
74 |
-
input1 = input1.contiguous()
|
75 |
-
input2 = input2.contiguous()
|
76 |
-
return chamfer_3DFunction.apply(input1, input2)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lidm/eval/modules/chamfer3D/setup.py
DELETED
@@ -1,14 +0,0 @@
|
|
1 |
-
from setuptools import setup
|
2 |
-
from torch.utils.cpp_extension import BuildExtension, CUDAExtension
|
3 |
-
|
4 |
-
setup(
|
5 |
-
name='chamfer_3D',
|
6 |
-
ext_modules=[
|
7 |
-
CUDAExtension('chamfer_3D', [
|
8 |
-
"/".join(__file__.split('/')[:-1] + ['chamfer_cuda.cpp']),
|
9 |
-
"/".join(__file__.split('/')[:-1] + ['chamfer3D.cu']),
|
10 |
-
]),
|
11 |
-
],
|
12 |
-
cmdclass={
|
13 |
-
'build_ext': BuildExtension
|
14 |
-
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lidm/eval/modules/emd/__init__.py
DELETED
File without changes
|
lidm/eval/modules/emd/emd.cpp
DELETED
@@ -1,31 +0,0 @@
|
|
1 |
-
// EMD approximation module (based on auction algorithm)
|
2 |
-
// author: Minghua Liu
|
3 |
-
#include <torch/extension.h>
|
4 |
-
#include <vector>
|
5 |
-
|
6 |
-
int emd_cuda_forward(at::Tensor xyz1, at::Tensor xyz2, at::Tensor dist, at::Tensor assignment, at::Tensor price,
|
7 |
-
at::Tensor assignment_inv, at::Tensor bid, at::Tensor bid_increments, at::Tensor max_increments,
|
8 |
-
at::Tensor unass_idx, at::Tensor unass_cnt, at::Tensor unass_cnt_sum, at::Tensor cnt_tmp, at::Tensor max_idx, float eps, int iters);
|
9 |
-
|
10 |
-
int emd_cuda_backward(at::Tensor xyz1, at::Tensor xyz2, at::Tensor gradxyz, at::Tensor graddist, at::Tensor idx);
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
int emd_forward(at::Tensor xyz1, at::Tensor xyz2, at::Tensor dist, at::Tensor assignment, at::Tensor price,
|
15 |
-
at::Tensor assignment_inv, at::Tensor bid, at::Tensor bid_increments, at::Tensor max_increments,
|
16 |
-
at::Tensor unass_idx, at::Tensor unass_cnt, at::Tensor unass_cnt_sum, at::Tensor cnt_tmp, at::Tensor max_idx, float eps, int iters) {
|
17 |
-
return emd_cuda_forward(xyz1, xyz2, dist, assignment, price, assignment_inv, bid, bid_increments, max_increments, unass_idx, unass_cnt, unass_cnt_sum, cnt_tmp, max_idx, eps, iters);
|
18 |
-
}
|
19 |
-
|
20 |
-
int emd_backward(at::Tensor xyz1, at::Tensor xyz2, at::Tensor gradxyz, at::Tensor graddist, at::Tensor idx) {
|
21 |
-
|
22 |
-
return emd_cuda_backward(xyz1, xyz2, gradxyz, graddist, idx);
|
23 |
-
}
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
|
29 |
-
m.def("forward", &emd_forward, "emd forward (CUDA)");
|
30 |
-
m.def("backward", &emd_backward, "emd backward (CUDA)");
|
31 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lidm/eval/modules/emd/emd_cuda.cu
DELETED
@@ -1,316 +0,0 @@
|
|
1 |
-
// EMD approximation module (based on auction algorithm)
|
2 |
-
// author: Minghua Liu
|
3 |
-
#include <stdio.h>
|
4 |
-
#include <ATen/ATen.h>
|
5 |
-
|
6 |
-
#include <cuda.h>
|
7 |
-
#include <iostream>
|
8 |
-
#include <cuda_runtime.h>
|
9 |
-
|
10 |
-
__device__ __forceinline__ float atomicMax(float *address, float val)
|
11 |
-
{
|
12 |
-
int ret = __float_as_int(*address);
|
13 |
-
while(val > __int_as_float(ret))
|
14 |
-
{
|
15 |
-
int old = ret;
|
16 |
-
if((ret = atomicCAS((int *)address, old, __float_as_int(val))) == old)
|
17 |
-
break;
|
18 |
-
}
|
19 |
-
return __int_as_float(ret);
|
20 |
-
}
|
21 |
-
|
22 |
-
|
23 |
-
__global__ void clear(int b, int * cnt_tmp, int * unass_cnt) {
|
24 |
-
for (int i = threadIdx.x; i < b; i += blockDim.x) {
|
25 |
-
cnt_tmp[i] = 0;
|
26 |
-
unass_cnt[i] = 0;
|
27 |
-
}
|
28 |
-
}
|
29 |
-
|
30 |
-
__global__ void calc_unass_cnt(int b, int n, int * assignment, int * unass_cnt) {
|
31 |
-
// count the number of unassigned points in each batch
|
32 |
-
const int BLOCK_SIZE = 1024;
|
33 |
-
__shared__ int scan_array[BLOCK_SIZE];
|
34 |
-
for (int i = blockIdx.x; i < b; i += gridDim.x) {
|
35 |
-
scan_array[threadIdx.x] = assignment[i * n + blockIdx.y * BLOCK_SIZE + threadIdx.x] == -1 ? 1 : 0;
|
36 |
-
__syncthreads();
|
37 |
-
|
38 |
-
int stride = 1;
|
39 |
-
while(stride <= BLOCK_SIZE / 2) {
|
40 |
-
int index = (threadIdx.x + 1) * stride * 2 - 1;
|
41 |
-
if(index < BLOCK_SIZE)
|
42 |
-
scan_array[index] += scan_array[index - stride];
|
43 |
-
stride = stride * 2;
|
44 |
-
__syncthreads();
|
45 |
-
}
|
46 |
-
__syncthreads();
|
47 |
-
|
48 |
-
if (threadIdx.x == BLOCK_SIZE - 1) {
|
49 |
-
atomicAdd(&unass_cnt[i], scan_array[threadIdx.x]);
|
50 |
-
}
|
51 |
-
__syncthreads();
|
52 |
-
}
|
53 |
-
}
|
54 |
-
|
55 |
-
__global__ void calc_unass_cnt_sum(int b, int * unass_cnt, int * unass_cnt_sum) {
|
56 |
-
// count the cumulative sum over over unass_cnt
|
57 |
-
const int BLOCK_SIZE = 512; // batch_size <= 512
|
58 |
-
__shared__ int scan_array[BLOCK_SIZE];
|
59 |
-
scan_array[threadIdx.x] = unass_cnt[threadIdx.x];
|
60 |
-
__syncthreads();
|
61 |
-
|
62 |
-
int stride = 1;
|
63 |
-
while(stride <= BLOCK_SIZE / 2) {
|
64 |
-
int index = (threadIdx.x + 1) * stride * 2 - 1;
|
65 |
-
if(index < BLOCK_SIZE)
|
66 |
-
scan_array[index] += scan_array[index - stride];
|
67 |
-
stride = stride * 2;
|
68 |
-
__syncthreads();
|
69 |
-
}
|
70 |
-
__syncthreads();
|
71 |
-
stride = BLOCK_SIZE / 4;
|
72 |
-
while(stride > 0) {
|
73 |
-
int index = (threadIdx.x + 1) * stride * 2 - 1;
|
74 |
-
if((index + stride) < BLOCK_SIZE)
|
75 |
-
scan_array[index + stride] += scan_array[index];
|
76 |
-
stride = stride / 2;
|
77 |
-
__syncthreads();
|
78 |
-
}
|
79 |
-
__syncthreads();
|
80 |
-
|
81 |
-
//printf("%d\n", unass_cnt_sum[b - 1]);
|
82 |
-
unass_cnt_sum[threadIdx.x] = scan_array[threadIdx.x];
|
83 |
-
}
|
84 |
-
|
85 |
-
__global__ void calc_unass_idx(int b, int n, int * assignment, int * unass_idx, int * unass_cnt, int * unass_cnt_sum, int * cnt_tmp) {
|
86 |
-
// list all the unassigned points
|
87 |
-
for (int i = blockIdx.x; i < b; i += gridDim.x) {
|
88 |
-
if (assignment[i * n + blockIdx.y * 1024 + threadIdx.x] == -1) {
|
89 |
-
int idx = atomicAdd(&cnt_tmp[i], 1);
|
90 |
-
unass_idx[unass_cnt_sum[i] - unass_cnt[i] + idx] = blockIdx.y * 1024 + threadIdx.x;
|
91 |
-
}
|
92 |
-
}
|
93 |
-
}
|
94 |
-
|
95 |
-
__global__ void Bid(int b, int n, const float * xyz1, const float * xyz2, float eps, int * assignment, int * assignment_inv, float * price,
|
96 |
-
int * bid, float * bid_increments, float * max_increments, int * unass_cnt, int * unass_cnt_sum, int * unass_idx) {
|
97 |
-
const int batch = 2048, block_size = 1024, block_cnt = n / 1024;
|
98 |
-
__shared__ float xyz2_buf[batch * 3];
|
99 |
-
__shared__ float price_buf[batch];
|
100 |
-
__shared__ float best_buf[block_size];
|
101 |
-
__shared__ float better_buf[block_size];
|
102 |
-
__shared__ int best_i_buf[block_size];
|
103 |
-
for (int i = blockIdx.x; i < b; i += gridDim.x) {
|
104 |
-
int _unass_cnt = unass_cnt[i];
|
105 |
-
if (_unass_cnt == 0)
|
106 |
-
continue;
|
107 |
-
int _unass_cnt_sum = unass_cnt_sum[i];
|
108 |
-
int unass_per_block = (_unass_cnt + block_cnt - 1) / block_cnt;
|
109 |
-
int thread_per_unass = block_size / unass_per_block;
|
110 |
-
int unass_this_block = max(min(_unass_cnt - (int) blockIdx.y * unass_per_block, unass_per_block), 0);
|
111 |
-
|
112 |
-
float x1, y1, z1, best = -1e9, better = -1e9;
|
113 |
-
int best_i = -1, _unass_id = -1, thread_in_unass;
|
114 |
-
|
115 |
-
if (threadIdx.x < thread_per_unass * unass_this_block) {
|
116 |
-
_unass_id = unass_per_block * blockIdx.y + threadIdx.x / thread_per_unass + _unass_cnt_sum - _unass_cnt;
|
117 |
-
_unass_id = unass_idx[_unass_id];
|
118 |
-
thread_in_unass = threadIdx.x % thread_per_unass;
|
119 |
-
|
120 |
-
x1 = xyz1[(i * n + _unass_id) * 3 + 0];
|
121 |
-
y1 = xyz1[(i * n + _unass_id) * 3 + 1];
|
122 |
-
z1 = xyz1[(i * n + _unass_id) * 3 + 2];
|
123 |
-
}
|
124 |
-
|
125 |
-
for (int k2 = 0; k2 < n; k2 += batch) {
|
126 |
-
int end_k = min(n, k2 + batch) - k2;
|
127 |
-
for (int j = threadIdx.x; j < end_k * 3; j += blockDim.x) {
|
128 |
-
xyz2_buf[j] = xyz2[(i * n + k2) * 3 + j];
|
129 |
-
}
|
130 |
-
for (int j = threadIdx.x; j < end_k; j += blockDim.x) {
|
131 |
-
price_buf[j] = price[i * n + k2 + j];
|
132 |
-
}
|
133 |
-
__syncthreads();
|
134 |
-
|
135 |
-
if (_unass_id != -1) {
|
136 |
-
int delta = (end_k + thread_per_unass - 1) / thread_per_unass;
|
137 |
-
int l = thread_in_unass * delta;
|
138 |
-
int r = min((thread_in_unass + 1) * delta, end_k);
|
139 |
-
for (int k = l; k < r; k++)
|
140 |
-
//if (!last || assignment_inv[i * n + k + k2] == -1)
|
141 |
-
{
|
142 |
-
float x2 = xyz2_buf[k * 3 + 0] - x1;
|
143 |
-
float y2 = xyz2_buf[k * 3 + 1] - y1;
|
144 |
-
float z2 = xyz2_buf[k * 3 + 2] - z1;
|
145 |
-
// the coordinates of points should be normalized to [0, 1]
|
146 |
-
float d = 3.0 - sqrtf(x2 * x2 + y2 * y2 + z2 * z2) - price_buf[k];
|
147 |
-
if (d > best) {
|
148 |
-
better = best;
|
149 |
-
best = d;
|
150 |
-
best_i = k + k2;
|
151 |
-
}
|
152 |
-
else if (d > better) {
|
153 |
-
better = d;
|
154 |
-
}
|
155 |
-
}
|
156 |
-
}
|
157 |
-
__syncthreads();
|
158 |
-
}
|
159 |
-
|
160 |
-
best_buf[threadIdx.x] = best;
|
161 |
-
better_buf[threadIdx.x] = better;
|
162 |
-
best_i_buf[threadIdx.x] = best_i;
|
163 |
-
__syncthreads();
|
164 |
-
|
165 |
-
if (_unass_id != -1 && thread_in_unass == 0) {
|
166 |
-
for (int j = threadIdx.x + 1; j < threadIdx.x + thread_per_unass; j++) {
|
167 |
-
if (best_buf[j] > best) {
|
168 |
-
better = max(best, better_buf[j]);
|
169 |
-
best = best_buf[j];
|
170 |
-
best_i = best_i_buf[j];
|
171 |
-
}
|
172 |
-
else better = max(better, best_buf[j]);
|
173 |
-
}
|
174 |
-
bid[i * n + _unass_id] = best_i;
|
175 |
-
bid_increments[i * n + _unass_id] = best - better + eps;
|
176 |
-
atomicMax(&max_increments[i * n + best_i], best - better + eps);
|
177 |
-
}
|
178 |
-
}
|
179 |
-
}
|
180 |
-
|
181 |
-
__global__ void GetMax(int b, int n, int * assignment, int * bid, float * bid_increments, float * max_increments, int * max_idx) {
|
182 |
-
for (int i = blockIdx.x; i < b; i += gridDim.x) {
|
183 |
-
int j = threadIdx.x + blockIdx.y * blockDim.x;
|
184 |
-
if (assignment[i * n + j] == -1) {
|
185 |
-
int bid_id = bid[i * n + j];
|
186 |
-
float bid_inc = bid_increments[i * n + j];
|
187 |
-
float max_inc = max_increments[i * n + bid_id];
|
188 |
-
if (bid_inc - 1e-6 <= max_inc && max_inc <= bid_inc + 1e-6)
|
189 |
-
{
|
190 |
-
max_idx[i * n + bid_id] = j;
|
191 |
-
}
|
192 |
-
}
|
193 |
-
}
|
194 |
-
}
|
195 |
-
|
196 |
-
__global__ void Assign(int b, int n, int * assignment, int * assignment_inv, float * price, int * bid, float * bid_increments, float * max_increments, int * max_idx, bool last) {
|
197 |
-
for (int i = blockIdx.x; i < b; i += gridDim.x) {
|
198 |
-
int j = threadIdx.x + blockIdx.y * blockDim.x;
|
199 |
-
if (assignment[i * n + j] == -1) {
|
200 |
-
int bid_id = bid[i * n + j];
|
201 |
-
if (last || max_idx[i * n + bid_id] == j)
|
202 |
-
{
|
203 |
-
float bid_inc = bid_increments[i * n + j];
|
204 |
-
int ass_inv = assignment_inv[i * n + bid_id];
|
205 |
-
if (!last && ass_inv != -1) {
|
206 |
-
assignment[i * n + ass_inv] = -1;
|
207 |
-
}
|
208 |
-
assignment_inv[i * n + bid_id] = j;
|
209 |
-
assignment[i * n + j] = bid_id;
|
210 |
-
price[i * n + bid_id] += bid_inc;
|
211 |
-
max_increments[i * n + bid_id] = -1e9;
|
212 |
-
}
|
213 |
-
}
|
214 |
-
}
|
215 |
-
}
|
216 |
-
|
217 |
-
__global__ void CalcDist(int b, int n, float * xyz1, float * xyz2, float * dist, int * assignment) {
|
218 |
-
for (int i = blockIdx.x; i < b; i += gridDim.x) {
|
219 |
-
int j = threadIdx.x + blockIdx.y * blockDim.x;
|
220 |
-
int k = assignment[i * n + j];
|
221 |
-
float deltax = xyz1[(i * n + j) * 3 + 0] - xyz2[(i * n + k) * 3 + 0];
|
222 |
-
float deltay = xyz1[(i * n + j) * 3 + 1] - xyz2[(i * n + k) * 3 + 1];
|
223 |
-
float deltaz = xyz1[(i * n + j) * 3 + 2] - xyz2[(i * n + k) * 3 + 2];
|
224 |
-
dist[i * n + j] = deltax * deltax + deltay * deltay + deltaz * deltaz;
|
225 |
-
}
|
226 |
-
}
|
227 |
-
|
228 |
-
int emd_cuda_forward(at::Tensor xyz1, at::Tensor xyz2, at::Tensor dist, at::Tensor assignment, at::Tensor price,
|
229 |
-
at::Tensor assignment_inv, at::Tensor bid, at::Tensor bid_increments, at::Tensor max_increments,
|
230 |
-
at::Tensor unass_idx, at::Tensor unass_cnt, at::Tensor unass_cnt_sum, at::Tensor cnt_tmp, at::Tensor max_idx, float eps, int iters) {
|
231 |
-
|
232 |
-
const auto batch_size = xyz1.size(0);
|
233 |
-
const auto n = xyz1.size(1); //num_points point cloud A
|
234 |
-
const auto m = xyz2.size(1); //num_points point cloud B
|
235 |
-
|
236 |
-
if (n != m) {
|
237 |
-
printf("Input Error! The two point clouds should have the same size.\n");
|
238 |
-
return -1;
|
239 |
-
}
|
240 |
-
|
241 |
-
if (batch_size > 512) {
|
242 |
-
printf("Input Error! The batch size should be less than 512.\n");
|
243 |
-
return -1;
|
244 |
-
}
|
245 |
-
|
246 |
-
if (n % 1024 != 0) {
|
247 |
-
printf("Input Error! The size of the point clouds should be a multiple of 1024.\n");
|
248 |
-
return -1;
|
249 |
-
}
|
250 |
-
|
251 |
-
//cudaEvent_t start,stop;
|
252 |
-
//cudaEventCreate(&start);
|
253 |
-
//cudaEventCreate(&stop);
|
254 |
-
//cudaEventRecord(start);
|
255 |
-
//int iters = 50;
|
256 |
-
for (int i = 0; i < iters; i++) {
|
257 |
-
clear<<<1, batch_size>>>(batch_size, cnt_tmp.data<int>(), unass_cnt.data<int>());
|
258 |
-
calc_unass_cnt<<<dim3(batch_size, n / 1024, 1), 1024>>>(batch_size, n, assignment.data<int>(), unass_cnt.data<int>());
|
259 |
-
calc_unass_cnt_sum<<<1, batch_size>>>(batch_size, unass_cnt.data<int>(), unass_cnt_sum.data<int>());
|
260 |
-
calc_unass_idx<<<dim3(batch_size, n / 1024, 1), 1024>>>(batch_size, n, assignment.data<int>(), unass_idx.data<int>(), unass_cnt.data<int>(),
|
261 |
-
unass_cnt_sum.data<int>(), cnt_tmp.data<int>());
|
262 |
-
Bid<<<dim3(batch_size, n / 1024, 1), 1024>>>(batch_size, n, xyz1.data<float>(), xyz2.data<float>(), eps, assignment.data<int>(), assignment_inv.data<int>(),
|
263 |
-
price.data<float>(), bid.data<int>(), bid_increments.data<float>(), max_increments.data<float>(),
|
264 |
-
unass_cnt.data<int>(), unass_cnt_sum.data<int>(), unass_idx.data<int>());
|
265 |
-
GetMax<<<dim3(batch_size, n / 1024, 1), 1024>>>(batch_size, n, assignment.data<int>(), bid.data<int>(), bid_increments.data<float>(), max_increments.data<float>(), max_idx.data<int>());
|
266 |
-
Assign<<<dim3(batch_size, n / 1024, 1), 1024>>>(batch_size, n, assignment.data<int>(), assignment_inv.data<int>(), price.data<float>(), bid.data<int>(),
|
267 |
-
bid_increments.data<float>(), max_increments.data<float>(), max_idx.data<int>(), i == iters - 1);
|
268 |
-
}
|
269 |
-
CalcDist<<<dim3(batch_size, n / 1024, 1), 1024>>>(batch_size, n, xyz1.data<float>(), xyz2.data<float>(), dist.data<float>(), assignment.data<int>());
|
270 |
-
//cudaEventRecord(stop);
|
271 |
-
//cudaEventSynchronize(stop);
|
272 |
-
//float elapsedTime;
|
273 |
-
//cudaEventElapsedTime(&elapsedTime,start,stop);
|
274 |
-
//printf("%lf\n", elapsedTime);
|
275 |
-
|
276 |
-
cudaError_t err = cudaGetLastError();
|
277 |
-
if (err != cudaSuccess) {
|
278 |
-
printf("error in nnd Output: %s\n", cudaGetErrorString(err));
|
279 |
-
return 0;
|
280 |
-
}
|
281 |
-
return 1;
|
282 |
-
}
|
283 |
-
|
284 |
-
__global__ void NmDistanceGradKernel(int b, int n, const float * xyz1, const float * xyz2, const float * grad_dist, const int * idx, float * grad_xyz){
|
285 |
-
for (int i = blockIdx.x; i < b; i += gridDim.x) {
|
286 |
-
for (int j = threadIdx.x + blockIdx.y * blockDim.x; j < n; j += blockDim.x * gridDim.y) {
|
287 |
-
float x1 = xyz1[(i * n + j) * 3 + 0];
|
288 |
-
float y1 = xyz1[(i * n + j) * 3 + 1];
|
289 |
-
float z1 = xyz1[(i * n + j) * 3 + 2];
|
290 |
-
int j2 = idx[i * n + j];
|
291 |
-
float x2 = xyz2[(i * n + j2) * 3 + 0];
|
292 |
-
float y2 = xyz2[(i * n + j2) * 3 + 1];
|
293 |
-
float z2 = xyz2[(i * n + j2) * 3 + 2];
|
294 |
-
float g = grad_dist[i * n + j] * 2;
|
295 |
-
atomicAdd(&(grad_xyz[(i * n + j) * 3 + 0]), g * (x1 - x2));
|
296 |
-
atomicAdd(&(grad_xyz[(i * n + j) * 3 + 1]), g * (y1 - y2));
|
297 |
-
atomicAdd(&(grad_xyz[(i * n + j) * 3 + 2]), g * (z1 - z2));
|
298 |
-
}
|
299 |
-
}
|
300 |
-
}
|
301 |
-
|
302 |
-
int emd_cuda_backward(at::Tensor xyz1, at::Tensor xyz2, at::Tensor gradxyz, at::Tensor graddist, at::Tensor idx){
|
303 |
-
const auto batch_size = xyz1.size(0);
|
304 |
-
const auto n = xyz1.size(1);
|
305 |
-
const auto m = xyz2.size(1);
|
306 |
-
|
307 |
-
NmDistanceGradKernel<<<dim3(batch_size, n / 1024, 1), 1024>>>(batch_size, n, xyz1.data<float>(), xyz2.data<float>(), graddist.data<float>(), idx.data<int>(), gradxyz.data<float>());
|
308 |
-
|
309 |
-
cudaError_t err = cudaGetLastError();
|
310 |
-
if (err != cudaSuccess) {
|
311 |
-
printf("error in nnd get grad: %s\n", cudaGetErrorString(err));
|
312 |
-
return 0;
|
313 |
-
}
|
314 |
-
return 1;
|
315 |
-
|
316 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lidm/eval/modules/emd/emd_module.py
DELETED
@@ -1,112 +0,0 @@
|
|
1 |
-
# EMD approximation module (based on auction algorithm)
|
2 |
-
# memory complexity: O(n)
|
3 |
-
# time complexity: O(n^2 * iter)
|
4 |
-
# author: Minghua Liu
|
5 |
-
|
6 |
-
# Input:
|
7 |
-
# xyz1, xyz2: [#batch, #points, 3]
|
8 |
-
# where xyz1 is the predicted point cloud and xyz2 is the ground truth point cloud
|
9 |
-
# two point clouds should have same size and be normalized to [0, 1]
|
10 |
-
# #points should be a multiple of 1024
|
11 |
-
# #batch should be no greater than 512
|
12 |
-
# eps is a parameter which balances the error rate and the speed of convergence
|
13 |
-
# iters is the number of iteration
|
14 |
-
# we only calculate gradient for xyz1
|
15 |
-
|
16 |
-
# Output:
|
17 |
-
# dist: [#batch, #points], sqrt(dist) -> L2 distance
|
18 |
-
# assignment: [#batch, #points], index of the matched point in the ground truth point cloud
|
19 |
-
# the result is an approximation and the assignment is not guranteed to be a bijection
|
20 |
-
import importlib
|
21 |
-
import os
|
22 |
-
import time
|
23 |
-
import numpy as np
|
24 |
-
import torch
|
25 |
-
from torch import nn
|
26 |
-
from torch.autograd import Function
|
27 |
-
|
28 |
-
emd_found = importlib.find_loader("emd") is not None
|
29 |
-
if not emd_found:
|
30 |
-
## Cool trick from https://github.com/chrdiller
|
31 |
-
print("Jitting EMD 3D")
|
32 |
-
|
33 |
-
from torch.utils.cpp_extension import load
|
34 |
-
|
35 |
-
emd = load(name="emd",
|
36 |
-
sources=[
|
37 |
-
"/".join(os.path.abspath(__file__).split('/')[:-1] + ["emd.cpp"]),
|
38 |
-
"/".join(os.path.abspath(__file__).split('/')[:-1] + ["emd_cuda.cu"]),
|
39 |
-
])
|
40 |
-
print("Loaded JIT 3D CUDA emd")
|
41 |
-
else:
|
42 |
-
import emd
|
43 |
-
print("Loaded compiled 3D CUDA emd")
|
44 |
-
|
45 |
-
|
46 |
-
class emdFunction(Function):
|
47 |
-
@staticmethod
|
48 |
-
def forward(ctx, xyz1, xyz2, eps, iters):
|
49 |
-
batchsize, n, _ = xyz1.size()
|
50 |
-
_, m, _ = xyz2.size()
|
51 |
-
|
52 |
-
assert (n == m)
|
53 |
-
assert (xyz1.size()[0] == xyz2.size()[0])
|
54 |
-
# assert(n % 1024 == 0)
|
55 |
-
assert (batchsize <= 512)
|
56 |
-
|
57 |
-
xyz1 = xyz1.contiguous().float().cuda()
|
58 |
-
xyz2 = xyz2.contiguous().float().cuda()
|
59 |
-
dist = torch.zeros(batchsize, n, device='cuda').contiguous()
|
60 |
-
assignment = torch.zeros(batchsize, n, device='cuda', dtype=torch.int32).contiguous() - 1
|
61 |
-
assignment_inv = torch.zeros(batchsize, m, device='cuda', dtype=torch.int32).contiguous() - 1
|
62 |
-
price = torch.zeros(batchsize, m, device='cuda').contiguous()
|
63 |
-
bid = torch.zeros(batchsize, n, device='cuda', dtype=torch.int32).contiguous()
|
64 |
-
bid_increments = torch.zeros(batchsize, n, device='cuda').contiguous()
|
65 |
-
max_increments = torch.zeros(batchsize, m, device='cuda').contiguous()
|
66 |
-
unass_idx = torch.zeros(batchsize * n, device='cuda', dtype=torch.int32).contiguous()
|
67 |
-
max_idx = torch.zeros(batchsize * m, device='cuda', dtype=torch.int32).contiguous()
|
68 |
-
unass_cnt = torch.zeros(512, dtype=torch.int32, device='cuda').contiguous()
|
69 |
-
unass_cnt_sum = torch.zeros(512, dtype=torch.int32, device='cuda').contiguous()
|
70 |
-
cnt_tmp = torch.zeros(512, dtype=torch.int32, device='cuda').contiguous()
|
71 |
-
|
72 |
-
emd.forward(xyz1, xyz2, dist, assignment, price, assignment_inv, bid, bid_increments, max_increments, unass_idx,
|
73 |
-
unass_cnt, unass_cnt_sum, cnt_tmp, max_idx, eps, iters)
|
74 |
-
|
75 |
-
ctx.save_for_backward(xyz1, xyz2, assignment)
|
76 |
-
return dist, assignment
|
77 |
-
|
78 |
-
@staticmethod
|
79 |
-
def backward(ctx, graddist, gradidx):
|
80 |
-
xyz1, xyz2, assignment = ctx.saved_tensors
|
81 |
-
graddist = graddist.contiguous()
|
82 |
-
|
83 |
-
gradxyz1 = torch.zeros(xyz1.size(), device='cuda').contiguous()
|
84 |
-
gradxyz2 = torch.zeros(xyz2.size(), device='cuda').contiguous()
|
85 |
-
|
86 |
-
emd.backward(xyz1, xyz2, gradxyz1, graddist, assignment)
|
87 |
-
return gradxyz1, gradxyz2, None, None
|
88 |
-
|
89 |
-
|
90 |
-
class emdModule(nn.Module):
|
91 |
-
def __init__(self):
|
92 |
-
super(emdModule, self).__init__()
|
93 |
-
|
94 |
-
def forward(self, input1, input2, eps, iters):
|
95 |
-
return emdFunction.apply(input1, input2, eps, iters)
|
96 |
-
|
97 |
-
|
98 |
-
def test_emd():
|
99 |
-
x1 = torch.rand(20, 8192, 3).cuda()
|
100 |
-
x2 = torch.rand(20, 8192, 3).cuda()
|
101 |
-
emd = emdModule()
|
102 |
-
start_time = time.perf_counter()
|
103 |
-
dis, assigment = emd(x1, x2, 0.05, 3000)
|
104 |
-
print("Input_size: ", x1.shape)
|
105 |
-
print("Runtime: %lfs" % (time.perf_counter() - start_time))
|
106 |
-
print("EMD: %lf" % np.sqrt(dis.cpu()).mean())
|
107 |
-
print("|set(assignment)|: %d" % assigment.unique().numel())
|
108 |
-
assigment = assigment.cpu().numpy()
|
109 |
-
assigment = np.expand_dims(assigment, -1)
|
110 |
-
x2 = np.take_along_axis(x2, assigment, axis=1)
|
111 |
-
d = (x1 - x2) * (x1 - x2)
|
112 |
-
print("Verified EMD: %lf" % np.sqrt(d.cpu().sum(-1)).mean())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lidm/eval/modules/emd/setup.py
DELETED
@@ -1,14 +0,0 @@
|
|
1 |
-
from setuptools import setup
|
2 |
-
from torch.utils.cpp_extension import BuildExtension, CUDAExtension
|
3 |
-
|
4 |
-
setup(
|
5 |
-
name='emd',
|
6 |
-
ext_modules=[
|
7 |
-
CUDAExtension('emd', [
|
8 |
-
'emd.cpp',
|
9 |
-
'emd_cuda.cu',
|
10 |
-
]),
|
11 |
-
],
|
12 |
-
cmdclass={
|
13 |
-
'build_ext': BuildExtension
|
14 |
-
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lidm/models/diffusion/ddim.py
CHANGED
@@ -17,7 +17,7 @@ class DDIMSampler(object):
|
|
17 |
|
18 |
def register_buffer(self, name, attr):
|
19 |
if type(attr) == torch.Tensor:
|
20 |
-
if attr.device != torch.device("cuda"):
|
21 |
attr = attr.to(torch.device("cuda"))
|
22 |
setattr(self, name, attr)
|
23 |
|
|
|
17 |
|
18 |
def register_buffer(self, name, attr):
|
19 |
if type(attr) == torch.Tensor:
|
20 |
+
if attr.device != torch.device("cuda") and torch.cuda.is_available():
|
21 |
attr = attr.to(torch.device("cuda"))
|
22 |
setattr(self, name, attr)
|
23 |
|
lidm/models/diffusion/plms.py
CHANGED
@@ -17,7 +17,7 @@ class PLMSSampler(object):
|
|
17 |
|
18 |
def register_buffer(self, name, attr):
|
19 |
if type(attr) == torch.Tensor:
|
20 |
-
if attr.device != torch.device("cuda"):
|
21 |
attr = attr.to(torch.device("cuda"))
|
22 |
setattr(self, name, attr)
|
23 |
|
|
|
17 |
|
18 |
def register_buffer(self, name, attr):
|
19 |
if type(attr) == torch.Tensor:
|
20 |
+
if attr.device != torch.device("cuda") and torch.cuda.is_available():
|
21 |
attr = attr.to(torch.device("cuda"))
|
22 |
setattr(self, name, attr)
|
23 |
|
sample_cond.py
CHANGED
@@ -103,7 +103,7 @@ def make_convolutional_sample(model, batch, batch_size, custom_steps=None, eta=1
|
|
103 |
def sample(model, cond):
|
104 |
batch = {'camera': cond}
|
105 |
img = make_convolutional_sample(model, batch, batch_size=1, custom_steps=CUSTOM_STEPS, eta=ETA) # TODO add arguments for batch_size, custom_steps and eta
|
106 |
-
img = img[0, 0]
|
107 |
pcd = custom_to_pcd(img, model_config)[0].astype(np.float32)
|
|
|
108 |
return img, pcd
|
109 |
|
|
|
103 |
def sample(model, cond):
|
104 |
batch = {'camera': cond}
|
105 |
img = make_convolutional_sample(model, batch, batch_size=1, custom_steps=CUSTOM_STEPS, eta=ETA) # TODO add arguments for batch_size, custom_steps and eta
|
|
|
106 |
pcd = custom_to_pcd(img, model_config)[0].astype(np.float32)
|
107 |
+
img = img.squeeze().detach().cpu().numpy()
|
108 |
return img, pcd
|
109 |
|