Showing preview only (2,734K chars total). Download the full file or copy to clipboard to get everything.
Repository: hancyran/RepSurf
Branch: main
Commit: 29bacd52fe61
Files: 115
Total size: 2.6 MB
Directory structure:
gitextract_ziqlnw07/
├── .gitignore
├── LICENSE.txt
├── README.md
├── classification/
│ ├── README.md
│ ├── dataset/
│ │ ├── ScanObjectNNDataLoader.py
│ │ └── __init__.py
│ ├── init.sh
│ ├── models/
│ │ ├── __init__.py
│ │ └── repsurf/
│ │ ├── __init__.py
│ │ ├── repsurf_ssg_umb.py
│ │ └── repsurf_ssg_umb_2x.py
│ ├── modules/
│ │ ├── __init__.py
│ │ ├── pointnet2_utils.py
│ │ ├── pointops/
│ │ │ ├── __init__.py
│ │ │ ├── functions/
│ │ │ │ ├── __init__.py
│ │ │ │ └── pointops.py
│ │ │ ├── setup.py
│ │ │ └── src/
│ │ │ ├── __init__.py
│ │ │ ├── ballquery/
│ │ │ │ ├── ballquery_cuda.cpp
│ │ │ │ ├── ballquery_cuda_kernel.cu
│ │ │ │ └── ballquery_cuda_kernel.h
│ │ │ ├── cuda_utils.h
│ │ │ ├── grouping/
│ │ │ │ ├── grouping_cuda.cpp
│ │ │ │ ├── grouping_cuda_kernel.cu
│ │ │ │ └── grouping_cuda_kernel.h
│ │ │ ├── grouping_int/
│ │ │ │ ├── grouping_int_cuda.cpp
│ │ │ │ ├── grouping_int_cuda_kernel.cu
│ │ │ │ └── grouping_int_cuda_kernel.h
│ │ │ ├── interpolation/
│ │ │ │ ├── interpolation_cuda.cpp
│ │ │ │ ├── interpolation_cuda_kernel.cu
│ │ │ │ └── interpolation_cuda_kernel.h
│ │ │ ├── knnquery/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── knnquery_cuda.cpp
│ │ │ │ ├── knnquery_cuda_kernel.cu
│ │ │ │ └── knnquery_cuda_kernel.h
│ │ │ ├── knnquery_heap/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── knnquery_heap_cuda.cpp
│ │ │ │ ├── knnquery_heap_cuda_kernel.cu
│ │ │ │ └── knnquery_heap_cuda_kernel.h
│ │ │ ├── pointops_api.cpp
│ │ │ └── sampling/
│ │ │ ├── sampling_cuda.cpp
│ │ │ ├── sampling_cuda_kernel.cu
│ │ │ └── sampling_cuda_kernel.h
│ │ ├── polar_utils.py
│ │ ├── ptaug_utils.py
│ │ ├── recons_utils.py
│ │ └── repsurface_utils.py
│ ├── scripts/
│ │ └── scanobjectnn/
│ │ ├── repsurf_ssg_umb.sh
│ │ └── repsurf_ssg_umb_2x.sh
│ ├── tool/
│ │ └── train_cls_scanobjectnn.py
│ └── util/
│ ├── __init__.py
│ └── utils.py
├── segmentation/
│ ├── README.md
│ ├── dataset/
│ │ ├── S3DISDataLoader.py
│ │ └── __init__.py
│ ├── init.sh
│ ├── models/
│ │ ├── __init__.py
│ │ ├── pointnet2/
│ │ │ ├── __init__.py
│ │ │ └── pointnet2_ssg.py
│ │ ├── pointtransformer/
│ │ │ ├── __init__.py
│ │ │ └── pointtransformer.py
│ │ └── repsurf/
│ │ ├── __init__.py
│ │ └── repsurf_umb_ssg.py
│ ├── modules/
│ │ ├── __init__.py
│ │ ├── aug_utils.py
│ │ ├── pointnet2_utils.py
│ │ ├── pointops/
│ │ │ ├── __init__.py
│ │ │ ├── functions/
│ │ │ │ ├── __init__.py
│ │ │ │ └── pointops.py
│ │ │ ├── setup.py
│ │ │ └── src/
│ │ │ ├── __init__.py
│ │ │ ├── aggregation/
│ │ │ │ ├── aggregation_cuda.cpp
│ │ │ │ ├── aggregation_cuda_kernel.cu
│ │ │ │ └── aggregation_cuda_kernel.h
│ │ │ ├── cuda_utils.h
│ │ │ ├── grouping/
│ │ │ │ ├── grouping_cuda.cpp
│ │ │ │ ├── grouping_cuda_kernel.cu
│ │ │ │ └── grouping_cuda_kernel.h
│ │ │ ├── interpolation/
│ │ │ │ ├── interpolation_cuda.cpp
│ │ │ │ ├── interpolation_cuda_kernel.cu
│ │ │ │ └── interpolation_cuda_kernel.h
│ │ │ ├── knnquery/
│ │ │ │ ├── knnquery_cuda.cpp
│ │ │ │ ├── knnquery_cuda_kernel.cu
│ │ │ │ └── knnquery_cuda_kernel.h
│ │ │ ├── pointops_api.cpp
│ │ │ ├── sampling/
│ │ │ │ ├── sampling_cuda.cpp
│ │ │ │ ├── sampling_cuda_kernel.cu
│ │ │ │ └── sampling_cuda_kernel.h
│ │ │ └── subtraction/
│ │ │ ├── subtraction_cuda.cpp
│ │ │ ├── subtraction_cuda_kernel.cu
│ │ │ └── subtraction_cuda_kernel.h
│ │ ├── pointtransformer_utils.py
│ │ ├── polar_utils.py
│ │ ├── recons_utils.py
│ │ ├── repsurface_utils.py
│ │ └── voxelize_utils.py
│ ├── scripts/
│ │ └── s3dis/
│ │ ├── test_pointnet2.sh
│ │ ├── test_pointtransformer.sh
│ │ ├── test_repsurf_umb.sh
│ │ ├── train_pointnet2.sh
│ │ ├── train_pointtransformer.sh
│ │ └── train_repsurf_umb.sh
│ ├── tool/
│ │ ├── test_s3dis.py
│ │ └── train.py
│ └── util/
│ ├── __init__.py
│ ├── data_util.py
│ └── utils.py
└── visualization/
├── airplane_0001.txt
├── bed_0001.txt
├── cup_0001.txt
├── table_0250.txt
├── triangled_airplane.obj
├── triangled_bed.obj
├── triangled_cup.obj
└── triangled_table.obj
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
.idea/
================================================
FILE: LICENSE.txt
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2022 Haoxi Ran.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: README.md
================================================
# RepSurf - Surface Representation for Point Clouds <br> [CVPR 2022 Oral]
By *[Haoxi Ran\*](https://hancyran.github.io/) , Jun Liu, Chengjie Wang* ( * : corresponding contact)
[](https://paperswithcode.com/sota/3d-point-cloud-classification-on-scanobjectnn?p=surface-representation-for-point-clouds) <br>
[](https://paperswithcode.com/sota/3d-object-detection-on-sun-rgbd-val?p=surface-representation-for-point-clouds) <br>
[](https://paperswithcode.com/sota/3d-point-cloud-classification-on-modelnet40?p=surface-representation-for-point-clouds) <br>
[](https://paperswithcode.com/sota/semantic-segmentation-on-s3dis?p=surface-representation-for-point-clouds) <br>
[](https://paperswithcode.com/sota/3d-object-detection-on-scannetv2?p=surface-representation-for-point-clouds) <br>
[](https://paperswithcode.com/sota/semantic-segmentation-on-s3dis-area5?p=surface-representation-for-point-clouds)
### The pytorch official implementation of "[Surface Representation for Point Clouds](http://arxiv.org/abs/2205.05740)"
### [PDF](https://openaccess.thecvf.com/content/CVPR2022/papers/Ran_Surface_Representation_for_Point_Clouds_CVPR_2022_paper.pdf) | [arXiv](http://arxiv.org/abs/2205.05740)
<div align="center">
<img src="assets/teaser.png" width="600px">
</div>
## News:
- (**Sep 10** NEW :fire:) We have uploaded the implementation of RepSurf on S3DIS along with its training log and pretrained weights.
- (**June 24** :fire:) We sucessfully finished our Oral presentation at CVPR 2022!
- (**May 11**) We have uploaded the implementation of RepSurf on ScanObjectNN along with its training log and pretrained weights.
## Tasks:
### We conduct experiments of different tasks on different codebases:
> Classification: **[3D Object Classification](./classification)** <br>
> Segmentation: **[3D Semantic Segmentation](./segmentation)**
## Visualization
We provide several visualization results in the folder **./visualization** for a closer look at the construction of
RepSurf.
## License
RepSurf is under the Apache-2.0 license. Please contact the primary author **Haoxi Ran (ranhaoxi@gmail.com)** for
commercial use.
================================================
FILE: classification/README.md
================================================
# RepSurf for Classification <br>
By *[Haoxi Ran\*](https://hancyran.github.io/) , Jun Liu, Chengjie Wang* ( * : corresponding contact)
### [PDF](https://openaccess.thecvf.com/content/CVPR2022/papers/Ran_Surface_Representation_for_Point_Clouds_CVPR_2022_paper.pdf) | [arXiv](http://arxiv.org/abs/2205.05740)
## Preparation
### Environment
We tested under the environment:
* python 3.7
* pytorch 1.6.0
* cuda 10.1
* gcc 7.2.0
* h5py
For anaconda user, initialize the conda environment **repsurf-cls** by:
```
sh init.sh
```
## Experiments
### ScanObjectNN (Data & Logs: [Google Drive](https://drive.google.com/drive/folders/1DGWT9W46MKVI0-lu18hJhB-R3BFVWuCs?usp=sharing))
* Performance:
<table style="width:100%">
<thead>
<tr>
<th>Model</th>
<th>Accuracy</th>
<th>#Params</th>
<th>Augment</th>
<th>Code</th>
<th>Log</th>
<th>Checkpoint</th>
</tr>
</thead>
<tbody>
<tr>
<td align="center"><a href="https://github.com/ajhamdi/MVTN">MVTN</a></td>
<td align="center">82.8</td>
<td align="center">4.24M</td>
<td align="center">None</td>
<td align="center"><a href="https://github.com/ajhamdi/MVTN/blob/master/models/mvtn.py">link</a></td>
<td align="center">N/A</td>
<td align="center"><a href="https://github.com/ajhamdi/MVTN/blob/master/results/checkpoints/scanobjectnn/model-00029.pth">link</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/ma-xu/pointMLP-pytorch">PointMLP</a></td>
<td align="center">85.7</td>
<td align="center">12.6M</td>
<td align="center">Scale, Shift</td>
<td align="center"><a href="https://github.com/ma-xu/pointMLP-pytorch/blob/main/classification_ScanObjectNN/models/pointmlp.py">link</a></td>
<td align="center"><a href="https://web.northeastern.edu/smilelab/xuma/pointMLP/checkpoints/fixstd/scanobjectnn/pointMLP-20220204021453/">link</a></td>
<td align="center"><a href="https://web.northeastern.edu/smilelab/xuma/pointMLP/checkpoints/fixstd/scanobjectnn/pointMLP-20220204021453/">link</a></td>
</tr>
<tr>
<td align="center">PointNet++ SSG</td>
<td align="center">77.9</td>
<td align="center">1.475M</td>
<td align="center">Rotate, Jitter</td>
<td align="center"><a href="https://github.com/hkust-vgd/scanobjectnn/blob/master/pointnet2/models/pointnet2_cls_ssg.py">link</a></td>
<td align="center">N/A</td>
<td align="center">N/A</td>
</tr>
<tr>
<td align="center"><b>Umbrella RepSurf</b> (PointNet++ SSG)</td>
<td align="center"><b>84.87</b></td>
<td align="center">1.483M</td>
<td align="center">None</td>
<td align="center"><a href="models/repsurf/repsurf_ssg_umb.py">link</a></td>
<td align="center"><a href="https://drive.google.com/file/d/1qJK8T3dhF6177Xla227aXPEeNtyNssLF/view?usp=sharing">google drive</a></td>
<td align="center"><a href="https://drive.google.com/file/d/17UDArfvNVjrJBTjr_HdxcOQipn0DWMMf/view?usp=sharing">google drive (6MB)</a></td>
</tr>
<tr>
<td align="center"><b>Umbrella RepSurf</b> (PointNet++ SSG, 2x)</td>
<td align="center"><b>86.05</b></td>
<td align="center">6.806M</td>
<td align="center">None</td>
<td align="center"><a href="models/repsurf/repsurf_ssg_umb_2x.py">link</a></td>
<td align="center"><a href="https://drive.google.com/file/d/15HwmAi1erL68G08dzNQILSipwCIDfNAw/view?usp=sharing">google drive</a></td>
<td align="center"><a href="https://drive.google.com/file/d/1yGPNt1REzxVwn8Guw-PFHFcwxvfueWgf/view?usp=sharing">google drive (27MB)</a></td>
</tr>
</tbody>
</table>
<br>
* To download dataset:
```
wget https://download.cs.stanford.edu/orion/scanobjectnn/h5_files.zip
unzip h5_files.zip
ln -s [PATH]/h5_files data/ScanObjectNN
```
**Note**: We conduct all experiments on the hardest variant of ScanObjectNN (**PB_T50_RS**).
<br>
* To train **Umbrella RepSurf** on ScanObjectNN:
```
sh scripts/scanobjectnn/repsurf_ssg_umb.sh
```
* To train **Umbrella RepSurf (2x setting)** on ScanObjectNN:
```
sh scripts/scanobjectnn/repsurf_ssg_umb_2x.sh
```
## Acknowledgment
We use part of the library [pointops](https://github.com/hszhao/PointWeb/tree/master/lib/pointops)
from [PointWeb](https://github.com/hszhao/PointWeb).
## License
RepSurf is under the Apache-2.0 license. Please contact the primary author **Haoxi Ran (ranhaoxi@gmail.com)** for
commercial use.
================================================
FILE: classification/dataset/ScanObjectNNDataLoader.py
================================================
"""
Author: Haoxi Ran
Date: 05/10/2022
"""
import h5py
import warnings
from torch.utils.data import Dataset
warnings.filterwarnings('ignore')
class ScanObjectNNDataLoader(Dataset):
def __init__(self, root, split='training', bg=True):
self.root = root
assert (split == 'training' or split == 'test')
if bg:
print('Use data with background points')
dir_name = 'main_split'
else:
print('Use data without background points')
dir_name = 'main_split_nobg'
file_name = '_objectdataset_augmentedrot_scale75.h5'
h5_name = '{}/{}/{}'.format(self.root, dir_name, split + file_name)
with h5py.File(h5_name, mode="r") as f:
self.data = f['data'][:].astype('float32')
self.label = f['label'][:].astype('int64')
print('The size of %s data is %d' % (split, self.data.shape[0]))
def __len__(self):
return self.data.shape[0]
def __getitem__(self, index):
return self.data[index].T, self.label[index]
================================================
FILE: classification/dataset/__init__.py
================================================
================================================
FILE: classification/init.sh
================================================
#!/bin/sh
mkdir -p log/PointAnalysis/log/ScanObjectNN
mkdir -p data/
conda create -n repsurf-cls python=3.7 -y
conda activate repsurf-cls
conda install pytorch=1.6.0 torchvision=0.7.0 cudatoolkit=10.1 -c pytorch -c conda-forge -y
conda install -c anaconda h5py -y
cd modules/pointops
python3 setup.py install
cd -
================================================
FILE: classification/models/__init__.py
================================================
================================================
FILE: classification/models/repsurf/__init__.py
================================================
================================================
FILE: classification/models/repsurf/repsurf_ssg_umb.py
================================================
"""
Author: Haoxi Ran
Date: 05/10/2022
"""
import torch.nn as nn
import torch.nn.functional as F
from modules.repsurface_utils import SurfaceAbstractionCD, UmbrellaSurfaceConstructor
class Model(nn.Module):
def __init__(self, args):
super(Model, self).__init__()
center_channel = 0 if not args.return_center else (6 if args.return_polar else 3)
repsurf_channel = 10
self.init_nsample = args.num_point
self.return_dist = args.return_dist
self.surface_constructor = UmbrellaSurfaceConstructor(args.group_size + 1, repsurf_channel,
return_dist=args.return_dist, aggr_type=args.umb_pool,
cuda=args.cuda_ops)
self.sa1 = SurfaceAbstractionCD(npoint=512, radius=0.2, nsample=32, feat_channel=repsurf_channel,
pos_channel=center_channel, mlp=[64, 64, 128], group_all=False,
return_polar=args.return_polar, cuda=args.cuda_ops)
self.sa2 = SurfaceAbstractionCD(npoint=128, radius=0.4, nsample=64, feat_channel=128 + repsurf_channel,
pos_channel=center_channel, mlp=[128, 128, 256], group_all=False,
return_polar=args.return_polar, cuda=args.cuda_ops)
self.sa3 = SurfaceAbstractionCD(npoint=None, radius=None, nsample=None, feat_channel=256 + repsurf_channel,
pos_channel=center_channel, mlp=[256, 512, 1024], group_all=True,
return_polar=args.return_polar, cuda=args.cuda_ops)
# modelnet40
self.classfier = nn.Sequential(
nn.Linear(1024, 512),
nn.BatchNorm1d(512),
nn.ReLU(True),
nn.Dropout(0.4),
nn.Linear(512, 256),
nn.BatchNorm1d(256),
nn.ReLU(True),
nn.Dropout(0.4),
nn.Linear(256, args.num_class))
def forward(self, points):
# init
center = points[:, :3, :]
normal = self.surface_constructor(center)
center, normal, feature = self.sa1(center, normal, None)
center, normal, feature = self.sa2(center, normal, feature)
center, normal, feature = self.sa3(center, normal, feature)
feature = feature.view(-1, 1024)
feature = self.classfier(feature)
feature = F.log_softmax(feature, -1)
return feature
================================================
FILE: classification/models/repsurf/repsurf_ssg_umb_2x.py
================================================
"""
Author: Haoxi Ran
Date: 05/10/2022
"""
import torch.nn as nn
import torch.nn.functional as F
from modules.repsurface_utils import SurfaceAbstractionCD, UmbrellaSurfaceConstructor
class Model(nn.Module):
def __init__(self, args):
super(Model, self).__init__()
center_channel = 0 if not args.return_center else (6 if args.return_polar else 3)
repsurf_channel = 10
self.init_nsample = args.num_point
self.return_dist = args.return_dist
self.surface_constructor = UmbrellaSurfaceConstructor(args.group_size + 1, repsurf_channel,
return_dist=args.return_dist, aggr_type=args.umb_pool,
cuda=args.cuda_ops)
self.sa1 = SurfaceAbstractionCD(npoint=512, radius=0.1, nsample=24, feat_channel=repsurf_channel,
pos_channel=center_channel, mlp=[128, 128, 256], group_all=False,
return_polar=args.return_polar, cuda=args.cuda_ops)
self.sa2 = SurfaceAbstractionCD(npoint=128, radius=0.2, nsample=24, feat_channel=256 + repsurf_channel,
pos_channel=center_channel, mlp=[256, 256, 512], group_all=False,
return_polar=args.return_polar, cuda=args.cuda_ops)
self.sa3 = SurfaceAbstractionCD(npoint=32, radius=0.4, nsample=24, feat_channel=512 + repsurf_channel,
pos_channel=center_channel, mlp=[512, 512, 1024], group_all=False,
return_polar=args.return_polar, cuda=args.cuda_ops)
self.sa4 = SurfaceAbstractionCD(npoint=None, radius=None, nsample=None, feat_channel=1024 + repsurf_channel,
pos_channel=center_channel, mlp=[1024, 1024, 2048], group_all=True,
return_polar=args.return_polar, cuda=args.cuda_ops)
# modelnet40
self.classfier = nn.Sequential(
nn.Linear(2048, 512),
nn.BatchNorm1d(512),
nn.ReLU(True),
nn.Dropout(0.4),
nn.Linear(512, 256),
nn.BatchNorm1d(256),
nn.ReLU(True),
nn.Dropout(0.4),
nn.Linear(256, args.num_class))
def forward(self, points):
# init
center = points[:, :3, :]
normal = self.surface_constructor(center)
center, normal, feature = self.sa1(center, normal, None)
center, normal, feature = self.sa2(center, normal, feature)
center, normal, feature = self.sa3(center, normal, feature)
center, normal, feature = self.sa4(center, normal, feature)
feature = feature.view(-1, 2048)
feature = self.classfier(feature)
feature = F.log_softmax(feature, -1)
return feature
================================================
FILE: classification/modules/__init__.py
================================================
================================================
FILE: classification/modules/pointnet2_utils.py
================================================
"""
Author: Haoxi Ran
Date: 05/10/2022
"""
import torch
try:
from modules.pointops.functions.pointops import furthestsampling, gathering, ballquery, knnquery, \
grouping, interpolation, nearestneighbor
except:
raise Exception('Failed to load pointops')
def square_distance(src, dst):
"""
Calculate Squared distance between each two points.
"""
B, N, _ = src.shape
_, M, _ = dst.shape
dist = -2 * torch.matmul(src, dst.permute(0, 2, 1))
dist += torch.sum(src ** 2, -1).view(B, N, 1)
dist += torch.sum(dst ** 2, -1).view(B, 1, M)
return dist
def index_points(points, idx, cuda=False, is_group=False):
if cuda:
if is_group:
points = grouping(points.transpose(1, 2).contiguous(), idx)
return points.permute(0, 2, 3, 1).contiguous()
else:
points = gathering(points.transpose(1, 2).contiguous(), idx)
return points.permute(0, 2, 1).contiguous()
device = points.device
B = points.shape[0]
view_shape = list(idx.shape)
view_shape[1:] = [1] * (len(view_shape) - 1)
repeat_shape = list(idx.shape)
repeat_shape[0] = 1
batch_indices = torch.arange(B, dtype=torch.long).to(device).view(view_shape).repeat(repeat_shape)
new_points = points[batch_indices, idx, :]
return new_points
def farthest_point_sample(xyz, npoint, cuda=False):
"""
Input:
xyz: pointcloud data, [B, N, 3]
npoint: number of samples
Return:
centroids: sampled pointcloud index, [B, npoint]
FLOPs:
S * (3 + 3 + 2)
"""
if cuda:
if not xyz.is_contiguous():
xyz = xyz.contiguous()
return furthestsampling(xyz, npoint)
device = xyz.device
B, N, C = xyz.shape
centroids = torch.zeros(B, npoint, dtype=torch.long).to(device)
distance = torch.ones(B, N).to(device) * 1e10
farthest = torch.randint(0, N, (B,), dtype=torch.long).to(device)
batch_indices = torch.arange(B, dtype=torch.long).to(device)
for i in range(npoint):
centroids[:, i] = farthest
centroid = xyz[batch_indices, farthest, :].view(B, 1, 3)
dist = torch.sum((xyz - centroid) ** 2, -1)
mask = dist < distance
distance[mask] = dist[mask]
farthest = torch.max(distance, -1)[1]
return centroids
def query_ball_point(radius, nsample, xyz, new_xyz, debug=False, cuda=False):
if cuda:
if not xyz.is_contiguous():
xyz = xyz.contiguous()
if not new_xyz.is_contiguous():
new_xyz = new_xyz.contiguous()
return ballquery(radius, nsample, xyz, new_xyz)
device = xyz.device
B, N, C = xyz.shape
_, S, _ = new_xyz.shape
group_idx = torch.arange(N, dtype=torch.long).to(device).view(1, 1, N).repeat([B, S, 1])
sqrdists = square_distance(new_xyz, xyz)
group_idx[sqrdists > radius ** 2] = N
group_idx = group_idx.sort(dim=-1)[0][:, :, :nsample]
group_first = group_idx[:, :, 0].view(B, S, 1).repeat([1, 1, nsample])
mask = group_idx == N
group_idx[mask] = group_first[mask]
if debug:
num_miss = torch.sum(mask)
num_over = torch.sum(torch.clamp(torch.sum(sqrdists < radius ** 2, dim=2) - nsample, min=0))
return num_miss, num_over
return group_idx
def query_knn_point(k, xyz, new_xyz, cuda=False):
if cuda:
if not xyz.is_contiguous():
xyz = xyz.contiguous()
if not new_xyz.is_contiguous():
new_xyz = new_xyz.contiguous()
return knnquery(k, xyz, new_xyz)
dist = square_distance(new_xyz, xyz)
group_idx = dist.sort(descending=False, dim=-1)[1][:, :, :k]
return group_idx
def sample(nsample, feature, cuda=False):
feature = feature.permute(0, 2, 1)
xyz = feature[:, :, :3]
fps_idx = farthest_point_sample(xyz, nsample, cuda=cuda) # [B, npoint, C]
torch.cuda.empty_cache()
feature = index_points(feature, fps_idx, cuda=cuda, is_group=False)
torch.cuda.empty_cache()
feature = feature.permute(0, 2, 1)
return feature
================================================
FILE: classification/modules/pointops/__init__.py
================================================
================================================
FILE: classification/modules/pointops/functions/__init__.py
================================================
from .pointops import *
================================================
FILE: classification/modules/pointops/functions/pointops.py
================================================
from typing import Tuple
import numpy as np
import torch
from torch.autograd import Function
import torch.nn as nn
try:
import pointops_cuda
except ImportError:
import warnings
import os
from torch.utils.cpp_extension import load
warnings.warn("Unable to load pointops_cuda cpp extension.")
pointops_cuda_src = os.path.join(os.path.dirname(__file__), "../src")
pointops_cuda = load('pointops_cuda', [
pointops_cuda_src + '/pointops_api.cpp',
pointops_cuda_src + '/ballquery/ballquery_cuda.cpp',
pointops_cuda_src + '/ballquery/ballquery_cuda_kernel.cu',
pointops_cuda_src + '/knnquery/knnquery_cuda.cpp',
pointops_cuda_src + '/knnquery/knnquery_cuda_kernel.cu',
pointops_cuda_src + '/knnquery_heap/knnquery_heap_cuda.cpp',
pointops_cuda_src + '/knnquery_heap/knnquery_heap_cuda_kernel.cu',
pointops_cuda_src + '/grouping/grouping_cuda.cpp',
pointops_cuda_src + '/grouping/grouping_cuda_kernel.cu',
pointops_cuda_src + '/grouping_int/grouping_int_cuda.cpp',
pointops_cuda_src + '/grouping_int/grouping_int_cuda_kernel.cu',
pointops_cuda_src + '/interpolation/interpolation_cuda.cpp',
pointops_cuda_src + '/interpolation/interpolation_cuda_kernel.cu',
pointops_cuda_src + '/sampling/sampling_cuda.cpp',
pointops_cuda_src + '/sampling/sampling_cuda_kernel.cu'
], build_directory=pointops_cuda_src, verbose=False)
class FurthestSampling(Function):
@staticmethod
def forward(ctx, xyz, m):
"""
input: xyz: (b, n, 3) and n > m, m: int32
output: idx: (b, m)
"""
assert xyz.is_contiguous()
b, n, _ = xyz.size()
idx = torch.cuda.IntTensor(b, m)
temp = torch.cuda.FloatTensor(b, n).fill_(1e10)
pointops_cuda.furthestsampling_cuda(b, n, m, xyz, temp, idx)
return idx
@staticmethod
def backward(xyz, a=None):
return None, None
furthestsampling = FurthestSampling.apply
class Gathering(Function):
@staticmethod
def forward(ctx, features, idx):
"""
input: features: (b, c, n), idx : (b, m) tensor
output: (b, c, m)
"""
assert features.is_contiguous()
assert idx.is_contiguous()
b, c, n = features.size()
m = idx.size(1)
output = torch.cuda.FloatTensor(b, c, m)
pointops_cuda.gathering_forward_cuda(b, c, n, m, features, idx, output)
ctx.for_backwards = (idx, c, n)
return output
@staticmethod
def backward(ctx, grad_out):
idx, c, n = ctx.for_backwards
b, m = idx.size()
grad_features = torch.cuda.FloatTensor(b, c, n).zero_()
grad_out_data = grad_out.data.contiguous()
pointops_cuda.gathering_backward_cuda(b, c, n, m, grad_out_data, idx, grad_features.data)
return grad_features, None
gathering = Gathering.apply
class NearestNeighbor(Function):
@staticmethod
def forward(ctx, unknown: torch.Tensor, known: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]:
"""
Find the three nearest neighbors of unknown in known
input: unknown: (b, n, 3), known: (b, m, 3)
output: dist2: (b, n, 3) l2 distance to the three nearest neighbors
idx: (b, n, 3) index of 3 nearest neighbors
"""
assert unknown.is_contiguous()
assert known.is_contiguous()
b, n, _ = unknown.size()
m = known.size(1)
dist2 = torch.cuda.FloatTensor(b, n, 3)
idx = torch.cuda.IntTensor(b, n, 3)
pointops_cuda.nearestneighbor_cuda(b, n, m, unknown, known, dist2, idx)
return torch.sqrt(dist2), idx
@staticmethod
def backward(ctx, a=None, b=None):
return None, None
nearestneighbor = NearestNeighbor.apply
class Interpolation(Function):
@staticmethod
def forward(ctx, features: torch.Tensor, idx: torch.Tensor, weight: torch.Tensor) -> torch.Tensor:
"""
Performs weight linear interpolation on 3 features
input: features: (b, c, m) features descriptors to be interpolated from
idx: (b, n, 3) three nearest neighbors of the target features in features
weight: (b, n, 3) weights
output: (b, c, n) tensor of the interpolated features
"""
features = features.contiguous()
assert features.is_contiguous()
assert idx.is_contiguous()
assert weight.is_contiguous()
b, c, m = features.size()
n = idx.size(1)
ctx.interpolation_for_backward = (idx, weight, m)
output = torch.cuda.FloatTensor(b, c, n)
pointops_cuda.interpolation_forward_cuda(b, c, m, n, features, idx, weight, output)
return output
@staticmethod
def backward(ctx, grad_out: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
"""
input: grad_out: (b, c, n)
output: grad_features: (b, c, m), None, None
"""
idx, weight, m = ctx.interpolation_for_backward
b, c, n = grad_out.size()
grad_features = torch.cuda.FloatTensor(b, c, m).zero_()
grad_out_data = grad_out.data.contiguous()
pointops_cuda.interpolation_backward_cuda(b, c, n, m, grad_out_data, idx, weight, grad_features.data)
return grad_features, None, None
interpolation = Interpolation.apply
class Grouping(Function):
@staticmethod
def forward(ctx, features: torch.Tensor, idx: torch.Tensor) -> torch.Tensor:
"""
input: features: (b, c, n), idx : (b, m, nsample) containing the indicies of features to group with
output: (b, c, m, nsample)
"""
assert features.is_contiguous()
assert idx.is_contiguous()
b, c, n = features.size()
_, m, nsample = idx.size()
output = torch.cuda.FloatTensor(b, c, m, nsample)
pointops_cuda.grouping_forward_cuda(b, c, n, m, nsample, features, idx, output)
ctx.for_backwards = (idx, n)
return output
@staticmethod
def backward(ctx, grad_out: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]:
"""
input: grad_out: (b, c, m, nsample)
output: (b, c, n), None
"""
idx, n = ctx.for_backwards
b, c, m, nsample = grad_out.size()
grad_features = torch.cuda.FloatTensor(b, c, n).zero_()
grad_out_data = grad_out.data.contiguous()
pointops_cuda.grouping_backward_cuda(b, c, n, m, nsample, grad_out_data, idx, grad_features.data)
return grad_features, None
grouping = Grouping.apply
class GroupingInt(Function):
@staticmethod
def forward(ctx, features: torch.Tensor, idx: torch.Tensor) -> torch.Tensor:
"""
input: features: (b, c, n), idx : (b, m, nsample) containing the indicies of features to group with
output: (b, c, m, nsample)
"""
assert features.is_contiguous()
assert idx.is_contiguous()
b, c, n = features.size()
_, m, nsample = idx.size()
output = torch.cuda.LongTensor(b, c, m, nsample)
pointops_cuda.grouping_int_forward_cuda(b, c, n, m, nsample, features, idx, output)
return output
@staticmethod
def backward(ctx, a=None):
return None, None
grouping_int = GroupingInt.apply
class BallQuery(Function):
@staticmethod
def forward(ctx, radius: float, nsample: int, xyz: torch.Tensor, new_xyz: torch.Tensor) -> torch.Tensor:
"""
input: radius: float, radius of the balls
nsample: int, maximum number of features in the balls
xyz: torch.Tensor, (b, n, 3) xyz coordinates of the features
new_xyz: torch.Tensor, (b, m, 3) centers of the ball query
output: (b, m, nsample) tensor with the indicies of the features that form the query balls
"""
assert xyz.is_contiguous()
assert new_xyz.is_contiguous()
b, n, _ = xyz.size()
m = new_xyz.size(1)
idx = torch.cuda.IntTensor(b, m, nsample).zero_()
pointops_cuda.ballquery_cuda(b, n, m, radius, nsample, new_xyz, xyz, idx)
return idx
@staticmethod
def backward(ctx, a=None):
return None, None, None, None
ballquery = BallQuery.apply
def pairwise_distances(x, y=None):
'''
Input: x is a Nxd matrix
y is an optional Mxd matirx
Output: dist is a NxM matrix where dist[i,j] is the square norm between x[i,:] and y[j,:]
if y is not given then use 'y=x'.
i.e. dist[i,j] = ||x[i,:]-y[j,:]||^2
'''
x_norm = (x ** 2).sum(1).view(-1, 1)
if y is not None:
y_t = torch.transpose(y, 0, 1)
y_norm = (y ** 2).sum(1).view(1, -1)
else:
y_t = torch.transpose(x, 0, 1)
y_norm = x_norm.view(1, -1)
dist = x_norm + y_norm - 2.0 * torch.mm(x, y_t)
import numpy as np
return torch.clamp(dist, 0.0, np.inf)
class KNNQueryNaive(Function):
@staticmethod
def forward(ctx, nsample: int, xyz: torch.Tensor, new_xyz: torch.Tensor = None) -> Tuple[torch.Tensor]:
"""
KNN Indexing
input: nsample: int32, Number of neighbor
xyz: (b, n, 3) coordinates of the features
new_xyz: (b, m, 3) centriods
output: idx: (b, m, nsample)
"""
if new_xyz is None:
new_xyz = xyz
b, m, _ = new_xyz.size()
n = xyz.size(1)
'''
idx = torch.zeros(b, m, nsample).int().cuda()
for i in range(b):
dist = pairwise_distances(new_xyz[i, :, :], xyz[i, :, :])
[_, idxs] = torch.sort(dist, dim=1)
idx[i, :, :] = idxs[:, 0:nsample]
'''
# '''
# new_xyz_repeat = new_xyz.repeat(1, 1, n).view(b, m * n, 3)
# xyz_repeat = xyz.repeat(1, m, 1).view(b, m * n, 3)
# dist = (new_xyz_repeat - xyz_repeat).pow(2).sum(dim=2).view(b, m, n)
dist = (new_xyz.repeat(1, 1, n).view(b, m * n, 3) - xyz.repeat(1, m, 1).view(b, m * n, 3)).pow(2).sum(
dim=2).view(b, m, n)
[_, idxs] = torch.sort(dist, dim=2)
idx = idxs[:, :, 0:nsample].int()
# '''
return idx
@staticmethod
def backward(ctx):
return None, None, None
knnquery_naive = KNNQueryNaive.apply
class KNNQuery(Function):
@staticmethod
def forward(ctx, nsample: int, xyz: torch.Tensor, new_xyz: torch.Tensor = None) -> Tuple[torch.Tensor]:
"""
KNN Indexing
input: nsample: int32, Number of neighbor
xyz: (b, n, 3) coordinates of the features
new_xyz: (b, m, 3) centriods
output: idx: (b, m, nsample)
( dist2: (b, m, nsample) )
"""
if new_xyz is None:
new_xyz = xyz
xyz = xyz.contiguous()
new_xyz = new_xyz.contiguous()
assert xyz.is_contiguous()
assert new_xyz.is_contiguous()
b, m, _ = new_xyz.size()
n = xyz.size(1)
idx = torch.cuda.IntTensor(b, m, nsample).zero_()
dist2 = torch.cuda.FloatTensor(b, m, nsample).zero_()
pointops_cuda.knnquery_cuda(b, n, m, nsample, xyz, new_xyz, idx, dist2)
return idx
@staticmethod
def backward(ctx, a=None):
return None, None, None
knnquery = KNNQuery.apply
class KNNQuery_Heap(Function):
@staticmethod
def forward(ctx, nsample: int, xyz: torch.Tensor, new_xyz: torch.Tensor = None) -> Tuple[torch.Tensor]:
"""
KNN Indexing
input: nsample: int32, Number of neighbor
xyz: (b, n, 3) coordinates of the features
new_xyz: (b, m, 3) centriods
output: idx: (b, m, nsample)
( dist2: (b, m, nsample) )
"""
if new_xyz is None:
new_xyz = xyz
assert xyz.is_contiguous()
assert new_xyz.is_contiguous()
b, m, _ = new_xyz.size()
n = xyz.size(1)
idx = torch.cuda.IntTensor(b, m, nsample).zero_()
dist2 = torch.cuda.FloatTensor(b, m, nsample).zero_()
pointops_cuda.knnquery_heap_cuda(b, n, m, nsample, xyz, new_xyz, idx, dist2)
ctx.mark_non_differentiable(idx)
return idx
@staticmethod
def backward(ctx, a=None):
return None, None, None
knnquery_heap = KNNQuery_Heap.apply
class QueryAndGroup(nn.Module):
"""
Groups with a ball query of radius
parameters:
radius: float32, Radius of ball
nsample: int32, Maximum number of features to gather in the ball
"""
def __init__(self, radius=None, nsample=32, use_xyz=True, return_idx=False):
super(QueryAndGroup, self).__init__()
self.radius, self.nsample, self.use_xyz = radius, nsample, use_xyz
self.return_idx = return_idx
def forward(self, xyz: torch.Tensor, new_xyz: torch.Tensor = None, features: torch.Tensor = None,
idx: torch.Tensor = None) -> torch.Tensor:
"""
input: xyz: (b, n, 3) coordinates of the features
new_xyz: (b, m, 3) centriods
features: (b, c, n)
idx: idx of neighbors
# idxs: (b, n)
output: new_features: (b, c+3, m, nsample)
# grouped_idxs: (b, m, nsample)
"""
if new_xyz is None:
new_xyz = xyz
if idx is None:
if self.radius is not None:
idx = ballquery(self.radius, self.nsample, xyz, new_xyz)
else:
# idx = knnquery_naive(self.nsample, xyz, new_xyz) # (b, m, nsample)
# idx = knnquery(self.nsample, xyz, new_xyz) # (b, m, nsample)
idx = knnquery_heap(self.nsample, xyz, new_xyz) # (b, m, nsample)
xyz_trans = xyz.transpose(1, 2).contiguous()
grouped_xyz = grouping(xyz_trans, idx) # (b, 3, m, nsample)
# grouped_idxs = grouping(idxs.unsqueeze(1).float(), idx).squeeze(1).int() # (b, m, nsample)
grouped_xyz_diff = grouped_xyz - new_xyz.transpose(1, 2).unsqueeze(-1)
if features is not None:
grouped_features = grouping(features, idx)
if self.use_xyz:
new_features = torch.cat([grouped_xyz_diff, grouped_features], dim=1) # (b, 3+c, m, nsample)
else:
new_features = grouped_features
else:
assert self.use_xyz, "Cannot have not features and not use xyz as a feature!"
new_features = grouped_xyz_diff
if self.return_idx:
return new_features, grouped_xyz, idx.long()
# (b,c,m,k), (b,3,m,k), (b,m,k)
else:
return new_features, grouped_xyz
================================================
FILE: classification/modules/pointops/setup.py
================================================
#python3 setup.py install
from setuptools import setup
from torch.utils.cpp_extension import BuildExtension, CUDAExtension
import os
from distutils.sysconfig import get_config_vars
(opt,) = get_config_vars('OPT')
os.environ['OPT'] = " ".join(
flag for flag in opt.split() if flag != '-Wstrict-prototypes'
)
setup(
name='pointops',
ext_modules=[
CUDAExtension('pointops_cuda', [
'src/pointops_api.cpp',
'src/ballquery/ballquery_cuda.cpp',
'src/ballquery/ballquery_cuda_kernel.cu',
'src/knnquery/knnquery_cuda.cpp',
'src/knnquery/knnquery_cuda_kernel.cu',
'src/knnquery_heap/knnquery_heap_cuda.cpp',
'src/knnquery_heap/knnquery_heap_cuda_kernel.cu',
'src/grouping/grouping_cuda.cpp',
'src/grouping/grouping_cuda_kernel.cu',
'src/grouping_int/grouping_int_cuda.cpp',
'src/grouping_int/grouping_int_cuda_kernel.cu',
'src/interpolation/interpolation_cuda.cpp',
'src/interpolation/interpolation_cuda_kernel.cu',
'src/sampling/sampling_cuda.cpp',
'src/sampling/sampling_cuda_kernel.cu',
],
extra_compile_args={'cxx': ['-g'],
'nvcc': ['-O2']})
],
cmdclass={'build_ext': BuildExtension})
================================================
FILE: classification/modules/pointops/src/__init__.py
================================================
================================================
FILE: classification/modules/pointops/src/ballquery/ballquery_cuda.cpp
================================================
#include <torch/serialize/tensor.h>
#include <vector>
#include <THC/THC.h>
#include <ATen/cuda/CUDAContext.h>
#include "ballquery_cuda_kernel.h"
extern THCState *state;
#define CHECK_CUDA(x) TORCH_CHECK(x.is_cuda(), #x, " must be a CUDAtensor ")
#define CHECK_CONTIGUOUS(x) TORCH_CHECK(x.is_contiguous(), #x, " must be contiguous ")
#define CHECK_INPUT(x) CHECK_CUDA(x);CHECK_CONTIGUOUS(x)
void ballquery_cuda(int b, int n, int m, float radius, int nsample, at::Tensor new_xyz_tensor, at::Tensor xyz_tensor, at::Tensor idx_tensor)
{
const float *new_xyz = new_xyz_tensor.data_ptr<float>();
const float *xyz = xyz_tensor.data_ptr<float>();
int *idx = idx_tensor.data_ptr<int>();
ballquery_cuda_launcher(b, n, m, radius, nsample, new_xyz, xyz, idx);
}
void ballquery_cuda_fast(int b, int n, int m, float radius, int nsample, at::Tensor new_xyz_tensor, at::Tensor xyz_tensor, at::Tensor idx_tensor)
{
CHECK_INPUT(new_xyz_tensor);
CHECK_INPUT(xyz_tensor);
const float *new_xyz = new_xyz_tensor.data_ptr<float>();
const float *xyz = xyz_tensor.data_ptr<float>();
int *idx = idx_tensor.data_ptr<int>();
cudaStream_t stream = at::cuda::getCurrentCUDAStream();
ballquery_cuda_launcher_fast(b, n, m, radius, nsample, new_xyz, xyz, idx, stream);
}
================================================
FILE: classification/modules/pointops/src/ballquery/ballquery_cuda_kernel.cu
================================================
#include "../cuda_utils.h"
#include "ballquery_cuda_kernel.h"
// input: new_xyz(b, m, 3) xyz(b, n, 3)
// output: idx(b, m, nsample)
__global__ void ballquery_cuda_kernel(int b, int n, int m, float radius, int nsample, const float *new_xyz, const float *xyz, int *idx)
{
int batch_index = blockIdx.x;
xyz += batch_index * n * 3;
new_xyz += batch_index * m * 3;
idx += m * nsample * batch_index;
int index = threadIdx.x;
int stride = blockDim.x;
float radius2 = radius * radius;
for (int j = index; j < m; j += stride)
{
float new_x = new_xyz[j * 3 + 0];
float new_y = new_xyz[j * 3 + 1];
float new_z = new_xyz[j * 3 + 2];
for (int k = 0, cnt = 0; k < n && cnt < nsample; ++k)
{
float x = xyz[k * 3 + 0];
float y = xyz[k * 3 + 1];
float z = xyz[k * 3 + 2];
float d2 = (new_x - x) * (new_x - x) + (new_y - y) * (new_y - y) + (new_z - z) * (new_z - z);
if (d2 < radius2)
{
if (cnt == 0)
{
for (int l = 0; l < nsample; ++l)
idx[j * nsample + l] = k;
}
idx[j * nsample + cnt] = k;
++cnt;
}
}
}
}
void ballquery_cuda_launcher(int b, int n, int m, float radius, int nsample, const float *new_xyz, const float *xyz, int *idx)
{
ballquery_cuda_kernel<<<b, opt_n_threads(m), 0>>>(b, n, m, radius, nsample, new_xyz, xyz, idx);
}
__global__ void ballquery_cuda_kernel_fast(int b, int n, int m, float radius, int nsample, const float *__restrict__ new_xyz, const float *__restrict__ xyz, int *__restrict__ idx) {
int bs_idx = blockIdx.y;
int pt_idx = blockIdx.x * blockDim.x + threadIdx.x;
if (bs_idx >= b || pt_idx >= m) return;
new_xyz += bs_idx * m * 3 + pt_idx * 3;
xyz += bs_idx * n * 3;
idx += bs_idx * m * nsample + pt_idx * nsample;
float radius2 = radius * radius;
float new_x = new_xyz[0];
float new_y = new_xyz[1];
float new_z = new_xyz[2];
int cnt = 0;
for (int k = 0; k < n; ++k) {
float x = xyz[k * 3 + 0];
float y = xyz[k * 3 + 1];
float z = xyz[k * 3 + 2];
float d2 = (new_x - x) * (new_x - x) + (new_y - y) * (new_y - y) + (new_z - z) * (new_z - z);
if (d2 < radius2){
if (cnt == 0){
for (int l = 0; l < nsample; ++l) {
idx[l] = k;
}
}
idx[cnt] = k;
++cnt;
if (cnt >= nsample){
break;
}
}
}
}
void ballquery_cuda_launcher_fast(int b, int n, int m, float radius, int nsample, const float *new_xyz, const float *xyz, int *idx, cudaStream_t stream) {
// param new_xyz: (B, m, 3)
// param xyz: (B, n, 3)
// param idx: (B, m, nsample)
cudaError_t err;
dim3 blocks(DIVUP(m, THREADS_PER_BLOCK), b); // blockIdx.x(col), blockIdx.y(row)
dim3 threads(THREADS_PER_BLOCK);
ballquery_cuda_kernel_fast<<<blocks, threads, 0, stream>>>(b, n, m, radius, nsample, new_xyz, xyz, idx);
// cudaDeviceSynchronize(); // for using printf in kernel function
err = cudaGetLastError();
if (cudaSuccess != err) {
fprintf(stderr, "CUDA kernel failed : %s\n", cudaGetErrorString(err));
exit(-1);
}
}
================================================
FILE: classification/modules/pointops/src/ballquery/ballquery_cuda_kernel.h
================================================
#ifndef _BALLQUERY_CUDA_KERNEL
#define _BALLQUERY_CUDA_KERNEL
#include <torch/serialize/tensor.h>
#include <vector>
#include <ATen/cuda/CUDAContext.h>
void ballquery_cuda(int b, int n, int m, float radius, int nsample, at::Tensor new_xyz_tensor, at::Tensor xyz_tensor, at::Tensor idx_tensor);
void ballquery_cuda_fast(int b, int n, int m, float radius, int nsample, at::Tensor new_xyz_tensor, at::Tensor xyz_tensor, at::Tensor idx_tensor);
#ifdef __cplusplus
extern "C" {
#endif
void ballquery_cuda_launcher(int b, int n, int m, float radius, int nsample, const float *xyz, const float *new_xyz, int *idx);
void ballquery_cuda_launcher_fast(int b, int n, int m, float radius, int nsample, const float *new_xyz, const float *xyz, int *idx, cudaStream_t stream);
#ifdef __cplusplus
}
#endif
#endif
================================================
FILE: classification/modules/pointops/src/cuda_utils.h
================================================
#ifndef _CUDA_UTILS_H
#define _CUDA_UTILS_H
#include <cmath>
#define TOTAL_THREADS 1024
#define CHECK_CUDA(x) TORCH_CHECK(x.is_cuda(), #x, " must be a CUDAtensor ")
#define CHECK_CONTIGUOUS(x) TORCH_CHECK(x.is_contiguous(), #x, " must be contiguous ")
#define CHECK_INPUT(x) CHECK_CUDA(x);CHECK_CONTIGUOUS(x)
#define THREADS_PER_BLOCK 256
#define DIVUP(m,n) ((m) / (n) + ((m) % (n) > 0))
inline int opt_n_threads(int work_size) {
const int pow_2 = std::log(static_cast<double>(work_size)) / std::log(2.0);
return max(min(1 << pow_2, TOTAL_THREADS), 1);
}
inline dim3 opt_block_config(int x, int y) {
const int x_threads = opt_n_threads(x);
const int y_threads = max(min(opt_n_threads(y), TOTAL_THREADS / x_threads), 1);
dim3 block_config(x_threads, y_threads, 1);
return block_config;
}
#endif
================================================
FILE: classification/modules/pointops/src/grouping/grouping_cuda.cpp
================================================
#include <torch/serialize/tensor.h>
#include <ATen/cuda/CUDAContext.h>
#include <vector>
#include <THC/THC.h>
#include "grouping_cuda_kernel.h"
extern THCState *state;
void grouping_forward_cuda(int b, int c, int n, int m, int nsample, at::Tensor points_tensor, at::Tensor idx_tensor, at::Tensor out_tensor)
{
const float *points = points_tensor.data_ptr<float>();
const int *idx = idx_tensor.data_ptr<int>();
float *out = out_tensor.data_ptr<float>();
grouping_forward_cuda_launcher(b, c, n, m, nsample, points, idx, out);
}
void grouping_backward_cuda(int b, int c, int n, int m, int nsample, at::Tensor grad_out_tensor, at::Tensor idx_tensor, at::Tensor grad_points_tensor)
{
float *grad_points = grad_points_tensor.data_ptr<float>();
const int *idx = idx_tensor.data_ptr<int>();
const float *grad_out = grad_out_tensor.data_ptr<float>();
grouping_backward_cuda_launcher(b, c, n, m, nsample, grad_out, idx, grad_points);
}
void grouping_forward_cuda_fast(int b, int c, int n, int npoints, int nsample, at::Tensor points_tensor, at::Tensor idx_tensor, at::Tensor out_tensor) {
const float *points = points_tensor.data_ptr<float>();
const int *idx = idx_tensor.data_ptr<int>();
float *out = out_tensor.data_ptr<float>();
grouping_forward_cuda_launcher_fast(b, c, n, npoints, nsample, points, idx, out);
}
================================================
FILE: classification/modules/pointops/src/grouping/grouping_cuda_kernel.cu
================================================
#include "../cuda_utils.h"
#include "grouping_cuda_kernel.h"
// input: points(b, c, n) idx(b, m, nsample)
// output: out(b, c, m, nsample)
__global__ void grouping_forward_cuda_kernel(int b, int c, int n, int m, int nsample, const float *points, const int *idx, float *out)
{
int batch_index = blockIdx.x;
points += batch_index * n * c;
idx += batch_index * m * nsample;
out += batch_index * m * nsample * c;
const int index = threadIdx.y * blockDim.x + threadIdx.x;
const int stride = blockDim.y * blockDim.x;
for (int i = index; i < c * m; i += stride)
{
const int l = i / m;
const int j = i % m;
for (int k = 0; k < nsample; ++k)
{
int ii = idx[j * nsample + k];
out[(l * m + j) * nsample + k] = points[l * n + ii];
}
}
}
// input: grad_out(b, c, m, nsample), idx(b, m, nsample)
// output: grad_points(b, c, n)
__global__ void grouping_backward_cuda_kernel(int b, int c, int n, int m, int nsample, const float *grad_out, const int *idx, float *grad_points)
{
int batch_index = blockIdx.x;
grad_out += batch_index * m * nsample * c;
idx += batch_index * m * nsample;
grad_points += batch_index * n * c;
const int index = threadIdx.y * blockDim.x + threadIdx.x;
const int stride = blockDim.y * blockDim.x;
for (int i = index; i < c * m; i += stride)
{
const int l = i / m;
const int j = i % m;
for (int k = 0; k < nsample; ++k)
{
int ii = idx[j * nsample + k];
atomicAdd(grad_points + l * n + ii, grad_out[(l * m + j) * nsample + k]);
}
}
}
void grouping_forward_cuda_launcher(int b, int c, int n, int m, int nsample, const float *points, const int *idx, float *out)
{
grouping_forward_cuda_kernel<<<b, opt_block_config(m, c), 0>>>(b, c, n, m, nsample, points, idx, out);
}
void grouping_backward_cuda_launcher(int b, int c, int n, int m, int nsample, const float *grad_out, const int *idx, float *grad_points)
{
grouping_backward_cuda_kernel<<<b, opt_block_config(m, c), 0>>>(b, c, n, m, nsample, grad_out, idx, grad_points);
}
// input: points(b, c, n) idx(b, npoints, nsample)
// output: out(b, c, npoints, nsample)
__global__ void grouping_forward_cuda_kernel_fast(int b, int c, int n, int npoints, int nsample, const float *__restrict__ points, const int *__restrict__ idx, float *__restrict__ out) {
int bs_idx = blockIdx.z;
int c_idx = blockIdx.y;
int index = blockIdx.x * blockDim.x + threadIdx.x;
int pt_idx = index / nsample;
if (bs_idx >= b || c_idx >= c || pt_idx >= npoints) return;
int sample_idx = index % nsample;
idx += bs_idx * npoints * nsample + pt_idx * nsample + sample_idx;
int in_idx = bs_idx * c * n + c_idx * n + idx[0];
int out_idx = bs_idx * c * npoints * nsample + c_idx * npoints * nsample + pt_idx * nsample + sample_idx;
out[out_idx] = points[in_idx];
}
// input: points(b, c, n) idx(b, npoints, nsample)
// output: out(b, c, npoints, nsample)
void grouping_forward_cuda_launcher_fast(int b, int c, int n, int npoints, int nsample, const float *points, const int *idx, float *out) {
cudaError_t err;
dim3 blocks(DIVUP(npoints * nsample, THREADS_PER_BLOCK), c, b); // blockIdx.x(col), blockIdx.y(row)
dim3 threads(THREADS_PER_BLOCK);
grouping_forward_cuda_kernel_fast<<<blocks, threads, 0>>>(b, c, n, npoints, nsample, points, idx, out);
// cudaDeviceSynchronize(); // for using printf in kernel function
err = cudaGetLastError();
if (cudaSuccess != err) {
fprintf(stderr, "CUDA kernel failed : %s\n", cudaGetErrorString(err));
exit(-1);
}
}
================================================
FILE: classification/modules/pointops/src/grouping/grouping_cuda_kernel.h
================================================
#ifndef _GROUPING_CUDA_KERNEL
#define _GROUPING_CUDA_KERNEL
#include <torch/serialize/tensor.h>
#include <vector>
#include <ATen/cuda/CUDAContext.h>
void grouping_forward_cuda(int b, int c, int n, int m, int nsample, at::Tensor points_tensor, at::Tensor idx_tensor, at::Tensor out);
void grouping_backward_cuda(int b, int c, int n, int m, int nsample, at::Tensor grad_out_tensor, at::Tensor idx_tensor, at::Tensor grad_points_tensor);
void grouping_forward_cuda_fast(int b, int c, int n, int npoints, int nsample, at::Tensor points_tensor, at::Tensor idx_tensor, at::Tensor out_tensor);
#ifdef __cplusplus
extern "C" {
#endif
void grouping_forward_cuda_launcher(int b, int c, int n, int m, int nsample, const float *points, const int *idx, float *out);
void grouping_backward_cuda_launcher(int b, int c, int n, int m, int nsample, const float *grad_out, const int *idx, float *grad_points);
void grouping_forward_cuda_launcher_fast(int b, int c, int n, int npoints, int nsample, const float *points, const int *idx, float *out);
#ifdef __cplusplus
}
#endif
#endif
================================================
FILE: classification/modules/pointops/src/grouping_int/grouping_int_cuda.cpp
================================================
#include <torch/serialize/tensor.h>
#include <vector>
#include <ATen/cuda/CUDAContext.h>
#include <THC/THC.h>
#include "grouping_int_cuda_kernel.h"
extern THCState *state;
void grouping_int_forward_cuda(int b, int c, int n, int m, int nsample, at::Tensor points_tensor, at::Tensor idx_tensor, at::Tensor out_tensor)
{
const long int *points = points_tensor.data_ptr<long int>();
const int *idx = idx_tensor.data_ptr<int>();
long int *out = out_tensor.data_ptr<long int>();
grouping_int_forward_cuda_launcher(b, c, n, m, nsample, points, idx, out);
}
void grouping_int_forward_cuda_fast(int b, int c, int n, int m, int nsample, at::Tensor points_tensor, at::Tensor idx_tensor, at::Tensor out_tensor)
{
const long int *points = points_tensor.data_ptr<long int>();
const int *idx = idx_tensor.data_ptr<int>();
long int *out = out_tensor.data_ptr<long int>();
grouping_int_forward_cuda_launcher_fast(b, c, n, m, nsample, points, idx, out);
}
================================================
FILE: classification/modules/pointops/src/grouping_int/grouping_int_cuda_kernel.cu
================================================
#include "../cuda_utils.h"
#include "grouping_int_cuda_kernel.h"
// input: points(b, c, n) idx(b, m, nsample)
// output: out(b, c, m, nsample)
__global__ void grouping_int_forward_cuda_kernel(int b, int c, int n, int m, int nsample, const long int *points, const int *idx, long int *out)
{
int batch_index = blockIdx.x;
points += batch_index * n * c;
idx += batch_index * m * nsample;
out += batch_index * m * nsample * c;
const int index = threadIdx.y * blockDim.x + threadIdx.x;
const int stride = blockDim.y * blockDim.x;
for (int i = index; i < c * m; i += stride)
{
const int l = i / m;
const int j = i % m;
for (int k = 0; k < nsample; ++k)
{
int ii = idx[j * nsample + k];
out[(l * m + j) * nsample + k] = points[l * n + ii];
}
}
}
void grouping_int_forward_cuda_launcher(int b, int c, int n, int m, int nsample, const long int *points, const int *idx, long int *out)
{
grouping_int_forward_cuda_kernel<<<b, opt_block_config(m, c), 0>>>(b, c, n, m, nsample, points, idx, out);
}
__global__ void grouping_int_forward_cuda_kernel_fast(int b, int c, int n, int npoints, int nsample, const long int *__restrict__ points, const int *__restrict__ idx, long int *__restrict__ out)
{
int bs_idx = blockIdx.z;
int c_idx = blockIdx.y;
int index = blockIdx.x * blockDim.x + threadIdx.x;
int pt_idx = index / nsample;
if (bs_idx >= b || c_idx >= c || pt_idx >= npoints) return;
int sample_idx = index % nsample;
idx += bs_idx * npoints * nsample + pt_idx * nsample + sample_idx;
int in_idx = bs_idx * c * n + c_idx * n + idx[0];
int out_idx = bs_idx * c * npoints * nsample + c_idx * npoints * nsample + pt_idx * nsample + sample_idx;
out[out_idx] = points[in_idx];
}
void grouping_int_forward_cuda_launcher_fast(int b, int c, int n, int npoints, int nsample, const long int *points, const int *idx, long int *out)
{
cudaError_t err;
dim3 blocks(DIVUP(npoints * nsample, THREADS_PER_BLOCK), c, b); // blockIdx.x(col), blockIdx.y(row)
dim3 threads(THREADS_PER_BLOCK);
grouping_int_forward_cuda_kernel_fast<<<blocks, threads, 0>>>(b, c, n, npoints, nsample, points, idx, out);
// cudaDeviceSynchronize(); // for using printf in kernel function
err = cudaGetLastError();
if (cudaSuccess != err) {
fprintf(stderr, "CUDA kernel failed : %s\n", cudaGetErrorString(err));
exit(-1);
}
}
================================================
FILE: classification/modules/pointops/src/grouping_int/grouping_int_cuda_kernel.h
================================================
#ifndef _GROUPING_INT_CUDA_KERNEL
#define _GROUPING_INT_CUDA_KERNEL
#include <torch/serialize/tensor.h>
#include <vector>
#include <ATen/cuda/CUDAContext.h>
void grouping_int_forward_cuda(int b, int c, int n, int m, int nsample, at::Tensor points_tensor, at::Tensor idx_tensor, at::Tensor out);
void grouping_int_forward_cuda_fast(int b, int c, int n, int m, int nsample, at::Tensor points_tensor, at::Tensor idx_tensor, at::Tensor out_tensor);
#ifdef __cplusplus
extern "C" {
#endif
void grouping_int_forward_cuda_launcher(int b, int c, int n, int m, int nsample, const long int *points, const int *idx, long int *out);
void grouping_int_forward_cuda_launcher_fast(int b, int c, int n, int npoints, int nsample, const long int *points, const int *idx, long int *out);
#ifdef __cplusplus
}
#endif
#endif
================================================
FILE: classification/modules/pointops/src/interpolation/interpolation_cuda.cpp
================================================
#include <torch/serialize/tensor.h>
#include <vector>
#include <ATen/cuda/CUDAContext.h>
#include <THC/THC.h>
#include "interpolation_cuda_kernel.h"
extern THCState *state;
void nearestneighbor_cuda(int b, int n, int m, at::Tensor unknown_tensor, at::Tensor known_tensor, at::Tensor dist2_tensor, at::Tensor idx_tensor)
{
const float *unknown = unknown_tensor.data_ptr<float>();
const float *known = known_tensor.data_ptr<float>();
float *dist2 = dist2_tensor.data_ptr<float>();
int *idx = idx_tensor.data_ptr<int>();
nearestneighbor_cuda_launcher(b, n, m, unknown, known, dist2, idx);
}
void interpolation_forward_cuda(int b, int c, int m, int n, at::Tensor points_tensor, at::Tensor idx_tensor, at::Tensor weight_tensor, at::Tensor out_tensor)
{
const float *points = points_tensor.data_ptr<float>();
const float *weight = weight_tensor.data_ptr<float>();
float *out = out_tensor.data_ptr<float>();
const int *idx = idx_tensor.data_ptr<int>();
interpolation_forward_cuda_launcher(b, c, m, n, points, idx, weight, out);
}
void interpolation_backward_cuda(int b, int c, int n, int m, at::Tensor grad_out_tensor, at::Tensor idx_tensor, at::Tensor weight_tensor, at::Tensor grad_points_tensor)
{
const float *grad_out = grad_out_tensor.data_ptr<float>();
const float *weight = weight_tensor.data_ptr<float>();
float *grad_points = grad_points_tensor.data_ptr<float>();
const int *idx = idx_tensor.data_ptr<int>();
interpolation_backward_cuda_launcher(b, c, n, m, grad_out, idx, weight, grad_points);
}
void nearestneighbor_cuda_fast(int b, int n, int m, at::Tensor unknown_tensor, at::Tensor known_tensor, at::Tensor dist2_tensor, at::Tensor idx_tensor) {
const float *unknown = unknown_tensor.data_ptr<float>();
const float *known = known_tensor.data_ptr<float>();
float *dist2 = dist2_tensor.data_ptr<float>();
int *idx = idx_tensor.data_ptr<int>();
nearestneighbor_cuda_launcher_fast(b, n, m, unknown, known, dist2, idx);
}
void interpolation_forward_cuda_fast(int b, int c, int m, int n, at::Tensor points_tensor, at::Tensor idx_tensor, at::Tensor weight_tensor, at::Tensor out_tensor) {
const float *points = points_tensor.data_ptr<float>();
const float *weight = weight_tensor.data_ptr<float>();
float *out = out_tensor.data_ptr<float>();
const int *idx = idx_tensor.data_ptr<int>();
interpolation_forward_cuda_launcher_fast(b, c, m, n, points, idx, weight, out);
}
================================================
FILE: classification/modules/pointops/src/interpolation/interpolation_cuda_kernel.cu
================================================
#include "../cuda_utils.h"
#include "interpolation_cuda_kernel.h"
// input: unknown(b, n, 3) known(b, m, 3)
// output: dist2(b, n, 3), idx(b, n, 3)
__global__ void nearestneighbor_cuda_kernel(int b, int n, int m, const float *unknown, const float *known, float *dist2, int *idx)
{
int batch_index = blockIdx.x;
unknown += batch_index * n * 3;
known += batch_index * m * 3;
dist2 += batch_index * n * 3;
idx += batch_index * n * 3;
int index = threadIdx.x;
int stride = blockDim.x;
for (int j = index; j < n; j += stride)
{
float ux = unknown[j * 3 + 0];
float uy = unknown[j * 3 + 1];
float uz = unknown[j * 3 + 2];
double best1 = 1e40, best2 = 1e40, best3 = 1e40;
int besti1 = 0, besti2 = 0, besti3 = 0;
for (int k = 0; k < m; ++k)
{
float x = known[k * 3 + 0];
float y = known[k * 3 + 1];
float z = known[k * 3 + 2];
float d =
(ux - x) * (ux - x) + (uy - y) * (uy - y) + (uz - z) * (uz - z);
if (d < best1)
{
best3 = best2;
besti3 = besti2;
best2 = best1;
besti2 = besti1;
best1 = d;
besti1 = k;
}
else if (d < best2)
{
best3 = best2;
besti3 = besti2;
best2 = d;
besti2 = k;
}
else if (d < best3)
{
best3 = d;
besti3 = k;
}
}
dist2[j * 3 + 0] = best1;
dist2[j * 3 + 1] = best2;
dist2[j * 3 + 2] = best3;
idx[j * 3 + 0] = besti1;
idx[j * 3 + 1] = besti2;
idx[j * 3 + 2] = besti3;
}
}
// input: points(b, c, m), idx(b, n, 3), weight(b, n, 3)
// output: out(b, c, n)
__global__ void interpolation_forward_cuda_kernel(int b, int c, int m, int n, const float *points, const int *idx, const float *weight, float *out)
{
int batch_index = blockIdx.x;
points += batch_index * m * c;
idx += batch_index * n * 3;
weight += batch_index * n * 3;
out += batch_index * n * c;
const int index = threadIdx.y * blockDim.x + threadIdx.x;
const int stride = blockDim.y * blockDim.x;
for (int i = index; i < c * n; i += stride)
{
const int l = i / n;
const int j = i % n;
float w1 = weight[j * 3 + 0];
float w2 = weight[j * 3 + 1];
float w3 = weight[j * 3 + 2];
int i1 = idx[j * 3 + 0];
int i2 = idx[j * 3 + 1];
int i3 = idx[j * 3 + 2];
out[i] = points[l * m + i1] * w1 + points[l * m + i2] * w2 + points[l * m + i3] * w3;
}
}
// input: grad_out(b, c, n), idx(b, n, 3), weight(b, n, 3)
// output: grad_points(b, c, m)
__global__ void interpolation_backward_cuda_kernel( int b, int c, int n, int m, const float *grad_out, const int *idx, const float *weight, float *grad_points)
{
int batch_index = blockIdx.x;
grad_out += batch_index * n * c;
idx += batch_index * n * 3;
weight += batch_index * n * 3;
grad_points += batch_index * m * c;
const int index = threadIdx.y * blockDim.x + threadIdx.x;
const int stride = blockDim.y * blockDim.x;
for (int i = index; i < c * n; i += stride)
{
const int l = i / n;
const int j = i % n;
float w1 = weight[j * 3 + 0];
float w2 = weight[j * 3 + 1];
float w3 = weight[j * 3 + 2];
int i1 = idx[j * 3 + 0];
int i2 = idx[j * 3 + 1];
int i3 = idx[j * 3 + 2];
atomicAdd(grad_points + l * m + i1, grad_out[i] * w1);
atomicAdd(grad_points + l * m + i2, grad_out[i] * w2);
atomicAdd(grad_points + l * m + i3, grad_out[i] * w3);
}
}
void nearestneighbor_cuda_launcher(int b, int n, int m, const float *unknown, const float *known, float *dist2, int *idx)
{
nearestneighbor_cuda_kernel<<<b, opt_n_threads(n), 0>>>(b, n, m, unknown, known, dist2, idx);
}
void interpolation_forward_cuda_launcher(int b, int c, int m, int n, const float *points, const int *idx, const float *weight, float *out)
{
interpolation_forward_cuda_kernel<<<b, opt_block_config(n, c), 0>>>(b, c, m, n, points, idx, weight, out);
}
void interpolation_backward_cuda_launcher(int b, int n, int c, int m, const float *grad_out, const int *idx, const float *weight, float *grad_points)
{
interpolation_backward_cuda_kernel<<<b, opt_block_config(n, c), 0>>>(b, n, c, m, grad_out, idx, weight, grad_points);
}
// input: unknown(b, n, 3) known(b, m, 3)
// output: dist2(b, n, 3), idx(b, n, 3)
__global__ void nearestneighbor_cuda_kernel_fast(int b, int n, int m, const float *__restrict__ unknown, const float *__restrict__ known, float *__restrict__ dist2, int *__restrict__ idx) {
int bs_idx = blockIdx.y;
int pt_idx = blockIdx.x * blockDim.x + threadIdx.x;
if (bs_idx >= b || pt_idx >= n) return;
unknown += bs_idx * n * 3 + pt_idx * 3;
known += bs_idx * m * 3;
dist2 += bs_idx * n * 3 + pt_idx * 3;
idx += bs_idx * n * 3 + pt_idx * 3;
float ux = unknown[0];
float uy = unknown[1];
float uz = unknown[2];
double best1 = 1e40, best2 = 1e40, best3 = 1e40;
int besti1 = 0, besti2 = 0, besti3 = 0;
for (int k = 0; k < m; ++k) {
float x = known[k * 3 + 0];
float y = known[k * 3 + 1];
float z = known[k * 3 + 2];
float d = (ux - x) * (ux - x) + (uy - y) * (uy - y) + (uz - z) * (uz - z);
if (d < best1) {
best3 = best2; besti3 = besti2;
best2 = best1; besti2 = besti1;
best1 = d; besti1 = k;
}
else if (d < best2) {
best3 = best2; besti3 = besti2;
best2 = d; besti2 = k;
}
else if (d < best3) {
best3 = d; besti3 = k;
}
}
dist2[0] = best1;
dist2[1] = best2;
dist2[2] = best3;
idx[0] = besti1;
idx[1] = besti2;
idx[2] = besti3;
}
// input: points(b, c, m), idx(b, n, 3), weight(b, n, 3)
// output: out(b, c, n)
__global__ void interpolation_forward_cuda_kernel_fast(int b, int c, int m, int n, const float *__restrict__ points, const int *__restrict__ idx, const float *__restrict__ weight, float *__restrict__ out) {
int bs_idx = blockIdx.z;
int c_idx = blockIdx.y;
int pt_idx = blockIdx.x * blockDim.x + threadIdx.x;
if (bs_idx >= b || c_idx >= c || pt_idx >= n) return;
weight += bs_idx * n * 3 + pt_idx * 3;
points += bs_idx * c * m + c_idx * m;
idx += bs_idx * n * 3 + pt_idx * 3;
out += bs_idx * c * n + c_idx * n;
out[pt_idx] = weight[0] * points[idx[0]] + weight[1] * points[idx[1]] + weight[2] * points[idx[2]];
}
void nearestneighbor_cuda_launcher_fast(int b, int n, int m, const float *unknown, const float *known, float *dist2, int *idx)
{
cudaError_t err;
dim3 blocks(DIVUP(n, THREADS_PER_BLOCK), b); // blockIdx.x(col), blockIdx.y(row)
dim3 threads(THREADS_PER_BLOCK);
nearestneighbor_cuda_kernel_fast<<<blocks, threads, 0>>>(b, n, m, unknown, known, dist2, idx);
err = cudaGetLastError();
if (cudaSuccess != err) {
fprintf(stderr, "CUDA kernel failed : %s\n", cudaGetErrorString(err));
exit(-1);
}
}
void interpolation_forward_cuda_launcher_fast(int b, int c, int m, int n, const float *points, const int *idx, const float *weight, float *out) {
cudaError_t err;
dim3 blocks(DIVUP(n, THREADS_PER_BLOCK), c, b); // blockIdx.x(col), blockIdx.y(row)
dim3 threads(THREADS_PER_BLOCK);
interpolation_forward_cuda_kernel_fast<<<blocks, threads, 0>>>(b, c, m, n, points, idx, weight, out);
err = cudaGetLastError();
if (cudaSuccess != err) {
fprintf(stderr, "CUDA kernel failed : %s\n",
cudaGetErrorString(err));
exit(-1);
}
}
================================================
FILE: classification/modules/pointops/src/interpolation/interpolation_cuda_kernel.h
================================================
#ifndef _INTERPOLATION_CUDA_KERNEL
#define _INTERPOLATION_CUDA_KERNEL
#include <torch/serialize/tensor.h>
#include <vector>
#include <ATen/cuda/CUDAContext.h>
void nearestneighbor_cuda(int b, int n, int m, at::Tensor unknown_tensor, at::Tensor known_tensor, at::Tensor dist2_tensor, at::Tensor idx_tensor);
void interpolation_forward_cuda(int b, int c, int m, int n, at::Tensor points_tensor, at::Tensor idx_tensor, at::Tensor weight_tensor, at::Tensor out_tensor);
void interpolation_backward_cuda(int b, int c, int n, int m, at::Tensor grad_out_tensor, at::Tensor idx_tensor, at::Tensor weight_tensor, at::Tensor grad_points_tensor);
void nearestneighbor_cuda_fast(int b, int n, int m, at::Tensor unknown_tensor, at::Tensor known_tensor, at::Tensor dist2_tensor, at::Tensor idx_tensor);
void interpolation_forward_cuda_fast(int b, int c, int m, int n, at::Tensor points_tensor, at::Tensor idx_tensor, at::Tensor weight_tensor, at::Tensor out_tensor);
#ifdef __cplusplus
extern "C" {
#endif
void nearestneighbor_cuda_launcher(int b, int n, int m, const float *unknown, const float *known, float *dist2, int *idx);
void interpolation_forward_cuda_launcher(int b, int c, int m, int n, const float *points, const int *idx, const float *weight, float *out);
void interpolation_backward_cuda_launcher(int b, int c, int n, int m, const float *grad_out, const int *idx, const float *weight, float *grad_points);
void nearestneighbor_cuda_launcher_fast(int b, int n, int m, const float *unknown, const float *known, float *dist2, int *idx);
void interpolation_forward_cuda_launcher_fast(int b, int c, int m, int n, const float *points, const int *idx, const float *weight, float *out);
#ifdef __cplusplus
}
#endif
#endif
================================================
FILE: classification/modules/pointops/src/knnquery/__init__.py
================================================
================================================
FILE: classification/modules/pointops/src/knnquery/knnquery_cuda.cpp
================================================
#include <torch/serialize/tensor.h>
#include <vector>
#include <THC/THC.h>
#include <ATen/cuda/CUDAContext.h>
#include "knnquery_cuda_kernel.h"
extern THCState *state;
#define CHECK_CUDA(x) TORCH_CHECK(x.is_cuda(), #x, " must be a CUDAtensor ")
#define CHECK_CONTIGUOUS(x) TORCH_CHECK(x.is_contiguous(), #x, " must be contiguous ")
#define CHECK_INPUT(x) CHECK_CUDA(x);CHECK_CONTIGUOUS(x)
void knnquery_cuda(int b, int n, int m, int nsample, at::Tensor xyz_tensor, at::Tensor new_xyz_tensor, at::Tensor idx_tensor, at::Tensor dist2_tensor)
{
CHECK_INPUT(new_xyz_tensor);
CHECK_INPUT(xyz_tensor);
const float *new_xyz = new_xyz_tensor.data_ptr<float>();
const float *xyz = xyz_tensor.data_ptr<float>();
int *idx = idx_tensor.data_ptr<int>();
float *dist2 = dist2_tensor.data_ptr<float>();
cudaStream_t stream = at::cuda::getCurrentCUDAStream();
knnquery_cuda_launcher(b, n, m, nsample, xyz, new_xyz, idx, dist2, stream);
}
================================================
FILE: classification/modules/pointops/src/knnquery/knnquery_cuda_kernel.cu
================================================
#include "../cuda_utils.h"
#include "knnquery_cuda_kernel.h"
// input: xyz (b, n, 3) new_xyz (b, m, 3)
// output: idx (b, m, nsample) dist2 (b, m, nsample)
__global__ void knnquery_cuda_kernel(int b, int n, int m, int nsample, const float *__restrict__ xyz, const float *__restrict__ new_xyz, int *__restrict__ idx, float *__restrict__ dist2) {
int bs_idx = blockIdx.y;
int pt_idx = blockIdx.x * blockDim.x + threadIdx.x;
if (bs_idx >= b || pt_idx >= m) return;
new_xyz += bs_idx * m * 3 + pt_idx * 3;
xyz += bs_idx * n * 3;
idx += bs_idx * m * nsample + pt_idx * nsample;
float new_x = new_xyz[0];
float new_y = new_xyz[1];
float new_z = new_xyz[2];
//double* best = new double[nsample];
//int* besti = new int[nsample];
double best[200];
int besti[200];
for(int i = 0; i < nsample; i++){
best[i] = 1e40;
besti[i] = 0;
}
for(int k = 0; k < n; k++){
float x = xyz[k * 3 + 0];
float y = xyz[k * 3 + 1];
float z = xyz[k * 3 + 2];
float d2 = (new_x - x) * (new_x - x) + (new_y - y) * (new_y - y) + (new_z - z) * (new_z - z);
for(int j = 0; j < nsample; j++){
if(d2 < best[j]){
for(int i = nsample - 1; i > j; i--){
best[i] = best[i - 1];
besti[i] = besti[i - 1];
}
best[j] = d2;
besti[j] = k;
break;
}
}
}
for(int i = 0; i < nsample; i++){
idx[i] = besti[i];
dist2[i] = best[i];
}
//delete []best;
//delete []besti;
}
void knnquery_cuda_launcher(int b, int n, int m, int nsample, const float *xyz, const float *new_xyz, int *idx, float *dist2, cudaStream_t stream) {
// param new_xyz: (B, m, 3)
// param xyz: (B, n, 3)
// param idx: (B, m, nsample)
cudaError_t err;
dim3 blocks(DIVUP(m, THREADS_PER_BLOCK), b); // blockIdx.x(col), blockIdx.y(row)
dim3 threads(THREADS_PER_BLOCK);
// fprintf('%d, %d', blocks, threads);
knnquery_cuda_kernel<<<blocks, threads, 0, stream>>>(b, n, m, nsample, xyz, new_xyz, idx, dist2);
// cudaDeviceSynchronize(); // for using printf in kernel function
// err = cudaGetLastError();
// if (cudaSuccess != err) {
// fprintf(stderr, "CUDA kernel failed : %s\n", cudaGetErrorString(err));
// exit(-1);
// }
}
================================================
FILE: classification/modules/pointops/src/knnquery/knnquery_cuda_kernel.h
================================================
#ifndef _KNNQUERY_CUDA_KERNEL
#define _KNNQUERY_CUDA_KERNEL
#include <torch/serialize/tensor.h>
#include <vector>
#include <ATen/cuda/CUDAContext.h>
void knnquery_cuda(int b, int n, int m, int nsample, at::Tensor xyz_tensor, at::Tensor new_xyz_tensor, at::Tensor idx_tensor, at::Tensor dist2_tensor);
#ifdef __cplusplus
extern "C" {
#endif
void knnquery_cuda_launcher(int b, int n, int m, int nsample, const float *xyz, const float *new_xyz, int *idx, float *dist2, cudaStream_t stream);
#ifdef __cplusplus
}
#endif
#endif
================================================
FILE: classification/modules/pointops/src/knnquery_heap/__init__.py
================================================
================================================
FILE: classification/modules/pointops/src/knnquery_heap/knnquery_heap_cuda.cpp
================================================
#include <torch/serialize/tensor.h>
#include <vector>
#include <THC/THC.h>
#include <ATen/cuda/CUDAContext.h>
#include "knnquery_heap_cuda_kernel.h"
extern THCState *state;
#define CHECK_CUDA(x) TORCH_CHECK(x.is_cuda(), #x, " must be a CUDAtensor ")
#define CHECK_CONTIGUOUS(x) TORCH_CHECK(x.is_contiguous(), #x, " must be contiguous ")
#define CHECK_INPUT(x) CHECK_CUDA(x);CHECK_CONTIGUOUS(x)
void knnquery_heap_cuda(int b, int n, int m, int nsample, at::Tensor xyz_tensor, at::Tensor new_xyz_tensor, at::Tensor idx_tensor, at::Tensor dist2_tensor)
{
CHECK_INPUT(new_xyz_tensor);
CHECK_INPUT(xyz_tensor);
const float *new_xyz = new_xyz_tensor.data_ptr<float>();
const float *xyz = xyz_tensor.data_ptr<float>();
int *idx = idx_tensor.data_ptr<int>();
float *dist2 = dist2_tensor.data_ptr<float>();
cudaStream_t stream = at::cuda::getCurrentCUDAStream();
knnquery_heap_cuda_launcher(b, n, m, nsample, xyz, new_xyz, idx, dist2, stream);
}
================================================
FILE: classification/modules/pointops/src/knnquery_heap/knnquery_heap_cuda_kernel.cu
================================================
#include "../cuda_utils.h"
#include "knnquery_heap_cuda_kernel.h"
__device__ void swap_float(float *x, float *y)
{
float tmp = *x;
*x = *y;
*y = tmp;
}
__device__ void swap_int(int *x, int *y)
{
int tmp = *x;
*x = *y;
*y = tmp;
}
__device__ void reheap(float *dist, int *idx, int k)
{
int root = 0;
int child = root * 2 + 1;
while (child < k)
{
if(child + 1 < k && dist[child+1] > dist[child])
child++;
if(dist[root] > dist[child])
return;
swap_float(&dist[root], &dist[child]);
swap_int(&idx[root], &idx[child]);
root = child;
child = root * 2 + 1;
}
}
__device__ void heap_sort(float *dist, int *idx, int k)
{
int i;
for (i = k - 1; i > 0; i--)
{
swap_float(&dist[0], &dist[i]);
swap_int(&idx[0], &idx[i]);
reheap(dist, idx, i);
}
}
// input: xyz (b, n, 3) new_xyz (b, m, 3)
// output: idx (b, m, nsample) dist2 (b, m, nsample)
__global__ void knnquery_heap_cuda_kernel(int b, int n, int m, int nsample, const float *__restrict__ xyz, const float *__restrict__ new_xyz, int *__restrict__ idx, float *__restrict__ dist2) {
int bs_idx = blockIdx.y;
int pt_idx = blockIdx.x * blockDim.x + threadIdx.x;
if (bs_idx >= b || pt_idx >= m) return;
new_xyz += bs_idx * m * 3 + pt_idx * 3;
xyz += bs_idx * n * 3;
idx += bs_idx * m * nsample + pt_idx * nsample;
dist2 += bs_idx * m * nsample + pt_idx * nsample;
float new_x = new_xyz[0];
float new_y = new_xyz[1];
float new_z = new_xyz[2];
float best_dist[100];
int best_idx[100];
for(int i = 0; i < nsample; i++){
best_dist[i] = 1e10;
best_idx[i] = 0;
}
for(int i = 0; i < n; i++){
float x = xyz[i * 3 + 0];
float y = xyz[i * 3 + 1];
float z = xyz[i * 3 + 2];
float d2 = (new_x - x) * (new_x - x) + (new_y - y) * (new_y - y) + (new_z - z) * (new_z - z);
if (d2 < best_dist[0]){
best_dist[0] = d2;
best_idx[0] = i;
reheap(best_dist, best_idx, nsample);
}
}
heap_sort(best_dist, best_idx, nsample);
for(int i = 0; i < nsample; i++){
idx[i] = best_idx[i];
dist2[i] = best_dist[i];
}
}
void knnquery_heap_cuda_launcher(int b, int n, int m, int nsample, const float *xyz, const float *new_xyz, int *idx, float *dist2, cudaStream_t stream) {
// param new_xyz: (B, m, 3)
// param xyz: (B, n, 3)
// param idx: (B, m, nsample)
cudaError_t err;
dim3 blocks(DIVUP(m, THREADS_PER_BLOCK), b); // blockIdx.x(col), blockIdx.y(row)
dim3 threads(THREADS_PER_BLOCK);
knnquery_heap_cuda_kernel<<<blocks, threads, 0, stream>>>(b, n, m, nsample, xyz, new_xyz, idx, dist2);
// cudaDeviceSynchronize(); // for using printf in kernel function
err = cudaGetLastError();
if (cudaSuccess != err) {
fprintf(stderr, "CUDA kernel failed : %s\n", cudaGetErrorString(err));
exit(-1);
}
}
================================================
FILE: classification/modules/pointops/src/knnquery_heap/knnquery_heap_cuda_kernel.h
================================================
#ifndef _KNNQUERY_HEAP_CUDA_KERNEL
#define _KNNQUERY_HEAP_CUDA_KERNEL
#include <torch/serialize/tensor.h>
#include <vector>
#include <ATen/cuda/CUDAContext.h>
void knnquery_heap_cuda(int b, int n, int m, int nsample, at::Tensor xyz_tensor, at::Tensor new_xyz_tensor, at::Tensor idx_tensor, at::Tensor dist2_tensor);
#ifdef __cplusplus
extern "C" {
#endif
void knnquery_heap_cuda_launcher(int b, int n, int m, int nsample, const float *xyz, const float *new_xyz, int *idx, float *dist2, cudaStream_t stream);
#ifdef __cplusplus
}
#endif
#endif
================================================
FILE: classification/modules/pointops/src/pointops_api.cpp
================================================
#include <torch/serialize/tensor.h>
#include <torch/extension.h>
#include "ballquery/ballquery_cuda_kernel.h"
#include "grouping/grouping_cuda_kernel.h"
#include "grouping_int/grouping_int_cuda_kernel.h"
#include "sampling/sampling_cuda_kernel.h"
#include "interpolation/interpolation_cuda_kernel.h"
#include "knnquery/knnquery_cuda_kernel.h"
#include "knnquery_heap/knnquery_heap_cuda_kernel.h"
PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
m.def("ballquery_cuda", &ballquery_cuda_fast, "ballquery_cuda_fast"); // name in python, cpp function address, docs
m.def("knnquery_cuda", &knnquery_cuda, "knnquery_cuda");
m.def("knnquery_heap_cuda", &knnquery_heap_cuda, "knnquery_heap_cuda");
m.def("grouping_forward_cuda", &grouping_forward_cuda_fast, "grouping_forward_cuda_fast");
m.def("grouping_backward_cuda", &grouping_backward_cuda, "grouping_backward_cuda");
m.def("grouping_int_forward_cuda", &grouping_int_forward_cuda_fast, "grouping_int_forward_cuda_fast");
m.def("gathering_forward_cuda", &gathering_forward_cuda, "gathering_forward_cuda");
m.def("gathering_backward_cuda", &gathering_backward_cuda, "gathering_backward_cuda");
m.def("furthestsampling_cuda", &furthestsampling_cuda, "furthestsampling_cuda");
m.def("nearestneighbor_cuda", &nearestneighbor_cuda_fast, "nearestneighbor_cuda_fast");
m.def("interpolation_forward_cuda", &interpolation_forward_cuda_fast, "interpolation_forward_cuda_fast");
m.def("interpolation_backward_cuda", &interpolation_backward_cuda, "interpolation_backward_cuda");
}
================================================
FILE: classification/modules/pointops/src/sampling/sampling_cuda.cpp
================================================
#include <torch/serialize/tensor.h>
#include <vector>
#include <ATen/cuda/CUDAContext.h>
#include <THC/THC.h>
#include "sampling_cuda_kernel.h"
extern THCState *state;
void gathering_forward_cuda(int b, int c, int n, int m, at::Tensor points_tensor, at::Tensor idx_tensor, at::Tensor out_tensor)
{
const float *points = points_tensor.data_ptr<float>();
const int *idx = idx_tensor.data_ptr<int>();
float *out = out_tensor.data_ptr<float>();
gathering_forward_cuda_launcher(b, c, n, m, points, idx, out);
}
void gathering_backward_cuda(int b, int c, int n, int m, at::Tensor grad_out_tensor, at::Tensor idx_tensor, at::Tensor grad_points_tensor)
{
const float *grad_out = grad_out_tensor.data_ptr<float>();
const int *idx = idx_tensor.data_ptr<int>();
float *grad_points = grad_points_tensor.data_ptr<float>();
gathering_backward_cuda_launcher(b, c, n, m, grad_out, idx, grad_points);
}
void furthestsampling_cuda(int b, int n, int m, at::Tensor points_tensor, at::Tensor temp_tensor, at::Tensor idx_tensor)
{
const float *points = points_tensor.data_ptr<float>();
float *temp = temp_tensor.data_ptr<float>();
int *idx = idx_tensor.data_ptr<int>();
furthestsampling_cuda_launcher(b, n, m, points, temp, idx);
}
================================================
FILE: classification/modules/pointops/src/sampling/sampling_cuda_kernel.cu
================================================
#include "../cuda_utils.h"
#include "sampling_cuda_kernel.h"
// input: points(b, c, n) idx(b, m)
// output: out(b, c, m)
__global__ void gathering_forward_cuda_kernel(int b, int c, int n, int m, const float *points, const int *idx, float *out)
{
for (int i = blockIdx.x; i < b; i += gridDim.x)
{
for (int l = blockIdx.y; l < c; l += gridDim.y)
{
for (int j = threadIdx.x; j < m; j += blockDim.x)
{
int a = idx[i * m + j];
out[(i * c + l) * m + j] = points[(i * c + l) * n + a];
}
}
}
}
// input: grad_out(b, c, m) idx(b, m)
// output: grad_points(b, c, n)
__global__ void gathering_backward_cuda_kernel(int b, int c, int n, int m, const float *grad_out, const int *idx, float *grad_points)
{
for (int i = blockIdx.x; i < b; i += gridDim.x)
{
for (int l = blockIdx.y; l < c; l += gridDim.y)
{
for (int j = threadIdx.x; j < m; j += blockDim.x)
{
int a = idx[i * m + j];
atomicAdd(grad_points + (i * c + l) * n + a, grad_out[(i * c + l) * m + j]);
}
}
}
}
void gathering_forward_cuda_launcher(int b, int c, int n, int m, const float *points, const int *idx, float *out)
{
gathering_forward_cuda_kernel<<<dim3(b, c, 1), opt_n_threads(m), 0>>>(b, c, n, m, points, idx, out);
}
void gathering_backward_cuda_launcher(int b, int c, int n, int m, const float *grad_out, const int *idx, float *grad_points)
{
gathering_backward_cuda_kernel<<<dim3(b, c, 1), opt_n_threads(m), 0>>>(b, c, n, m, grad_out, idx, grad_points);
}
__device__ void __update(float *dists, int *dists_i,
int idx1, int idx2) {
const float v1 = dists[idx1], v2 = dists[idx2];
const int i1 = dists_i[idx1], i2 = dists_i[idx2];
dists[idx1] = max(v1, v2);
dists_i[idx1] = v2 > v1 ? i2 : i1;
}
// Input dataset: (b, n, 3), tmp: (b, n)
// Ouput idxs (b, m)
template <unsigned int block_size>
__global__ void furthestsampling_cuda_kernel(int b, int n, int m, const float *dataset, float *temp, int *idxs)
{
if (m <= 0)
return;
__shared__ float dists[block_size];
__shared__ int dists_i[block_size];
int batch_index = blockIdx.x;
dataset += batch_index * n * 3;
temp += batch_index * n;
idxs += batch_index * m;
int tid = threadIdx.x;
const int stride = block_size;
int old = 0;
if (threadIdx.x == 0)
idxs[0] = old;
__syncthreads();
for (int j = 1; j < m; j++)
{
int besti = 0;
float best = -1;
float x1 = dataset[old * 3 + 0];
float y1 = dataset[old * 3 + 1];
float z1 = dataset[old * 3 + 2];
for (int k = tid; k < n; k += stride)
{
float x2, y2, z2;
x2 = dataset[k * 3 + 0];
y2 = dataset[k * 3 + 1];
z2 = dataset[k * 3 + 2];
//float mag = (x2 * x2) + (y2 * y2) + (z2 * z2);
//if (mag <= 1e-3)
// continue;
float d = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1) + (z2 - z1) * (z2 - z1);
float d2 = min(d, temp[k]);
temp[k] = d2;
besti = d2 > best ? k : besti;
best = d2 > best ? d2 : best;
}
dists[tid] = best;
dists_i[tid] = besti;
__syncthreads();
if (block_size >= 1024) {
if (tid < 512) {
__update(dists, dists_i, tid, tid + 512);
}
__syncthreads();
}
if (block_size >= 512) {
if (tid < 256) {
__update(dists, dists_i, tid, tid + 256);
}
__syncthreads();
}
if (block_size >= 256) {
if (tid < 128) {
__update(dists, dists_i, tid, tid + 128);
}
__syncthreads();
}
if (block_size >= 128) {
if (tid < 64) {
__update(dists, dists_i, tid, tid + 64);
}
__syncthreads();
}
if (block_size >= 64) {
if (tid < 32) {
__update(dists, dists_i, tid, tid + 32);
}
__syncthreads();
}
if (block_size >= 32) {
if (tid < 16) {
__update(dists, dists_i, tid, tid + 16);
}
__syncthreads();
}
if (block_size >= 16) {
if (tid < 8) {
__update(dists, dists_i, tid, tid + 8);
}
__syncthreads();
}
if (block_size >= 8) {
if (tid < 4) {
__update(dists, dists_i, tid, tid + 4);
}
__syncthreads();
}
if (block_size >= 4) {
if (tid < 2) {
__update(dists, dists_i, tid, tid + 2);
}
__syncthreads();
}
if (block_size >= 2) {
if (tid < 1) {
__update(dists, dists_i, tid, tid + 1);
}
__syncthreads();
}
old = dists_i[0];
if (tid == 0)
idxs[j] = old;
}
}
void furthestsampling_cuda_launcher(int b, int n, int m, const float *dataset, float *temp, int *idxs)
{
unsigned int n_threads = opt_n_threads(n);
switch (n_threads) {
case 1024:
furthestsampling_cuda_kernel<1024><<<b, n_threads, 0>>>(b, n, m, dataset, temp, idxs);
break;
case 512:
furthestsampling_cuda_kernel<512><<<b, n_threads, 0>>>(b, n, m, dataset, temp, idxs);
break;
case 256:
furthestsampling_cuda_kernel<256><<<b, n_threads, 0>>>(b, n, m, dataset, temp, idxs);
break;
case 128:
furthestsampling_cuda_kernel<128><<<b, n_threads, 0>>>(b, n, m, dataset, temp, idxs);
break;
case 64:
furthestsampling_cuda_kernel<64><<<b, n_threads, 0>>>(b, n, m, dataset, temp, idxs);
break;
case 32:
furthestsampling_cuda_kernel<32><<<b, n_threads, 0>>>(b, n, m, dataset, temp, idxs);
break;
case 16:
furthestsampling_cuda_kernel<16><<<b, n_threads, 0>>>(b, n, m, dataset, temp, idxs);
break;
case 8:
furthestsampling_cuda_kernel<8><<<b, n_threads, 0>>>(b, n, m, dataset, temp, idxs);
break;
case 4:
furthestsampling_cuda_kernel<4><<<b, n_threads, 0>>>(b, n, m, dataset, temp, idxs);
break;
case 2:
furthestsampling_cuda_kernel<2><<<b, n_threads, 0>>>(b, n, m, dataset, temp, idxs);
break;
case 1:
furthestsampling_cuda_kernel<1><<<b, n_threads, 0>>>(b, n, m, dataset, temp, idxs);
break;
default:
furthestsampling_cuda_kernel<512><<<b, n_threads, 0>>>(b, n, m, dataset, temp, idxs);
}
}
================================================
FILE: classification/modules/pointops/src/sampling/sampling_cuda_kernel.h
================================================
#ifndef _SAMPLING_CUDA_KERNEL
#define _SAMPLING_CUDA_KERNEL
#include <torch/serialize/tensor.h>
#include <vector>
#include <ATen/cuda/CUDAContext.h>
void gathering_forward_cuda(int b, int c, int n, int m, at::Tensor points_tensor, at::Tensor idx_tensor, at::Tensor out_tensor);
void gathering_backward_cuda(int b, int c, int n, int m, at::Tensor grad_out_tensor, at::Tensor idx_tensor, at::Tensor grad_points_tensor);
void furthestsampling_cuda(int b, int n, int m, at::Tensor points_tensor, at::Tensor temp_tensor, at::Tensor idx_tensor);
#ifdef __cplusplus
extern "C" {
#endif
void gathering_forward_cuda_launcher(int b, int c, int n, int m, const float *points, const int *idx, float *out);
void gathering_backward_cuda_launcher(int b, int c, int n, int m, const float *grad_out, const int *idx, float *grad_points);
void furthestsampling_cuda_launcher(int b, int n, int m, const float *dataset, float *temp, int *idxs);
#ifdef __cplusplus
}
#endif
#endif
================================================
FILE: classification/modules/polar_utils.py
================================================
"""
Author: Haoxi Ran
Date: 05/10/2022
"""
import torch
import numpy as np
def xyz2sphere(xyz, normalize=True):
"""
Convert XYZ to Spherical Coordinate
reference: https://en.wikipedia.org/wiki/Spherical_coordinate_system
:param xyz: [B, N, 3] / [B, N, G, 3]
:return: (rho, theta, phi) [B, N, 3] / [B, N, G, 3]
"""
rho = torch.sqrt(torch.sum(torch.pow(xyz, 2), dim=-1, keepdim=True))
rho = torch.clamp(rho, min=0) # range: [0, inf]
theta = torch.acos(xyz[..., 2, None] / rho) # range: [0, pi]
phi = torch.atan2(xyz[..., 1, None], xyz[..., 0, None]) # range: [-pi, pi]
# check nan
idx = rho == 0
theta[idx] = 0
if normalize:
theta = theta / np.pi # [0, 1]
phi = phi / (2 * np.pi) + .5 # [0, 1]
out = torch.cat([rho, theta, phi], dim=-1)
return out
def xyz2cylind(xyz, normalize=True):
"""
Convert XYZ to Cylindrical Coordinate
reference: https://en.wikipedia.org/wiki/Cylindrical_coordinate_system
:param normalize: Normalize phi & z
:param xyz: [B, N, 3] / [B, N, G, 3]
:return: (rho, phi, z) [B, N, 3]
"""
rho = torch.sqrt(torch.sum(torch.pow(xyz[..., :2], 2), dim=-1, keepdim=True))
rho = torch.clamp(rho, 0, 1) # range: [0, 1]
phi = torch.atan2(xyz[..., 1, None], xyz[..., 0, None]) # range: [-pi, pi]
z = xyz[..., 2, None]
z = torch.clamp(z, -1, 1) # range: [-1, 1]
if normalize:
phi = phi / (2 * np.pi) + .5
z = (z + 1.) / 2.
out = torch.cat([rho, phi, z], dim=-1)
return out
================================================
FILE: classification/modules/ptaug_utils.py
================================================
"""
Author: Haoxi Ran
Date: 05/10/2022
"""
import torch
#################
# MAIN
#################
def get_aug_args(args):
dataset = args.dataset
if dataset == 'ScanObjectNN':
aug_args = {'scale_factor': 0.5, 'shift_factor': 0.3}
return aug_args
else:
raise Exception('No such dataset')
def transform_point_cloud(batch, args, aug_args, train=True, label=None):
"""batch: B x 3/6 x N"""
if args.aug_scale:
batch[:, 0:3] = scale_point_cloud(batch[:, 0:3], aug_args['scale_factor'])
if args.aug_shift:
batch[:, 0:3] = shift_point_cloud(batch[:, 0:3], shift_range=aug_args['shift_factor'])
if label is not None:
return batch, label
return batch
#################
# Shift
#################
def shift_point_cloud(batch_data, shift_range=0.2):
""" Randomly shift point cloud. Shift is per point cloud.
Input:
B x C x N array, original batch of point clouds
Return:
B x C x N array, shifted batch of point clouds
"""
shifts = (torch.rand(batch_data.shape[0], 3, 1, device=batch_data.device) * 2. - 1.) * shift_range
batch_data += shifts
return batch_data
#################
# Scale
#################
def scale_point_cloud(batch_data, scale_range=0.2):
""" Randomly scale the point cloud. Scale is per point cloud.
Input:
B x C x N array, original batch of point clouds
Return:
B x C x N array, scaled batch of point clouds
"""
scales = (torch.rand(batch_data.shape[0], 3, 1, device=batch_data.device) * 2. - 1.) * scale_range + 1.
batch_data *= scales
return batch_data
================================================
FILE: classification/modules/recons_utils.py
================================================
"""
Author: Haoxi Ran
Date: 05/10/2022
"""
import torch
from torch import nn
from modules.pointnet2_utils import query_knn_point, index_points
def _recons_factory(type):
if type == 'knn':
return knn_recons
else:
raise Exception('Not Implemented Reconstruction Type')
def knn_recons(k, center, context, cuda=False):
idx = query_knn_point(k, context, center, cuda=cuda)
torch.cuda.empty_cache()
group_xyz = index_points(context, idx, cuda=cuda, is_group=True) # [B, N, K, C]
torch.cuda.empty_cache()
return group_xyz
def cal_normal(group_xyz, random_inv=False, is_group=False):
"""
Calculate Normal Vector (Unit Form + First Term Positive)
:param group_xyz: [B, N, K=3, 3] / [B, N, G, K=3, 3]
:param random_inv:
:param return_intersect:
:param return_const:
:return: [B, N, 3]
"""
edge_vec1 = group_xyz[..., 1, :] - group_xyz[..., 0, :] # [B, N, 3]
edge_vec2 = group_xyz[..., 2, :] - group_xyz[..., 0, :] # [B, N, 3]
nor = torch.cross(edge_vec1, edge_vec2, dim=-1)
unit_nor = nor / torch.norm(nor, dim=-1, keepdim=True) # [B, N, 3] / [B, N, G, 3]
if not is_group:
pos_mask = (unit_nor[..., 0] > 0).float() * 2. - 1. # keep x_n positive
else:
pos_mask = (unit_nor[..., 0:1, 0] > 0).float() * 2. - 1.
unit_nor = unit_nor * pos_mask.unsqueeze(-1)
# batch-wise random inverse normal vector (prob: 0.5)
if random_inv:
random_mask = torch.randint(0, 2, (group_xyz.size(0), 1, 1)).float() * 2. - 1.
random_mask = random_mask.to(unit_nor.device)
if not is_group:
unit_nor = unit_nor * random_mask
else:
unit_nor = unit_nor * random_mask.unsqueeze(-1)
return unit_nor
def pca(X, k, center=True):
"""
Principal Components Analysis impl. with SVD function
:param X:
:param k:
:param center:
:return:
"""
n = X.size()[0]
ones = torch.ones(n).view([n, 1])
h = ((1 / n) * torch.mm(ones, ones.t())) if center else torch.zeros(n * n).view([n, n])
H = torch.eye(n) - h
X_center = torch.mm(H.double(), X.double())
u, s, v = torch.svd(X_center)
components = v[:k].t()
explained_variance = torch.mul(s[:k], s[:k]) / (n - 1)
return {'X': X, 'k': k, 'components': components,
'explained_variance': explained_variance}
def cal_center(group_xyz):
"""
Calculate Global Coordinates of the Center of Triangle
:param group_xyz: [B, N, K, 3] / [B, N, G, K, 3]; K >= 3
:return: [B, N, 3] / [B, N, G, 3]
"""
center = torch.mean(group_xyz, dim=-2)
return center
def cal_area(group_xyz):
"""
Calculate Area of Triangle
:param group_xyz: [B, N, K, 3] / [B, N, G, K, 3]; K = 3
:return: [B, N, 1] / [B, N, G, 1]
"""
pad_shape = group_xyz[..., 0, None].shape
det_xy = torch.det(torch.cat([group_xyz[..., 0, None], group_xyz[..., 1, None], torch.ones(pad_shape)], dim=-1))
det_yz = torch.det(torch.cat([group_xyz[..., 1, None], group_xyz[..., 2, None], torch.ones(pad_shape)], dim=-1))
det_zx = torch.det(torch.cat([group_xyz[..., 2, None], group_xyz[..., 0, None], torch.ones(pad_shape)], dim=-1))
area = torch.sqrt(det_xy ** 2 + det_yz ** 2 + det_zx ** 2).unsqueeze(-1)
return area
def cal_const(normal, center, is_normalize=True):
"""
Calculate Constant Term (Standard Version, with x_normal to be 1)
math::
const = x_nor * x_0 + y_nor * y_0 + z_nor * z_0
:param is_normalize:
:param normal: [B, N, 3] / [B, N, G, 3]
:param center: [B, N, 3] / [B, N, G, 3]
:return: [B, N, 1] / [B, N, G, 1]
"""
const = torch.sum(normal * center, dim=-1, keepdim=True)
factor = torch.sqrt(torch.Tensor([3])).to(normal.device)
const = const / factor if is_normalize else const
return const
def check_nan(normal, center, pos=None):
"""
Check & Remove NaN in normal tensor
:param pos: [B, N, 1]
:param center: [B, N, 3]
:param normal: [B, N, 3]
:return:
"""
B, N, _ = normal.shape
mask = torch.sum(torch.isnan(normal), dim=-1) > 0
mask_first = torch.argmax((~mask).int(), dim=-1)
normal_first = normal[torch.arange(B), None, mask_first].repeat([1, N, 1])
normal[mask] = normal_first[mask]
center_first = center[torch.arange(B), None, mask_first].repeat([1, N, 1])
center[mask] = center_first[mask]
if pos is not None:
pos_first = pos[torch.arange(B), None, mask_first].repeat([1, N, 1])
pos[mask] = pos_first[mask]
return normal, center, pos
return normal, center
def check_nan_umb(normal, center, pos=None):
"""
Check & Remove NaN in normal tensor
:param pos: [B, N, G, 1]
:param center: [B, N, G, 3]
:param normal: [B, N, G, 3]
:return:
"""
B, N, G, _ = normal.shape
mask = torch.sum(torch.isnan(normal), dim=-1) > 0
mask_first = torch.argmax((~mask).int(), dim=-1)
b_idx = torch.arange(B).unsqueeze(1).repeat([1, N])
n_idx = torch.arange(N).unsqueeze(0).repeat([B, 1])
normal_first = normal[b_idx, n_idx, None, mask_first].repeat([1, 1, G, 1])
normal[mask] = normal_first[mask]
center_first = center[b_idx, n_idx, None, mask_first].repeat([1, 1, G, 1])
center[mask] = center_first[mask]
if pos is not None:
pos_first = pos[b_idx, n_idx, None, mask_first].repeat([1, 1, G, 1])
pos[mask] = pos_first[mask]
return normal, center, pos
return normal, center
class SurfaceConstructor(nn.Module):
"""
Surface Constructor for Point Clouds
Formulation of A Surface:
A * (x - x_0) + B * (y - y_0) + C * (z - z_0) = 0,
where A^2 + B^2 + C^2 = 1 & A > 0
"""
def __init__(self, r=None, k=3, recons_type='knn', return_dist=False, random_inv=True, cuda=False):
super(SurfaceConstructor, self).__init__()
self.K = k
self.R = r
self.recons = _recons_factory(recons_type)
self.cuda = cuda
self.return_dist = return_dist
self.random_inv = random_inv
def forward(self, center, context):
"""
Input:
center: input points position as centroid points, [B, 3, N]
context: input points position as context points, [B, 3, N']
Output:
normal: normals of constructed triangles, [B, 3, N]
center: centroids of constructed triangles, [B, 3, N]
pos: position info of constructed triangles, [B, 1, N]
"""
center = center.permute(0, 2, 1)
context = context.permute(0, 2, 1)
group_xyz = self.recons(self.K, center, context, cuda=self.cuda)
normal = cal_normal(group_xyz, random_inv=self.random_inv)
center = cal_center(group_xyz)
if self.return_dist:
pos = cal_const(normal, center)
normal, center, pos = check_nan(normal, center, pos)
normal = normal.permute(0, 2, 1)
center = center.permute(0, 2, 1)
pos = pos.permute(0, 2, 1)
return normal, center, pos
normal, center = check_nan(normal, center)
normal = normal.permute(0, 2, 1)
center = center.permute(0, 2, 1)
return normal, center
if __name__ == '__main__':
xyz = torch.rand(1, 3, 1024) * 2. - 1.
constructor = SurfaceConstructor(return_dist=True)
normal, center, pos = constructor(xyz, xyz)
print(normal.shape)
print(center.shape)
================================================
FILE: classification/modules/repsurface_utils.py
================================================
"""
Author: Haoxi Ran
Date: 05/10/2022
"""
import torch
import torch.nn as nn
import torch.nn.functional as F
from modules.pointnet2_utils import farthest_point_sample, index_points, query_knn_point, query_ball_point
from modules.polar_utils import xyz2sphere
from modules.recons_utils import cal_const, cal_normal, cal_center, check_nan_umb
def sample_and_group(npoint, radius, nsample, center, normal, feature, return_normal=True, return_polar=False, cuda=False):
"""
Input:
center: input points position data
normal: input points normal data
feature: input points feature
Return:
new_center: sampled points position data
new_normal: sampled points normal data
new_feature: sampled points feature
"""
# sample
fps_idx = farthest_point_sample(center, npoint, cuda=cuda) # [B, npoint, A]
torch.cuda.empty_cache()
# sample center
new_center = index_points(center, fps_idx, cuda=cuda, is_group=False)
torch.cuda.empty_cache()
# sample normal
new_normal = index_points(normal, fps_idx, cuda=cuda, is_group=False)
torch.cuda.empty_cache()
# group
idx = query_ball_point(radius, nsample, center, new_center, cuda=cuda)
torch.cuda.empty_cache()
# group normal
group_normal = index_points(normal, idx, cuda=cuda, is_group=True) # [B, npoint, nsample, B]
torch.cuda.empty_cache()
# group center
group_center = index_points(center, idx, cuda=cuda, is_group=True) # [B, npoint, nsample, A]
torch.cuda.empty_cache()
group_center_norm = group_center - new_center.unsqueeze(2)
torch.cuda.empty_cache()
# group polar
if return_polar:
group_polar = xyz2sphere(group_center_norm)
group_center_norm = torch.cat([group_center_norm, group_polar], dim=-1)
if feature is not None:
group_feature = index_points(feature, idx, cuda=cuda, is_group=True)
new_feature = torch.cat([group_center_norm, group_normal, group_feature], dim=-1) if return_normal \
else torch.cat([group_center_norm, group_feature], dim=-1)
else:
new_feature = torch.cat([group_center_norm, group_normal], dim=-1)
return new_center, new_normal, new_feature
def sample_and_group_all(center, normal, feature, return_normal=True, return_polar=False):
"""
Input:
center: input centroid position data
normal: input normal data
feature: input feature data
Return:
new_center: sampled points position data
new_normal: sampled points position data
new_feature: sampled points data
"""
device = center.device
B, N, C = normal.shape
new_center = torch.zeros(B, 1, 3).to(device)
new_normal = new_center
group_normal = normal.view(B, 1, N, C)
group_center = center.view(B, 1, N, 3)
if return_polar:
group_polar = xyz2sphere(group_center)
group_center = torch.cat([group_center, group_polar], dim=-1)
new_feature = torch.cat([group_center, group_normal, feature.view(B, 1, N, -1)], dim=-1) if return_normal \
else torch.cat([group_center, feature.view(B, 1, N, -1)], dim=-1)
return new_center, new_normal, new_feature
def resort_points(points, idx):
"""
Resort Set of points along G dim
"""
device = points.device
B, N, G, _ = points.shape
view_shape = [B, 1, 1]
repeat_shape = [1, N, G]
b_indices = torch.arange(B, dtype=torch.long).to(device).view(view_shape).repeat(repeat_shape)
view_shape = [1, N, 1]
repeat_shape = [B, 1, G]
n_indices = torch.arange(N, dtype=torch.long).to(device).view(view_shape).repeat(repeat_shape)
new_points = points[b_indices, n_indices, idx, :]
return new_points
def group_by_umbrella(xyz, new_xyz, k=9, cuda=False):
"""
Group a set of points into umbrella surfaces
"""
idx = query_knn_point(k, xyz, new_xyz, cuda=cuda)
torch.cuda.empty_cache()
group_xyz = index_points(xyz, idx, cuda=cuda, is_group=True)[:, :, 1:] # [B, N', K-1, 3]
torch.cuda.empty_cache()
group_xyz_norm = group_xyz - new_xyz.unsqueeze(-2)
group_phi = xyz2sphere(group_xyz_norm)[..., 2] # [B, N', K-1]
sort_idx = group_phi.argsort(dim=-1) # [B, N', K-1]
# [B, N', K-1, 1, 3]
sorted_group_xyz = resort_points(group_xyz_norm, sort_idx).unsqueeze(-2)
sorted_group_xyz_roll = torch.roll(sorted_group_xyz, -1, dims=-3)
group_centriod = torch.zeros_like(sorted_group_xyz)
umbrella_group_xyz = torch.cat([group_centriod, sorted_group_xyz, sorted_group_xyz_roll], dim=-2)
return umbrella_group_xyz
class SurfaceAbstraction(nn.Module):
"""
Surface Abstraction Module
"""
def __init__(self, npoint, radius, nsample, in_channel, mlp, group_all, return_polar=True, return_normal=True, cuda=False):
super(SurfaceAbstraction, self).__init__()
self.npoint = npoint
self.radius = radius
self.nsample = nsample
self.return_normal = return_normal
self.return_polar = return_polar
self.cuda = cuda
self.group_all = group_all
self.mlp_convs = nn.ModuleList()
self.mlp_bns = nn.ModuleList()
last_channel = in_channel
for out_channel in mlp:
self.mlp_convs.append(nn.Conv2d(last_channel, out_channel, 1))
self.mlp_bns.append(nn.BatchNorm2d(out_channel))
last_channel = out_channel
def forward(self, center, normal, feature):
normal = normal.permute(0, 2, 1)
center = center.permute(0, 2, 1)
if feature is not None:
feature = feature.permute(0, 2, 1)
if self.group_all:
new_center, new_normal, new_feature = sample_and_group_all(center, normal, feature,
return_polar=self.return_polar,
return_normal=self.return_normal)
else:
new_center, new_normal, new_feature = sample_and_group(self.npoint, self.radius, self.nsample, center,
normal, feature, return_polar=self.return_polar,
return_normal=self.return_normal, cuda=self.cuda)
new_feature = new_feature.permute(0, 3, 2, 1)
for i, conv in enumerate(self.mlp_convs):
bn = self.mlp_bns[i]
new_feature = F.relu(bn(conv(new_feature)))
new_feature = torch.max(new_feature, 2)[0]
new_center = new_center.permute(0, 2, 1)
new_normal = new_normal.permute(0, 2, 1)
return new_center, new_normal, new_feature
class SurfaceAbstractionCD(nn.Module):
"""
Surface Abstraction Module
"""
def __init__(self, npoint, radius, nsample, feat_channel, pos_channel, mlp, group_all,
return_normal=True, return_polar=False, cuda=False):
super(SurfaceAbstractionCD, self).__init__()
self.npoint = npoint
self.radius = radius
self.nsample = nsample
self.return_normal = return_normal
self.return_polar = return_polar
self.cuda = cuda
self.mlp_convs = nn.ModuleList()
self.mlp_bns = nn.ModuleList()
self.pos_channel = pos_channel
self.group_all = group_all
self.mlp_l0 = nn.Conv2d(self.pos_channel, mlp[0], 1)
self.mlp_f0 = nn.Conv2d(feat_channel, mlp[0], 1)
self.bn_l0 = nn.BatchNorm2d(mlp[0])
self.bn_f0 = nn.BatchNorm2d(mlp[0])
# mlp_l0+mlp_f0 can be considered as the first layer of mlp_convs
last_channel = mlp[0]
for out_channel in mlp[1:]:
self.mlp_convs.append(nn.Conv2d(last_channel, out_channel, 1))
self.mlp_bns.append(nn.BatchNorm2d(out_channel))
last_channel = out_channel
def forward(self, center, normal, feature):
normal = normal.permute(0, 2, 1)
center = center.permute(0, 2, 1)
if feature is not None:
feature = feature.permute(0, 2, 1)
if self.group_all:
new_center, new_normal, new_feature = sample_and_group_all(center, normal, feature,
return_normal=self.return_normal,
return_polar=self.return_polar)
else:
new_center, new_normal, new_feature = sample_and_group(self.npoint, self.radius, self.nsample, center,
normal, feature, return_normal=self.return_normal,
return_polar=self.return_polar, cuda=self.cuda)
new_feature = new_feature.permute(0, 3, 2, 1)
# init layer
loc = self.bn_l0(self.mlp_l0(new_feature[:, :self.pos_channel]))
feat = self.bn_f0(self.mlp_f0(new_feature[:, self.pos_channel:]))
new_feature = loc + feat
new_feature = F.relu(new_feature)
for i, conv in enumerate(self.mlp_convs):
bn = self.mlp_bns[i]
new_feature = F.relu(bn(conv(new_feature)))
new_feature = torch.max(new_feature, 2)[0]
new_center = new_center.permute(0, 2, 1)
new_normal = new_normal.permute(0, 2, 1)
return new_center, new_normal, new_feature
class UmbrellaSurfaceConstructor(nn.Module):
"""
Umbrella-based Surface Abstraction Module
"""
def __init__(self, k, in_channel, aggr_type='sum', return_dist=False, random_inv=True, cuda=False):
super(UmbrellaSurfaceConstructor, self).__init__()
self.k = k
self.return_dist = return_dist
self.random_inv = random_inv
self.aggr_type = aggr_type
self.cuda = cuda
self.mlps = nn.Sequential(
nn.Conv2d(in_channel, in_channel, 1, bias=False),
nn.BatchNorm2d(in_channel),
nn.ReLU(True),
nn.Conv2d(in_channel, in_channel, 1, bias=True),
nn.BatchNorm2d(in_channel),
nn.ReLU(True),
nn.Conv2d(in_channel, in_channel, 1, bias=True),
)
def forward(self, center):
center = center.permute(0, 2, 1)
# surface construction
group_xyz = group_by_umbrella(center, center, k=self.k, cuda=self.cuda) # [B, N, K-1, 3 (points), 3 (coord.)]
# normal
group_normal = cal_normal(group_xyz, random_inv=self.random_inv, is_group=True)
# coordinate
group_center = cal_center(group_xyz)
# polar
group_polar = xyz2sphere(group_center)
if self.return_dist:
group_pos = cal_const(group_normal, group_center)
group_normal, group_center, group_pos = check_nan_umb(group_normal, group_center, group_pos)
new_feature = torch.cat([group_center, group_polar, group_normal, group_pos], dim=-1) # N+P+CP: 10
else:
group_normal, group_center = check_nan_umb(group_normal, group_center)
new_feature = torch.cat([group_center, group_polar, group_normal], dim=-1)
new_feature = new_feature.permute(0, 3, 2, 1) # [B, C, G, N]
# mapping
new_feature = self.mlps(new_feature)
# aggregation
if self.aggr_type == 'max':
new_feature = torch.max(new_feature, 2)[0]
elif self.aggr_type == 'avg':
new_feature = torch.mean(new_feature, 2)
else:
new_feature = torch.sum(new_feature, 2)
return new_feature
================================================
FILE: classification/scripts/scanobjectnn/repsurf_ssg_umb.sh
================================================
#!/usr/bin/env bash
set -v
python3 tool/train_cls_scanobjectnn.py \
--cuda_ops \
--batch_size 64 \
--model repsurf.repsurf_ssg_umb \
--epoch 250 \
--log_dir repsurf_cls_ssg_umb \
--gpus 0 \
--n_workers 12 \
--return_center \
--return_dist \
--return_polar \
--group_size 8 \
--umb_pool sum \
--num_point 1024
================================================
FILE: classification/scripts/scanobjectnn/repsurf_ssg_umb_2x.sh
================================================
#!/usr/bin/env bash
set -v
python3 tool/train_cls_scanobjectnn.py \
--cuda_ops \
--batch_size 64 \
--model repsurf.repsurf_ssg_umb_2x \
--epoch 250 \
--log_dir repsurf_cls_ssg_umb_2x \
--gpus 0 \
--n_workers 12 \
--return_center \
--return_dist \
--return_polar \
--group_size 8 \
--umb_pool sum \
--num_point 1024
================================================
FILE: classification/tool/train_cls_scanobjectnn.py
================================================
"""
Author: Haoxi Ran
Date: 05/10/2022
"""
from functools import partial
import argparse
import numpy as np
import os
import torch
import datetime
import logging
from pathlib import Path
from dataset.ScanObjectNNDataLoader import ScanObjectNNDataLoader
from modules.ptaug_utils import transform_point_cloud, scale_point_cloud, get_aug_args
from modules.pointnet2_utils import sample
from util.utils import get_model, get_loss, set_seed, weight_init
def parse_args():
"""PARAMETERS"""
parser = argparse.ArgumentParser('RepSurf')
# Basic
parser.add_argument('--log_dir', type=str, default=None, help='experiment root')
parser.add_argument('--data_dir', type=str, default='./data', help='data dir')
parser.add_argument('--log_root', type=str, default='./log', help='log root dir')
parser.add_argument('--model', default='repsurf.scanobjectnn.repsurf_ssg_umb',
help='model file name [default: repsurf_ssg_umb]')
parser.add_argument('--gpus', nargs='+', type=str, default=None)
parser.add_argument('--seed', type=int, default=2800, help='Training Seed')
parser.add_argument('--cuda_ops', action='store_true', default=False,
help='Whether to use cuda version operations [default: False]')
# Training
parser.add_argument('--batch_size', type=int, default=64, help='batch size in training [default: 64]')
parser.add_argument('--optimizer', type=str, default='Adam', help='optimizer for training [Adam, SGD]')
parser.add_argument('--scheduler', type=str, default='step', help='scheduler for training')
parser.add_argument('--epoch', default=500, type=int, help='number of epoch in training [default: 500]')
parser.add_argument('--learning_rate', default=0.001, type=float, help='learning rate in training [default: 0.001]')
parser.add_argument('--decay_rate', type=float, default=1e-4, help='decay rate [default: 1e-4]')
parser.add_argument('--decay_step', default=20, type=int, help='number of epoch per decay [default: 20]')
parser.add_argument('--n_workers', type=int, default=4, help='DataLoader Workers Number [default: 4]')
parser.add_argument('--init', type=str, default=None, help='initializer for model [kaiming, xavier]')
# Evaluation
parser.add_argument('--min_val', type=int, default=100, help='Min val epoch [default: 100]')
# Augmentation
parser.add_argument('--aug_scale', action='store_true', default=False,
help='Whether to augment by scaling [default: False]')
parser.add_argument('--aug_shift', action='store_true', default=False,
help='Whether to augment by shifting [default: False]')
# Modeling
parser.add_argument('--num_point', type=int, default=1024, help='Point Number [default: 1024]')
parser.add_argument('--return_dist', action='store_true', default=False,
help='Whether to use signed distance [default: False]')
parser.add_argument('--return_center', action='store_true', default=False,
help='Whether to return center in surface abstraction [default: False]')
parser.add_argument('--return_polar', action='store_true', default=False,
help='Whether to return polar coordinate in surface abstraction [default: False]')
parser.add_argument('--group_size', type=int, default=8, help='Size of umbrella group [default: 8]')
parser.add_argument('--umb_pool', type=str, default='sum', help='pooling for umbrella repsurf [sum, mean, max]')
return parser.parse_args()
def test(model, loader, num_class=15, num_point=1024, num_votes=10, total_num=1):
vote_correct = 0
sing_correct = 0
classifier = model.eval()
for j, data in enumerate(loader):
points, target = data
points, target = points.cuda(), target.cuda()
# preprocess
points = sample(num_point, points)
# vote
vote_pool = torch.zeros(target.shape[0], num_class).cuda()
for i in range(num_votes):
new_points = points.clone()
# scale
if i > 0:
new_points[:, :3] = scale_point_cloud(new_points[:, :3])
# predict
pred = classifier(new_points)
# single
if i == 0:
sing_pred = pred
# vote
vote_pool += pred
vote_pred = vote_pool / num_votes
# single pred
sing_pred_choice = sing_pred.data.max(1)[1]
sing_correct += sing_pred_choice.eq(target.long().data).cpu().sum()
# vote pred
vote_pred_choice = vote_pred.data.max(1)[1]
vote_correct += vote_pred_choice.eq(target.long().data).cpu().sum()
sing_acc = sing_correct.item() / total_num
vote_acc = vote_correct.item() / total_num
return sing_acc, vote_acc
def main(args):
def log_string(s):
logger.info(s)
print(s)
'''HYPER PARAMETER'''
os.environ["CUDA_VISIBLE_DEVICES"] = ','.join(args.gpus)
set_seed(args.seed)
'''CREATE DIR'''
experiment_dir = Path(os.path.join(args.log_root, 'PointAnalysis', 'log'))
experiment_dir.mkdir(exist_ok=True)
experiment_dir = experiment_dir.joinpath('ScanObjectNN')
experiment_dir.mkdir(exist_ok=True)
if args.log_dir is None:
timestr = str(datetime.datetime.now().strftime('%Y-%m-%d_%H-%M'))
experiment_dir = experiment_dir.joinpath(timestr)
else:
experiment_dir = experiment_dir.joinpath(args.log_dir)
experiment_dir.mkdir(exist_ok=True)
checkpoints_dir = experiment_dir.joinpath('checkpoints/')
checkpoints_dir.mkdir(exist_ok=True)
log_dir = experiment_dir.joinpath('logs/')
log_dir.mkdir(exist_ok=True)
'''LOG'''
args = parse_args()
logger = logging.getLogger("Model")
logger.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler = logging.FileHandler('%s/%s.txt' % (log_dir, args.model))
file_handler.setLevel(logging.INFO)
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
log_string('PARAMETER ...')
log_string(args)
'''DATA LOADING'''
log_string('Load dataset ...')
args.num_class = 15
args.dataset = 'ScanObjectNN'
args.normal = False
aug_args = get_aug_args(args)
DATA_PATH = os.path.join(args.data_dir, 'ScanObjectNN')
TRAIN_DATASET = ScanObjectNNDataLoader(root=DATA_PATH, split='training')
TEST_DATASET = ScanObjectNNDataLoader(root=DATA_PATH, split='test')
trainDataLoader = torch.utils.data.DataLoader(TRAIN_DATASET, batch_size=args.batch_size, shuffle=True,
num_workers=args.n_workers, drop_last=True)
testDataLoader = torch.utils.data.DataLoader(TEST_DATASET, batch_size=args.batch_size, shuffle=False,
num_workers=args.n_workers)
'''MODEL BUILDING'''
classifier = torch.nn.DataParallel(get_model(args)).cuda()
criterion = get_loss().cuda()
try:
checkpoint = torch.load(str(experiment_dir) + '/checkpoints/best_model.pth')
start_epoch = checkpoint['epoch']
classifier.load_state_dict(checkpoint['model_state_dict'])
log_string('Use pretrain model')
except:
log_string('No existing model, starting training from scratch...')
start_epoch = 0
if args.init:
init_func = partial(weight_init, init_type=args.init)
classifier = classifier.apply(init_func)
'''OPTIMIZER'''
if args.optimizer == 'Adam':
optimizer = torch.optim.Adam(
classifier.parameters(),
lr=args.learning_rate,
betas=(0.9, 0.999),
eps=1e-08,
weight_decay=args.decay_rate)
elif args.optimizer == 'SGD':
optimizer = torch.optim.SGD(
classifier.parameters(),
lr=args.learning_rate,
momentum=0.9)
'''LR SCHEDULER'''
if args.scheduler == 'step':
scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=args.decay_step, gamma=0.7)
else:
raise Exception('No Such Scheduler')
global_epoch = 0
global_step = 0
best_sing_acc = 0.0
best_vote_acc = 0.0
loader_len = len(trainDataLoader)
'''TRANING'''
logger.info('Start training...')
for epoch in range(start_epoch, args.epoch):
log_string('Epoch %d (%d/%s):' % (global_epoch + 1, epoch + 1, args.epoch))
train_loss = []
train_correct = 0
scheduler.step()
for batch_id, data in enumerate(trainDataLoader):
'''INPUT'''
points, target = data
points, target = points.cuda(), target.cuda()
'''PREPROCESS'''
points = sample(args.num_point, points)
points = transform_point_cloud(points, args, aug_args)
'''FORWARD'''
optimizer.zero_grad()
lr = optimizer.state_dict()['param_groups'][0]['lr']
classifier = classifier.train()
pred = classifier(points)
loss = criterion(pred, target.long())
pred_choice = pred.data.max(1)[1]
correct = pred_choice.eq(target.long().data).cpu().sum()
train_correct += correct
train_loss.append(loss.item())
'''BACKWARD'''
loss.backward()
optimizer.step()
global_step += 1
if batch_id % 80 == 0:
print('Epoch: [{0}][{1}/{2}] lr {lr:.6f} loss {loss:.4f}'.
format(epoch, batch_id, len(trainDataLoader), lr=lr, loss=loss.item()))
train_instance_acc = train_correct.item() / (loader_len * args.batch_size)
train_mean_loss = np.mean(train_loss)
log_string('Train Instance Accuracy: %.2f, Loss: %f' % (train_instance_acc * 100, train_mean_loss))
if epoch >= args.min_val:
with torch.no_grad():
sing_acc, vote_acc = test(classifier.eval(), testDataLoader, num_point=args.num_point,
total_num=len(TEST_DATASET))
if sing_acc >= best_sing_acc:
best_sing_acc = sing_acc
if vote_acc >= best_vote_acc:
best_vote_acc = vote_acc
best_epoch = epoch + 1
log_string('Test Single Accuracy: %.2f' % (sing_acc * 100))
log_string('Best Single Accuracy: %.2f' % (best_sing_acc * 100))
log_string('Test Vote Accuracy: %.2f' % (vote_acc * 100))
log_string('Best Vote Accuracy: %.2f' % (best_vote_acc * 100))
if vote_acc >= best_vote_acc:
logger.info('Save model...')
savepath = str(checkpoints_dir) + '/best_model.pth'
log_string('Saving at %s' % savepath)
state = {
'epoch': best_epoch,
'vote_acc': vote_acc,
'model_state_dict': classifier.state_dict(),
'optimizer_state_dict': optimizer.state_dict(),
}
torch.save(state, savepath)
global_epoch += 1
logger.info('End of training...')
if __name__ == '__main__':
args = parse_args()
main(args)
================================================
FILE: classification/util/__init__.py
================================================
================================================
FILE: classification/util/utils.py
================================================
import importlib
import argparse
import random
import numpy as np
import torch
from torch import nn
import torch.nn.functional as F
def set_seed(seed):
"""
Setting of Global Seed
"""
torch.backends.cudnn.enabled = True
torch.backends.cudnn.deterministic = True # consistent results on the cpu and gpu
torch.backends.cudnn.benchmark = True
np.random.seed(seed)
random.seed(seed)
torch.manual_seed(seed) # cpu
torch.cuda.manual_seed(seed)
torch.cuda.manual_seed_all(seed) # gpu
def weight_init(m, init_type):
if init_type == 'xavier':
init_func = torch.nn.init.xavier_normal_
elif init_type == 'kaiming':
init_func = torch.nn.init.kaiming_normal_
else:
raise Exception('No such init type')
if isinstance(m, (torch.nn.Linear, torch.nn.Conv2d, torch.nn.Conv1d)):
init_func(m.weight)
if m.bias is not None:
torch.nn.init.constant_(m.bias, 0)
elif isinstance(m, (torch.nn.BatchNorm2d, torch.nn.BatchNorm1d)):
torch.nn.init.constant_(m.weight, 1) # constant
# torch.nn.init.normal_(m.weight, 1.0, 0.02) # normal
torch.nn.init.constant_(m.bias, 0)
class ClsLoss(nn.Module):
def __init__(self):
super(ClsLoss, self).__init__()
def forward(self, pred, target):
total_loss = F.nll_loss(pred, target)
return total_loss
class SmoothClsLoss(nn.Module):
def __init__(self, smoothing_ratio=0.1):
super(SmoothClsLoss, self).__init__()
self.smoothing_ratio = smoothing_ratio
def forward(self, pred, target):
eps = self.smoothing_ratio
n_class = pred.size(1)
one_hot = torch.zeros_like(pred).scatter(1, target.view(-1, 1), 1)
one_hot = one_hot * (1 - eps) + (1 - one_hot) * eps / (n_class - 1)
# log_prb = F.log_softmax(pred, dim=1)
loss = -(one_hot * pred).sum(dim=1).mean()
return loss
def get_model(args):
module = importlib.import_module('models.%s' % args.model)
return module.Model(args)
def get_loss():
return SmoothClsLoss()
def get_test_args():
return argparse.Namespace()
================================================
FILE: segmentation/README.md
================================================
# RepSurf for Segmentation <br>
By *[Haoxi Ran\*](https://hancyran.github.io/) , Jun Liu, Chengjie Wang* ( * : corresponding contact)
### [PDF](https://openaccess.thecvf.com/content/CVPR2022/papers/Ran_Surface_Representation_for_Point_Clouds_CVPR_2022_paper.pdf) | [arXiv](http://arxiv.org/abs/2205.05740)
## Preparation
### Environment
We tested under the environment:
* python 3.7
* pytorch 1.6.0 / 1.8.0
* cuda 10.1 / 11.1
* gcc 7.2.0
* h5py
* sharedarray
* tensorboardx
For anaconda user, initialize the conda environment **repsurf-seg** by:
```
sh init.sh
```
## Experiments
### S3DIS Area-5 (Data & Logs: [Google Drive](https://drive.google.com/drive/folders/1jIZuy4RPFJ4YHAE8ScVQgwtBwNGgfKnv?usp=sharing))
* Performance using the same settings:
<table style="width:100%">
<thead>
<tr>
<th>Model</th>
<th>mIoU</th>
<th>mAcc</th>
<th>OA</th>
<th>#Params</th>
<th>Training Time</th>
<th>Code</th>
<th>Training Log</th>
<th>Test Log</th>
<th>Checkpoint</th>
</tr>
</thead>
<tbody>
<tr>
<td align="center">Point Transformer <br> (our settings)</td>
<td align="center">70.37 (official: 70.4)</td>
<td align="center">77.02 (official: 76.5)</td>
<td align="center">90.80 (official: 90.8)</td>
<td align="center">7.767M</td>
<td align="center">19.91h</td>
<td align="center"><a href="./models/pointtransformer/pointtransformer.py">pointtransformer.py</a></td>
<td align="center"><a href="https://drive.google.com/file/d/1cLQetUso-fVzlfcJODXlfV-7MXa3vl-Y/view?usp=sharing">google drive</a></td>
<td align="center"><a href="https://drive.google.com/file/d/1umrMvmwLsexKUZytcMdE12ek8xIk8E3_/view?usp=sharing">google drive</a></td>
<td align="center"><a href="https://drive.google.com/file/d/1XnbRR2Yi6MFWVl5LVtBxLOTBN9qhuxlV/view?usp=sharing">google drive <br> (30 MB)</a></td>
</tr>
<tr>
<td align="center">PointNet++ SSG (our settings)</td>
<td align="center">64.05</td>
<td align="center">71.52</td>
<td align="center">87.92</td>
<td align="center">0.968M</td>
<td align="center">9.08h</td>
<td align="center"><a href="./models/pointnet2/pointnet2_ssg.py">pointnet2_ssg.py</a></td>
<td align="center"><a href="https://drive.google.com/file/d/1xUkUB0iT-WYzzzR5yiWhZkSYPjjarKlC/view?usp=sharing">google drive</a></td>
<td align="center"><a href="https://drive.google.com/file/d/1floQ53zgTxSs_nDn_MosIUWz4Rt7eHQx/view?usp=sharing">google drive</a></td>
<td align="center"><a href="https://drive.google.com/file/d/1hdj7G8dplCouHYor16pChd7pB8M4rodu/view?usp=sharing">google drive <br> (4 MB)</a></td>
</tr>
<tr>
<td align="center">PointNet++ SSG <b>w/ Umbrella RepSurf</b> (ours)</td>
<td align="center"><b>68.86</b></td>
<td align="center"><b>76.54</b></td>
<td align="center"><b>90.22</b></td>
<td align="center"><b>0.976M</b></td>
<td align="center">9.18h</td>
<td align="center"><a href="./models/repsurf/repsurf_umb_ssg.py">repsurf_umb_ssg.py</a></td>
<td align="center"><a href="https://drive.google.com/file/d/1C1mG7XFsJAiQYHMNuA8bVitEuY4TGXKY/view?usp=sharing">google drive</a></td>
<td align="center"><a href="https://drive.google.com/file/d/1mNgmWhYcp2njwJybkGjLVModERCR9fr8/view?usp=sharing">google drive</a></td>
<td align="center"><a href="https://drive.google.com/file/d/1pmXBt4wHKpC5llmD6pMNo2NmZZKNIQaq/view?usp=sharing">google drive <br> (4 MB)</a></td>
</tr>
</tbody>
</table>
<br>
**Note**:
1. The performance (mIoU/mAcc/OA) are from the final predictions on the whole scenes of S3DIS Area-5, while the results during training is on sub-sampled scenes for fast validation.
2. The training time of all above implementations is estimated on four NVIDIA RTX 3090. The time in the logs contains both training and validating time.
3. To speed up the training process, we apply Sectorized FPS (in the first stage) for all above methods. It can save 30~40% training time and does not affect the performance.
4. To lessen the instability from grid sampling during inference, we apply median filtering to all the above implementations. Besides, it can slightly improve the results (~0.4 mIoU).
* To (firstly install gdown by **pip install gdown** and) download dataset:
```
cd ./data/S3DIS
gdown https://drive.google.com/u/1/uc?id=1UDM-bjrtqoIR9FWoIRyqLUJGyKEs22fP
tar zxf s3dis.tar.gz && rm s3dis.tar.gz && cd -
```
* To train one model (**Umbrella RepSurf, Point Transformer, PointNet2**) for S3DIS Area-5:
```
sh scripts/s3dis/train_[MODEL].sh # MODEL: repsurf_umb, pointnet2, pointtransformer
```
* To test one model (**Our Umbrella RepSurf, Point Transformer, PointNet2**) for S3DIS Area-5 on whole scenes:
```
sh scripts/s3dis/test_[MODEL].sh # MODEL: repsurf_umb, pointnet2, pointtransformer
```
## Acknowledgment
We thank the [Point Transformer Implementation](https://github.com/POSTECH-CVLab/point-transformer) for the library pointops.
## License
RepSurf is under the Apache-2.0 license. Please contact the primary author **Haoxi Ran (ranhaoxi@gmail.com)** for
commercial use.
================================================
FILE: segmentation/dataset/S3DISDataLoader.py
================================================
"""
Author: Haoxi Ran
Date: 06/30/2022
"""
import os
import numpy as np
import SharedArray as SA
from torch.utils.data import Dataset
from util.data_util import sa_create, data_prepare
NUM_CLASS = 13
class S3DIS(Dataset):
def __init__(self, args, split, coord_transform=None, rgb_transform=None,
rgb_mean=None, rgb_std=None, shuffle_index=False):
super().__init__()
self.args, self.split, self.coord_transform, self.rgb_transform, self.rgb_mean, self.rgb_std, self.shuffle_index = \
args, split, coord_transform, rgb_transform, rgb_mean, rgb_std, shuffle_index
self.stop_aug = False
data_list = sorted(os.listdir(args.data_dir))
data_list = [item[:-4] for item in data_list if 'Area_' in item]
if split == 'train':
self.data_list = [item for item in data_list if not 'Area_{}'.format(args.test_area) in item]
else:
self.data_list = [item for item in data_list if 'Area_{}'.format(args.test_area) in item]
self.data_idx = np.arange(len(self.data_list))
for item in self.data_list:
if not os.path.exists("/dev/shm/s3dis_{}".format(item)):
data_path = os.path.join(args.data_dir, item + '.npy')
data = np.load(data_path).astype(np.float32) # xyzrgbl, N*7
sa_create("shm://s3dis_{}".format(item), data)
def __getitem__(self, idx):
data_idx = self.data_idx[idx % len(self.data_idx)]
data = SA.attach("shm://s3dis_{}".format(self.data_list[data_idx])).copy()
coord, feat, label = data[:, 0:3], data[:, 3:6], data[:, 6]
coord, feat, label = \
data_prepare(coord, feat, label, self.args, self.split, self.coord_transform, self.rgb_transform,
self.rgb_mean, self.rgb_std, self.shuffle_index, self.stop_aug)
return coord, feat, label
def __len__(self):
return len(self.data_idx) * self.args.loop
@staticmethod
def print_weight(data_root, data_list):
print('Computing label weight...')
num_point_list = []
label_freq = np.zeros(NUM_CLASS)
label_total = np.zeros(NUM_CLASS)
# load data
for idx, item in enumerate(data_list):
data_path = os.path.join(data_root, item + '.npy')
data = np.load(data_path)
labels = data[:, 6]
freq = np.histogram(labels, range(NUM_CLASS + 1))[0]
label_freq += freq
label_total += (freq > 0).astype(np.float) * labels.size
num_point_list.append(labels.size)
# label weight
label_freq = label_freq / label_total
label_weight = np.median(label_freq) / label_freq
print(label_weight)
@staticmethod
def print_mean_std(data_root, data_list):
print('Computing color mean & std...')
point_list = []
for idx, item in enumerate(data_list):
data_path = os.path.join(data_root, item + '.npy')
data = np.load(data_path)
point_list.append(data[:, 3:6])
points = np.vstack(point_list) / 255.
mean = np.mean(points, 0)
std = np.std(points, 0)
print(f'mean: {mean}, std:{std}')
================================================
FILE: segmentation/dataset/__init__.py
================================================
================================================
FILE: segmentation/init.sh
================================================
#!/bin/sh
mkdir -p log/PointAnalysis/log/S3DIS
mkdir -p log/PointAnalysis/log/ScanNet
mkdir -p data/S3DIS
mkdir -p data/ScanNet
conda create -n repsurf-seg python=3.7 -y
conda activate repsurf-seg
#conda install pytorch=1.6.0 torchvision=0.7.0 cudatoolkit=10.1 -c pytorch -c conda-forge -y
pip install torch==1.8.0+cu111 torchvision==0.9.0+cu111 torchaudio==0.8.0 -f https://download.pytorch.org/whl/torch_stable.html
conda install -c anaconda h5py pyyaml -y
conda install -c conda-forge sharedarray tensorboardx -y
cd modules/pointops
python3 setup.py install
cd -
================================================
FILE: segmentation/models/__init__.py
================================================
================================================
FILE: segmentation/models/pointnet2/__init__.py
================================================
================================================
FILE: segmentation/models/pointnet2/pointnet2_ssg.py
================================================
"""
Author: Haoxi Ran
Date: 06/30/2022
"""
import torch
import torch.nn as nn
from modules.pointnet2_utils import PointNetSetAbstraction, PointNetFeaturePropagation
class Model(nn.Module):
def __init__(self, args):
super(Model, self).__init__()
self.sa1 = PointNetSetAbstraction(4, 32, 6 + 3, [32, 32, 64], num_sector=4)
self.sa2 = PointNetSetAbstraction(4, 32, 64 + 3, [64, 64, 128])
self.sa3 = PointNetSetAbstraction(4, 32, 128 + 3, [128, 128, 256])
self.sa4 = PointNetSetAbstraction(4, 32, 256 + 3, [256, 256, 512])
self.fp4 = PointNetFeaturePropagation(768, [256, 256])
self.fp3 = PointNetFeaturePropagation(384, [256, 256])
self.fp2 = PointNetFeaturePropagation(320, [256, 128])
self.fp1 = PointNetFeaturePropagation(128, [128, 128, 128])
self.classifier = nn.Sequential(
nn.Linear(128, 128),
nn.BatchNorm1d(128),
nn.ReLU(True),
nn.Dropout(0.5),
nn.Linear(128, args.num_class),
)
def forward(self, pos_feat_off0):
pos_feat_off0[1] = torch.cat([pos_feat_off0[0], pos_feat_off0[1]], 1)
pos_feat_off1 = self.sa1(pos_feat_off0)
pos_feat_off2 = self.sa2(pos_feat_off1)
pos_feat_off3 = self.sa3(pos_feat_off2)
pos_feat_off4 = self.sa4(pos_feat_off3)
pos_feat_off3[1] = self.fp4(pos_feat_off3, pos_feat_off4)
pos_feat_off2[1] = self.fp3(pos_feat_off2, pos_feat_off3)
pos_feat_off1[1] = self.fp2(pos_feat_off1, pos_feat_off2)
pos_feat_off0[1] = self.fp1([pos_feat_off0[0], None, pos_feat_off0[2]], pos_feat_off1)
feature = self.classifier(pos_feat_off0[1])
return feature
================================================
FILE: segmentation/models/pointtransformer/__init__.py
================================================
================================================
FILE: segmentation/models/pointtransformer/pointtransformer.py
================================================
import torch
import torch.nn as nn
from modules.pointtransformer_utils import PointTransformerBlock, TransitionDown, TransitionUp
class Model(nn.Module):
def __init__(self, args):
super().__init__()
block = PointTransformerBlock
num_block = [2, 3, 4, 6, 3]
self.in_c = args.in_channel
self.in_planes, planes = self.in_c, [32, 64, 128, 256, 512]
fpn_planes, fpnhead_planes, share_planes = 128, 64, 8
stride, nsample = [1, 4, 4, 4, 4], [16, 16, 16, 16, 16]
self.enc1 = self._make_enc(block, planes[0], num_block[0], share_planes, stride=stride[0],
nsample=nsample[0]) # N/1
self.enc2 = self._make_enc(block, planes[1], num_block[1], share_planes, stride=stride[1],
nsample=nsample[1], num_sector=4) # N/4
self.enc3 = self._make_enc(block, planes[2], num_block[2], share_planes, stride=stride[2],
nsample=nsample[2]) # N/16
self.enc4 = self._make_enc(block, planes[3], num_block[3], share_planes, stride=stride[3],
nsample=nsample[3]) # N/64
self.enc5 = self._make_enc(block, planes[4], num_block[4], share_planes, stride=stride[4],
nsample=nsample[4]) # N/256
self.dec5 = self._make_dec(block, planes[4], 2, share_planes, nsample=nsample[4], is_head=True) # transform p5
self.dec4 = self._make_dec(block, planes[3], 2, share_planes, nsample=nsample[3]) # fusion p5 and p4
self.dec3 = self._make_dec(block, planes[2], 2, share_planes, nsample=nsample[2]) # fusion p4 and p3
self.dec2 = self._make_dec(block, planes[1], 2, share_planes, nsample=nsample[1]) # fusion p3 and p2
self.dec1 = self._make_dec(block, planes[0], 2, share_planes, nsample=nsample[0]) # fusion p2 and p1
self.cls = nn.Sequential(nn.Linear(planes[0], planes[0]), nn.BatchNorm1d(planes[0]), nn.ReLU(inplace=True),
nn.Linear(planes[0], args.num_class))
def _make_enc(self, block, planes, blocks, share_planes=8, stride=1, nsample=16, num_sector=1):
layers = [TransitionDown(self.in_planes, planes * block.expansion, stride, nsample, num_sector)]
self.in_planes = planes * block.expansion
for _ in range(1, blocks):
layers.append(block(self.in_planes, self.in_planes, share_planes, nsample=nsample))
return nn.Sequential(*layers)
def _make_dec(self, block, planes, blocks, share_planes=8, nsample=16, is_head=False):
layers = [TransitionUp(self.in_planes, None if is_head else planes * block.expansion)]
self.in_planes = planes * block.expansion
for _ in range(1, blocks):
layers.append(block(self.in_planes, self.in_planes, share_planes, nsample=nsample))
return nn.Sequential(*layers)
def forward(self, pxo, *args):
p0, x0, o0 = pxo # (n, 3), (n, c), (b)
x0 = p0 if self.in_c == 3 else torch.cat((p0, x0), 1)
p1, x1, o1 = self.enc1([p0, x0, o0])
p2, x2, o2 = self.enc2([p1, x1, o1])
p3, x3, o3 = self.enc3([p2, x2, o2])
p4, x4, o4 = self.enc4([p3, x3, o3])
p5, x5, o5 = self.enc5([p4, x4, o4])
x5 = self.dec5[1:]([p5, self.dec5[0]([p5, x5, o5]), o5])[1]
x4 = self.dec4[1:]([p4, self.dec4[0]([p4, x4, o4], [p5, x5, o5]), o4])[1]
x3 = self.dec3[1:]([p3, self.dec3[0]([p3, x3, o3], [p4, x4, o4]), o3])[1]
x2 = self.dec2[1:]([p2, self.dec2[0]([p2, x2, o2], [p3, x3, o3]), o2])[1]
x1 = self.dec1[1:]([p1, self.dec1[0]([p1, x1, o1], [p2, x2, o2]), o1])[1]
x = self.cls(x1)
return x
================================================
FILE: segmentation/models/repsurf/__init__.py
================================================
================================================
FILE: segmentation/models/repsurf/repsurf_umb_ssg.py
================================================
"""
Author: Haoxi Ran
Date: 06/30/2022
"""
import torch
import torch.nn as nn
from modules.repsurface_utils import UmbrellaSurfaceConstructor, SurfaceAbstractionCD, SurfaceFeaturePropagationCD
class Model(nn.Module):
def __init__(self, args):
super(Model, self).__init__()
center_channel = 6 if args.return_polar else 3
repsurf_in_channel = 10
repsurf_out_channel = 10
self.sa1 = SurfaceAbstractionCD(4, 32, args.in_channel + repsurf_out_channel, center_channel, [32, 32, 64],
True, args.return_polar, num_sector=4)
self.sa2 = SurfaceAbstractionCD(4, 32, 64 + repsurf_out_channel, center_channel, [64, 64, 128],
True, args.return_polar)
self.sa3 = SurfaceAbstractionCD(4, 32, 128 + repsurf_out_channel, center_channel, [128, 128, 256],
True, args.return_polar)
self.sa4 = SurfaceAbstractionCD(4, 32, 256 + repsurf_out_channel, center_channel, [256, 256, 512],
True, args.return_polar)
self.fp4 = SurfaceFeaturePropagationCD(512, 256, [256, 256])
self.fp3 = SurfaceFeaturePropagationCD(256, 128, [256, 256])
self.fp2 = SurfaceFeaturePropagationCD(256, 64, [256, 128])
self.fp1 = SurfaceFeaturePropagationCD(128, None, [128, 128, 128])
self.classifier = nn.Sequential(
nn.Linear(128, 128),
nn.BatchNorm1d(128),
nn.ReLU(True),
nn.Dropout(0.5),
nn.Linear(128, args.num_class),
)
self.surface_constructor = UmbrellaSurfaceConstructor(args.group_size + 1, repsurf_in_channel, repsurf_out_channel)
def forward(self, pos_feat_off0):
pos_nor_feat_off0 = [
pos_feat_off0[0],
self.surface_constructor(pos_feat_off0[0], pos_feat_off0[2]),
torch.cat([pos_feat_off0[0], pos_feat_off0[1]], 1),
pos_feat_off0[2]
]
pos_nor_feat_off1 = self.sa1(pos_nor_feat_off0)
pos_nor_feat_off2 = self.sa2(pos_nor_feat_off1)
pos_nor_feat_off3 = self.sa3(pos_nor_feat_off2)
pos_nor_feat_off4 = self.sa4(pos_nor_feat_off3)
del pos_nor_feat_off0[1], pos_nor_feat_off1[1], pos_nor_feat_off2[1], pos_nor_feat_off3[1], pos_nor_feat_off4[1]
pos_nor_feat_off3[1] = self.fp4(pos_nor_feat_off3, pos_nor_feat_off4)
pos_nor_feat_off2[1] = self.fp3(pos_nor_feat_off2, pos_nor_feat_off3)
pos_nor_feat_off1[1] = self.fp2(pos_nor_feat_off1, pos_nor_feat_off2)
pos_nor_feat_off0[1] = self.fp1([pos_nor_feat_off0[0], None, pos_nor_feat_off0[2]], pos_nor_feat_off1)
feature = self.classifier(pos_nor_feat_off0[1])
return feature
================================================
FILE: segmentation/modules/__init__.py
================================================
================================================
FILE: segmentation/modules/aug_utils.py
================================================
"""
Author: Haoxi Ran
Date: 06/30/2022
"""
import numpy as np
def transform_point_cloud_coord(args):
transform_list = []
aug_args = args.aug_args
if args.aug_scale:
transform_list.append(
RandomScale(aug_args['scale_factor'], aug_args['scale_ani'], aug_args['scale_prob']))
if args.aug_rotate:
if args.aug_rotate == 'pert':
transform_list.append(
RandomRotatePerturb(aug_args['pert_factor'], 3 * aug_args['pert_factor'], aug_args['pert_prob']))
elif args.aug_rotate == 'pert_z':
transform_list.append(
RandomRotatePerturbAligned(aug_args['pert_factor'], 3 * aug_args['pert_factor'], aug_args['pert_prob']))
elif args.aug_rotate == 'rot':
transform_list.append(
RandomRotate(prob=aug_args['rot_prob']))
elif args.aug_rotate == 'rot_z':
transform_list.append(
RandomRotateAligned(prob=aug_args['rot_prob']))
if args.aug_jitter:
transform_list.append(
RandomJitter(aug_args['jitter_factor'], 5 * aug_args['jitter_factor'], aug_args['jitter_prob'], args.lidar))
if args.aug_flip:
transform_list.append(RandomFlip())
if args.aug_shift:
transform_list.append(RandomShift(aug_args['shifts'], aug_args['shift_prob']))
return Compose(transform_list) if len(transform_list) > 0 else None
def transform_point_cloud_rgb(args):
transform_list = []
aug_args = args.aug_args
if args.color_contrast:
transform_list.append(ChromaticAutoContrast())
if args.color_shift:
transform_list.append(ChromaticTranslation())
if args.color_jitter:
transform_list.append(ChromaticJitter())
if args.hs_shift:
transform_list.append(HueSaturationTranslation())
if args.color_drop:
transform_list.append(RandomDropColor())
return Compose(transform_list) if len(transform_list) > 0 else None
class Compose(object):
def __init__(self, transforms):
self.transforms = transforms
def __call__(self, coord, feat, label, mask=None):
for t in self.transforms:
coord, feat, label = t(coord, feat, label, mask)
return coord, feat, label
class RandomRotate(object):
def __init__(self, rot=(np.pi/24, np.pi/24, np.pi/4), prob=1.):
self.rot = rot
self.prob = prob
def __call__(self, coord, feat, label, mask=None):
if np.random.rand() < self.prob:
angle_x = np.random.uniform(-self.rot[0], self.rot[0])
angle_y = np.random.uniform(-self.rot[1], self.rot[1])
angle_z = np.random.uniform(-self.rot[2], self.rot[2])
cos_x, sin_x = np.cos(angle_x), np.sin(angle_x)
cos_y, sin_y = np.cos(angle_y), np.sin(angle_y)
cos_z, sin_z = np.cos(angle_z), np.sin(angle_z)
R_x = np.array([[1, 0, 0], [0, cos_x, -sin_x], [0, sin_x, cos_x]])
R_y = np.array([[cos_y, 0, sin_y], [0, 1, 0], [-sin_y, 0, cos_y]])
R_z = np.array([[cos_z, -sin_z, 0], [sin_z, cos_z, 0], [0, 0, 1]])
R = np.dot(R_z, np.dot(R_y, R_x))
coord = np.dot(coord, np.transpose(R))
return coord, feat, label
class RandomRotateAligned(object):
def __init__(self, rot=np.pi, prob=1.):
self.rot = rot
self.prob = prob
def __call__(self, coord, feat, label, mask=None):
if np.random.rand() < self.prob:
angle_z = np.random.uniform(-self.rot, self.rot)
cos_z, sin_z = np.cos(angle_z), np.sin(angle_z)
R = np.array([[cos_z, -sin_z, 0], [sin_z, cos_z, 0], [0, 0, 1]])
coord = np.dot(coord, R)
return coord, feat, label
class RandomRotatePerturb(object):
def __init__(self, sigma=0.03, clip=0.09, prob=1.):
self.sigma = sigma
self.clip = clip
self.prob = prob
def __call__(self, coord, feat, label, mask=None):
if np.random.rand() < self.prob:
angle_x = np.clip(np.random.normal() * self.sigma, -self.clip, self.clip)
angle_y = np.clip(np.random.normal() * self.sigma, -self.clip, self.clip)
angle_z = np.clip(np.random.normal() * self.sigma, -self.clip, self.clip)
cos_x, sin_x = np.cos(angle_x), np.sin(angle_x)
cos_y, sin_y = np.cos(angle_y), np.sin(angle_y)
cos_z, sin_z = np.cos(angle_z), np.sin(angle_z)
R_x = np.array([[1, 0, 0], [0, cos_x, -sin_x], [0, sin_x, cos_x]])
R_y = np.array([[cos_y, 0, sin_y], [0, 1, 0], [-sin_y, 0, cos_y]])
R_z = np.array([[cos_z, -sin_z, 0], [sin_z, cos_z, 0], [0, 0, 1]])
R = np.dot(R_z, np.dot(R_y, R_x))
coord = np.dot(coord, np.transpose(R))
return coord, feat, label
class RandomRotatePerturbAligned(object):
def __init__(self, sigma=0.03, clip=0.09, prob=1.):
self.sigma = sigma
self.clip = clip
self.prob = prob
def __call__(self, coord, feat, label, mask=None):
if np.random.rand() < self.prob:
angle_z = np.clip(np.random.normal() * self.sigma, -self.clip, self.clip)
cos_z, sin_z = np.cos(angle_z), np.sin(angle_z)
R = np.array([[cos_z, -sin_z, 0], [sin_z, cos_z, 0], [0, 0, 1]])
coord = np.dot(coord, R)
return coord, feat, label
class RandomScale(object):
def __init__(self, scale=0.1, anisotropic=False, prob=1.):
self.scale = scale
self.anisotropic = anisotropic
self.prob = prob
def __call__(self, coord, feat, label, mask=None):
if np.random.rand() < self.prob:
scale = np.random.uniform(1 - self.scale, 1 + self.scale, 3 if self.anisotropic else 1)
coord *= scale
return coord, feat, label
class RandomShift(object):
def __init__(self, shift=(0.2, 0.2, 0), p=0.95):
self.shift = shift
self.p = p
def __call__(self, coord, feat, label, mask=None):
if np.random.rand() < self.p:
shift_x = np.random.uniform(-self.shift[0], self.shift[0])
shift_y = np.random.uniform(-self.shift[1], self.shift[1])
shift_z = np.random.uniform(-self.shift[2], self.shift[2])
coord += [shift_x, shift_y, shift_z]
return coord, feat, label
class RandomFlip(object):
def __init__(self, p=1.):
self.p = p
def __call__(self, coord, feat, label, mask=None):
if np.random.rand() < self.p:
if np.random.rand() < 0.5:
coord[:, 0] = -coord[:, 0]
if np.random.rand() < 0.5:
coord[:, 1] = -coord[:, 1]
return coord, feat, label
class RandomJitter(object):
def __init__(self, sigma=0.01, clip=0.05, p=1., is_lidar=False):
self.sigma = sigma
self.clip = clip
self.p = p
self.is_lidar = is_lidar
def __call__(self, coord, feat, label, mask=None):
if np.random.rand() < self.p:
assert (self.clip > 0)
jitter = np.clip(self.sigma * np.random.randn(coord.shape[0], 3), -1 * self.clip, self.clip)
if self.is_lidar:
jitter[:, 2] *= 0.1 # re-scale z-axis jitter
coord += jitter
return coord, feat, label
class ChromaticAutoContrast(object):
def __init__(self, p=0.2, blend_factor=None):
self.p = p
self.blend_factor = blend_factor
def __call__(self, coord, feat, label, mask=None):
if np.random.rand() < self.p:
tmp_feat = feat[mask] if mask is not None else feat
lo = np.min(tmp_feat, 0, keepdims=True)
hi = np.max(tmp_feat, 0, keepdims=True)
scale = 255 / (hi - lo)
contrast_feat = (tmp_feat[:, :3] - lo) * scale
blend_factor = np.random.rand() if self.blend_factor is None else self.blend_factor
tmp_feat[:, :3] = (1 - blend_factor) * tmp_feat[:, :3] + blend_factor * contrast_feat
if mask is not None:
feat[mask] = tmp_feat
else:
feat = tmp_feat
return coord, feat, label
class ChromaticTranslation(object):
def __init__(self, p=0.95, ratio=0.05):
self.p = p
self.ratio = ratio
def __call__(self, coord, feat, label, mask=None):
if np.random.rand() < self.p:
tr = (np.random.rand(1, feat.shape[1]) - 0.5) * 255 * 2 * self.ratio
feat[:, :3] = np.clip(tr + feat[:, :3], 0, 255)
if mask is not None:
feat[:, :3][~mask] = 0.
return coord, feat, label
class ChromaticJitter(object):
def __init__(self, p=0.95, std=0.005):
self.p = p
self.std = std
def __call__(self, coord, feat, label, mask=None):
if np.random.rand() < self.p:
noise = np.random.randn(*feat.shape)
noise *= self.std * 255
feat[:, :3] = np.clip(noise + feat[:, :3], 0, 255)
if mask is not None:
feat[:, :3][~mask] = 0.
return coord, feat, label
class HueSaturationTranslation(object):
@staticmethod
def rgb_to_hsv(rgb):
# Translated from source of colorsys.rgb_to_hsv
# r,g,b should be a numpy arrays with values between 0 and 255
# rgb_to_hsv returns an array of floats between 0.0 and 1.0.
rgb = rgb.astype('float')
hsv = np.zeros_like(rgb)
# in case an RGBA array was passed, just copy the A channel
hsv[..., 3:] = rgb[..., 3:]
r, g, b = rgb[..., 0], rgb[..., 1], rgb[..., 2]
maxc = np.max(rgb[..., :3], axis=-1)
minc = np.min(rgb[..., :3], axis=-1)
hsv[..., 2] = maxc
mask = maxc != minc
hsv[mask, 1] = (maxc - minc)[mask] / maxc[mask]
rc = np.zeros_like(r)
gc = np.zeros_like(g)
bc = np.zeros_like(b)
rc[mask] = (maxc - r)[mask] / (maxc - minc)[mask]
gc[mask] = (maxc - g)[mask] / (maxc - minc)[mask]
bc[mask] = (maxc - b)[mask] / (maxc - minc)[mask]
hsv[..., 0] = np.select([r == maxc, g == maxc], [bc - gc, 2.0 + rc - bc], default=4.0 + gc - rc)
hsv[..., 0] = (hsv[..., 0] / 6.0) % 1.0
return hsv
@staticmethod
def hsv_to_rgb(hsv):
# Translated from source of colorsys.hsv_to_rgb
# h,s should be a numpy arrays with values between 0.0 and 1.0
# v should be a numpy array with values between 0.0 and 255.0
# hsv_to_rgb returns an array of uints between 0 and 255.
rgb = np.empty_like(hsv)
rgb[..., 3:] = hsv[..., 3:]
h, s, v = hsv[..., 0], hsv[..., 1], hsv[..., 2]
i = (h * 6.0).astype('uint8')
f = (h * 6.0) - i
p = v * (1.0 - s)
q = v * (1.0 - s * f)
t = v * (1.0 - s * (1.0 - f))
i = i % 6
conditions = [s == 0.0, i == 1, i == 2, i == 3, i == 4, i == 5]
rgb[..., 0] = np.select(conditions, [v, q, p, p, t, v], default=v)
rgb[..., 1] = np.select(conditions, [v, v, v, q, p, p], default=t)
rgb[..., 2] = np.select(conditions, [v, p, t, v, v, q], default=p)
return rgb.astype('uint8')
def __init__(self, hue_max=0.5, saturation_max=0.2, p=1.):
self.hue_max = hue_max
self.saturation_max = saturation_max
self.p = p
def __call__(self, coord, feat, label, mask=None):
if np.random.rand() < self.p:
# Assume feat[:, :3] is rgb
tmp_feat = feat[mask] if mask is not None else feat
hsv = HueSaturationTranslation.rgb_to_hsv(tmp_feat[:, :3])
hue_val = (np.random.rand() - 0.5) * 2 * self.hue_max
sat_ratio = 1 + (np.random.rand() - 0.5) * 2 * self.saturation_max
hsv[..., 0] = np.remainder(hue_val + hsv[..., 0] + 1, 1)
hsv[..., 1] = np.clip(sat_ratio * hsv[..., 1], 0, 1)
tmp_feat[:, :3] = np.clip(HueSaturationTranslation.hsv_to_rgb(hsv), 0, 255)
if mask is not None:
feat[mask] = tmp_feat
else:
feat = tmp_feat
return coord, feat, label
class RandomDropColor(object):
def __init__(self, p=0.2):
self.p = p
def __call__(self, coord, feat, label, mask=None):
if np.random.rand() < self.p:
feat[:, :3] = 0
return coord, feat, label
================================================
FILE: segmentation/modules/pointnet2_utils.py
================================================
"""
Author: Haoxi Ran
Date: 06/30/2022
"""
import torch
import torch.nn as nn
import torch.nn.functional as F
from modules.pointops.functions import pointops
def sample_and_group(stride, nsample, xyz, points, offset, return_idx=False, num_sector=1):
# sample
if stride > 1:
new_offset, sample_idx = [offset[0].item() // stride], offset[0].item() // stride
for i in range(1, offset.shape[0]):
sample_idx += (offset[i].item() - offset[i - 1].item()) // stride
new_offset.append(sample_idx)
new_offset = torch.cuda.IntTensor(new_offset)
if num_sector > 1:
fps_idx = pointops.sectorized_fps(xyz, offset, new_offset, num_sector) # [M]
else:
fps_idx = pointops.furthestsampling(xyz, offset, new_offset) # [M]
new_xyz = xyz[fps_idx.long(), :] # [M, 3]
else:
new_xyz = xyz
new_offset = offset
# group
N, M = xyz.shape[0], new_xyz.shape[0]
group_idx, _ = pointops.knnquery(nsample, xyz, new_xyz, offset, new_offset) # [M, nsample]
group_xyz = xyz[group_idx.view(-1).long(), :].view(M, nsample, 3) # [M, nsample, 3]
group_xyz_norm = group_xyz - new_xyz.unsqueeze(1)
if points is not None and not return_idx:
C = points.shape[1]
group_points = points[group_idx.view(-1).long(), :].view(M, nsample, C)
new_points = torch.cat([group_xyz_norm, group_points], dim=-1) # [M, nsample, 3/6+C]
else:
new_points = group_xyz_norm
if return_idx:
return new_xyz, new_points, new_offset, group_idx
else:
return new_xyz, new_points, new_offset
class PointNetSetAbstraction(nn.Module):
"""
PointNet2 SA Module
"""
def __init__(self, stride, nsample, in_channel, mlp, num_sector=1):
super(PointNetSetAbstraction, self).__init__()
self.stride = stride
self.nsample = nsample
self.num_sector = num_sector
self.mlp_convs = nn.ModuleList()
self.mlp_bns = nn.ModuleList()
last_channel = in_channel
for out_channel in mlp:
self.mlp_convs.append(nn.Conv1d(last_channel, out_channel, 1))
self.mlp_bns.append(nn.BatchNorm1d(out_channel))
last_channel = out_channel
def forward(self, pos_feat_off):
xyz, points, offset = pos_feat_off # [N, 3], [N, C], [B]
new_xyz, new_points, new_offset = sample_and_group(self.stride, self.nsample, xyz, points, offset,
num_sector=self.num_sector)
# new_xyz: sampled points position data, [M, 3]
# new_points: sampled points data, [M, nsample, 3+C]
new_points = new_points.transpose(1, 2).contiguous() # [M, 3+C, nsample]
for i, conv in enumerate(self.mlp_convs):
bn = self.mlp_bns[i]
new_points = F.relu(bn(conv(new_points)))
new_points = torch.max(new_points, 2)[0]
return [new_xyz, new_points, new_offset]
class PointNetFeaturePropagation(nn.Module):
"""
PointNet2 FP Module
"""
def __init__(self, in_channel, mlp):
super(PointNetFeaturePropagation, self).__init__()
self.mlp_convs = nn.ModuleList()
self.mlp_bns = nn.ModuleList()
last_channel = in_channel
for out_channel in mlp:
self.mlp_convs.append(nn.Linear(last_channel, out_channel))
self.mlp_bns.append(nn.BatchNorm1d(out_channel))
last_channel = out_channel
def forward(self, pos_feat_off1, pos_feat_off2):
xyz1, points1, offset1 = pos_feat_off1 # [N, 3], [N, C], [B]
xyz2, points2, offset2 = pos_feat_off2 # [M, 3], [M, C], [B]
idx, dist = pointops.knnquery(3, xyz2, xyz1, offset2, offset1) # [M, 3], [M, 3]
dist_recip = 1.0 / (dist + 1e-8) # [M, 3]
norm = torch.sum(dist_recip, dim=1, keepdim=True)
weight = dist_recip / norm # [M, 3]
interpolated_points = torch.cuda.FloatTensor(xyz1.shape[0], points2.shape[1]).zero_()
for i in range(3):
interpolated_points += points2[idx[:, i].long(), :] * weight[:, i].unsqueeze(-1)
# skip connection
if points1 is not None:
new_points = torch.cat([points1, interpolated_points], dim=1) # [M, C1+C2]
else:
new_points = interpolated_points
# mlp
for i, conv in enumerate(self.mlp_convs):
bn = self.mlp_bns[i]
new_points = F.relu(bn(conv(new_points)))
return new_points
================================================
FILE: segmentation/modules/pointops/__init__.py
================================================
================================================
FILE: segmentation/modules/pointops/functions/__init__.py
================================================
================================================
FILE: segmentation/modules/pointops/functions/pointops.py
================================================
from typing import Tuple
import torch
from torch.autograd import Function
import torch.nn as nn
try:
import pointops_cuda
except ImportError:
import warnings
import os
from torch.utils.cpp_extension import load
warnings.warn("Unable to load pointops_cuda cpp extension.")
pointops_cuda_src = os.path.join(os.path.dirname(__file__), "../src")
pointops_cuda = load('pointops_cuda', [
pointops_cuda_src + '/pointops_api.cpp',
pointops_cuda_src + '/knnquery/knnquery_cuda.cpp',
pointops_cuda_src + '/knnquery/knnquery_cuda_kernel.cu',
pointops_cuda_src + '/interpolation/interpolation_cuda.cpp',
pointops_cuda_src + '/interpolation/interpolation_cuda_kernel.cu',
pointops_cuda_src + '/sampling/sampling_cuda.cpp',
pointops_cuda_src + '/sampling/sampling_cuda_kernel.cu',
pointops_cuda_src + '/subtraction/subtraction_cuda.cpp',
pointops_cuda_src + '/subtraction/subtraction_cuda_kernel.cu',
pointops_cuda_src + '/aggregation/aggregation_cuda.cpp',
pointops_cuda_src + '/aggregation/aggregation_cuda_kernel.cu',
], build_directory=pointops_cuda_src, verbose=False)
class FurthestSampling(Function):
@staticmethod
def forward(ctx, xyz, offset, new_offset):
"""
input: xyz: (n, 3), offset: (b), new_offset: (b)
output: idx: (m)
"""
assert xyz.is_contiguous()
n, b, n_max = xyz.shape[0], offset.shape[0], offset[0]
for i in range(1, b):
n_max = max(offset[i] - offset[i - 1], n_max)
idx = torch.cuda.IntTensor(new_offset[b - 1].item()).zero_()
tmp = torch.cuda.FloatTensor(n).fill_(1e10)
pointops_cuda.furthestsampling_cuda(b, n_max, xyz, offset, new_offset, tmp, idx)
del tmp
return idx
furthestsampling = FurthestSampling.apply
class SectorizedFurthestSampling(Function):
@staticmethod
def forward(ctx, xyz, offset, new_offset, num_sectors, min_points=10000):
"""
input: xyz: (n, 3), offset: (b), new_offset: (b)
output: idx: (m)
"""
assert xyz.is_contiguous()
# cut into batches
last_offset = 0
sizes = []
new_sizes = []
indices = []
for i in range(offset.shape[0]):
size = offset[i] - last_offset
if size < min_points:
tmp_num_sectors = 1
else:
tmp_num_sectors = num_sectors
batch_xyz = xyz[last_offset:last_offset + size]
angle = torch.atan2(batch_xyz[:, 0], batch_xyz[:, 1]) # [0, 2*pi]
sector_range = torch.linspace(angle.min(), angle.max() + 1e-4, tmp_num_sectors + 1)
for s in range(tmp_num_sectors):
indices.append(
torch.where((angle >= sector_range[s]) & (angle < sector_range[s + 1]))[0] + last_offset
)
sizes.append(indices[-1].shape[0])
if i > 0:
new_size = (new_offset[i] - new_offset[i - 1]).item()
else:
new_size = new_offset[i].item()
new_sizes_this_batch = [new_size // tmp_num_sectors for i in range(tmp_num_sectors)]
new_sizes_this_batch[-1] += new_size % tmp_num_sectors
new_sizes += new_sizes_this_batch
last_offset = offset[i]
sizes = torch.tensor(sizes, dtype=torch.long).to(offset)
sector_offset = sizes.cumsum(dim=0)
new_sizes = torch.tensor(new_sizes, dtype=torch.long).to(offset)
new_sector_offset = new_sizes.cumsum(dim=0)
indices = torch.cat(indices).long().to(offset.device)
sector_xyz = xyz[indices].contiguous()
# transform to sectors
new_xyz = []
n, b, n_max = sector_xyz.shape[0], sector_offset.shape[0], sector_offset[0]
for i in range(1, b):
n_max = max(sector_offset[i] - sector_offset[i - 1], n_max)
idx = torch.cuda.IntTensor(new_sector_offset[b - 1].item()).zero_()
tmp = torch.cuda.FloatTensor(n).fill_(1e10)
pointops_cuda.furthestsampling_cuda(b, n_max, sector_xyz, sector_offset.int(), new_sector_offset.int(), tmp,
idx)
idx = indices[idx.long()]
del tmp
del sector_xyz
return idx
sectorized_fps = SectorizedFurthestSampling.apply
class KNNQuery(Function):
@staticmethod
def forward(ctx, nsample, xyz, new_xyz, offset, new_offset):
"""
input: xyz: (n, 3), new_xyz: (m, 3), offset: (b), new_offset: (b)
output: idx: (m, nsample), dist2: (m, nsample)
"""
if new_xyz is None: new_xyz = xyz
assert xyz.is_contiguous() and new_xyz.is_contiguous()
m = new_xyz.shape[0]
idx = torch.cuda.IntTensor(m, nsample).zero_()
dist2 = torch.cuda.FloatTensor(m, nsample).zero_()
pointops_cuda.knnquery_cuda(m, nsample, xyz, new_xyz, offset, new_offset, idx, dist2)
return idx, torch.sqrt(dist2)
knnquery = KNNQuery.apply
class Grouping(Function):
@staticmethod
def forward(ctx, input, idx):
"""
input: input: (n, c), idx : (m, nsample)
output: (m, nsample, c)
"""
assert input.is_contiguous() and idx.is_contiguous()
m, nsample, n, c = idx.shape[0], idx.shape[1], input.shape[0], input.shape[1]
output = torch.cuda.FloatTensor(m, nsample, c)
pointops_cuda.grouping_forward_cuda(m, nsample, c, input, idx, output)
ctx.n = n
ctx.save_for_backward(idx)
return output
@staticmethod
def backward(ctx, grad_output):
"""
input: grad_out: (m, c, nsample)
output: (n, c), None
"""
n = ctx.n
idx, = ctx.saved_tensors
m, nsample, c = grad_output.shape
grad_input = torch.cuda.FloatTensor(n, c).zero_()
pointops_cuda.grouping_backward_cuda(m, nsample, c, grad_output, idx, grad_input)
return grad_input, None
grouping = Grouping.apply
def queryandgroup(nsample, xyz, new_xyz, feat, idx, offset, new_offset, use_xyz=True):
"""
input: xyz: (n, 3), new_xyz: (m, 3), feat: (n, c), idx: (m, nsample), offset: (b), new_offset: (b)
output: new_feat: (m, c+3, nsample), grouped_idx: (m, nsample)
"""
assert xyz.is_contiguous() and new_xyz.is_contiguous() and feat.is_contiguous()
if new_xyz is None:
new_xyz = xyz
if idx is None:
idx, _ = knnquery(nsample, xyz, new_xyz, offset, new_offset) # (m, nsample)
n, m, c = xyz.shape[0], new_xyz.shape[0], feat.shape[1]
grouped_xyz = xyz[idx.view(-1).long(), :].view(m, nsample, 3) # (m, nsample, 3)
# grouped_xyz = grouping(xyz, idx) # (m, nsample, 3)
grouped_xyz -= new_xyz.unsqueeze(1) # (m, nsample, 3)
grouped_feat = feat[idx.view(-1).long(), :].view(m, nsample, c) # (m, nsample, c)
# grouped_feat = grouping(feat, idx) # (m, nsample, c)
if use_xyz:
return torch.cat((grouped_xyz, grouped_feat), -1) # (m, nsample, 3+c)
else:
return grouped_feat
class Subtraction(Function):
@staticmethod
def forward(ctx, input1, input2, idx):
"""
input: input1: (n, c), input2: (n, c), idx: (n, nsample)
output: (n, nsample, c)
"""
assert input1.is_contiguous() and input2.is_contiguous()
n, c = input1.shape;
nsample = idx.shape[-1]
output = torch.cuda.FloatTensor(n, nsample, c).zero_()
pointops_cuda.subtraction_forward_cuda(n, nsample, c, input1, input2, idx, output)
ctx.save_for_backward(idx)
return output
@staticmethod
def backward(ctx, grad_output):
"""
input: grad_out: (n, nsample, c)
output: grad_input1: (n, c), grad_input2: (n, c)
"""
idx, = ctx.saved_tensors
n, nsample, c = grad_output.shape
grad_input1 = torch.cuda.FloatTensor(n, c).zero_()
grad_input2 = torch.cuda.FloatTensor(n, c).zero_()
pointops_cuda.subtraction_backward_cuda(n, nsample, c, idx, grad_output, grad_input1, grad_input2)
return grad_input1, grad_input2, None
subtraction = Subtraction.apply
class Aggregation(Function):
@staticmethod
def forward(ctx, input, position, weight, idx):
"""
input: input: (n, c), position: (n, nsample, c), weight : (n, nsample, c'), idx: (n, nsample)
output: (n, c)
"""
assert input.is_contiguous() and position.is_contiguous() and weight.is_contiguous()
n, nsample, c = position.shape;
w_c = weight.shape[-1]
output = torch.cuda.FloatTensor(n, c).zero_()
pointops_cuda.aggregation_forward_cuda(n, nsample, c, w_c, input, position, weight, idx, output)
ctx.save_for_backward(input, position, weight, idx)
return output
@staticmethod
def backward(ctx, grad_output):
"""
input: grad_out: (n, c)
output: grad_input: (n, c), grad_position: (n, nsample, c), grad_weight : (n, nsample, c')
"""
input, position, weight, idx = ctx.saved_tensors
n, nsample, c = position.shape;
w_c = weight.shape[-1]
grad_input = torch.cuda.FloatTensor(n, c).zero_()
grad_position = torch.cuda.FloatTensor(n, nsample, c).zero_()
grad_weight = torch.cuda.FloatTensor(n, nsample, w_c).zero_()
pointops_cuda.aggregation_backward_cuda(n, nsample, c, w_c, input, position, weight, idx, grad_output,
grad_input, grad_position, grad_weight)
return grad_input, grad_position, grad_weight, None
aggregation = Aggregation.apply
def interpolation(xyz, new_xyz, feat, offset, new_offset, k=3):
"""
input: xyz: (m, 3), new_xyz: (n, 3), feat: (m, c), offset: (b), new_offset: (b)
output: (n, c)
"""
assert xyz.is_contiguous() and new_xyz.is_contiguous() and feat.is_contiguous()
idx, dist = knnquery(k, xyz, new_xyz, offset, new_offset) # (n, 3), (n, 3)
dist_recip = 1.0 / (dist + 1e-8) # (n, 3)
norm = torch.sum(dist_recip, dim=1, keepdim=True)
weight = dist_recip / norm # (n, 3)
new_feat = torch.cuda.FloatTensor(new_xyz.shape[0], feat.shape[1]).zero_()
for i in range(k):
new_feat += feat[idx[:, i].long(), :] * weight[:, i].unsqueeze(-1)
return new_feat
class Interpolation(Function):
@staticmethod
def forward(ctx, xyz, new_xyz, input, offset, new_offset, k=3):
"""
input: xyz: (m, 3), new_xyz: (n, 3), input: (m, c), offset: (b), new_offset: (b)
output: (n, c)
"""
assert xyz.is_contiguous() and new_xyz.is_contiguous() and input.is_contiguous()
idx, dist = knnquery(k, xyz, new_xyz, offset, new_offset) # (n, k), (n, k)
dist_recip = 1.0 / (dist + 1e-8) # (n, k)
norm = torch.sum(dist_recip, dim=1, keepdim=True)
weight = dist_recip / norm # (n, k)
n, c, m = new_xyz.shape[0], input.shape[1], input.shape[0]
output = torch.cuda.FloatTensor(n, c).zero_()
pointops_cuda.interpolation_forward_cuda(n, c, k, input, idx, weight, output)
ctx.m, ctx.k = m, k
ctx.save_for_backward(idx, weight)
return output
@staticmethod
def backward(ctx, grad_output):
"""
input: xyz: (m, 3), new_xyz: (n, 3), input: (m, c), offset: (b), new_offset: (b)
output: (n, c)
"""
m, k = ctx.m, ctx.k
idx, weight = ctx.saved_tensors
n, c = grad_output.shape
grad_input = torch.cuda.FloatTensor(m, c).zero_()
pointops_cuda.interpolation_backward_cuda(n, c, k, grad_output, idx, weight, grad_input)
return None, None, grad_input, None, None, None
interpolation2 = Interpolation.apply
================================================
FILE: segmentation/modules/pointops/setup.py
================================================
#python3 setup.py install
from setuptools import setup
from torch.utils.cpp_extension import BuildExtension, CUDAExtension
import os
from distutils.sysconfig import get_config_vars
(opt,) = get_config_vars('OPT')
os.environ['OPT'] = " ".join(
flag for flag in opt.split() if flag != '-Wstrict-prototypes'
)
setup(
name='pointops_cuda',
author='Hengshuang Zhao',
ext_modules=[
CUDAExtension('pointops_cuda', [
'src/pointops_api.cpp',
'src/knnquery/knnquery_cuda.cpp',
'src/knnquery/knnquery_cuda_kernel.cu',
'src/sampling/sampling_cuda.cpp',
'src/sampling/sampling_cuda_kernel.cu',
'src/grouping/grouping_cuda.cpp',
'src/grouping/grouping_cuda_kernel.cu',
'src/interpolation/interpolation_cuda.cpp',
'src/interpolation/interpolation_cuda_kernel.cu',
'src/subtraction/subtraction_cuda.cpp',
'src/subtraction/subtraction_cuda_kernel.cu',
'src/aggregation/aggregation_cuda.cpp',
'src/aggregation/aggregation_cuda_kernel.cu',
],
extra_compile_args={'cxx': ['-g'], 'nvcc': ['-O2']}
)
],
cmdclass={'build_ext': BuildExtension}
)
================================================
FILE: segmentation/modules/pointops/src/__init__.py
================================================
================================================
FILE: segmentation/modules/pointops/src/aggregation/aggregation_cuda.cpp
================================================
#include <vector>
#include <THC/THC.h>
#include <torch/serialize/tensor.h>
#include <ATen/cuda/CUDAContext.h>
#include "aggregation_cuda_kernel.h"
void aggregation_forward_cuda(int n, int nsample, int c, int w_c, at::Tensor input_tensor, at::Tensor position_tensor, at::Tensor weight_tensor, at::Tensor idx_tensor, at::Tensor output_tensor)
{
const float *input = input_tensor.data_ptr<float>();
const float *position = position_tensor.data_ptr<float>();
const float *weight = weight_tensor.data_ptr<float>();
const int *idx = idx_tensor.data_ptr<int>();
float *output = output_tensor.data_ptr<float>();
aggregation_forward_cuda_launcher(n, nsample, c, w_c, input, position, weight, idx, output);
}
void aggregation_backward_cuda(int n, int nsample, int c, int w_c, at::Tensor input_tensor, at::Tensor position_tensor, at::Tensor weight_tensor, at::Tensor idx_tensor, at::Tensor grad_output_tensor, at::Tensor grad_input_tensor, at::Tensor grad_position_tensor, at::Tensor grad_weight_tensor)
{
const float *input = input_tensor.data_ptr<float>();
const float *position = position_tensor.data_ptr<float>();
const float *weight = weight_tensor.data_ptr<float>();
const int *idx = idx_tensor.data_ptr<int>();
const float *grad_output = grad_output_tensor.data_ptr<float>();
float *grad_input = grad_input_tensor.data_ptr<float>();
float *grad_position = grad_position_tensor.data_ptr<float>();
float *grad_weight = grad_weight_tensor.data_ptr<float>();
aggregation_backward_cuda_launcher(n, nsample, c, w_c, input, position, weight, idx, grad_output, grad_input, grad_position, grad_weight);
}
================================================
FILE: segmentation/modules/pointops/src/aggregation/aggregation_cuda_kernel.cu
================================================
#include "../cuda_utils.h"
#include "aggregation_cuda_kernel.h"
__global__ void aggregation_forward_cuda_kernel(int n, int nsample, int c, int w_c, const float *input, const float *position, const float *weight, const int *idx, float *output) {
// input: input: (n, c), position: (n, nsample, c), weight: (n, nsample, w_c), idx: (n, nsample), output: (n, c)
int index = blockIdx.x * blockDim.x + threadIdx.x;
if (index >= n * c) return;
const int c_idx = index % c;
const int n_idx = index / c;
const int w_c_idx = c_idx % w_c;
for (int nsample_idx = 0; nsample_idx < nsample; nsample_idx++)
{
int idx_idx = n_idx * nsample + nsample_idx;
int input_idx = idx[idx_idx] * c + c_idx;
int position_idx = n_idx * nsample * c + nsample_idx * c + c_idx;
int weight_idx = n_idx * nsample * w_c + nsample_idx * w_c + w_c_idx;
output[index] += (input[input_idx] + position[position_idx]) * weight[weight_idx];
}
}
__global__ void aggregation_backward_cuda_kernel(int n, int nsample, int c, int w_c, const float *input, const float *position, const float *weight, const int *idx, const float *grad_output, float *grad_input, float *grad_position, float *grad_weight) {
// input: grad_output: (n, c), output: grad_input: (n, c), grad_position: (n, nsample, c), grad_weight: (n, nsample, w_c)
int index = blockIdx.x * blockDim.x + threadIdx.x;
if (index >= n * c) return;
const int c_idx = index % c;
const int n_idx = index / c;
const int w_c_idx = c_idx % w_c;
for (int nsample_idx = 0; nsample_idx < nsample; nsample_idx++)
{
int idx_idx = n_idx * nsample + nsample_idx;
int input_idx = idx[idx_idx] * c + c_idx;
int position_idx = n_idx * nsample * c + nsample_idx * c + c_idx;
int weight_idx = n_idx * nsample * w_c + nsample_idx * w_c + w_c_idx;
atomicAdd(grad_input + input_idx, grad_output[index] * weight[weight_idx]);
grad_position[position_idx] = grad_output[index] * weight[weight_idx];
atomicAdd(grad_weight + weight_idx, grad_output[index] * (input[input_idx] + position[position_idx]));
}
}
void aggregation_forward_cuda_launcher(int n, int nsample, int c, int w_c, const float *input, const float *position, const float *weight, const int *idx, float *output) {
// input: input: (n, c), position: (n, nsample, c), weight: (n, nsample, w_c), idx: (n, nsample), output: (n, c)
dim3 blocks(DIVUP(n * c, THREADS_PER_BLOCK));
dim3 threads(THREADS_PER_BLOCK);
aggregation_forward_cuda_kernel<<<blocks, threads, 0>>>(n, nsample, c, w_c, input, position, weight, idx, output);
}
void aggregation_backward_cuda_launcher(int n, int nsample, int c, int w_c, const float *input, const float *position, const float *weight, const int *idx, const float *grad_output, float *grad_input, float *grad_position, float *grad_weight) {
// input: grad_output: (n, c), output: grad_input: (n, c), grad_position: (n, nsample, c), grad_weight: (n, nsample, w_c)
dim3 blocks(DIVUP(n * c, THREADS_PER_BLOCK));
dim3 threads(THREADS_PER_BLOCK);
aggregation_backward_cuda_kernel<<<blocks, threads, 0>>>(n, nsample, c, w_c, input, position, weight, idx, grad_output, grad_input, grad_position, grad_weight);
}
================================================
FILE: segmentation/modules/pointops/src/aggregation/aggregation_cuda_kernel.h
================================================
#ifndef _AGGREGATION_CUDA_KERNEL
#define _AGGREGATION_CUDA_KERNEL
#include <vector>
#include <torch/serialize/tensor.h>
#include <ATen/cuda/CUDAContext.h>
void aggregation_forward_cuda(int n, int nsample, int c, int w_c, at::Tensor input_tensor, at::Tensor position_tensor, at::Tensor weight_tensor, at::Tensor idx_tensor, at::Tensor output_tensor);
void aggregation_backward_cuda(int n, int nsample, int c, int w_c, at::Tensor input_tensor, at::Tensor position_tensor, at::Tensor weight_tensor, at::Tensor idx_tensor, at::Tensor grad_output_tensor, at::Tensor grad_input_tensor, at::Tensor grad_position_tensor, at::Tensor grad_weight_tensor);
#ifdef __cplusplus
extern "C" {
#endif
void aggregation_forward_cuda_launcher(int n, int nsample, int c, int w_c, const float *input, const float *position, const float *weight, const int *idx, float *output);
void aggregation_backward_cuda_launcher(int n, int nsample, int c, int w_c, const float *input, const float *position, const float *weight, const int *idx, const float *grad_output, float *grad_input, float *grad_position, float *grad_weight);
#ifdef __cplusplus
}
#endif
#endif
================================================
FILE: segmentation/modules/pointops/src/cuda_utils.h
================================================
#ifndef _CUDA_UTILS_H
#define _CUDA_UTILS_H
#include <cmath>
#include <algorithm>
#define TOTAL_THREADS 1024
#define THREADS_PER_BLOCK 256
#define DIVUP(m, n) ((m) / (n) + ((m) % (n) > 0))
inline int opt_n_threads(int work_size) {
const int pow_2 = std::log(static_cast<double>(work_size)) / std::log(2.0);
return std::max(std::min(1 << pow_2, TOTAL_THREADS), 1);
}
inline dim3 opt_block_config(int x, int y) {
const int x_threads = opt_n_threads(x);
const int y_threads = std::max(std::min(opt_n_threads(y), TOTAL_THREADS / x_threads), 1);
dim3 block_config(x_threads, y_threads, 1);
return block_config;
}
#endif
================================================
FILE: segmentation/modules/pointops/src/grouping/grouping_cuda.cpp
================================================
#include <vector>
#include <THC/THC.h>
#include <torch/serialize/tensor.h>
#include <ATen/cuda/CUDAContext.h>
#include "grouping_cuda_kernel.h"
void grouping_forward_cuda(int m, int nsample, int c, at::Tensor input_tensor, at::Tensor idx_tensor, at::Tensor output_tensor)
{
const float *input = input_tensor.data_ptr<float>();
const int *idx = idx_tensor.data_ptr<int>();
float *output = output_tensor.data_ptr<float>();
grouping_forward_cuda_launcher(m, nsample, c, input, idx, output);
}
void grouping_backward_cuda(int m, int nsample, int c, at::Tensor grad_output_tensor, at::Tensor idx_tensor, at::Tensor grad_input_tensor)
{
const float *grad_output = grad_output_tensor.data_ptr<float>();
const int *idx = idx_tensor.data_ptr<int>();
float *grad_input = grad_input_tensor.data_ptr<float>();
grouping_backward_cuda_launcher(m, nsample, c, grad_output, idx, grad_input);
}
================================================
FILE: segmentation/modules/pointops/src/grouping/grouping_cuda_kernel.cu
================================================
#include "../cuda_utils.h"
#include "grouping_cuda_kernel.h"
__global__ void grouping_forward_cuda_kernel(int m, int nsample, int c, const float *__restrict__ input, const int *__restrict__ idx, float *__restrict__ output) {
// input: input: (n, c), idx: (m, nsample), output: (m, nsample, c)
int index = blockIdx.x * blockDim.x + threadIdx.x;
if (index >= m * nsample * c) return;
const int c_idx = index % c;
const int nsample_idx = (index / c) % nsample;
const int m_idx = index / nsample / c;
const int input_idx = idx[m_idx * nsample + nsample_idx] * c + c_idx;
output[index] = input[input_idx];
}
__global__ void grouping_backward_cuda_kernel(int m, int nsample, int c, const float *__restrict__ grad_output, const int *__restrict__ idx, float *__restrict__ grad_input) {
// input: grad_output: (m, nsample, c), idx: (m, nsample), output: grad_input: (n, c)
int index = blockIdx.x * blockDim.x + threadIdx.x;
if (index >= m * nsample * c) return;
const int c_idx = index % c;
const int nsample_idx = (index / c) % nsample;
const int m_idx = index / nsample / c;
const int input_idx = idx[m_idx * nsample + nsample_idx] * c + c_idx;
atomicAdd(grad_input + input_idx, grad_output[index]);
}
void grouping_forward_cuda_launcher(int m, int nsample, int c, const float *input, const int *idx, float *outpu
gitextract_ziqlnw07/
├── .gitignore
├── LICENSE.txt
├── README.md
├── classification/
│ ├── README.md
│ ├── dataset/
│ │ ├── ScanObjectNNDataLoader.py
│ │ └── __init__.py
│ ├── init.sh
│ ├── models/
│ │ ├── __init__.py
│ │ └── repsurf/
│ │ ├── __init__.py
│ │ ├── repsurf_ssg_umb.py
│ │ └── repsurf_ssg_umb_2x.py
│ ├── modules/
│ │ ├── __init__.py
│ │ ├── pointnet2_utils.py
│ │ ├── pointops/
│ │ │ ├── __init__.py
│ │ │ ├── functions/
│ │ │ │ ├── __init__.py
│ │ │ │ └── pointops.py
│ │ │ ├── setup.py
│ │ │ └── src/
│ │ │ ├── __init__.py
│ │ │ ├── ballquery/
│ │ │ │ ├── ballquery_cuda.cpp
│ │ │ │ ├── ballquery_cuda_kernel.cu
│ │ │ │ └── ballquery_cuda_kernel.h
│ │ │ ├── cuda_utils.h
│ │ │ ├── grouping/
│ │ │ │ ├── grouping_cuda.cpp
│ │ │ │ ├── grouping_cuda_kernel.cu
│ │ │ │ └── grouping_cuda_kernel.h
│ │ │ ├── grouping_int/
│ │ │ │ ├── grouping_int_cuda.cpp
│ │ │ │ ├── grouping_int_cuda_kernel.cu
│ │ │ │ └── grouping_int_cuda_kernel.h
│ │ │ ├── interpolation/
│ │ │ │ ├── interpolation_cuda.cpp
│ │ │ │ ├── interpolation_cuda_kernel.cu
│ │ │ │ └── interpolation_cuda_kernel.h
│ │ │ ├── knnquery/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── knnquery_cuda.cpp
│ │ │ │ ├── knnquery_cuda_kernel.cu
│ │ │ │ └── knnquery_cuda_kernel.h
│ │ │ ├── knnquery_heap/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── knnquery_heap_cuda.cpp
│ │ │ │ ├── knnquery_heap_cuda_kernel.cu
│ │ │ │ └── knnquery_heap_cuda_kernel.h
│ │ │ ├── pointops_api.cpp
│ │ │ └── sampling/
│ │ │ ├── sampling_cuda.cpp
│ │ │ ├── sampling_cuda_kernel.cu
│ │ │ └── sampling_cuda_kernel.h
│ │ ├── polar_utils.py
│ │ ├── ptaug_utils.py
│ │ ├── recons_utils.py
│ │ └── repsurface_utils.py
│ ├── scripts/
│ │ └── scanobjectnn/
│ │ ├── repsurf_ssg_umb.sh
│ │ └── repsurf_ssg_umb_2x.sh
│ ├── tool/
│ │ └── train_cls_scanobjectnn.py
│ └── util/
│ ├── __init__.py
│ └── utils.py
├── segmentation/
│ ├── README.md
│ ├── dataset/
│ │ ├── S3DISDataLoader.py
│ │ └── __init__.py
│ ├── init.sh
│ ├── models/
│ │ ├── __init__.py
│ │ ├── pointnet2/
│ │ │ ├── __init__.py
│ │ │ └── pointnet2_ssg.py
│ │ ├── pointtransformer/
│ │ │ ├── __init__.py
│ │ │ └── pointtransformer.py
│ │ └── repsurf/
│ │ ├── __init__.py
│ │ └── repsurf_umb_ssg.py
│ ├── modules/
│ │ ├── __init__.py
│ │ ├── aug_utils.py
│ │ ├── pointnet2_utils.py
│ │ ├── pointops/
│ │ │ ├── __init__.py
│ │ │ ├── functions/
│ │ │ │ ├── __init__.py
│ │ │ │ └── pointops.py
│ │ │ ├── setup.py
│ │ │ └── src/
│ │ │ ├── __init__.py
│ │ │ ├── aggregation/
│ │ │ │ ├── aggregation_cuda.cpp
│ │ │ │ ├── aggregation_cuda_kernel.cu
│ │ │ │ └── aggregation_cuda_kernel.h
│ │ │ ├── cuda_utils.h
│ │ │ ├── grouping/
│ │ │ │ ├── grouping_cuda.cpp
│ │ │ │ ├── grouping_cuda_kernel.cu
│ │ │ │ └── grouping_cuda_kernel.h
│ │ │ ├── interpolation/
│ │ │ │ ├── interpolation_cuda.cpp
│ │ │ │ ├── interpolation_cuda_kernel.cu
│ │ │ │ └── interpolation_cuda_kernel.h
│ │ │ ├── knnquery/
│ │ │ │ ├── knnquery_cuda.cpp
│ │ │ │ ├── knnquery_cuda_kernel.cu
│ │ │ │ └── knnquery_cuda_kernel.h
│ │ │ ├── pointops_api.cpp
│ │ │ ├── sampling/
│ │ │ │ ├── sampling_cuda.cpp
│ │ │ │ ├── sampling_cuda_kernel.cu
│ │ │ │ └── sampling_cuda_kernel.h
│ │ │ └── subtraction/
│ │ │ ├── subtraction_cuda.cpp
│ │ │ ├── subtraction_cuda_kernel.cu
│ │ │ └── subtraction_cuda_kernel.h
│ │ ├── pointtransformer_utils.py
│ │ ├── polar_utils.py
│ │ ├── recons_utils.py
│ │ ├── repsurface_utils.py
│ │ └── voxelize_utils.py
│ ├── scripts/
│ │ └── s3dis/
│ │ ├── test_pointnet2.sh
│ │ ├── test_pointtransformer.sh
│ │ ├── test_repsurf_umb.sh
│ │ ├── train_pointnet2.sh
│ │ ├── train_pointtransformer.sh
│ │ └── train_repsurf_umb.sh
│ ├── tool/
│ │ ├── test_s3dis.py
│ │ └── train.py
│ └── util/
│ ├── __init__.py
│ ├── data_util.py
│ └── utils.py
└── visualization/
├── airplane_0001.txt
├── bed_0001.txt
├── cup_0001.txt
├── table_0250.txt
├── triangled_airplane.obj
├── triangled_bed.obj
├── triangled_cup.obj
└── triangled_table.obj
SYMBOL INDEX (297 symbols across 44 files)
FILE: classification/dataset/ScanObjectNNDataLoader.py
class ScanObjectNNDataLoader (line 13) | class ScanObjectNNDataLoader(Dataset):
method __init__ (line 14) | def __init__(self, root, split='training', bg=True):
method __len__ (line 31) | def __len__(self):
method __getitem__ (line 34) | def __getitem__(self, index):
FILE: classification/models/repsurf/repsurf_ssg_umb.py
class Model (line 11) | class Model(nn.Module):
method __init__ (line 12) | def __init__(self, args):
method forward (line 43) | def forward(self, points):
FILE: classification/models/repsurf/repsurf_ssg_umb_2x.py
class Model (line 11) | class Model(nn.Module):
method __init__ (line 12) | def __init__(self, args):
method forward (line 46) | def forward(self, points):
FILE: classification/modules/pointnet2_utils.py
function square_distance (line 15) | def square_distance(src, dst):
function index_points (line 28) | def index_points(points, idx, cuda=False, is_group=False):
function farthest_point_sample (line 47) | def farthest_point_sample(xyz, npoint, cuda=False):
function query_ball_point (line 78) | def query_ball_point(radius, nsample, xyz, new_xyz, debug=False, cuda=Fa...
function query_knn_point (line 102) | def query_knn_point(k, xyz, new_xyz, cuda=False):
function sample (line 114) | def sample(nsample, feature, cuda=False):
FILE: classification/modules/pointops/functions/pointops.py
class FurthestSampling (line 35) | class FurthestSampling(Function):
method forward (line 37) | def forward(ctx, xyz, m):
method backward (line 50) | def backward(xyz, a=None):
class Gathering (line 57) | class Gathering(Function):
method forward (line 59) | def forward(ctx, features, idx):
method backward (line 74) | def backward(ctx, grad_out):
class NearestNeighbor (line 86) | class NearestNeighbor(Function):
method forward (line 88) | def forward(ctx, unknown: torch.Tensor, known: torch.Tensor) -> Tuple[...
method backward (line 105) | def backward(ctx, a=None, b=None):
class Interpolation (line 112) | class Interpolation(Function):
method forward (line 114) | def forward(ctx, features: torch.Tensor, idx: torch.Tensor, weight: to...
method backward (line 134) | def backward(ctx, grad_out: torch.Tensor) -> Tuple[torch.Tensor, torch...
class Grouping (line 150) | class Grouping(Function):
method forward (line 152) | def forward(ctx, features: torch.Tensor, idx: torch.Tensor) -> torch.T...
method backward (line 167) | def backward(ctx, grad_out: torch.Tensor) -> Tuple[torch.Tensor, torch...
class GroupingInt (line 183) | class GroupingInt(Function):
method forward (line 185) | def forward(ctx, features: torch.Tensor, idx: torch.Tensor) -> torch.T...
method backward (line 199) | def backward(ctx, a=None):
class BallQuery (line 206) | class BallQuery(Function):
method forward (line 208) | def forward(ctx, radius: float, nsample: int, xyz: torch.Tensor, new_x...
method backward (line 225) | def backward(ctx, a=None):
function pairwise_distances (line 232) | def pairwise_distances(x, y=None):
class KNNQueryNaive (line 252) | class KNNQueryNaive(Function):
method forward (line 254) | def forward(ctx, nsample: int, xyz: torch.Tensor, new_xyz: torch.Tenso...
method backward (line 287) | def backward(ctx):
class KNNQuery (line 294) | class KNNQuery(Function):
method forward (line 296) | def forward(ctx, nsample: int, xyz: torch.Tensor, new_xyz: torch.Tenso...
method backward (line 319) | def backward(ctx, a=None):
class KNNQuery_Heap (line 326) | class KNNQuery_Heap(Function):
method forward (line 328) | def forward(ctx, nsample: int, xyz: torch.Tensor, new_xyz: torch.Tenso...
method backward (line 350) | def backward(ctx, a=None):
class QueryAndGroup (line 357) | class QueryAndGroup(nn.Module):
method __init__ (line 365) | def __init__(self, radius=None, nsample=32, use_xyz=True, return_idx=F...
method forward (line 370) | def forward(self, xyz: torch.Tensor, new_xyz: torch.Tensor = None, fea...
FILE: classification/modules/pointops/src/ballquery/ballquery_cuda.cpp
function ballquery_cuda (line 14) | void ballquery_cuda(int b, int n, int m, float radius, int nsample, at::...
function ballquery_cuda_fast (line 24) | void ballquery_cuda_fast(int b, int n, int m, float radius, int nsample,...
FILE: classification/modules/pointops/src/cuda_utils.h
function opt_n_threads (line 15) | inline int opt_n_threads(int work_size) {
function dim3 (line 20) | inline dim3 opt_block_config(int x, int y) {
FILE: classification/modules/pointops/src/grouping/grouping_cuda.cpp
function grouping_forward_cuda (line 10) | void grouping_forward_cuda(int b, int c, int n, int m, int nsample, at::...
function grouping_backward_cuda (line 18) | void grouping_backward_cuda(int b, int c, int n, int m, int nsample, at:...
function grouping_forward_cuda_fast (line 26) | void grouping_forward_cuda_fast(int b, int c, int n, int npoints, int ns...
FILE: classification/modules/pointops/src/grouping_int/grouping_int_cuda.cpp
function grouping_int_forward_cuda (line 10) | void grouping_int_forward_cuda(int b, int c, int n, int m, int nsample, ...
function grouping_int_forward_cuda_fast (line 18) | void grouping_int_forward_cuda_fast(int b, int c, int n, int m, int nsam...
FILE: classification/modules/pointops/src/interpolation/interpolation_cuda.cpp
function nearestneighbor_cuda (line 9) | void nearestneighbor_cuda(int b, int n, int m, at::Tensor unknown_tensor...
function interpolation_forward_cuda (line 18) | void interpolation_forward_cuda(int b, int c, int m, int n, at::Tensor p...
function interpolation_backward_cuda (line 27) | void interpolation_backward_cuda(int b, int c, int n, int m, at::Tensor ...
function nearestneighbor_cuda_fast (line 36) | void nearestneighbor_cuda_fast(int b, int n, int m, at::Tensor unknown_t...
function interpolation_forward_cuda_fast (line 44) | void interpolation_forward_cuda_fast(int b, int c, int m, int n, at::Ten...
FILE: classification/modules/pointops/src/knnquery/knnquery_cuda.cpp
function knnquery_cuda (line 15) | void knnquery_cuda(int b, int n, int m, int nsample, at::Tensor xyz_tens...
FILE: classification/modules/pointops/src/knnquery_heap/knnquery_heap_cuda.cpp
function knnquery_heap_cuda (line 15) | void knnquery_heap_cuda(int b, int n, int m, int nsample, at::Tensor xyz...
FILE: classification/modules/pointops/src/pointops_api.cpp
function PYBIND11_MODULE (line 13) | PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
FILE: classification/modules/pointops/src/sampling/sampling_cuda.cpp
function gathering_forward_cuda (line 9) | void gathering_forward_cuda(int b, int c, int n, int m, at::Tensor point...
function gathering_backward_cuda (line 17) | void gathering_backward_cuda(int b, int c, int n, int m, at::Tensor grad...
function furthestsampling_cuda (line 26) | void furthestsampling_cuda(int b, int n, int m, at::Tensor points_tensor...
FILE: classification/modules/polar_utils.py
function xyz2sphere (line 10) | def xyz2sphere(xyz, normalize=True):
function xyz2cylind (line 34) | def xyz2cylind(xyz, normalize=True):
FILE: classification/modules/ptaug_utils.py
function get_aug_args (line 13) | def get_aug_args(args):
function transform_point_cloud (line 22) | def transform_point_cloud(batch, args, aug_args, train=True, label=None):
function shift_point_cloud (line 37) | def shift_point_cloud(batch_data, shift_range=0.2):
function scale_point_cloud (line 53) | def scale_point_cloud(batch_data, scale_range=0.2):
FILE: classification/modules/recons_utils.py
function _recons_factory (line 11) | def _recons_factory(type):
function knn_recons (line 18) | def knn_recons(k, center, context, cuda=False):
function cal_normal (line 27) | def cal_normal(group_xyz, random_inv=False, is_group=False):
function pca (line 60) | def pca(X, k, center=True):
function cal_center (line 82) | def cal_center(group_xyz):
function cal_area (line 93) | def cal_area(group_xyz):
function cal_const (line 108) | def cal_const(normal, center, is_normalize=True):
function check_nan (line 127) | def check_nan(normal, center, pos=None):
function check_nan_umb (line 152) | def check_nan_umb(normal, center, pos=None):
class SurfaceConstructor (line 179) | class SurfaceConstructor(nn.Module):
method __init__ (line 188) | def __init__(self, r=None, k=3, recons_type='knn', return_dist=False, ...
method forward (line 198) | def forward(self, center, context):
FILE: classification/modules/repsurface_utils.py
function sample_and_group (line 15) | def sample_and_group(npoint, radius, nsample, center, normal, feature, r...
function sample_and_group_all (line 62) | def sample_and_group_all(center, normal, feature, return_normal=True, re...
function resort_points (line 91) | def resort_points(points, idx):
function group_by_umbrella (line 112) | def group_by_umbrella(xyz, new_xyz, k=9, cuda=False):
class SurfaceAbstraction (line 135) | class SurfaceAbstraction(nn.Module):
method __init__ (line 141) | def __init__(self, npoint, radius, nsample, in_channel, mlp, group_all...
method forward (line 159) | def forward(self, center, normal, feature):
class SurfaceAbstractionCD (line 186) | class SurfaceAbstractionCD(nn.Module):
method __init__ (line 192) | def __init__(self, npoint, radius, nsample, feat_channel, pos_channel,...
method forward (line 218) | def forward(self, center, normal, feature):
class UmbrellaSurfaceConstructor (line 252) | class UmbrellaSurfaceConstructor(nn.Module):
method __init__ (line 258) | def __init__(self, k, in_channel, aggr_type='sum', return_dist=False, ...
method forward (line 276) | def forward(self, center):
FILE: classification/tool/train_cls_scanobjectnn.py
function parse_args (line 22) | def parse_args():
function test (line 70) | def test(model, loader, num_class=15, num_point=1024, num_votes=10, tota...
function main (line 111) | def main(args):
FILE: classification/util/utils.py
function set_seed (line 11) | def set_seed(seed):
function weight_init (line 27) | def weight_init(m, init_type):
class ClsLoss (line 45) | class ClsLoss(nn.Module):
method __init__ (line 46) | def __init__(self):
method forward (line 49) | def forward(self, pred, target):
class SmoothClsLoss (line 55) | class SmoothClsLoss(nn.Module):
method __init__ (line 56) | def __init__(self, smoothing_ratio=0.1):
method forward (line 60) | def forward(self, pred, target):
function get_model (line 72) | def get_model(args):
function get_loss (line 77) | def get_loss():
function get_test_args (line 81) | def get_test_args():
FILE: segmentation/dataset/S3DISDataLoader.py
class S3DIS (line 16) | class S3DIS(Dataset):
method __init__ (line 17) | def __init__(self, args, split, coord_transform=None, rgb_transform=None,
method __getitem__ (line 37) | def __getitem__(self, idx):
method __len__ (line 47) | def __len__(self):
method print_weight (line 51) | def print_weight(data_root, data_list):
method print_mean_std (line 72) | def print_mean_std(data_root, data_list):
FILE: segmentation/models/pointnet2/pointnet2_ssg.py
class Model (line 11) | class Model(nn.Module):
method __init__ (line 12) | def __init__(self, args):
method forward (line 32) | def forward(self, pos_feat_off0):
FILE: segmentation/models/pointtransformer/pointtransformer.py
class Model (line 6) | class Model(nn.Module):
method __init__ (line 7) | def __init__(self, args):
method _make_enc (line 33) | def _make_enc(self, block, planes, blocks, share_planes=8, stride=1, n...
method _make_dec (line 40) | def _make_dec(self, block, planes, blocks, share_planes=8, nsample=16,...
method forward (line 47) | def forward(self, pxo, *args):
FILE: segmentation/models/repsurf/repsurf_umb_ssg.py
class Model (line 11) | class Model(nn.Module):
method __init__ (line 12) | def __init__(self, args):
method forward (line 42) | def forward(self, pos_feat_off0):
FILE: segmentation/modules/aug_utils.py
function transform_point_cloud_coord (line 9) | def transform_point_cloud_coord(args):
function transform_point_cloud_rgb (line 38) | def transform_point_cloud_rgb(args):
class Compose (line 54) | class Compose(object):
method __init__ (line 55) | def __init__(self, transforms):
method __call__ (line 58) | def __call__(self, coord, feat, label, mask=None):
class RandomRotate (line 64) | class RandomRotate(object):
method __init__ (line 65) | def __init__(self, rot=(np.pi/24, np.pi/24, np.pi/4), prob=1.):
method __call__ (line 69) | def __call__(self, coord, feat, label, mask=None):
class RandomRotateAligned (line 85) | class RandomRotateAligned(object):
method __init__ (line 86) | def __init__(self, rot=np.pi, prob=1.):
method __call__ (line 90) | def __call__(self, coord, feat, label, mask=None):
class RandomRotatePerturb (line 99) | class RandomRotatePerturb(object):
method __init__ (line 100) | def __init__(self, sigma=0.03, clip=0.09, prob=1.):
method __call__ (line 105) | def __call__(self, coord, feat, label, mask=None):
class RandomRotatePerturbAligned (line 121) | class RandomRotatePerturbAligned(object):
method __init__ (line 122) | def __init__(self, sigma=0.03, clip=0.09, prob=1.):
method __call__ (line 127) | def __call__(self, coord, feat, label, mask=None):
class RandomScale (line 136) | class RandomScale(object):
method __init__ (line 137) | def __init__(self, scale=0.1, anisotropic=False, prob=1.):
method __call__ (line 142) | def __call__(self, coord, feat, label, mask=None):
class RandomShift (line 149) | class RandomShift(object):
method __init__ (line 150) | def __init__(self, shift=(0.2, 0.2, 0), p=0.95):
method __call__ (line 154) | def __call__(self, coord, feat, label, mask=None):
class RandomFlip (line 163) | class RandomFlip(object):
method __init__ (line 164) | def __init__(self, p=1.):
method __call__ (line 167) | def __call__(self, coord, feat, label, mask=None):
class RandomJitter (line 176) | class RandomJitter(object):
method __init__ (line 177) | def __init__(self, sigma=0.01, clip=0.05, p=1., is_lidar=False):
method __call__ (line 183) | def __call__(self, coord, feat, label, mask=None):
class ChromaticAutoContrast (line 193) | class ChromaticAutoContrast(object):
method __init__ (line 194) | def __init__(self, p=0.2, blend_factor=None):
method __call__ (line 198) | def __call__(self, coord, feat, label, mask=None):
class ChromaticTranslation (line 214) | class ChromaticTranslation(object):
method __init__ (line 215) | def __init__(self, p=0.95, ratio=0.05):
method __call__ (line 219) | def __call__(self, coord, feat, label, mask=None):
class ChromaticJitter (line 228) | class ChromaticJitter(object):
method __init__ (line 229) | def __init__(self, p=0.95, std=0.005):
method __call__ (line 233) | def __call__(self, coord, feat, label, mask=None):
class HueSaturationTranslation (line 243) | class HueSaturationTranslation(object):
method rgb_to_hsv (line 245) | def rgb_to_hsv(rgb):
method hsv_to_rgb (line 270) | def hsv_to_rgb(hsv):
method __init__ (line 290) | def __init__(self, hue_max=0.5, saturation_max=0.2, p=1.):
method __call__ (line 295) | def __call__(self, coord, feat, label, mask=None):
class RandomDropColor (line 312) | class RandomDropColor(object):
method __init__ (line 313) | def __init__(self, p=0.2):
method __call__ (line 316) | def __call__(self, coord, feat, label, mask=None):
FILE: segmentation/modules/pointnet2_utils.py
function sample_and_group (line 13) | def sample_and_group(stride, nsample, xyz, points, offset, return_idx=Fa...
class PointNetSetAbstraction (line 49) | class PointNetSetAbstraction(nn.Module):
method __init__ (line 55) | def __init__(self, stride, nsample, in_channel, mlp, num_sector=1):
method forward (line 69) | def forward(self, pos_feat_off):
class PointNetFeaturePropagation (line 86) | class PointNetFeaturePropagation(nn.Module):
method __init__ (line 92) | def __init__(self, in_channel, mlp):
method forward (line 102) | def forward(self, pos_feat_off1, pos_feat_off2):
FILE: segmentation/modules/pointops/functions/pointops.py
class FurthestSampling (line 31) | class FurthestSampling(Function):
method forward (line 33) | def forward(ctx, xyz, offset, new_offset):
class SectorizedFurthestSampling (line 52) | class SectorizedFurthestSampling(Function):
method forward (line 54) | def forward(ctx, xyz, offset, new_offset, num_sectors, min_points=10000):
class KNNQuery (line 114) | class KNNQuery(Function):
method forward (line 116) | def forward(ctx, nsample, xyz, new_xyz, offset, new_offset):
class Grouping (line 133) | class Grouping(Function):
method forward (line 135) | def forward(ctx, input, idx):
method backward (line 149) | def backward(ctx, grad_output):
function queryandgroup (line 165) | def queryandgroup(nsample, xyz, new_xyz, feat, idx, offset, new_offset, ...
class Subtraction (line 189) | class Subtraction(Function):
method forward (line 191) | def forward(ctx, input1, input2, idx):
method backward (line 205) | def backward(ctx, grad_output):
class Aggregation (line 221) | class Aggregation(Function):
method forward (line 223) | def forward(ctx, input, position, weight, idx):
method backward (line 237) | def backward(ctx, grad_output):
function interpolation (line 256) | def interpolation(xyz, new_xyz, feat, offset, new_offset, k=3):
class Interpolation (line 273) | class Interpolation(Function):
method forward (line 275) | def forward(ctx, xyz, new_xyz, input, offset, new_offset, k=3):
method backward (line 294) | def backward(ctx, grad_output):
FILE: segmentation/modules/pointops/src/aggregation/aggregation_cuda.cpp
function aggregation_forward_cuda (line 8) | void aggregation_forward_cuda(int n, int nsample, int c, int w_c, at::Te...
function aggregation_backward_cuda (line 18) | void aggregation_backward_cuda(int n, int nsample, int c, int w_c, at::T...
FILE: segmentation/modules/pointops/src/cuda_utils.h
function opt_n_threads (line 11) | inline int opt_n_threads(int work_size) {
function dim3 (line 16) | inline dim3 opt_block_config(int x, int y) {
FILE: segmentation/modules/pointops/src/grouping/grouping_cuda.cpp
function grouping_forward_cuda (line 8) | void grouping_forward_cuda(int m, int nsample, int c, at::Tensor input_t...
function grouping_backward_cuda (line 16) | void grouping_backward_cuda(int m, int nsample, int c, at::Tensor grad_o...
FILE: segmentation/modules/pointops/src/interpolation/interpolation_cuda.cpp
function interpolation_forward_cuda (line 8) | void interpolation_forward_cuda(int n, int c, int k, at::Tensor input_te...
function interpolation_backward_cuda (line 17) | void interpolation_backward_cuda(int n, int c, int k, at::Tensor grad_ou...
FILE: segmentation/modules/pointops/src/knnquery/knnquery_cuda.cpp
function knnquery_cuda (line 8) | void knnquery_cuda(int m, int nsample, at::Tensor xyz_tensor, at::Tensor...
FILE: segmentation/modules/pointops/src/pointops_api.cpp
function PYBIND11_MODULE (line 12) | PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
FILE: segmentation/modules/pointops/src/sampling/sampling_cuda.cpp
function furthestsampling_cuda (line 8) | void furthestsampling_cuda(int b, int n, at::Tensor xyz_tensor, at::Tens...
FILE: segmentation/modules/pointops/src/subtraction/subtraction_cuda.cpp
function subtraction_forward_cuda (line 8) | void subtraction_forward_cuda(int n, int nsample, int c, at::Tensor inpu...
function subtraction_backward_cuda (line 17) | void subtraction_backward_cuda(int n, int nsample, int c, at::Tensor idx...
FILE: segmentation/modules/pointtransformer_utils.py
class PointTransformerLayer (line 7) | class PointTransformerLayer(nn.Module):
method __init__ (line 8) | def __init__(self, in_planes, out_planes, share_planes=8, nsample=16):
method forward (line 25) | def forward(self, pxo) -> torch.Tensor:
class TransitionDown (line 45) | class TransitionDown(nn.Module):
method __init__ (line 46) | def __init__(self, in_planes, out_planes, stride=1, nsample=16, num_se...
method forward (line 57) | def forward(self, pxo):
class TransitionUp (line 79) | class TransitionUp(nn.Module):
method __init__ (line 80) | def __init__(self, in_planes, out_planes=None):
method forward (line 92) | def forward(self, pxo1, pxo2=None):
class PointTransformerBlock (line 113) | class PointTransformerBlock(nn.Module):
method __init__ (line 116) | def __init__(self, in_planes, planes, share_planes=8, nsample=16):
method forward (line 126) | def forward(self, pxo):
FILE: segmentation/modules/polar_utils.py
function xyz2sphere (line 10) | def xyz2sphere(xyz, normalize=True):
function xyz2cylind (line 34) | def xyz2cylind(xyz, normalize=True):
FILE: segmentation/modules/recons_utils.py
function cal_normal (line 10) | def cal_normal(group_xyz, offset, random_inv=False, is_group=False):
function cal_center (line 48) | def cal_center(group_xyz):
function cal_area (line 59) | def cal_area(group_xyz):
function cal_const (line 74) | def cal_const(normal, center, is_normalize=True):
function check_nan (line 93) | def check_nan(normal, center, pos=None):
function check_nan_umb (line 117) | def check_nan_umb(normal, center, pos=None):
FILE: segmentation/modules/repsurface_utils.py
function sample_and_group (line 15) | def sample_and_group(stride, nsample, center, normal, feature, offset, r...
function resort_points (line 54) | def resort_points(points, idx):
function _fixed_rotate (line 71) | def _fixed_rotate(xyz):
function group_by_umbrella_v2 (line 77) | def group_by_umbrella_v2(xyz, new_xyz, offset, new_offset, k=9):
function group_by_umbrella (line 101) | def group_by_umbrella(xyz, new_xyz, offset, new_offset, k=9):
function sort_factory (line 125) | def sort_factory(s_type):
class SurfaceAbstraction (line 134) | class SurfaceAbstraction(nn.Module):
method __init__ (line 140) | def __init__(self, stride, nsample, in_channel, mlp, return_polar=True...
method forward (line 155) | def forward(self, pos_nor_feat_off):
class SurfaceAbstractionCD (line 176) | class SurfaceAbstractionCD(nn.Module):
method __init__ (line 182) | def __init__(self, stride, nsample, feat_channel, pos_channel, mlp, re...
method forward (line 205) | def forward(self, pos_nor_feat_off):
class SurfaceFeaturePropagationCD (line 233) | class SurfaceFeaturePropagationCD(nn.Module):
method __init__ (line 239) | def __init__(self, prev_channel, skip_channel, mlp):
method forward (line 257) | def forward(self, pos_feat_off1, pos_feat_off2):
class UmbrellaSurfaceConstructor (line 287) | class UmbrellaSurfaceConstructor(nn.Module):
method __init__ (line 293) | def __init__(self, k, in_channel, out_channel, random_inv=True, sort='...
method forward (line 306) | def forward(self, center, offset):
FILE: segmentation/modules/voxelize_utils.py
function fnv_hash_vec (line 4) | def fnv_hash_vec(arr):
function ravel_hash_vec (line 20) | def ravel_hash_vec(arr):
function voxelize (line 40) | def voxelize(coord, voxel_size=0.05, hash_type='fnv', mode=0):
FILE: segmentation/tool/test_s3dis.py
function parse_args (line 34) | def parse_args():
function main (line 61) | def main():
function data_prepare (line 105) | def data_prepare():
function data_load (line 114) | def data_load(data_name):
function data_process (line 133) | def data_process(coord, feat, idx_data):
function input_normalize (line 162) | def input_normalize(coord, feat):
function visualize_scene (line 177) | def visualize_scene(coord, pred, label, name):
function test (line 186) | def test(model):
FILE: segmentation/tool/train.py
function parse_args (line 33) | def parse_args():
function main_worker (line 106) | def main_worker(gpu, ngpus_per_node, argss):
function train (line 261) | def train(train_loader, model, criterion, optimizer, epoch):
function validate (line 350) | def validate(val_loader, model, criterion):
FILE: segmentation/util/data_util.py
function sa_create (line 8) | def sa_create(name, var):
function collate_fn (line 15) | def collate_fn(batch):
function data_prepare (line 26) | def data_prepare(coord, feat, label, args, split, coord_transform, rgb_t...
FILE: segmentation/util/utils.py
function main_process (line 16) | def main_process(args):
class AverageMeter (line 25) | class AverageMeter(object):
method __init__ (line 28) | def __init__(self):
method reset (line 31) | def reset(self):
method update (line 37) | def update(self, val, n=1):
function intersectionAndUnion (line 44) | def intersectionAndUnion(output, target, K, ignore_index=255):
function intersectionAndUnionGPU (line 59) | def intersectionAndUnionGPU(output, target, K, ignore_index=255):
function find_free_port (line 74) | def find_free_port():
function set_seed (line 85) | def set_seed(seed):
function worker_init_fn (line 99) | def worker_init_fn(worker_id, seed=None):
function get_logger (line 108) | def get_logger(log_dir, model):
function get_aug_args (line 125) | def get_aug_args(args):
function get_dataset_obj (line 136) | def get_dataset_obj(args):
function get_dataset_description (line 142) | def get_dataset_description(args):
function get_loop (line 150) | def get_loop(args):
function get_class_weights (line 159) | def get_class_weights(dataset_name):
function get_rgb_stat (line 192) | def get_rgb_stat(args):
function get_model (line 202) | def get_model(args):
function get_optimizer (line 207) | def get_optimizer(args, model):
function get_scheduler (line 219) | def get_scheduler(args, optimizer):
function get_loss (line 227) | def get_loss(weight=None, ignore_label=None):
function get_test_args (line 231) | def get_test_args():
function pc_median_filter_gpu (line 235) | def pc_median_filter_gpu(coord, label, group_size=16):
Condensed preview — 115 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (2,775K chars).
[
{
"path": ".gitignore",
"chars": 1805,
"preview": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packagi"
},
{
"path": "LICENSE.txt",
"chars": 11340,
"preview": " Apache License\n Version 2.0, January 2004\n "
},
{
"path": "README.md",
"chars": 3082,
"preview": "# RepSurf - Surface Representation for Point Clouds <br> [CVPR 2022 Oral]\n\nBy *[Haoxi Ran\\*](https://hancyran.github.io/"
},
{
"path": "classification/README.md",
"chars": 4481,
"preview": "# RepSurf for Classification <br>\n\nBy *[Haoxi Ran\\*](https://hancyran.github.io/) , Jun Liu, Chengjie Wang* ( * : corres"
},
{
"path": "classification/dataset/ScanObjectNNDataLoader.py",
"chars": 1053,
"preview": "\"\"\"\nAuthor: Haoxi Ran\nDate: 05/10/2022\n\"\"\"\n\nimport h5py\nimport warnings\nfrom torch.utils.data import Dataset\n\nwarnings.f"
},
{
"path": "classification/dataset/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "classification/init.sh",
"chars": 318,
"preview": "#!/bin/sh\n\nmkdir -p log/PointAnalysis/log/ScanObjectNN\nmkdir -p data/\n\nconda create -n repsurf-cls python=3.7 -y\nconda a"
},
{
"path": "classification/models/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "classification/models/repsurf/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "classification/models/repsurf/repsurf_ssg_umb.py",
"chars": 2547,
"preview": "\"\"\"\nAuthor: Haoxi Ran\nDate: 05/10/2022\n\"\"\"\n\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom modules.repsurfac"
},
{
"path": "classification/models/repsurf/repsurf_ssg_umb_2x.py",
"chars": 2930,
"preview": "\"\"\"\nAuthor: Haoxi Ran\nDate: 05/10/2022\n\"\"\"\n\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom modules.repsurfac"
},
{
"path": "classification/modules/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "classification/modules/pointnet2_utils.py",
"chars": 4064,
"preview": "\"\"\"\nAuthor: Haoxi Ran\nDate: 05/10/2022\n\"\"\"\n\nimport torch\n\ntry:\n from modules.pointops.functions.pointops import furth"
},
{
"path": "classification/modules/pointops/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "classification/modules/pointops/functions/__init__.py",
"chars": 24,
"preview": "from .pointops import *\n"
},
{
"path": "classification/modules/pointops/functions/pointops.py",
"chars": 14681,
"preview": "from typing import Tuple\nimport numpy as np\nimport torch\nfrom torch.autograd import Function\nimport torch.nn as nn\n\ntry:"
},
{
"path": "classification/modules/pointops/setup.py",
"chars": 1363,
"preview": "#python3 setup.py install\n\nfrom setuptools import setup\nfrom torch.utils.cpp_extension import BuildExtension, CUDAExtens"
},
{
"path": "classification/modules/pointops/src/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "classification/modules/pointops/src/ballquery/ballquery_cuda.cpp",
"chars": 1292,
"preview": "#include <torch/serialize/tensor.h>\n#include <vector>\n#include <THC/THC.h>\n#include <ATen/cuda/CUDAContext.h>\n\n#include "
},
{
"path": "classification/modules/pointops/src/ballquery/ballquery_cuda_kernel.cu",
"chars": 3389,
"preview": "#include \"../cuda_utils.h\"\n#include \"ballquery_cuda_kernel.h\"\n\n// input: new_xyz(b, m, 3) xyz(b, n, 3)\n// output: idx(b,"
},
{
"path": "classification/modules/pointops/src/ballquery/ballquery_cuda_kernel.h",
"chars": 803,
"preview": "#ifndef _BALLQUERY_CUDA_KERNEL\n#define _BALLQUERY_CUDA_KERNEL\n#include <torch/serialize/tensor.h>\n#include <vector>\n#inc"
},
{
"path": "classification/modules/pointops/src/cuda_utils.h",
"chars": 824,
"preview": "#ifndef _CUDA_UTILS_H\n#define _CUDA_UTILS_H\n\n#include <cmath>\n\n#define TOTAL_THREADS 1024\n\n#define CHECK_CUDA(x) TORCH_C"
},
{
"path": "classification/modules/pointops/src/grouping/grouping_cuda.cpp",
"chars": 1359,
"preview": "#include <torch/serialize/tensor.h>\n#include <ATen/cuda/CUDAContext.h>\n#include <vector>\n#include <THC/THC.h>\n\n#include "
},
{
"path": "classification/modules/pointops/src/grouping/grouping_cuda_kernel.cu",
"chars": 3686,
"preview": "#include \"../cuda_utils.h\"\n#include \"grouping_cuda_kernel.h\"\n\n// input: points(b, c, n) idx(b, m, nsample)\n// output: ou"
},
{
"path": "classification/modules/pointops/src/grouping/grouping_cuda_kernel.h",
"chars": 1070,
"preview": "#ifndef _GROUPING_CUDA_KERNEL\n#define _GROUPING_CUDA_KERNEL\n#include <torch/serialize/tensor.h>\n#include <vector>\n#inclu"
},
{
"path": "classification/modules/pointops/src/grouping_int/grouping_int_cuda.cpp",
"chars": 973,
"preview": "#include <torch/serialize/tensor.h>\n#include <vector>\n#include <ATen/cuda/CUDAContext.h>\n#include <THC/THC.h>\n\n#include "
},
{
"path": "classification/modules/pointops/src/grouping_int/grouping_int_cuda_kernel.cu",
"chars": 2479,
"preview": "#include \"../cuda_utils.h\"\n#include \"grouping_int_cuda_kernel.h\"\n\n// input: points(b, c, n) idx(b, m, nsample)\n// output"
},
{
"path": "classification/modules/pointops/src/grouping_int/grouping_int_cuda_kernel.h",
"chars": 810,
"preview": "#ifndef _GROUPING_INT_CUDA_KERNEL\n#define _GROUPING_INT_CUDA_KERNEL\n#include <torch/serialize/tensor.h>\n#include <vector"
},
{
"path": "classification/modules/pointops/src/interpolation/interpolation_cuda.cpp",
"chars": 2476,
"preview": "#include <torch/serialize/tensor.h>\n#include <vector>\n#include <ATen/cuda/CUDAContext.h>\n#include <THC/THC.h>\n#include \""
},
{
"path": "classification/modules/pointops/src/interpolation/interpolation_cuda_kernel.cu",
"chars": 7415,
"preview": "#include \"../cuda_utils.h\"\n#include \"interpolation_cuda_kernel.h\"\n\n// input: unknown(b, n, 3) known(b, m, 3)\n// output: "
},
{
"path": "classification/modules/pointops/src/interpolation/interpolation_cuda_kernel.h",
"chars": 1720,
"preview": "#ifndef _INTERPOLATION_CUDA_KERNEL\n#define _INTERPOLATION_CUDA_KERNEL\n#include <torch/serialize/tensor.h>\n#include <vect"
},
{
"path": "classification/modules/pointops/src/knnquery/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "classification/modules/pointops/src/knnquery/knnquery_cuda.cpp",
"chars": 962,
"preview": "#include <torch/serialize/tensor.h>\n#include <vector>\n#include <THC/THC.h>\n#include <ATen/cuda/CUDAContext.h>\n\n#include "
},
{
"path": "classification/modules/pointops/src/knnquery/knnquery_cuda_kernel.cu",
"chars": 2420,
"preview": "#include \"../cuda_utils.h\"\n#include \"knnquery_cuda_kernel.h\"\n\n// input: xyz (b, n, 3) new_xyz (b, m, 3)\n// output: idx ("
},
{
"path": "classification/modules/pointops/src/knnquery/knnquery_cuda_kernel.h",
"chars": 528,
"preview": "#ifndef _KNNQUERY_CUDA_KERNEL\n#define _KNNQUERY_CUDA_KERNEL\n\n#include <torch/serialize/tensor.h>\n#include <vector>\n#incl"
},
{
"path": "classification/modules/pointops/src/knnquery_heap/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "classification/modules/pointops/src/knnquery_heap/knnquery_heap_cuda.cpp",
"chars": 977,
"preview": "#include <torch/serialize/tensor.h>\n#include <vector>\n#include <THC/THC.h>\n#include <ATen/cuda/CUDAContext.h>\n\n#include "
},
{
"path": "classification/modules/pointops/src/knnquery_heap/knnquery_heap_cuda_kernel.cu",
"chars": 3025,
"preview": "#include \"../cuda_utils.h\"\n#include \"knnquery_heap_cuda_kernel.h\"\n\n\n__device__ void swap_float(float *x, float *y)\n{\n "
},
{
"path": "classification/modules/pointops/src/knnquery_heap/knnquery_heap_cuda_kernel.h",
"chars": 548,
"preview": "#ifndef _KNNQUERY_HEAP_CUDA_KERNEL\n#define _KNNQUERY_HEAP_CUDA_KERNEL\n\n#include <torch/serialize/tensor.h>\n#include <vec"
},
{
"path": "classification/modules/pointops/src/pointops_api.cpp",
"chars": 1567,
"preview": "#include <torch/serialize/tensor.h>\n#include <torch/extension.h>\n\n#include \"ballquery/ballquery_cuda_kernel.h\"\n#include "
},
{
"path": "classification/modules/pointops/src/sampling/sampling_cuda.cpp",
"chars": 1263,
"preview": "#include <torch/serialize/tensor.h>\n#include <vector>\n#include <ATen/cuda/CUDAContext.h>\n#include <THC/THC.h>\n#include \""
},
{
"path": "classification/modules/pointops/src/sampling/sampling_cuda_kernel.cu",
"chars": 6625,
"preview": "#include \"../cuda_utils.h\"\n#include \"sampling_cuda_kernel.h\"\n\n// input: points(b, c, n) idx(b, m)\n// output: out(b, c, m"
},
{
"path": "classification/modules/pointops/src/sampling/sampling_cuda_kernel.h",
"chars": 963,
"preview": "#ifndef _SAMPLING_CUDA_KERNEL\n#define _SAMPLING_CUDA_KERNEL\n#include <torch/serialize/tensor.h>\n#include <vector>\n#inclu"
},
{
"path": "classification/modules/polar_utils.py",
"chars": 1555,
"preview": "\"\"\"\nAuthor: Haoxi Ran\nDate: 05/10/2022\n\"\"\"\n\nimport torch\nimport numpy as np\n\n\ndef xyz2sphere(xyz, normalize=True):\n \""
},
{
"path": "classification/modules/ptaug_utils.py",
"chars": 1669,
"preview": "\"\"\"\nAuthor: Haoxi Ran\nDate: 05/10/2022\n\"\"\"\n\nimport torch\n\n\n#################\n# MAIN\n#################\n\ndef get_aug_args("
},
{
"path": "classification/modules/recons_utils.py",
"chars": 7491,
"preview": "\"\"\"\nAuthor: Haoxi Ran\nDate: 05/10/2022\n\"\"\"\n\nimport torch\nfrom torch import nn\nfrom modules.pointnet2_utils import query_"
},
{
"path": "classification/modules/repsurface_utils.py",
"chars": 11636,
"preview": "\"\"\"\nAuthor: Haoxi Ran\nDate: 05/10/2022\n\"\"\"\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\n\nfrom mod"
},
{
"path": "classification/scripts/scanobjectnn/repsurf_ssg_umb.sh",
"chars": 439,
"preview": "#!/usr/bin/env bash\nset -v\n\npython3 tool/train_cls_scanobjectnn.py \\\n --cuda_ops \\\n --batch_size 64 \\\n"
},
{
"path": "classification/scripts/scanobjectnn/repsurf_ssg_umb_2x.sh",
"chars": 445,
"preview": "#!/usr/bin/env bash\nset -v\n\npython3 tool/train_cls_scanobjectnn.py \\\n --cuda_ops \\\n --batch_size 64 \\\n"
},
{
"path": "classification/tool/train_cls_scanobjectnn.py",
"chars": 11432,
"preview": "\"\"\"\nAuthor: Haoxi Ran\nDate: 05/10/2022\n\"\"\"\n\nfrom functools import partial\n\nimport argparse\nimport numpy as np\nimport os\n"
},
{
"path": "classification/util/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "classification/util/utils.py",
"chars": 2159,
"preview": "import importlib\nimport argparse\nimport random\n\nimport numpy as np\nimport torch\nfrom torch import nn\nimport torch.nn.fun"
},
{
"path": "segmentation/README.md",
"chars": 5202,
"preview": "# RepSurf for Segmentation <br>\n\nBy *[Haoxi Ran\\*](https://hancyran.github.io/) , Jun Liu, Chengjie Wang* ( * : correspo"
},
{
"path": "segmentation/dataset/S3DISDataLoader.py",
"chars": 3246,
"preview": "\"\"\"\nAuthor: Haoxi Ran\nDate: 06/30/2022\n\"\"\"\n\nimport os\nimport numpy as np\nimport SharedArray as SA\nfrom torch.utils.data "
},
{
"path": "segmentation/dataset/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "segmentation/init.sh",
"chars": 570,
"preview": "#!/bin/sh\n\nmkdir -p log/PointAnalysis/log/S3DIS\nmkdir -p log/PointAnalysis/log/ScanNet\nmkdir -p data/S3DIS\nmkdir -p data"
},
{
"path": "segmentation/models/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "segmentation/models/pointnet2/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "segmentation/models/pointnet2/pointnet2_ssg.py",
"chars": 1723,
"preview": "\"\"\"\nAuthor: Haoxi Ran\nDate: 06/30/2022\n\"\"\"\n\nimport torch\nimport torch.nn as nn\nfrom modules.pointnet2_utils import Point"
},
{
"path": "segmentation/models/pointtransformer/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "segmentation/models/pointtransformer/pointtransformer.py",
"chars": 3727,
"preview": "import torch\nimport torch.nn as nn\nfrom modules.pointtransformer_utils import PointTransformerBlock, TransitionDown, Tra"
},
{
"path": "segmentation/models/repsurf/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "segmentation/models/repsurf/repsurf_umb_ssg.py",
"chars": 2791,
"preview": "\"\"\"\nAuthor: Haoxi Ran\nDate: 06/30/2022\n\"\"\"\n\nimport torch\nimport torch.nn as nn\nfrom modules.repsurface_utils import Umbr"
},
{
"path": "segmentation/modules/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "segmentation/modules/aug_utils.py",
"chars": 12363,
"preview": "\"\"\"\nAuthor: Haoxi Ran\nDate: 06/30/2022\n\"\"\"\n\nimport numpy as np\n\n\ndef transform_point_cloud_coord(args):\n transform_li"
},
{
"path": "segmentation/modules/pointnet2_utils.py",
"chars": 4551,
"preview": "\"\"\"\nAuthor: Haoxi Ran\nDate: 06/30/2022\n\"\"\"\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\n\nfrom mod"
},
{
"path": "segmentation/modules/pointops/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "segmentation/modules/pointops/functions/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "segmentation/modules/pointops/functions/pointops.py",
"chars": 11817,
"preview": "from typing import Tuple\n\nimport torch\nfrom torch.autograd import Function\nimport torch.nn as nn\n\ntry:\n import pointo"
},
{
"path": "segmentation/modules/pointops/setup.py",
"chars": 1240,
"preview": "#python3 setup.py install\nfrom setuptools import setup\nfrom torch.utils.cpp_extension import BuildExtension, CUDAExtensi"
},
{
"path": "segmentation/modules/pointops/src/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "segmentation/modules/pointops/src/aggregation/aggregation_cuda.cpp",
"chars": 1652,
"preview": "#include <vector>\n#include <THC/THC.h>\n#include <torch/serialize/tensor.h>\n#include <ATen/cuda/CUDAContext.h>\n#include \""
},
{
"path": "segmentation/modules/pointops/src/aggregation/aggregation_cuda_kernel.cu",
"chars": 3296,
"preview": "#include \"../cuda_utils.h\"\n#include \"aggregation_cuda_kernel.h\"\n\n\n__global__ void aggregation_forward_cuda_kernel(int n,"
},
{
"path": "segmentation/modules/pointops/src/aggregation/aggregation_cuda_kernel.h",
"chars": 1138,
"preview": "#ifndef _AGGREGATION_CUDA_KERNEL\n#define _AGGREGATION_CUDA_KERNEL\n#include <vector>\n#include <torch/serialize/tensor.h>\n"
},
{
"path": "segmentation/modules/pointops/src/cuda_utils.h",
"chars": 644,
"preview": "#ifndef _CUDA_UTILS_H\n#define _CUDA_UTILS_H\n\n#include <cmath>\n#include <algorithm>\n\n#define TOTAL_THREADS 1024\n#define T"
},
{
"path": "segmentation/modules/pointops/src/grouping/grouping_cuda.cpp",
"chars": 913,
"preview": "#include <vector>\n#include <THC/THC.h>\n#include <torch/serialize/tensor.h>\n#include <ATen/cuda/CUDAContext.h>\n#include \""
},
{
"path": "segmentation/modules/pointops/src/grouping/grouping_cuda_kernel.cu",
"chars": 2066,
"preview": "#include \"../cuda_utils.h\"\n#include \"grouping_cuda_kernel.h\"\n\n\n__global__ void grouping_forward_cuda_kernel(int m, int n"
},
{
"path": "segmentation/modules/pointops/src/grouping/grouping_cuda_kernel.h",
"chars": 737,
"preview": "#ifndef _GROUPING_CUDA_KERNEL\n#define _GROUPING_CUDA_KERNEL\n#include <vector>\n#include <torch/serialize/tensor.h>\n#inclu"
},
{
"path": "segmentation/modules/pointops/src/interpolation/interpolation_cuda.cpp",
"chars": 1100,
"preview": "#include <vector>\n#include <THC/THC.h>\n#include <torch/serialize/tensor.h>\n#include <ATen/cuda/CUDAContext.h>\n#include \""
},
{
"path": "segmentation/modules/pointops/src/interpolation/interpolation_cuda_kernel.cu",
"chars": 2074,
"preview": "#include \"../cuda_utils.h\"\n#include \"interpolation_cuda_kernel.h\"\n\n\n__global__ void interpolation_forward_cuda_kernel(in"
},
{
"path": "segmentation/modules/pointops/src/interpolation/interpolation_cuda_kernel.h",
"chars": 837,
"preview": "#ifndef _INTERPOLATION_CUDA_KERNEL\n#define _INTERPOLATION_CUDA_KERNEL\n#include <vector>\n#include <torch/serialize/tensor"
},
{
"path": "segmentation/modules/pointops/src/knnquery/knnquery_cuda.cpp",
"chars": 755,
"preview": "#include <vector>\n#include <THC/THC.h>\n#include <torch/serialize/tensor.h>\n#include <ATen/cuda/CUDAContext.h>\n#include \""
},
{
"path": "segmentation/modules/pointops/src/knnquery/knnquery_cuda_kernel.cu",
"chars": 2985,
"preview": "#include \"../cuda_utils.h\"\n#include \"knnquery_cuda_kernel.h\"\n\n\n__device__ void swap_float(float *x, float *y)\n{\n floa"
},
{
"path": "segmentation/modules/pointops/src/knnquery/knnquery_cuda_kernel.h",
"chars": 576,
"preview": "#ifndef _KNNQUERY_CUDA_KERNEL\n#define _KNNQUERY_CUDA_KERNEL\n#include <vector>\n#include <torch/serialize/tensor.h>\n#inclu"
},
{
"path": "segmentation/modules/pointops/src/pointops_api.cpp",
"chars": 1297,
"preview": "#include <torch/serialize/tensor.h>\n#include <torch/extension.h>\n\n#include \"knnquery/knnquery_cuda_kernel.h\"\n#include \"s"
},
{
"path": "segmentation/modules/pointops/src/sampling/sampling_cuda.cpp",
"chars": 654,
"preview": "#include <vector>\n#include <THC/THC.h>\n#include <torch/serialize/tensor.h>\n#include <ATen/cuda/CUDAContext.h>\n#include \""
},
{
"path": "segmentation/modules/pointops/src/sampling/sampling_cuda_kernel.cu",
"chars": 5476,
"preview": "#include \"../cuda_utils.h\"\n#include \"sampling_cuda_kernel.h\"\n\n\n__device__ void __update(float *dists, int *dists_i, int "
},
{
"path": "segmentation/modules/pointops/src/sampling/sampling_cuda_kernel.h",
"chars": 527,
"preview": "#ifndef _SAMPLING_CUDA_KERNEL\n#define _SAMPLING_CUDA_KERNEL\n#include <vector>\n#include <torch/serialize/tensor.h>\n#inclu"
},
{
"path": "segmentation/modules/pointops/src/subtraction/subtraction_cuda.cpp",
"chars": 1136,
"preview": "#include <vector>\n#include <THC/THC.h>\n#include <torch/serialize/tensor.h>\n#include <ATen/cuda/CUDAContext.h>\n#include \""
},
{
"path": "segmentation/modules/pointops/src/subtraction/subtraction_cuda_kernel.cu",
"chars": 2405,
"preview": "#include \"../cuda_utils.h\"\n#include \"subtraction_cuda_kernel.h\"\n\n\n__global__ void subtraction_forward_cuda_kernel(int n,"
},
{
"path": "segmentation/modules/pointops/src/subtraction/subtraction_cuda_kernel.h",
"chars": 857,
"preview": "#ifndef _SUBTRACTION_CUDA_KERNEL\n#define _SUBTRACTION_CUDA_KERNEL\n#include <vector>\n#include <torch/serialize/tensor.h>\n"
},
{
"path": "segmentation/modules/pointtransformer_utils.py",
"chars": 6380,
"preview": "import torch\nimport torch.nn as nn\n\nfrom modules.pointops.functions import pointops\n\n\nclass PointTransformerLayer(nn.Mod"
},
{
"path": "segmentation/modules/polar_utils.py",
"chars": 1546,
"preview": "\"\"\"\nAuthor: Haoxi Ran\nDate: 06/30/2022\n\"\"\"\n\nimport torch\nimport numpy as np\n\n\ndef xyz2sphere(xyz, normalize=True):\n \""
},
{
"path": "segmentation/modules/recons_utils.py",
"chars": 4526,
"preview": "\"\"\"\nAuthor: Haoxi Ran\nDate: 06/30/2022\n\"\"\"\n\nimport torch\nimport numpy as np\n\n\ndef cal_normal(group_xyz, offset, random_i"
},
{
"path": "segmentation/modules/repsurface_utils.py",
"chars": 12794,
"preview": "\"\"\"\nAuthor: Haoxi Ran\nDate: 06/30/2022\n\"\"\"\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\n\nfrom mod"
},
{
"path": "segmentation/modules/voxelize_utils.py",
"chars": 1634,
"preview": "import numpy as np\n\n\ndef fnv_hash_vec(arr):\n \"\"\"\n FNV64-1A\n\n \"\"\"\n assert arr.ndim == 2\n # Floor first for"
},
{
"path": "segmentation/scripts/s3dis/test_pointnet2.sh",
"chars": 253,
"preview": "#!/bin/bash\n\nexport PYTHONPATH=./\n\nlog_dir='pointnet2_A5'\n\npython3 tool/test_s3dis.py --log_dir ${log_dir} \\\n -"
},
{
"path": "segmentation/scripts/s3dis/test_pointtransformer.sh",
"chars": 270,
"preview": "#!/bin/bash\n\nexport PYTHONPATH=./\n\nlog_dir='pointtransformer_A5'\n\npython3 tool/test_s3dis.py --log_dir ${log_dir} \\\n "
},
{
"path": "segmentation/scripts/s3dis/test_repsurf_umb.sh",
"chars": 255,
"preview": "#!/bin/bash\n\nexport PYTHONPATH=./\n\nlog_dir='repsurf_umb_A5'\n\npython3 tool/test_s3dis.py --log_dir ${log_dir} \\\n "
},
{
"path": "segmentation/scripts/s3dis/train_pointnet2.sh",
"chars": 635,
"preview": "#!/bin/bash\n\nexport PYTHONPATH=./\n\nlog_dir='pointnet2_A5'\n\npython3 tool/train.py --log_dir ${log_dir} --dataset S3DIS \\\n"
},
{
"path": "segmentation/scripts/s3dis/train_pointtransformer.sh",
"chars": 652,
"preview": "#!/bin/bash\n\nexport PYTHONPATH=./\n\nlog_dir='pointtransformer_A5'\n\npython3 tool/train.py --log_dir ${log_dir} --dataset S"
},
{
"path": "segmentation/scripts/s3dis/train_repsurf_umb.sh",
"chars": 644,
"preview": "#!/bin/bash\n\nexport PYTHONPATH=./\n\nlog_dir='repsurf_umb_A5'\n\npython3 tool/train.py --log_dir ${log_dir} --dataset S3DIS "
},
{
"path": "segmentation/tool/test_s3dis.py",
"chars": 11239,
"preview": "\"\"\"\nAuthor: Haoxi Ran\nDate: 06/30/2022\n\"\"\"\n\nimport json\nimport os\nimport time\nimport random\nimport numpy as np\nimport ar"
},
{
"path": "segmentation/tool/train.py",
"chars": 23707,
"preview": "\"\"\"\nAuthor: Haoxi Ran\nDate: 06/30/2022\n\"\"\"\n\nimport json\nimport os\nimport time\nfrom functools import partial\n\nimport nump"
},
{
"path": "segmentation/util/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "segmentation/util/data_util.py",
"chars": 2380,
"preview": "import numpy as np\nimport SharedArray as SA\nimport torch\n\nfrom modules.voxelize_utils import voxelize\n\n\ndef sa_create(na"
},
{
"path": "segmentation/util/utils.py",
"chars": 9070,
"preview": "import importlib\nimport argparse\nimport random\nimport logging\nimport sys\n\nimport numpy as np\nimport torch\nfrom torch imp"
},
{
"path": "visualization/airplane_0001.txt",
"chars": 568281,
"preview": "-0.098790,-0.182300,0.163800,0.829000,-0.557200,-0.048180\n0.994600,0.074420,0.010250,0.331800,-0.939500,0.085320\n0.18990"
},
{
"path": "visualization/bed_0001.txt",
"chars": 564658,
"preview": "-0.542300,-0.123700,0.526500,-0.992000,-0.126300,0.000012\n0.569300,-0.228200,-0.783900,0.938000,-0.003276,-0.346800\n0.57"
},
{
"path": "visualization/cup_0001.txt",
"chars": 568933,
"preview": "-0.868900,-0.443700,-0.015050,-0.964900,-0.262100,0.013750\n0.896300,0.442900,0.009749,0.905200,0.415800,0.088010\n-0.3431"
},
{
"path": "visualization/table_0250.txt",
"chars": 564121,
"preview": "-0.007806,0.454500,0.219200,0.000000,-1.000000,0.000000\n0.409800,-0.665000,-0.622300,0.188600,-0.637800,-0.746700\n-0.423"
},
{
"path": "visualization/triangled_airplane.obj",
"chars": 39117,
"preview": "# OBJ file\nv -0.4375 0.0937 0.0383\nv 0.9678 -0.1802 0.1294\nv 0.2323 -0.2877 -0.9109\nv 0.1780 -0.3677 0.4331\nv 0.0602 -0."
},
{
"path": "visualization/triangled_bed.obj",
"chars": 39397,
"preview": "# OBJ file\nv 0.1776 -0.0033 -0.0430\nv -0.5717 0.3573 0.7347\nv -0.5588 -0.2315 -0.7948\nv 0.5700 0.3575 0.7345\nv -0.0846 -"
},
{
"path": "visualization/triangled_cup.obj",
"chars": 39309,
"preview": "# OBJ file\nv -0.8078 -0.4863 0.2729\nv 0.8957 0.4441 -0.0088\nv -0.4467 0.6879 -0.5017\nv 0.1788 -0.5011 -0.6617\nv 0.3709 -"
},
{
"path": "visualization/triangled_table.obj",
"chars": 38916,
"preview": "# OBJ file\nv 0.3054 0.4966 -0.1096\nv -0.4233 -0.6658 0.6134\nv 0.4098 -0.6650 -0.6223\nv -0.5037 0.4803 0.7048\nv -0.5260 0"
}
]
About this extraction
This page contains the full source code of the hancyran/RepSurf GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 115 files (2.6 MB), approximately 683.7k tokens, and a symbol index with 297 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.