├── part_segmentation ├── util │ ├── __init__.py │ ├── util.py │ └── data_util.py └── model │ └── __init__.py ├── pointnet2_ops_lib ├── MANIFEST.in ├── pointnet2_ops │ ├── _version.py │ ├── __init__.py │ ├── _ext-src │ │ ├── include │ │ │ ├── ball_query.h │ │ │ ├── group_points.h │ │ │ ├── sampling.h │ │ │ ├── interpolate.h │ │ │ ├── utils.h │ │ │ └── cuda_utils.h │ │ └── src │ │ │ ├── bindings.cpp │ │ │ ├── ball_query.cpp │ │ │ ├── ball_query_gpu.cu │ │ │ ├── group_points.cpp │ │ │ ├── group_points_gpu.cu │ │ │ ├── sampling.cpp │ │ │ ├── interpolate.cpp │ │ │ ├── interpolate_gpu.cu │ │ │ └── sampling_gpu.cu │ ├── pointnet2_modules.py │ └── pointnet2_utils.py └── setup.py ├── classification_ModelNet40 ├── utils │ ├── progress │ │ ├── MANIFEST.in │ │ ├── .gitignore │ │ ├── demo.gif │ │ ├── LICENSE │ │ ├── setup.py │ │ ├── progress │ │ │ ├── spinner.py │ │ │ ├── counter.py │ │ │ ├── bar.py │ │ │ ├── helpers.py │ │ │ └── __init__.py │ │ ├── test_progress.py │ │ └── README.rst │ ├── __init__.py │ ├── logger.py │ └── misc.py ├── models │ ├── __init__.py │ └── pointmlp.py ├── helper.py ├── data.py ├── test.py ├── voting.py └── main.py ├── overview.pdf ├── overview.png ├── classification_ScanObjectNN ├── utils │ ├── progress │ │ ├── MANIFEST.in │ │ ├── .gitignore │ │ ├── demo.gif │ │ ├── LICENSE │ │ ├── setup.py │ │ ├── progress │ │ │ ├── spinner.py │ │ │ ├── counter.py │ │ │ ├── bar.py │ │ │ ├── helpers.py │ │ │ └── __init__.py │ │ ├── test_progress.py │ │ └── README.rst │ ├── __init__.py │ ├── logger.py │ └── misc.py ├── models │ └── __init__.py ├── ScanObjectNN.py └── main.py ├── images ├── neu.png ├── smile.png ├── columbia.png └── overview.png ├── requirements.txt ├── environment.yml ├── analysis.py ├── .gitignore ├── README.md └── LICENSE /part_segmentation/util/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pointnet2_ops_lib/MANIFEST.in: -------------------------------------------------------------------------------- 1 | graft pointnet2_ops/_ext-src 2 | -------------------------------------------------------------------------------- /pointnet2_ops_lib/pointnet2_ops/_version.py: -------------------------------------------------------------------------------- 1 | __version__ = "3.0.0" 2 | -------------------------------------------------------------------------------- /classification_ModelNet40/utils/progress/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst LICENSE 2 | -------------------------------------------------------------------------------- /overview.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ma-xu/pointMLP-pytorch/HEAD/overview.pdf -------------------------------------------------------------------------------- /overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ma-xu/pointMLP-pytorch/HEAD/overview.png -------------------------------------------------------------------------------- /classification_ScanObjectNN/utils/progress/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst LICENSE 2 | -------------------------------------------------------------------------------- /images/neu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ma-xu/pointMLP-pytorch/HEAD/images/neu.png -------------------------------------------------------------------------------- /images/smile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ma-xu/pointMLP-pytorch/HEAD/images/smile.png -------------------------------------------------------------------------------- /classification_ModelNet40/utils/progress/.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.egg-info 3 | build/ 4 | dist/ 5 | -------------------------------------------------------------------------------- /images/columbia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ma-xu/pointMLP-pytorch/HEAD/images/columbia.png -------------------------------------------------------------------------------- /images/overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ma-xu/pointMLP-pytorch/HEAD/images/overview.png -------------------------------------------------------------------------------- /classification_ScanObjectNN/utils/progress/.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.egg-info 3 | build/ 4 | dist/ 5 | -------------------------------------------------------------------------------- /part_segmentation/model/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from .pointMLP import pointMLP 3 | -------------------------------------------------------------------------------- /classification_ModelNet40/models/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | 3 | from .pointmlp import pointMLP, pointMLPElite 4 | -------------------------------------------------------------------------------- /classification_ScanObjectNN/models/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | 3 | from .pointmlp import pointMLP, pointMLPElite 4 | -------------------------------------------------------------------------------- /classification_ModelNet40/utils/progress/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ma-xu/pointMLP-pytorch/HEAD/classification_ModelNet40/utils/progress/demo.gif -------------------------------------------------------------------------------- /classification_ScanObjectNN/utils/progress/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ma-xu/pointMLP-pytorch/HEAD/classification_ScanObjectNN/utils/progress/demo.gif -------------------------------------------------------------------------------- /classification_ModelNet40/utils/__init__.py: -------------------------------------------------------------------------------- 1 | """Useful utils 2 | """ 3 | from .misc import * 4 | from .logger import * 5 | from .progress.progress.bar import Bar as Bar 6 | -------------------------------------------------------------------------------- /classification_ScanObjectNN/utils/__init__.py: -------------------------------------------------------------------------------- 1 | """Useful utils 2 | """ 3 | from .misc import * 4 | from .logger import * 5 | from .progress.progress.bar import Bar as Bar 6 | -------------------------------------------------------------------------------- /pointnet2_ops_lib/pointnet2_ops/__init__.py: -------------------------------------------------------------------------------- 1 | import pointnet2_ops.pointnet2_modules 2 | import pointnet2_ops.pointnet2_utils 3 | from pointnet2_ops._version import __version__ 4 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | torch 2 | torchvision 3 | cudatoolkit 4 | cycler 5 | einops 6 | h5py 7 | matplotlib==3.4.2 8 | pytorch 9 | pyyaml==5.4.1 10 | scikit-learn==0.24.2 11 | scipy 12 | tqdm 13 | -------------------------------------------------------------------------------- /pointnet2_ops_lib/pointnet2_ops/_ext-src/include/ball_query.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | at::Tensor ball_query(at::Tensor new_xyz, at::Tensor xyz, const float radius, 5 | const int nsample); 6 | -------------------------------------------------------------------------------- /pointnet2_ops_lib/pointnet2_ops/_ext-src/include/group_points.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | at::Tensor group_points(at::Tensor points, at::Tensor idx); 5 | at::Tensor group_points_grad(at::Tensor grad_out, at::Tensor idx, const int n); 6 | -------------------------------------------------------------------------------- /pointnet2_ops_lib/pointnet2_ops/_ext-src/include/sampling.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | at::Tensor gather_points(at::Tensor points, at::Tensor idx); 5 | at::Tensor gather_points_grad(at::Tensor grad_out, at::Tensor idx, const int n); 6 | at::Tensor furthest_point_sampling(at::Tensor points, const int nsamples); 7 | -------------------------------------------------------------------------------- /pointnet2_ops_lib/pointnet2_ops/_ext-src/include/interpolate.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | std::vector three_nn(at::Tensor unknowns, at::Tensor knows); 7 | at::Tensor three_interpolate(at::Tensor points, at::Tensor idx, 8 | at::Tensor weight); 9 | at::Tensor three_interpolate_grad(at::Tensor grad_out, at::Tensor idx, 10 | at::Tensor weight, const int m); 11 | -------------------------------------------------------------------------------- /environment.yml: -------------------------------------------------------------------------------- 1 | name: pointmlp 2 | channels: 3 | - pytorch 4 | - nvidia 5 | - conda-forge 6 | dependencies: 7 | # - cudatoolkit=10.2.89 8 | - cudatoolkit=11.1 9 | - cycler=0.10.0 10 | - einops=0.3.0 11 | - h5py=3.2.1 12 | - matplotlib=3.4.2 13 | - numpy=1.20.2 14 | - numpy-base=1.20.2 15 | - pytorch=1.8.1 16 | - pyyaml=5.4.1 17 | - scikit-learn=0.24.2 18 | - scipy=1.6.3 19 | - torchvision=0.9.1 20 | - tqdm=4.61.1 21 | - pip 22 | - pip: 23 | - pointnet2_ops_lib/. 24 | -------------------------------------------------------------------------------- /analysis.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import fvcore.nn 3 | import fvcore.common 4 | from fvcore.nn import FlopCountAnalysis 5 | from classification_ScanObjectNN.models import pointMLPElite 6 | 7 | model = pointMLPElite() 8 | model.eval() 9 | # model = deit_tiny_patch16_224() 10 | 11 | inputs = (torch.randn((1,3,1024))) 12 | k = 1024.0 13 | flops = FlopCountAnalysis(model, inputs).total() 14 | print(f"Flops : {flops}") 15 | flops = flops/(k**3) 16 | print(f"Flops : {flops:.1f}G") 17 | params = fvcore.nn.parameter_count(model)[""] 18 | print(f"Params : {params}") 19 | params = params/(k**2) 20 | print(f"Params : {params:.1f}M") 21 | -------------------------------------------------------------------------------- /pointnet2_ops_lib/pointnet2_ops/_ext-src/src/bindings.cpp: -------------------------------------------------------------------------------- 1 | #include "ball_query.h" 2 | #include "group_points.h" 3 | #include "interpolate.h" 4 | #include "sampling.h" 5 | 6 | PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { 7 | m.def("gather_points", &gather_points); 8 | m.def("gather_points_grad", &gather_points_grad); 9 | m.def("furthest_point_sampling", &furthest_point_sampling); 10 | 11 | m.def("three_nn", &three_nn); 12 | m.def("three_interpolate", &three_interpolate); 13 | m.def("three_interpolate_grad", &three_interpolate_grad); 14 | 15 | m.def("ball_query", &ball_query); 16 | 17 | m.def("group_points", &group_points); 18 | m.def("group_points_grad", &group_points_grad); 19 | } 20 | -------------------------------------------------------------------------------- /classification_ModelNet40/helper.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn.functional as F 3 | 4 | def cal_loss(pred, gold, smoothing=True): 5 | ''' Calculate cross entropy loss, apply label smoothing if needed. ''' 6 | 7 | gold = gold.contiguous().view(-1) 8 | 9 | if smoothing: 10 | eps = 0.2 11 | n_class = pred.size(1) 12 | 13 | one_hot = torch.zeros_like(pred).scatter(1, gold.view(-1, 1), 1) 14 | one_hot = one_hot * (1 - eps) + (1 - one_hot) * eps / (n_class - 1) 15 | log_prb = F.log_softmax(pred, dim=1) 16 | 17 | loss = -(one_hot * log_prb).sum(dim=1).mean() 18 | else: 19 | loss = F.cross_entropy(pred, gold, reduction='mean') 20 | 21 | return loss 22 | -------------------------------------------------------------------------------- /classification_ModelNet40/utils/progress/LICENSE: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2012 Giorgos Verigakis 2 | # 3 | # Permission to use, copy, modify, and distribute this software for any 4 | # purpose with or without fee is hereby granted, provided that the above 5 | # copyright notice and this permission notice appear in all copies. 6 | # 7 | # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /classification_ScanObjectNN/utils/progress/LICENSE: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2012 Giorgos Verigakis 2 | # 3 | # Permission to use, copy, modify, and distribute this software for any 4 | # purpose with or without fee is hereby granted, provided that the above 5 | # copyright notice and this permission notice appear in all copies. 6 | # 7 | # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /classification_ModelNet40/utils/progress/setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from setuptools import setup 4 | 5 | import progress 6 | 7 | 8 | setup( 9 | name='progress', 10 | version=progress.__version__, 11 | description='Easy to use progress bars', 12 | long_description=open('README.rst').read(), 13 | author='Giorgos Verigakis', 14 | author_email='verigak@gmail.com', 15 | url='http://github.com/verigak/progress/', 16 | license='ISC', 17 | packages=['progress'], 18 | classifiers=[ 19 | 'Environment :: Console', 20 | 'Intended Audience :: Developers', 21 | 'License :: OSI Approved :: ISC License (ISCL)', 22 | 'Programming Language :: Python :: 2.6', 23 | 'Programming Language :: Python :: 2.7', 24 | 'Programming Language :: Python :: 3.3', 25 | 'Programming Language :: Python :: 3.4', 26 | 'Programming Language :: Python :: 3.5', 27 | 'Programming Language :: Python :: 3.6', 28 | ] 29 | ) 30 | -------------------------------------------------------------------------------- /classification_ScanObjectNN/utils/progress/setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from setuptools import setup 4 | 5 | import progress 6 | 7 | 8 | setup( 9 | name='progress', 10 | version=progress.__version__, 11 | description='Easy to use progress bars', 12 | long_description=open('README.rst').read(), 13 | author='Giorgos Verigakis', 14 | author_email='verigak@gmail.com', 15 | url='http://github.com/verigak/progress/', 16 | license='ISC', 17 | packages=['progress'], 18 | classifiers=[ 19 | 'Environment :: Console', 20 | 'Intended Audience :: Developers', 21 | 'License :: OSI Approved :: ISC License (ISCL)', 22 | 'Programming Language :: Python :: 2.6', 23 | 'Programming Language :: Python :: 2.7', 24 | 'Programming Language :: Python :: 3.3', 25 | 'Programming Language :: Python :: 3.4', 26 | 'Programming Language :: Python :: 3.5', 27 | 'Programming Language :: Python :: 3.6', 28 | ] 29 | ) 30 | -------------------------------------------------------------------------------- /pointnet2_ops_lib/pointnet2_ops/_ext-src/include/utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | #define CHECK_CUDA(x) \ 6 | do { \ 7 | AT_ASSERT(x.is_cuda(), #x " must be a CUDA tensor"); \ 8 | } while (0) 9 | 10 | #define CHECK_CONTIGUOUS(x) \ 11 | do { \ 12 | AT_ASSERT(x.is_contiguous(), #x " must be a contiguous tensor"); \ 13 | } while (0) 14 | 15 | #define CHECK_IS_INT(x) \ 16 | do { \ 17 | AT_ASSERT(x.scalar_type() == at::ScalarType::Int, \ 18 | #x " must be an int tensor"); \ 19 | } while (0) 20 | 21 | #define CHECK_IS_FLOAT(x) \ 22 | do { \ 23 | AT_ASSERT(x.scalar_type() == at::ScalarType::Float, \ 24 | #x " must be a float tensor"); \ 25 | } while (0) 26 | -------------------------------------------------------------------------------- /pointnet2_ops_lib/pointnet2_ops/_ext-src/src/ball_query.cpp: -------------------------------------------------------------------------------- 1 | #include "ball_query.h" 2 | #include "utils.h" 3 | 4 | void query_ball_point_kernel_wrapper(int b, int n, int m, float radius, 5 | int nsample, const float *new_xyz, 6 | const float *xyz, int *idx); 7 | 8 | at::Tensor ball_query(at::Tensor new_xyz, at::Tensor xyz, const float radius, 9 | const int nsample) { 10 | CHECK_CONTIGUOUS(new_xyz); 11 | CHECK_CONTIGUOUS(xyz); 12 | CHECK_IS_FLOAT(new_xyz); 13 | CHECK_IS_FLOAT(xyz); 14 | 15 | if (new_xyz.is_cuda()) { 16 | CHECK_CUDA(xyz); 17 | } 18 | 19 | at::Tensor idx = 20 | torch::zeros({new_xyz.size(0), new_xyz.size(1), nsample}, 21 | at::device(new_xyz.device()).dtype(at::ScalarType::Int)); 22 | 23 | if (new_xyz.is_cuda()) { 24 | query_ball_point_kernel_wrapper(xyz.size(0), xyz.size(1), new_xyz.size(1), 25 | radius, nsample, new_xyz.data_ptr(), 26 | xyz.data_ptr(), idx.data_ptr()); 27 | } else { 28 | AT_ASSERT(false, "CPU not supported"); 29 | } 30 | 31 | return idx; 32 | } 33 | -------------------------------------------------------------------------------- /pointnet2_ops_lib/setup.py: -------------------------------------------------------------------------------- 1 | import glob 2 | import os 3 | import os.path as osp 4 | 5 | from setuptools import find_packages, setup 6 | from torch.utils.cpp_extension import BuildExtension, CUDAExtension 7 | 8 | this_dir = osp.dirname(osp.abspath(__file__)) 9 | _ext_src_root = osp.join("pointnet2_ops", "_ext-src") 10 | _ext_sources = glob.glob(osp.join(_ext_src_root, "src", "*.cpp")) + glob.glob( 11 | osp.join(_ext_src_root, "src", "*.cu") 12 | ) 13 | _ext_headers = glob.glob(osp.join(_ext_src_root, "include", "*")) 14 | 15 | requirements = ["torch>=1.4"] 16 | 17 | exec(open(osp.join("pointnet2_ops", "_version.py")).read()) 18 | 19 | os.environ["TORCH_CUDA_ARCH_LIST"] = "3.7+PTX;5.0;6.0;6.1;6.2;7.0;7.5" 20 | setup( 21 | name="pointnet2_ops", 22 | version=__version__, 23 | author="Erik Wijmans", 24 | packages=find_packages(), 25 | install_requires=requirements, 26 | ext_modules=[ 27 | CUDAExtension( 28 | name="pointnet2_ops._ext", 29 | sources=_ext_sources, 30 | extra_compile_args={ 31 | "cxx": ["-O3"], 32 | "nvcc": ["-O3", "-Xfatbin", "-compress-all"], 33 | }, 34 | include_dirs=[osp.join(this_dir, _ext_src_root, "include")], 35 | ) 36 | ], 37 | cmdclass={"build_ext": BuildExtension}, 38 | include_package_data=True, 39 | ) 40 | -------------------------------------------------------------------------------- /pointnet2_ops_lib/pointnet2_ops/_ext-src/include/cuda_utils.h: -------------------------------------------------------------------------------- 1 | #ifndef _CUDA_UTILS_H 2 | #define _CUDA_UTILS_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | #define TOTAL_THREADS 512 14 | 15 | inline int opt_n_threads(int work_size) { 16 | const int pow_2 = std::log(static_cast(work_size)) / std::log(2.0); 17 | 18 | return max(min(1 << pow_2, TOTAL_THREADS), 1); 19 | } 20 | 21 | inline dim3 opt_block_config(int x, int y) { 22 | const int x_threads = opt_n_threads(x); 23 | const int y_threads = 24 | max(min(opt_n_threads(y), TOTAL_THREADS / x_threads), 1); 25 | dim3 block_config(x_threads, y_threads, 1); 26 | 27 | return block_config; 28 | } 29 | 30 | #define CUDA_CHECK_ERRORS() \ 31 | do { \ 32 | cudaError_t err = cudaGetLastError(); \ 33 | if (cudaSuccess != err) { \ 34 | fprintf(stderr, "CUDA kernel failed : %s\n%s at L:%d in %s\n", \ 35 | cudaGetErrorString(err), __PRETTY_FUNCTION__, __LINE__, \ 36 | __FILE__); \ 37 | exit(-1); \ 38 | } \ 39 | } while (0) 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /classification_ModelNet40/utils/progress/progress/spinner.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (c) 2012 Giorgos Verigakis 4 | # 5 | # Permission to use, copy, modify, and distribute this software for any 6 | # purpose with or without fee is hereby granted, provided that the above 7 | # copyright notice and this permission notice appear in all copies. 8 | # 9 | # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | 17 | from __future__ import unicode_literals 18 | from . import Infinite 19 | from .helpers import WriteMixin 20 | 21 | 22 | class Spinner(WriteMixin, Infinite): 23 | message = '' 24 | phases = ('-', '\\', '|', '/') 25 | hide_cursor = True 26 | 27 | def update(self): 28 | i = self.index % len(self.phases) 29 | self.write(self.phases[i]) 30 | 31 | 32 | class PieSpinner(Spinner): 33 | phases = ['◷', '◶', '◵', '◴'] 34 | 35 | 36 | class MoonSpinner(Spinner): 37 | phases = ['◑', '◒', '◐', '◓'] 38 | 39 | 40 | class LineSpinner(Spinner): 41 | phases = ['⎺', '⎻', '⎼', '⎽', '⎼', '⎻'] 42 | 43 | class PixelSpinner(Spinner): 44 | phases = ['⣾','⣷', '⣯', '⣟', '⡿', '⢿', '⣻', '⣽'] 45 | -------------------------------------------------------------------------------- /classification_ScanObjectNN/utils/progress/progress/spinner.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (c) 2012 Giorgos Verigakis 4 | # 5 | # Permission to use, copy, modify, and distribute this software for any 6 | # purpose with or without fee is hereby granted, provided that the above 7 | # copyright notice and this permission notice appear in all copies. 8 | # 9 | # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | 17 | from __future__ import unicode_literals 18 | from . import Infinite 19 | from .helpers import WriteMixin 20 | 21 | 22 | class Spinner(WriteMixin, Infinite): 23 | message = '' 24 | phases = ('-', '\\', '|', '/') 25 | hide_cursor = True 26 | 27 | def update(self): 28 | i = self.index % len(self.phases) 29 | self.write(self.phases[i]) 30 | 31 | 32 | class PieSpinner(Spinner): 33 | phases = ['◷', '◶', '◵', '◴'] 34 | 35 | 36 | class MoonSpinner(Spinner): 37 | phases = ['◑', '◒', '◐', '◓'] 38 | 39 | 40 | class LineSpinner(Spinner): 41 | phases = ['⎺', '⎻', '⎼', '⎽', '⎼', '⎻'] 42 | 43 | class PixelSpinner(Spinner): 44 | phases = ['⣾','⣷', '⣯', '⣟', '⡿', '⢿', '⣻', '⣽'] 45 | -------------------------------------------------------------------------------- /classification_ModelNet40/utils/progress/test_progress.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from __future__ import print_function 4 | 5 | import random 6 | import time 7 | 8 | from progress.bar import (Bar, ChargingBar, FillingSquaresBar, 9 | FillingCirclesBar, IncrementalBar, PixelBar, 10 | ShadyBar) 11 | from progress.spinner import (Spinner, PieSpinner, MoonSpinner, LineSpinner, 12 | PixelSpinner) 13 | from progress.counter import Counter, Countdown, Stack, Pie 14 | 15 | 16 | def sleep(): 17 | t = 0.01 18 | t += t * random.uniform(-0.1, 0.1) # Add some variance 19 | time.sleep(t) 20 | 21 | 22 | for bar_cls in (Bar, ChargingBar, FillingSquaresBar, FillingCirclesBar): 23 | suffix = '%(index)d/%(max)d [%(elapsed)d / %(eta)d / %(eta_td)s]' 24 | bar = bar_cls(bar_cls.__name__, suffix=suffix) 25 | for i in bar.iter(range(200)): 26 | sleep() 27 | 28 | for bar_cls in (IncrementalBar, PixelBar, ShadyBar): 29 | suffix = '%(percent)d%% [%(elapsed_td)s / %(eta)d / %(eta_td)s]' 30 | bar = bar_cls(bar_cls.__name__, suffix=suffix) 31 | for i in bar.iter(range(200)): 32 | sleep() 33 | 34 | for spin in (Spinner, PieSpinner, MoonSpinner, LineSpinner, PixelSpinner): 35 | for i in spin(spin.__name__ + ' ').iter(range(100)): 36 | sleep() 37 | print() 38 | 39 | for singleton in (Counter, Countdown, Stack, Pie): 40 | for i in singleton(singleton.__name__ + ' ').iter(range(100)): 41 | sleep() 42 | print() 43 | 44 | bar = IncrementalBar('Random', suffix='%(index)d') 45 | for i in range(100): 46 | bar.goto(random.randint(0, 100)) 47 | sleep() 48 | bar.finish() 49 | -------------------------------------------------------------------------------- /classification_ScanObjectNN/utils/progress/test_progress.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from __future__ import print_function 4 | 5 | import random 6 | import time 7 | 8 | from progress.bar import (Bar, ChargingBar, FillingSquaresBar, 9 | FillingCirclesBar, IncrementalBar, PixelBar, 10 | ShadyBar) 11 | from progress.spinner import (Spinner, PieSpinner, MoonSpinner, LineSpinner, 12 | PixelSpinner) 13 | from progress.counter import Counter, Countdown, Stack, Pie 14 | 15 | 16 | def sleep(): 17 | t = 0.01 18 | t += t * random.uniform(-0.1, 0.1) # Add some variance 19 | time.sleep(t) 20 | 21 | 22 | for bar_cls in (Bar, ChargingBar, FillingSquaresBar, FillingCirclesBar): 23 | suffix = '%(index)d/%(max)d [%(elapsed)d / %(eta)d / %(eta_td)s]' 24 | bar = bar_cls(bar_cls.__name__, suffix=suffix) 25 | for i in bar.iter(range(200)): 26 | sleep() 27 | 28 | for bar_cls in (IncrementalBar, PixelBar, ShadyBar): 29 | suffix = '%(percent)d%% [%(elapsed_td)s / %(eta)d / %(eta_td)s]' 30 | bar = bar_cls(bar_cls.__name__, suffix=suffix) 31 | for i in bar.iter(range(200)): 32 | sleep() 33 | 34 | for spin in (Spinner, PieSpinner, MoonSpinner, LineSpinner, PixelSpinner): 35 | for i in spin(spin.__name__ + ' ').iter(range(100)): 36 | sleep() 37 | print() 38 | 39 | for singleton in (Counter, Countdown, Stack, Pie): 40 | for i in singleton(singleton.__name__ + ' ').iter(range(100)): 41 | sleep() 42 | print() 43 | 44 | bar = IncrementalBar('Random', suffix='%(index)d') 45 | for i in range(100): 46 | bar.goto(random.randint(0, 100)) 47 | sleep() 48 | bar.finish() 49 | -------------------------------------------------------------------------------- /classification_ModelNet40/utils/progress/progress/counter.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (c) 2012 Giorgos Verigakis 4 | # 5 | # Permission to use, copy, modify, and distribute this software for any 6 | # purpose with or without fee is hereby granted, provided that the above 7 | # copyright notice and this permission notice appear in all copies. 8 | # 9 | # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | 17 | from __future__ import unicode_literals 18 | from . import Infinite, Progress 19 | from .helpers import WriteMixin 20 | 21 | 22 | class Counter(WriteMixin, Infinite): 23 | message = '' 24 | hide_cursor = True 25 | 26 | def update(self): 27 | self.write(str(self.index)) 28 | 29 | 30 | class Countdown(WriteMixin, Progress): 31 | hide_cursor = True 32 | 33 | def update(self): 34 | self.write(str(self.remaining)) 35 | 36 | 37 | class Stack(WriteMixin, Progress): 38 | phases = (' ', '▁', '▂', '▃', '▄', '▅', '▆', '▇', '█') 39 | hide_cursor = True 40 | 41 | def update(self): 42 | nphases = len(self.phases) 43 | i = min(nphases - 1, int(self.progress * nphases)) 44 | self.write(self.phases[i]) 45 | 46 | 47 | class Pie(Stack): 48 | phases = ('○', '◔', '◑', '◕', '●') 49 | -------------------------------------------------------------------------------- /classification_ScanObjectNN/utils/progress/progress/counter.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (c) 2012 Giorgos Verigakis 4 | # 5 | # Permission to use, copy, modify, and distribute this software for any 6 | # purpose with or without fee is hereby granted, provided that the above 7 | # copyright notice and this permission notice appear in all copies. 8 | # 9 | # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | 17 | from __future__ import unicode_literals 18 | from . import Infinite, Progress 19 | from .helpers import WriteMixin 20 | 21 | 22 | class Counter(WriteMixin, Infinite): 23 | message = '' 24 | hide_cursor = True 25 | 26 | def update(self): 27 | self.write(str(self.index)) 28 | 29 | 30 | class Countdown(WriteMixin, Progress): 31 | hide_cursor = True 32 | 33 | def update(self): 34 | self.write(str(self.remaining)) 35 | 36 | 37 | class Stack(WriteMixin, Progress): 38 | phases = (' ', '▁', '▂', '▃', '▄', '▅', '▆', '▇', '█') 39 | hide_cursor = True 40 | 41 | def update(self): 42 | nphases = len(self.phases) 43 | i = min(nphases - 1, int(self.progress * nphases)) 44 | self.write(self.phases[i]) 45 | 46 | 47 | class Pie(Stack): 48 | phases = ('○', '◔', '◑', '◕', '●') 49 | -------------------------------------------------------------------------------- /pointnet2_ops_lib/pointnet2_ops/_ext-src/src/ball_query_gpu.cu: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "cuda_utils.h" 6 | 7 | // input: new_xyz(b, m, 3) xyz(b, n, 3) 8 | // output: idx(b, m, nsample) 9 | __global__ void query_ball_point_kernel(int b, int n, int m, float radius, 10 | int nsample, 11 | const float *__restrict__ new_xyz, 12 | const float *__restrict__ xyz, 13 | int *__restrict__ idx) { 14 | int batch_index = blockIdx.x; 15 | xyz += batch_index * n * 3; 16 | new_xyz += batch_index * m * 3; 17 | idx += m * nsample * batch_index; 18 | 19 | int index = threadIdx.x; 20 | int stride = blockDim.x; 21 | 22 | float radius2 = radius * radius; 23 | for (int j = index; j < m; j += stride) { 24 | float new_x = new_xyz[j * 3 + 0]; 25 | float new_y = new_xyz[j * 3 + 1]; 26 | float new_z = new_xyz[j * 3 + 2]; 27 | for (int k = 0, cnt = 0; k < n && cnt < nsample; ++k) { 28 | float x = xyz[k * 3 + 0]; 29 | float y = xyz[k * 3 + 1]; 30 | float z = xyz[k * 3 + 2]; 31 | float d2 = (new_x - x) * (new_x - x) + (new_y - y) * (new_y - y) + 32 | (new_z - z) * (new_z - z); 33 | if (d2 < radius2) { 34 | if (cnt == 0) { 35 | for (int l = 0; l < nsample; ++l) { 36 | idx[j * nsample + l] = k; 37 | } 38 | } 39 | idx[j * nsample + cnt] = k; 40 | ++cnt; 41 | } 42 | } 43 | } 44 | } 45 | 46 | void query_ball_point_kernel_wrapper(int b, int n, int m, float radius, 47 | int nsample, const float *new_xyz, 48 | const float *xyz, int *idx) { 49 | cudaStream_t stream = at::cuda::getCurrentCUDAStream(); 50 | query_ball_point_kernel<<>>( 51 | b, n, m, radius, nsample, new_xyz, xyz, idx); 52 | 53 | CUDA_CHECK_ERRORS(); 54 | } 55 | -------------------------------------------------------------------------------- /pointnet2_ops_lib/pointnet2_ops/_ext-src/src/group_points.cpp: -------------------------------------------------------------------------------- 1 | #include "group_points.h" 2 | #include "utils.h" 3 | 4 | void group_points_kernel_wrapper(int b, int c, int n, int npoints, int nsample, 5 | const float *points, const int *idx, 6 | float *out); 7 | 8 | void group_points_grad_kernel_wrapper(int b, int c, int n, int npoints, 9 | int nsample, const float *grad_out, 10 | const int *idx, float *grad_points); 11 | 12 | at::Tensor group_points(at::Tensor points, at::Tensor idx) { 13 | CHECK_CONTIGUOUS(points); 14 | CHECK_CONTIGUOUS(idx); 15 | CHECK_IS_FLOAT(points); 16 | CHECK_IS_INT(idx); 17 | 18 | if (points.is_cuda()) { 19 | CHECK_CUDA(idx); 20 | } 21 | 22 | at::Tensor output = 23 | torch::zeros({points.size(0), points.size(1), idx.size(1), idx.size(2)}, 24 | at::device(points.device()).dtype(at::ScalarType::Float)); 25 | 26 | if (points.is_cuda()) { 27 | group_points_kernel_wrapper(points.size(0), points.size(1), points.size(2), 28 | idx.size(1), idx.size(2), 29 | points.data_ptr(), idx.data_ptr(), 30 | output.data_ptr()); 31 | } else { 32 | AT_ASSERT(false, "CPU not supported"); 33 | } 34 | 35 | return output; 36 | } 37 | 38 | at::Tensor group_points_grad(at::Tensor grad_out, at::Tensor idx, const int n) { 39 | CHECK_CONTIGUOUS(grad_out); 40 | CHECK_CONTIGUOUS(idx); 41 | CHECK_IS_FLOAT(grad_out); 42 | CHECK_IS_INT(idx); 43 | 44 | if (grad_out.is_cuda()) { 45 | CHECK_CUDA(idx); 46 | } 47 | 48 | at::Tensor output = 49 | torch::zeros({grad_out.size(0), grad_out.size(1), n}, 50 | at::device(grad_out.device()).dtype(at::ScalarType::Float)); 51 | 52 | if (grad_out.is_cuda()) { 53 | group_points_grad_kernel_wrapper( 54 | grad_out.size(0), grad_out.size(1), n, idx.size(1), idx.size(2), 55 | grad_out.data_ptr(), idx.data_ptr(), 56 | output.data_ptr()); 57 | } else { 58 | AT_ASSERT(false, "CPU not supported"); 59 | } 60 | 61 | return output; 62 | } 63 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | .idea 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | .DS_Store 131 | -------------------------------------------------------------------------------- /part_segmentation/util/util.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import torch 3 | import torch.nn.functional as F 4 | 5 | 6 | def cal_loss(pred, gold, smoothing=True): 7 | ''' Calculate cross entropy loss, apply label smoothing if needed. ''' 8 | 9 | gold = gold.contiguous().view(-1) # gold is the groudtruth label in the dataloader 10 | 11 | if smoothing: 12 | eps = 0.2 13 | n_class = pred.size(1) # the number of feature_dim of the ouput, which is output channels 14 | 15 | one_hot = torch.zeros_like(pred).scatter(1, gold.view(-1, 1), 1) 16 | one_hot = one_hot * (1 - eps) + (1 - one_hot) * eps / (n_class - 1) 17 | log_prb = F.log_softmax(pred, dim=1) 18 | 19 | loss = -(one_hot * log_prb).sum(dim=1).mean() 20 | else: 21 | loss = F.cross_entropy(pred, gold, reduction='mean') 22 | 23 | return loss 24 | 25 | 26 | # create a file and write the text into it: 27 | class IOStream(): 28 | def __init__(self, path): 29 | self.f = open(path, 'a') 30 | 31 | def cprint(self, text): 32 | print(text) 33 | self.f.write(text+'\n') 34 | self.f.flush() 35 | 36 | def close(self): 37 | self.f.close() 38 | 39 | 40 | def to_categorical(y, num_classes): 41 | """ 1-hot encodes a tensor """ 42 | new_y = torch.eye(num_classes)[y.cpu().data.numpy(),] 43 | if (y.is_cuda): 44 | return new_y.cuda(non_blocking=True) 45 | return new_y 46 | 47 | 48 | def compute_overall_iou(pred, target, num_classes): 49 | shape_ious = [] 50 | pred = pred.max(dim=2)[1] # (batch_size, num_points) the pred_class_idx of each point in each sample 51 | pred_np = pred.cpu().data.numpy() 52 | 53 | target_np = target.cpu().data.numpy() 54 | for shape_idx in range(pred.size(0)): # sample_idx 55 | part_ious = [] 56 | for part in range(num_classes): # class_idx! no matter which category, only consider all part_classes of all categories, check all 50 classes 57 | # for target, each point has a class no matter which category owns this point! also 50 classes!!! 58 | # only return 1 when both belongs to this class, which means correct: 59 | I = np.sum(np.logical_and(pred_np[shape_idx] == part, target_np[shape_idx] == part)) 60 | # always return 1 when either is belongs to this class: 61 | U = np.sum(np.logical_or(pred_np[shape_idx] == part, target_np[shape_idx] == part)) 62 | 63 | F = np.sum(target_np[shape_idx] == part) 64 | 65 | if F != 0: 66 | iou = I / float(U) # iou across all points for this class 67 | part_ious.append(iou) # append the iou of this class 68 | shape_ious.append(np.mean(part_ious)) # each time append an average iou across all classes of this sample (sample_level!) 69 | return shape_ious # [batch_size] 70 | -------------------------------------------------------------------------------- /classification_ScanObjectNN/ScanObjectNN.py: -------------------------------------------------------------------------------- 1 | """ 2 | ScanObjectNN download: http://103.24.77.34/scanobjectnn/h5_files.zip 3 | """ 4 | 5 | import os 6 | import sys 7 | import glob 8 | import h5py 9 | import numpy as np 10 | from torch.utils.data import Dataset 11 | 12 | os.environ["HDF5_USE_FILE_LOCKING"] = "FALSE" 13 | 14 | 15 | import os 16 | 17 | def download(): 18 | BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 19 | DATA_DIR = os.path.join(BASE_DIR, 'data') 20 | H5_DIR = os.path.join(DATA_DIR, 'h5_files') 21 | 22 | expected_file = os.path.join(H5_DIR, "main_split", "test_objectdataset_augmentedrot_scale75.h5") 23 | 24 | if os.path.exists(expected_file): 25 | print("Dataset already downloaded. Skipping download.") 26 | return 27 | 28 | if not os.path.exists(H5_DIR): 29 | os.makedirs(H5_DIR) 30 | 31 | www = 'https://github.com/ma-xu/pointMLP-pytorch/releases/download/dataset/h5_files.zip' 32 | zipfile = os.path.basename(www) 33 | 34 | os.system(f'wget {www} --no-check-certificate') 35 | os.system(f'unzip {zipfile} -d {H5_DIR}') 36 | os.system(f'rm {zipfile}') 37 | os.system(f'rm -rf "{os.path.join(H5_DIR, "main_files")}"') 38 | os.system(f'rm -rf "{os.path.join(H5_DIR, "__MACOSX")}"') 39 | 40 | print("Download and extraction complete.") 41 | 42 | 43 | def load_scanobjectnn_data(partition): 44 | download() 45 | BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 46 | all_data = [] 47 | all_label = [] 48 | 49 | h5_name = BASE_DIR + '/data/h5_files/main_split/' + partition + '_objectdataset_augmentedrot_scale75.h5' 50 | f = h5py.File(h5_name, mode="r") 51 | data = f['data'][:].astype('float32') 52 | label = f['label'][:].astype('int64') 53 | f.close() 54 | all_data.append(data) 55 | all_label.append(label) 56 | all_data = np.concatenate(all_data, axis=0) 57 | all_label = np.concatenate(all_label, axis=0) 58 | return all_data, all_label 59 | 60 | 61 | def translate_pointcloud(pointcloud): 62 | xyz1 = np.random.uniform(low=2. / 3., high=3. / 2., size=[3]) 63 | xyz2 = np.random.uniform(low=-0.2, high=0.2, size=[3]) 64 | 65 | translated_pointcloud = np.add(np.multiply(pointcloud, xyz1), xyz2).astype('float32') 66 | return translated_pointcloud 67 | 68 | 69 | class ScanObjectNN(Dataset): 70 | def __init__(self, num_points, partition='training'): 71 | self.data, self.label = load_scanobjectnn_data(partition) 72 | self.num_points = num_points 73 | self.partition = partition 74 | 75 | def __getitem__(self, item): 76 | pointcloud = self.data[item][:self.num_points] 77 | label = self.label[item] 78 | if self.partition == 'training': 79 | pointcloud = translate_pointcloud(pointcloud) 80 | np.random.shuffle(pointcloud) 81 | return pointcloud, label 82 | 83 | def __len__(self): 84 | return self.data.shape[0] 85 | 86 | 87 | if __name__ == '__main__': 88 | train = ScanObjectNN(1024) 89 | test = ScanObjectNN(1024, 'test') 90 | for data, label in train: 91 | print(data.shape) 92 | print(label) 93 | -------------------------------------------------------------------------------- /classification_ModelNet40/utils/progress/progress/bar.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (c) 2012 Giorgos Verigakis 4 | # 5 | # Permission to use, copy, modify, and distribute this software for any 6 | # purpose with or without fee is hereby granted, provided that the above 7 | # copyright notice and this permission notice appear in all copies. 8 | # 9 | # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | 17 | from __future__ import unicode_literals 18 | from . import Progress 19 | from .helpers import WritelnMixin 20 | 21 | 22 | class Bar(WritelnMixin, Progress): 23 | width = 32 24 | message = '' 25 | suffix = '%(index)d/%(max)d' 26 | bar_prefix = ' |' 27 | bar_suffix = '| ' 28 | empty_fill = ' ' 29 | fill = '#' 30 | hide_cursor = True 31 | 32 | def update(self): 33 | filled_length = int(self.width * self.progress) 34 | empty_length = self.width - filled_length 35 | 36 | message = self.message % self 37 | bar = self.fill * filled_length 38 | empty = self.empty_fill * empty_length 39 | suffix = self.suffix % self 40 | line = ''.join([message, self.bar_prefix, bar, empty, self.bar_suffix, 41 | suffix]) 42 | self.writeln(line) 43 | 44 | 45 | class ChargingBar(Bar): 46 | suffix = '%(percent)d%%' 47 | bar_prefix = ' ' 48 | bar_suffix = ' ' 49 | empty_fill = '∙' 50 | fill = '█' 51 | 52 | 53 | class FillingSquaresBar(ChargingBar): 54 | empty_fill = '▢' 55 | fill = '▣' 56 | 57 | 58 | class FillingCirclesBar(ChargingBar): 59 | empty_fill = '◯' 60 | fill = '◉' 61 | 62 | 63 | class IncrementalBar(Bar): 64 | phases = (' ', '▏', '▎', '▍', '▌', '▋', '▊', '▉', '█') 65 | 66 | def update(self): 67 | nphases = len(self.phases) 68 | filled_len = self.width * self.progress 69 | nfull = int(filled_len) # Number of full chars 70 | phase = int((filled_len - nfull) * nphases) # Phase of last char 71 | nempty = self.width - nfull # Number of empty chars 72 | 73 | message = self.message % self 74 | bar = self.phases[-1] * nfull 75 | current = self.phases[phase] if phase > 0 else '' 76 | empty = self.empty_fill * max(0, nempty - len(current)) 77 | suffix = self.suffix % self 78 | line = ''.join([message, self.bar_prefix, bar, current, empty, 79 | self.bar_suffix, suffix]) 80 | self.writeln(line) 81 | 82 | 83 | class PixelBar(IncrementalBar): 84 | phases = ('⡀', '⡄', '⡆', '⡇', '⣇', '⣧', '⣷', '⣿') 85 | 86 | 87 | class ShadyBar(IncrementalBar): 88 | phases = (' ', '░', '▒', '▓', '█') 89 | -------------------------------------------------------------------------------- /classification_ScanObjectNN/utils/progress/progress/bar.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (c) 2012 Giorgos Verigakis 4 | # 5 | # Permission to use, copy, modify, and distribute this software for any 6 | # purpose with or without fee is hereby granted, provided that the above 7 | # copyright notice and this permission notice appear in all copies. 8 | # 9 | # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | 17 | from __future__ import unicode_literals 18 | from . import Progress 19 | from .helpers import WritelnMixin 20 | 21 | 22 | class Bar(WritelnMixin, Progress): 23 | width = 32 24 | message = '' 25 | suffix = '%(index)d/%(max)d' 26 | bar_prefix = ' |' 27 | bar_suffix = '| ' 28 | empty_fill = ' ' 29 | fill = '#' 30 | hide_cursor = True 31 | 32 | def update(self): 33 | filled_length = int(self.width * self.progress) 34 | empty_length = self.width - filled_length 35 | 36 | message = self.message % self 37 | bar = self.fill * filled_length 38 | empty = self.empty_fill * empty_length 39 | suffix = self.suffix % self 40 | line = ''.join([message, self.bar_prefix, bar, empty, self.bar_suffix, 41 | suffix]) 42 | self.writeln(line) 43 | 44 | 45 | class ChargingBar(Bar): 46 | suffix = '%(percent)d%%' 47 | bar_prefix = ' ' 48 | bar_suffix = ' ' 49 | empty_fill = '∙' 50 | fill = '█' 51 | 52 | 53 | class FillingSquaresBar(ChargingBar): 54 | empty_fill = '▢' 55 | fill = '▣' 56 | 57 | 58 | class FillingCirclesBar(ChargingBar): 59 | empty_fill = '◯' 60 | fill = '◉' 61 | 62 | 63 | class IncrementalBar(Bar): 64 | phases = (' ', '▏', '▎', '▍', '▌', '▋', '▊', '▉', '█') 65 | 66 | def update(self): 67 | nphases = len(self.phases) 68 | filled_len = self.width * self.progress 69 | nfull = int(filled_len) # Number of full chars 70 | phase = int((filled_len - nfull) * nphases) # Phase of last char 71 | nempty = self.width - nfull # Number of empty chars 72 | 73 | message = self.message % self 74 | bar = self.phases[-1] * nfull 75 | current = self.phases[phase] if phase > 0 else '' 76 | empty = self.empty_fill * max(0, nempty - len(current)) 77 | suffix = self.suffix % self 78 | line = ''.join([message, self.bar_prefix, bar, current, empty, 79 | self.bar_suffix, suffix]) 80 | self.writeln(line) 81 | 82 | 83 | class PixelBar(IncrementalBar): 84 | phases = ('⡀', '⡄', '⡆', '⡇', '⣇', '⣧', '⣷', '⣿') 85 | 86 | 87 | class ShadyBar(IncrementalBar): 88 | phases = (' ', '░', '▒', '▓', '█') 89 | -------------------------------------------------------------------------------- /pointnet2_ops_lib/pointnet2_ops/_ext-src/src/group_points_gpu.cu: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "cuda_utils.h" 5 | 6 | // input: points(b, c, n) idx(b, npoints, nsample) 7 | // output: out(b, c, npoints, nsample) 8 | __global__ void group_points_kernel(int b, int c, int n, int npoints, 9 | int nsample, 10 | const float *__restrict__ points, 11 | const int *__restrict__ idx, 12 | float *__restrict__ out) { 13 | int batch_index = blockIdx.x; 14 | points += batch_index * n * c; 15 | idx += batch_index * npoints * nsample; 16 | out += batch_index * npoints * nsample * c; 17 | 18 | const int index = threadIdx.y * blockDim.x + threadIdx.x; 19 | const int stride = blockDim.y * blockDim.x; 20 | for (int i = index; i < c * npoints; i += stride) { 21 | const int l = i / npoints; 22 | const int j = i % npoints; 23 | for (int k = 0; k < nsample; ++k) { 24 | int ii = idx[j * nsample + k]; 25 | out[(l * npoints + j) * nsample + k] = points[l * n + ii]; 26 | } 27 | } 28 | } 29 | 30 | void group_points_kernel_wrapper(int b, int c, int n, int npoints, int nsample, 31 | const float *points, const int *idx, 32 | float *out) { 33 | cudaStream_t stream = at::cuda::getCurrentCUDAStream(); 34 | 35 | group_points_kernel<<>>( 36 | b, c, n, npoints, nsample, points, idx, out); 37 | 38 | CUDA_CHECK_ERRORS(); 39 | } 40 | 41 | // input: grad_out(b, c, npoints, nsample), idx(b, npoints, nsample) 42 | // output: grad_points(b, c, n) 43 | __global__ void group_points_grad_kernel(int b, int c, int n, int npoints, 44 | int nsample, 45 | const float *__restrict__ grad_out, 46 | const int *__restrict__ idx, 47 | float *__restrict__ grad_points) { 48 | int batch_index = blockIdx.x; 49 | grad_out += batch_index * npoints * nsample * c; 50 | idx += batch_index * npoints * nsample; 51 | grad_points += batch_index * n * c; 52 | 53 | const int index = threadIdx.y * blockDim.x + threadIdx.x; 54 | const int stride = blockDim.y * blockDim.x; 55 | for (int i = index; i < c * npoints; i += stride) { 56 | const int l = i / npoints; 57 | const int j = i % npoints; 58 | for (int k = 0; k < nsample; ++k) { 59 | int ii = idx[j * nsample + k]; 60 | atomicAdd(grad_points + l * n + ii, 61 | grad_out[(l * npoints + j) * nsample + k]); 62 | } 63 | } 64 | } 65 | 66 | void group_points_grad_kernel_wrapper(int b, int c, int n, int npoints, 67 | int nsample, const float *grad_out, 68 | const int *idx, float *grad_points) { 69 | cudaStream_t stream = at::cuda::getCurrentCUDAStream(); 70 | 71 | group_points_grad_kernel<<>>( 72 | b, c, n, npoints, nsample, grad_out, idx, grad_points); 73 | 74 | CUDA_CHECK_ERRORS(); 75 | } 76 | -------------------------------------------------------------------------------- /classification_ModelNet40/utils/progress/progress/helpers.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2012 Giorgos Verigakis 2 | # 3 | # Permission to use, copy, modify, and distribute this software for any 4 | # purpose with or without fee is hereby granted, provided that the above 5 | # copyright notice and this permission notice appear in all copies. 6 | # 7 | # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | from __future__ import print_function 16 | 17 | 18 | HIDE_CURSOR = '\x1b[?25l' 19 | SHOW_CURSOR = '\x1b[?25h' 20 | 21 | 22 | class WriteMixin(object): 23 | hide_cursor = False 24 | 25 | def __init__(self, message=None, **kwargs): 26 | super(WriteMixin, self).__init__(**kwargs) 27 | self._width = 0 28 | if message: 29 | self.message = message 30 | 31 | if self.file.isatty(): 32 | if self.hide_cursor: 33 | print(HIDE_CURSOR, end='', file=self.file) 34 | print(self.message, end='', file=self.file) 35 | self.file.flush() 36 | 37 | def write(self, s): 38 | if self.file.isatty(): 39 | b = '\b' * self._width 40 | c = s.ljust(self._width) 41 | print(b + c, end='', file=self.file) 42 | self._width = max(self._width, len(s)) 43 | self.file.flush() 44 | 45 | def finish(self): 46 | if self.file.isatty() and self.hide_cursor: 47 | print(SHOW_CURSOR, end='', file=self.file) 48 | 49 | 50 | class WritelnMixin(object): 51 | hide_cursor = False 52 | 53 | def __init__(self, message=None, **kwargs): 54 | super(WritelnMixin, self).__init__(**kwargs) 55 | if message: 56 | self.message = message 57 | 58 | if self.file.isatty() and self.hide_cursor: 59 | print(HIDE_CURSOR, end='', file=self.file) 60 | 61 | def clearln(self): 62 | if self.file.isatty(): 63 | print('\r\x1b[K', end='', file=self.file) 64 | 65 | def writeln(self, line): 66 | if self.file.isatty(): 67 | self.clearln() 68 | print(line, end='', file=self.file) 69 | self.file.flush() 70 | 71 | def finish(self): 72 | if self.file.isatty(): 73 | print(file=self.file) 74 | if self.hide_cursor: 75 | print(SHOW_CURSOR, end='', file=self.file) 76 | 77 | 78 | from signal import signal, SIGINT 79 | from sys import exit 80 | 81 | 82 | class SigIntMixin(object): 83 | """Registers a signal handler that calls finish on SIGINT""" 84 | 85 | def __init__(self, *args, **kwargs): 86 | super(SigIntMixin, self).__init__(*args, **kwargs) 87 | signal(SIGINT, self._sigint_handler) 88 | 89 | def _sigint_handler(self, signum, frame): 90 | self.finish() 91 | exit(0) 92 | -------------------------------------------------------------------------------- /classification_ScanObjectNN/utils/progress/progress/helpers.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2012 Giorgos Verigakis 2 | # 3 | # Permission to use, copy, modify, and distribute this software for any 4 | # purpose with or without fee is hereby granted, provided that the above 5 | # copyright notice and this permission notice appear in all copies. 6 | # 7 | # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | from __future__ import print_function 16 | 17 | 18 | HIDE_CURSOR = '\x1b[?25l' 19 | SHOW_CURSOR = '\x1b[?25h' 20 | 21 | 22 | class WriteMixin(object): 23 | hide_cursor = False 24 | 25 | def __init__(self, message=None, **kwargs): 26 | super(WriteMixin, self).__init__(**kwargs) 27 | self._width = 0 28 | if message: 29 | self.message = message 30 | 31 | if self.file.isatty(): 32 | if self.hide_cursor: 33 | print(HIDE_CURSOR, end='', file=self.file) 34 | print(self.message, end='', file=self.file) 35 | self.file.flush() 36 | 37 | def write(self, s): 38 | if self.file.isatty(): 39 | b = '\b' * self._width 40 | c = s.ljust(self._width) 41 | print(b + c, end='', file=self.file) 42 | self._width = max(self._width, len(s)) 43 | self.file.flush() 44 | 45 | def finish(self): 46 | if self.file.isatty() and self.hide_cursor: 47 | print(SHOW_CURSOR, end='', file=self.file) 48 | 49 | 50 | class WritelnMixin(object): 51 | hide_cursor = False 52 | 53 | def __init__(self, message=None, **kwargs): 54 | super(WritelnMixin, self).__init__(**kwargs) 55 | if message: 56 | self.message = message 57 | 58 | if self.file.isatty() and self.hide_cursor: 59 | print(HIDE_CURSOR, end='', file=self.file) 60 | 61 | def clearln(self): 62 | if self.file.isatty(): 63 | print('\r\x1b[K', end='', file=self.file) 64 | 65 | def writeln(self, line): 66 | if self.file.isatty(): 67 | self.clearln() 68 | print(line, end='', file=self.file) 69 | self.file.flush() 70 | 71 | def finish(self): 72 | if self.file.isatty(): 73 | print(file=self.file) 74 | if self.hide_cursor: 75 | print(SHOW_CURSOR, end='', file=self.file) 76 | 77 | 78 | from signal import signal, SIGINT 79 | from sys import exit 80 | 81 | 82 | class SigIntMixin(object): 83 | """Registers a signal handler that calls finish on SIGINT""" 84 | 85 | def __init__(self, *args, **kwargs): 86 | super(SigIntMixin, self).__init__(*args, **kwargs) 87 | signal(SIGINT, self._sigint_handler) 88 | 89 | def _sigint_handler(self, signum, frame): 90 | self.finish() 91 | exit(0) 92 | -------------------------------------------------------------------------------- /pointnet2_ops_lib/pointnet2_ops/_ext-src/src/sampling.cpp: -------------------------------------------------------------------------------- 1 | #include "sampling.h" 2 | #include "utils.h" 3 | 4 | void gather_points_kernel_wrapper(int b, int c, int n, int npoints, 5 | const float *points, const int *idx, 6 | float *out); 7 | void gather_points_grad_kernel_wrapper(int b, int c, int n, int npoints, 8 | const float *grad_out, const int *idx, 9 | float *grad_points); 10 | 11 | void furthest_point_sampling_kernel_wrapper(int b, int n, int m, 12 | const float *dataset, float *temp, 13 | int *idxs); 14 | 15 | at::Tensor gather_points(at::Tensor points, at::Tensor idx) { 16 | CHECK_CONTIGUOUS(points); 17 | CHECK_CONTIGUOUS(idx); 18 | CHECK_IS_FLOAT(points); 19 | CHECK_IS_INT(idx); 20 | 21 | if (points.is_cuda()) { 22 | CHECK_CUDA(idx); 23 | } 24 | 25 | at::Tensor output = 26 | torch::zeros({points.size(0), points.size(1), idx.size(1)}, 27 | at::device(points.device()).dtype(at::ScalarType::Float)); 28 | 29 | if (points.is_cuda()) { 30 | gather_points_kernel_wrapper(points.size(0), points.size(1), points.size(2), 31 | idx.size(1), points.data_ptr(), 32 | idx.data_ptr(), output.data_ptr()); 33 | } else { 34 | AT_ASSERT(false, "CPU not supported"); 35 | } 36 | 37 | return output; 38 | } 39 | 40 | at::Tensor gather_points_grad(at::Tensor grad_out, at::Tensor idx, 41 | const int n) { 42 | CHECK_CONTIGUOUS(grad_out); 43 | CHECK_CONTIGUOUS(idx); 44 | CHECK_IS_FLOAT(grad_out); 45 | CHECK_IS_INT(idx); 46 | 47 | if (grad_out.is_cuda()) { 48 | CHECK_CUDA(idx); 49 | } 50 | 51 | at::Tensor output = 52 | torch::zeros({grad_out.size(0), grad_out.size(1), n}, 53 | at::device(grad_out.device()).dtype(at::ScalarType::Float)); 54 | 55 | if (grad_out.is_cuda()) { 56 | gather_points_grad_kernel_wrapper(grad_out.size(0), grad_out.size(1), n, 57 | idx.size(1), grad_out.data_ptr(), 58 | idx.data_ptr(), 59 | output.data_ptr()); 60 | } else { 61 | AT_ASSERT(false, "CPU not supported"); 62 | } 63 | 64 | return output; 65 | } 66 | at::Tensor furthest_point_sampling(at::Tensor points, const int nsamples) { 67 | CHECK_CONTIGUOUS(points); 68 | CHECK_IS_FLOAT(points); 69 | 70 | at::Tensor output = 71 | torch::zeros({points.size(0), nsamples}, 72 | at::device(points.device()).dtype(at::ScalarType::Int)); 73 | 74 | at::Tensor tmp = 75 | torch::full({points.size(0), points.size(1)}, 1e10, 76 | at::device(points.device()).dtype(at::ScalarType::Float)); 77 | 78 | if (points.is_cuda()) { 79 | furthest_point_sampling_kernel_wrapper( 80 | points.size(0), points.size(1), nsamples, points.data_ptr(), 81 | tmp.data_ptr(), output.data_ptr()); 82 | } else { 83 | AT_ASSERT(false, "CPU not supported"); 84 | } 85 | 86 | return output; 87 | } 88 | -------------------------------------------------------------------------------- /classification_ModelNet40/utils/progress/README.rst: -------------------------------------------------------------------------------- 1 | Easy progress reporting for Python 2 | ================================== 3 | 4 | |pypi| 5 | 6 | |demo| 7 | 8 | .. |pypi| image:: https://img.shields.io/pypi/v/progress.svg 9 | .. |demo| image:: https://raw.github.com/verigak/progress/master/demo.gif 10 | :alt: Demo 11 | 12 | Bars 13 | ---- 14 | 15 | There are 7 progress bars to choose from: 16 | 17 | - ``Bar`` 18 | - ``ChargingBar`` 19 | - ``FillingSquaresBar`` 20 | - ``FillingCirclesBar`` 21 | - ``IncrementalBar`` 22 | - ``PixelBar`` 23 | - ``ShadyBar`` 24 | 25 | To use them, just call ``next`` to advance and ``finish`` to finish: 26 | 27 | .. code-block:: python 28 | 29 | from progress.bar import Bar 30 | 31 | bar = Bar('Processing', max=20) 32 | for i in range(20): 33 | # Do some work 34 | bar.next() 35 | bar.finish() 36 | 37 | The result will be a bar like the following: :: 38 | 39 | Processing |############# | 42/100 40 | 41 | To simplify the common case where the work is done in an iterator, you can 42 | use the ``iter`` method: 43 | 44 | .. code-block:: python 45 | 46 | for i in Bar('Processing').iter(it): 47 | # Do some work 48 | 49 | Progress bars are very customizable, you can change their width, their fill 50 | character, their suffix and more: 51 | 52 | .. code-block:: python 53 | 54 | bar = Bar('Loading', fill='@', suffix='%(percent)d%%') 55 | 56 | This will produce a bar like the following: :: 57 | 58 | Loading |@@@@@@@@@@@@@ | 42% 59 | 60 | You can use a number of template arguments in ``message`` and ``suffix``: 61 | 62 | ========== ================================ 63 | Name Value 64 | ========== ================================ 65 | index current value 66 | max maximum value 67 | remaining max - index 68 | progress index / max 69 | percent progress * 100 70 | avg simple moving average time per item (in seconds) 71 | elapsed elapsed time in seconds 72 | elapsed_td elapsed as a timedelta (useful for printing as a string) 73 | eta avg * remaining 74 | eta_td eta as a timedelta (useful for printing as a string) 75 | ========== ================================ 76 | 77 | Instead of passing all configuration options on instatiation, you can create 78 | your custom subclass: 79 | 80 | .. code-block:: python 81 | 82 | class FancyBar(Bar): 83 | message = 'Loading' 84 | fill = '*' 85 | suffix = '%(percent).1f%% - %(eta)ds' 86 | 87 | You can also override any of the arguments or create your own: 88 | 89 | .. code-block:: python 90 | 91 | class SlowBar(Bar): 92 | suffix = '%(remaining_hours)d hours remaining' 93 | @property 94 | def remaining_hours(self): 95 | return self.eta // 3600 96 | 97 | 98 | Spinners 99 | ======== 100 | 101 | For actions with an unknown number of steps you can use a spinner: 102 | 103 | .. code-block:: python 104 | 105 | from progress.spinner import Spinner 106 | 107 | spinner = Spinner('Loading ') 108 | while state != 'FINISHED': 109 | # Do some work 110 | spinner.next() 111 | 112 | There are 5 predefined spinners: 113 | 114 | - ``Spinner`` 115 | - ``PieSpinner`` 116 | - ``MoonSpinner`` 117 | - ``LineSpinner`` 118 | - ``PixelSpinner`` 119 | 120 | 121 | Other 122 | ===== 123 | 124 | There are a number of other classes available too, please check the source or 125 | subclass one of them to create your own. 126 | 127 | 128 | License 129 | ======= 130 | 131 | progress is licensed under ISC 132 | -------------------------------------------------------------------------------- /classification_ScanObjectNN/utils/progress/README.rst: -------------------------------------------------------------------------------- 1 | Easy progress reporting for Python 2 | ================================== 3 | 4 | |pypi| 5 | 6 | |demo| 7 | 8 | .. |pypi| image:: https://img.shields.io/pypi/v/progress.svg 9 | .. |demo| image:: https://raw.github.com/verigak/progress/master/demo.gif 10 | :alt: Demo 11 | 12 | Bars 13 | ---- 14 | 15 | There are 7 progress bars to choose from: 16 | 17 | - ``Bar`` 18 | - ``ChargingBar`` 19 | - ``FillingSquaresBar`` 20 | - ``FillingCirclesBar`` 21 | - ``IncrementalBar`` 22 | - ``PixelBar`` 23 | - ``ShadyBar`` 24 | 25 | To use them, just call ``next`` to advance and ``finish`` to finish: 26 | 27 | .. code-block:: python 28 | 29 | from progress.bar import Bar 30 | 31 | bar = Bar('Processing', max=20) 32 | for i in range(20): 33 | # Do some work 34 | bar.next() 35 | bar.finish() 36 | 37 | The result will be a bar like the following: :: 38 | 39 | Processing |############# | 42/100 40 | 41 | To simplify the common case where the work is done in an iterator, you can 42 | use the ``iter`` method: 43 | 44 | .. code-block:: python 45 | 46 | for i in Bar('Processing').iter(it): 47 | # Do some work 48 | 49 | Progress bars are very customizable, you can change their width, their fill 50 | character, their suffix and more: 51 | 52 | .. code-block:: python 53 | 54 | bar = Bar('Loading', fill='@', suffix='%(percent)d%%') 55 | 56 | This will produce a bar like the following: :: 57 | 58 | Loading |@@@@@@@@@@@@@ | 42% 59 | 60 | You can use a number of template arguments in ``message`` and ``suffix``: 61 | 62 | ========== ================================ 63 | Name Value 64 | ========== ================================ 65 | index current value 66 | max maximum value 67 | remaining max - index 68 | progress index / max 69 | percent progress * 100 70 | avg simple moving average time per item (in seconds) 71 | elapsed elapsed time in seconds 72 | elapsed_td elapsed as a timedelta (useful for printing as a string) 73 | eta avg * remaining 74 | eta_td eta as a timedelta (useful for printing as a string) 75 | ========== ================================ 76 | 77 | Instead of passing all configuration options on instatiation, you can create 78 | your custom subclass: 79 | 80 | .. code-block:: python 81 | 82 | class FancyBar(Bar): 83 | message = 'Loading' 84 | fill = '*' 85 | suffix = '%(percent).1f%% - %(eta)ds' 86 | 87 | You can also override any of the arguments or create your own: 88 | 89 | .. code-block:: python 90 | 91 | class SlowBar(Bar): 92 | suffix = '%(remaining_hours)d hours remaining' 93 | @property 94 | def remaining_hours(self): 95 | return self.eta // 3600 96 | 97 | 98 | Spinners 99 | ======== 100 | 101 | For actions with an unknown number of steps you can use a spinner: 102 | 103 | .. code-block:: python 104 | 105 | from progress.spinner import Spinner 106 | 107 | spinner = Spinner('Loading ') 108 | while state != 'FINISHED': 109 | # Do some work 110 | spinner.next() 111 | 112 | There are 5 predefined spinners: 113 | 114 | - ``Spinner`` 115 | - ``PieSpinner`` 116 | - ``MoonSpinner`` 117 | - ``LineSpinner`` 118 | - ``PixelSpinner`` 119 | 120 | 121 | Other 122 | ===== 123 | 124 | There are a number of other classes available too, please check the source or 125 | subclass one of them to create your own. 126 | 127 | 128 | License 129 | ======= 130 | 131 | progress is licensed under ISC 132 | -------------------------------------------------------------------------------- /pointnet2_ops_lib/pointnet2_ops/_ext-src/src/interpolate.cpp: -------------------------------------------------------------------------------- 1 | #include "interpolate.h" 2 | #include "utils.h" 3 | 4 | void three_nn_kernel_wrapper(int b, int n, int m, const float *unknown, 5 | const float *known, float *dist2, int *idx); 6 | void three_interpolate_kernel_wrapper(int b, int c, int m, int n, 7 | const float *points, const int *idx, 8 | const float *weight, float *out); 9 | void three_interpolate_grad_kernel_wrapper(int b, int c, int n, int m, 10 | const float *grad_out, 11 | const int *idx, const float *weight, 12 | float *grad_points); 13 | 14 | std::vector three_nn(at::Tensor unknowns, at::Tensor knows) { 15 | CHECK_CONTIGUOUS(unknowns); 16 | CHECK_CONTIGUOUS(knows); 17 | CHECK_IS_FLOAT(unknowns); 18 | CHECK_IS_FLOAT(knows); 19 | 20 | if (unknowns.is_cuda()) { 21 | CHECK_CUDA(knows); 22 | } 23 | 24 | at::Tensor idx = 25 | torch::zeros({unknowns.size(0), unknowns.size(1), 3}, 26 | at::device(unknowns.device()).dtype(at::ScalarType::Int)); 27 | at::Tensor dist2 = 28 | torch::zeros({unknowns.size(0), unknowns.size(1), 3}, 29 | at::device(unknowns.device()).dtype(at::ScalarType::Float)); 30 | 31 | if (unknowns.is_cuda()) { 32 | three_nn_kernel_wrapper(unknowns.size(0), unknowns.size(1), knows.size(1), 33 | unknowns.data_ptr(), knows.data_ptr(), 34 | dist2.data_ptr(), idx.data_ptr()); 35 | } else { 36 | AT_ASSERT(false, "CPU not supported"); 37 | } 38 | 39 | return {dist2, idx}; 40 | } 41 | 42 | at::Tensor three_interpolate(at::Tensor points, at::Tensor idx, 43 | at::Tensor weight) { 44 | CHECK_CONTIGUOUS(points); 45 | CHECK_CONTIGUOUS(idx); 46 | CHECK_CONTIGUOUS(weight); 47 | CHECK_IS_FLOAT(points); 48 | CHECK_IS_INT(idx); 49 | CHECK_IS_FLOAT(weight); 50 | 51 | if (points.is_cuda()) { 52 | CHECK_CUDA(idx); 53 | CHECK_CUDA(weight); 54 | } 55 | 56 | at::Tensor output = 57 | torch::zeros({points.size(0), points.size(1), idx.size(1)}, 58 | at::device(points.device()).dtype(at::ScalarType::Float)); 59 | 60 | if (points.is_cuda()) { 61 | three_interpolate_kernel_wrapper( 62 | points.size(0), points.size(1), points.size(2), idx.size(1), 63 | points.data_ptr(), idx.data_ptr(), weight.data_ptr(), 64 | output.data_ptr()); 65 | } else { 66 | AT_ASSERT(false, "CPU not supported"); 67 | } 68 | 69 | return output; 70 | } 71 | at::Tensor three_interpolate_grad(at::Tensor grad_out, at::Tensor idx, 72 | at::Tensor weight, const int m) { 73 | CHECK_CONTIGUOUS(grad_out); 74 | CHECK_CONTIGUOUS(idx); 75 | CHECK_CONTIGUOUS(weight); 76 | CHECK_IS_FLOAT(grad_out); 77 | CHECK_IS_INT(idx); 78 | CHECK_IS_FLOAT(weight); 79 | 80 | if (grad_out.is_cuda()) { 81 | CHECK_CUDA(idx); 82 | CHECK_CUDA(weight); 83 | } 84 | 85 | at::Tensor output = 86 | torch::zeros({grad_out.size(0), grad_out.size(1), m}, 87 | at::device(grad_out.device()).dtype(at::ScalarType::Float)); 88 | 89 | if (grad_out.is_cuda()) { 90 | three_interpolate_grad_kernel_wrapper( 91 | grad_out.size(0), grad_out.size(1), grad_out.size(2), m, 92 | grad_out.data_ptr(), idx.data_ptr(), 93 | weight.data_ptr(), output.data_ptr()); 94 | } else { 95 | AT_ASSERT(false, "CPU not supported"); 96 | } 97 | 98 | return output; 99 | } 100 | -------------------------------------------------------------------------------- /classification_ModelNet40/utils/progress/progress/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2012 Giorgos Verigakis 2 | # 3 | # Permission to use, copy, modify, and distribute this software for any 4 | # purpose with or without fee is hereby granted, provided that the above 5 | # copyright notice and this permission notice appear in all copies. 6 | # 7 | # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | from __future__ import division 16 | 17 | from collections import deque 18 | from datetime import timedelta 19 | from math import ceil 20 | from sys import stderr 21 | from time import time 22 | 23 | 24 | __version__ = '1.3' 25 | 26 | 27 | class Infinite(object): 28 | file = stderr 29 | sma_window = 10 # Simple Moving Average window 30 | 31 | def __init__(self, *args, **kwargs): 32 | self.index = 0 33 | self.start_ts = time() 34 | self.avg = 0 35 | self._ts = self.start_ts 36 | self._xput = deque(maxlen=self.sma_window) 37 | for key, val in kwargs.items(): 38 | setattr(self, key, val) 39 | 40 | def __getitem__(self, key): 41 | if key.startswith('_'): 42 | return None 43 | return getattr(self, key, None) 44 | 45 | @property 46 | def elapsed(self): 47 | return int(time() - self.start_ts) 48 | 49 | @property 50 | def elapsed_td(self): 51 | return timedelta(seconds=self.elapsed) 52 | 53 | def update_avg(self, n, dt): 54 | if n > 0: 55 | self._xput.append(dt / n) 56 | self.avg = sum(self._xput) / len(self._xput) 57 | 58 | def update(self): 59 | pass 60 | 61 | def start(self): 62 | pass 63 | 64 | def finish(self): 65 | pass 66 | 67 | def next(self, n=1): 68 | now = time() 69 | dt = now - self._ts 70 | self.update_avg(n, dt) 71 | self._ts = now 72 | self.index = self.index + n 73 | self.update() 74 | 75 | def iter(self, it): 76 | try: 77 | for x in it: 78 | yield x 79 | self.next() 80 | finally: 81 | self.finish() 82 | 83 | 84 | class Progress(Infinite): 85 | def __init__(self, *args, **kwargs): 86 | super(Progress, self).__init__(*args, **kwargs) 87 | self.max = kwargs.get('max', 100) 88 | 89 | @property 90 | def eta(self): 91 | return int(ceil(self.avg * self.remaining)) 92 | 93 | @property 94 | def eta_td(self): 95 | return timedelta(seconds=self.eta) 96 | 97 | @property 98 | def percent(self): 99 | return self.progress * 100 100 | 101 | @property 102 | def progress(self): 103 | return min(1, self.index / self.max) 104 | 105 | @property 106 | def remaining(self): 107 | return max(self.max - self.index, 0) 108 | 109 | def start(self): 110 | self.update() 111 | 112 | def goto(self, index): 113 | incr = index - self.index 114 | self.next(incr) 115 | 116 | def iter(self, it): 117 | try: 118 | self.max = len(it) 119 | except TypeError: 120 | pass 121 | 122 | try: 123 | for x in it: 124 | yield x 125 | self.next() 126 | finally: 127 | self.finish() 128 | -------------------------------------------------------------------------------- /classification_ScanObjectNN/utils/progress/progress/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2012 Giorgos Verigakis 2 | # 3 | # Permission to use, copy, modify, and distribute this software for any 4 | # purpose with or without fee is hereby granted, provided that the above 5 | # copyright notice and this permission notice appear in all copies. 6 | # 7 | # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | from __future__ import division 16 | 17 | from collections import deque 18 | from datetime import timedelta 19 | from math import ceil 20 | from sys import stderr 21 | from time import time 22 | 23 | 24 | __version__ = '1.3' 25 | 26 | 27 | class Infinite(object): 28 | file = stderr 29 | sma_window = 10 # Simple Moving Average window 30 | 31 | def __init__(self, *args, **kwargs): 32 | self.index = 0 33 | self.start_ts = time() 34 | self.avg = 0 35 | self._ts = self.start_ts 36 | self._xput = deque(maxlen=self.sma_window) 37 | for key, val in kwargs.items(): 38 | setattr(self, key, val) 39 | 40 | def __getitem__(self, key): 41 | if key.startswith('_'): 42 | return None 43 | return getattr(self, key, None) 44 | 45 | @property 46 | def elapsed(self): 47 | return int(time() - self.start_ts) 48 | 49 | @property 50 | def elapsed_td(self): 51 | return timedelta(seconds=self.elapsed) 52 | 53 | def update_avg(self, n, dt): 54 | if n > 0: 55 | self._xput.append(dt / n) 56 | self.avg = sum(self._xput) / len(self._xput) 57 | 58 | def update(self): 59 | pass 60 | 61 | def start(self): 62 | pass 63 | 64 | def finish(self): 65 | pass 66 | 67 | def next(self, n=1): 68 | now = time() 69 | dt = now - self._ts 70 | self.update_avg(n, dt) 71 | self._ts = now 72 | self.index = self.index + n 73 | self.update() 74 | 75 | def iter(self, it): 76 | try: 77 | for x in it: 78 | yield x 79 | self.next() 80 | finally: 81 | self.finish() 82 | 83 | 84 | class Progress(Infinite): 85 | def __init__(self, *args, **kwargs): 86 | super(Progress, self).__init__(*args, **kwargs) 87 | self.max = kwargs.get('max', 100) 88 | 89 | @property 90 | def eta(self): 91 | return int(ceil(self.avg * self.remaining)) 92 | 93 | @property 94 | def eta_td(self): 95 | return timedelta(seconds=self.eta) 96 | 97 | @property 98 | def percent(self): 99 | return self.progress * 100 100 | 101 | @property 102 | def progress(self): 103 | return min(1, self.index / self.max) 104 | 105 | @property 106 | def remaining(self): 107 | return max(self.max - self.index, 0) 108 | 109 | def start(self): 110 | self.update() 111 | 112 | def goto(self, index): 113 | incr = index - self.index 114 | self.next(incr) 115 | 116 | def iter(self, it): 117 | try: 118 | self.max = len(it) 119 | except TypeError: 120 | pass 121 | 122 | try: 123 | for x in it: 124 | yield x 125 | self.next() 126 | finally: 127 | self.finish() 128 | -------------------------------------------------------------------------------- /classification_ModelNet40/data.py: -------------------------------------------------------------------------------- 1 | import os 2 | import glob 3 | import h5py 4 | import numpy as np 5 | from torch.utils.data import Dataset 6 | os.environ["HDF5_USE_FILE_LOCKING"] = "FALSE" 7 | 8 | def download(): 9 | BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 10 | DATA_DIR = os.path.join(BASE_DIR, 'data') 11 | if not os.path.exists(DATA_DIR): 12 | os.mkdir(DATA_DIR) 13 | if not os.path.exists(os.path.join(DATA_DIR, 'modelnet40_ply_hdf5_2048')): 14 | # www = 'https://shapenet.cs.stanford.edu/media/modelnet40_ply_hdf5_2048.zip' 15 | # updated by Xu for new dataset link; 16 | # the file is from: https://huggingface.co/datasets/Msun/modelnet40/tree/main 17 | www = "https://github.com/ma-xu/pointMLP-pytorch/releases/download/Modenet40_dataset/modelnet40_ply_hdf5_2048.zip" 18 | zipfile = os.path.basename(www) 19 | os.system('wget %s --no-check-certificate; unzip %s' % (www, zipfile)) 20 | os.system('mv %s %s' % (zipfile[:-4], DATA_DIR)) 21 | os.system('rm %s' % (zipfile)) 22 | 23 | def load_data(partition): 24 | download() 25 | BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 26 | DATA_DIR = os.path.join(BASE_DIR, 'data') 27 | all_data = [] 28 | all_label = [] 29 | for h5_name in glob.glob(os.path.join(DATA_DIR, 'modelnet40_ply_hdf5_2048', 'ply_data_%s*.h5'%partition)): 30 | # print(f"h5_name: {h5_name}") 31 | f = h5py.File(h5_name,'r') 32 | data = f['data'][:].astype('float32') 33 | label = f['label'][:].astype('int64') 34 | f.close() 35 | all_data.append(data) 36 | all_label.append(label) 37 | all_data = np.concatenate(all_data, axis=0) 38 | all_label = np.concatenate(all_label, axis=0) 39 | return all_data, all_label 40 | 41 | def random_point_dropout(pc, max_dropout_ratio=0.875): 42 | ''' batch_pc: BxNx3 ''' 43 | # for b in range(batch_pc.shape[0]): 44 | dropout_ratio = np.random.random()*max_dropout_ratio # 0~0.875 45 | drop_idx = np.where(np.random.random((pc.shape[0]))<=dropout_ratio)[0] 46 | # print ('use random drop', len(drop_idx)) 47 | 48 | if len(drop_idx)>0: 49 | pc[drop_idx,:] = pc[0,:] # set to the first point 50 | return pc 51 | 52 | def translate_pointcloud(pointcloud): 53 | xyz1 = np.random.uniform(low=2./3., high=3./2., size=[3]) 54 | xyz2 = np.random.uniform(low=-0.2, high=0.2, size=[3]) 55 | 56 | translated_pointcloud = np.add(np.multiply(pointcloud, xyz1), xyz2).astype('float32') 57 | return translated_pointcloud 58 | 59 | def jitter_pointcloud(pointcloud, sigma=0.01, clip=0.02): 60 | N, C = pointcloud.shape 61 | pointcloud += np.clip(sigma * np.random.randn(N, C), -1*clip, clip) 62 | return pointcloud 63 | 64 | 65 | class ModelNet40(Dataset): 66 | def __init__(self, num_points, partition='train'): 67 | self.data, self.label = load_data(partition) 68 | self.num_points = num_points 69 | self.partition = partition 70 | 71 | def __getitem__(self, item): 72 | pointcloud = self.data[item][:self.num_points] 73 | label = self.label[item] 74 | if self.partition == 'train': 75 | # pointcloud = random_point_dropout(pointcloud) # open for dgcnn not for our idea for all 76 | pointcloud = translate_pointcloud(pointcloud) 77 | np.random.shuffle(pointcloud) 78 | return pointcloud, label 79 | 80 | def __len__(self): 81 | return self.data.shape[0] 82 | 83 | 84 | if __name__ == '__main__': 85 | train = ModelNet40(1024) 86 | test = ModelNet40(1024, 'test') 87 | # for data, label in train: 88 | # print(data.shape) 89 | # print(label.shape) 90 | from torch.utils.data import DataLoader 91 | train_loader = DataLoader(ModelNet40(partition='train', num_points=1024), num_workers=4, 92 | batch_size=32, shuffle=True, drop_last=True) 93 | for batch_idx, (data, label) in enumerate(train_loader): 94 | print(f"batch_idx: {batch_idx} | data shape: {data.shape} | ;lable shape: {label.shape}") 95 | 96 | train_set = ModelNet40(partition='train', num_points=1024) 97 | test_set = ModelNet40(partition='test', num_points=1024) 98 | print(f"train_set size {train_set.__len__()}") 99 | print(f"test_set size {test_set.__len__()}") 100 | -------------------------------------------------------------------------------- /classification_ModelNet40/test.py: -------------------------------------------------------------------------------- 1 | """ 2 | python test.py --model pointMLP --msg 20220209053148-404 3 | """ 4 | import argparse 5 | import os 6 | import datetime 7 | import torch 8 | import torch.nn.parallel 9 | import torch.backends.cudnn as cudnn 10 | import torch.optim 11 | import torch.utils.data 12 | import torch.utils.data.distributed 13 | from torch.utils.data import DataLoader 14 | import models as models 15 | from utils import progress_bar, IOStream 16 | from data import ModelNet40 17 | import sklearn.metrics as metrics 18 | from helper import cal_loss 19 | import numpy as np 20 | import torch.nn.functional as F 21 | 22 | model_names = sorted(name for name in models.__dict__ 23 | if callable(models.__dict__[name])) 24 | 25 | 26 | def parse_args(): 27 | """Parameters""" 28 | parser = argparse.ArgumentParser('training') 29 | parser.add_argument('-c', '--checkpoint', type=str, metavar='PATH', 30 | help='path to save checkpoint (default: checkpoint)') 31 | parser.add_argument('--msg', type=str, help='message after checkpoint') 32 | parser.add_argument('--batch_size', type=int, default=16, help='batch size in training') 33 | parser.add_argument('--model', default='pointMLP', help='model name [default: pointnet_cls]') 34 | parser.add_argument('--num_classes', default=40, type=int, choices=[10, 40], help='training on ModelNet10/40') 35 | parser.add_argument('--num_points', type=int, default=1024, help='Point Number') 36 | return parser.parse_args() 37 | 38 | def main(): 39 | args = parse_args() 40 | print(f"args: {args}") 41 | os.environ["HDF5_USE_FILE_LOCKING"] = "FALSE" 42 | 43 | if torch.cuda.is_available(): 44 | device = 'cuda' 45 | else: 46 | device = 'cpu' 47 | print(f"==> Using device: {device}") 48 | if args.msg is None: 49 | message = str(datetime.datetime.now().strftime('-%Y%m%d%H%M%S')) 50 | else: 51 | message = "-"+args.msg 52 | args.checkpoint = 'checkpoints/' + args.model + message 53 | 54 | print('==> Preparing data..') 55 | test_loader = DataLoader(ModelNet40(partition='test', num_points=args.num_points), num_workers=4, 56 | batch_size=args.batch_size, shuffle=False, drop_last=False) 57 | # Model 58 | print('==> Building model..') 59 | net = models.__dict__[args.model]() 60 | criterion = cal_loss 61 | net = net.to(device) 62 | checkpoint_path = os.path.join(args.checkpoint, 'best_checkpoint.pth') 63 | checkpoint = torch.load(checkpoint_path, map_location=torch.device('cpu')) 64 | # criterion = criterion.to(device) 65 | if device == 'cuda': 66 | net = torch.nn.DataParallel(net) 67 | cudnn.benchmark = True 68 | net.load_state_dict(checkpoint['net']) 69 | 70 | test_out = validate(net, test_loader, criterion, device) 71 | print(f"Vanilla out: {test_out}") 72 | 73 | 74 | def validate(net, testloader, criterion, device): 75 | net.eval() 76 | test_loss = 0 77 | correct = 0 78 | total = 0 79 | test_true = [] 80 | test_pred = [] 81 | time_cost = datetime.datetime.now() 82 | with torch.no_grad(): 83 | for batch_idx, (data, label) in enumerate(testloader): 84 | data, label = data.to(device), label.to(device).squeeze() 85 | data = data.permute(0, 2, 1) 86 | logits = net(data) 87 | loss = criterion(logits, label) 88 | test_loss += loss.item() 89 | preds = logits.max(dim=1)[1] 90 | test_true.append(label.cpu().numpy()) 91 | test_pred.append(preds.detach().cpu().numpy()) 92 | total += label.size(0) 93 | correct += preds.eq(label).sum().item() 94 | progress_bar(batch_idx, len(testloader), 'Loss: %.3f | Acc: %.3f%% (%d/%d)' 95 | % (test_loss / (batch_idx + 1), 100. * correct / total, correct, total)) 96 | 97 | time_cost = int((datetime.datetime.now() - time_cost).total_seconds()) 98 | test_true = np.concatenate(test_true) 99 | test_pred = np.concatenate(test_pred) 100 | return { 101 | "loss": float("%.3f" % (test_loss / (batch_idx + 1))), 102 | "acc": float("%.3f" % (100. * metrics.accuracy_score(test_true, test_pred))), 103 | "acc_avg": float("%.3f" % (100. * metrics.balanced_accuracy_score(test_true, test_pred))), 104 | "time": time_cost 105 | } 106 | 107 | 108 | if __name__ == '__main__': 109 | main() 110 | -------------------------------------------------------------------------------- /classification_ModelNet40/utils/logger.py: -------------------------------------------------------------------------------- 1 | # A simple torch style logger 2 | # (C) Wei YANG 2017 3 | from __future__ import absolute_import 4 | import matplotlib.pyplot as plt 5 | import os 6 | import sys 7 | import numpy as np 8 | 9 | __all__ = ['Logger', 'LoggerMonitor', 'savefig'] 10 | 11 | def savefig(fname, dpi=None): 12 | dpi = 150 if dpi == None else dpi 13 | plt.savefig(fname, dpi=dpi) 14 | 15 | def plot_overlap(logger, names=None): 16 | names = logger.names if names == None else names 17 | numbers = logger.numbers 18 | for _, name in enumerate(names): 19 | x = np.arange(len(numbers[name])) 20 | plt.plot(x, np.asarray(numbers[name])) 21 | return [logger.title + '(' + name + ')' for name in names] 22 | 23 | class Logger(object): 24 | '''Save training process to log file with simple plot function.''' 25 | def __init__(self, fpath, title=None, resume=False): 26 | self.file = None 27 | self.resume = resume 28 | self.title = '' if title == None else title 29 | if fpath is not None: 30 | if resume: 31 | self.file = open(fpath, 'r') 32 | name = self.file.readline() 33 | self.names = name.rstrip().split('\t') 34 | self.numbers = {} 35 | for _, name in enumerate(self.names): 36 | self.numbers[name] = [] 37 | 38 | for numbers in self.file: 39 | numbers = numbers.rstrip().split('\t') 40 | for i in range(0, len(numbers)): 41 | self.numbers[self.names[i]].append(numbers[i]) 42 | self.file.close() 43 | self.file = open(fpath, 'a') 44 | else: 45 | self.file = open(fpath, 'w') 46 | 47 | def set_names(self, names): 48 | if self.resume: 49 | pass 50 | # initialize numbers as empty list 51 | self.numbers = {} 52 | self.names = names 53 | for _, name in enumerate(self.names): 54 | self.file.write(name) 55 | self.file.write('\t') 56 | self.numbers[name] = [] 57 | self.file.write('\n') 58 | self.file.flush() 59 | 60 | 61 | def append(self, numbers): 62 | assert len(self.names) == len(numbers), 'Numbers do not match names' 63 | for index, num in enumerate(numbers): 64 | self.file.write("{0:.6f}".format(num)) 65 | self.file.write('\t') 66 | self.numbers[self.names[index]].append(num) 67 | self.file.write('\n') 68 | self.file.flush() 69 | 70 | def plot(self, names=None): 71 | names = self.names if names == None else names 72 | numbers = self.numbers 73 | for _, name in enumerate(names): 74 | x = np.arange(len(numbers[name])) 75 | plt.plot(x, np.asarray(numbers[name])) 76 | plt.legend([self.title + '(' + name + ')' for name in names]) 77 | plt.grid(True) 78 | 79 | def close(self): 80 | if self.file is not None: 81 | self.file.close() 82 | 83 | class LoggerMonitor(object): 84 | '''Load and visualize multiple logs.''' 85 | def __init__ (self, paths): 86 | '''paths is a distionary with {name:filepath} pair''' 87 | self.loggers = [] 88 | for title, path in paths.items(): 89 | logger = Logger(path, title=title, resume=True) 90 | self.loggers.append(logger) 91 | 92 | def plot(self, names=None): 93 | plt.figure() 94 | plt.subplot(121) 95 | legend_text = [] 96 | for logger in self.loggers: 97 | legend_text += plot_overlap(logger, names) 98 | plt.legend(legend_text, bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.) 99 | plt.grid(True) 100 | 101 | if __name__ == '__main__': 102 | # # Example 103 | # logger = Logger('test.txt') 104 | # logger.set_names(['Train loss', 'Valid loss','Test loss']) 105 | 106 | # length = 100 107 | # t = np.arange(length) 108 | # train_loss = np.exp(-t / 10.0) + np.random.rand(length) * 0.1 109 | # valid_loss = np.exp(-t / 10.0) + np.random.rand(length) * 0.1 110 | # test_loss = np.exp(-t / 10.0) + np.random.rand(length) * 0.1 111 | 112 | # for i in range(0, length): 113 | # logger.append([train_loss[i], valid_loss[i], test_loss[i]]) 114 | # logger.plot() 115 | 116 | # Example: logger monitor 117 | paths = { 118 | 'resadvnet20':'/home/wyang/code/pytorch-classification/checkpoint/cifar10/resadvnet20/log.txt', 119 | 'resadvnet32':'/home/wyang/code/pytorch-classification/checkpoint/cifar10/resadvnet32/log.txt', 120 | 'resadvnet44':'/home/wyang/code/pytorch-classification/checkpoint/cifar10/resadvnet44/log.txt', 121 | } 122 | 123 | field = ['Valid Acc.'] 124 | 125 | monitor = LoggerMonitor(paths) 126 | monitor.plot(names=field) 127 | savefig('test.eps') -------------------------------------------------------------------------------- /classification_ScanObjectNN/utils/logger.py: -------------------------------------------------------------------------------- 1 | # A simple torch style logger 2 | # (C) Wei YANG 2017 3 | from __future__ import absolute_import 4 | import matplotlib.pyplot as plt 5 | import os 6 | import sys 7 | import numpy as np 8 | 9 | __all__ = ['Logger', 'LoggerMonitor', 'savefig'] 10 | 11 | def savefig(fname, dpi=None): 12 | dpi = 150 if dpi == None else dpi 13 | plt.savefig(fname, dpi=dpi) 14 | 15 | def plot_overlap(logger, names=None): 16 | names = logger.names if names == None else names 17 | numbers = logger.numbers 18 | for _, name in enumerate(names): 19 | x = np.arange(len(numbers[name])) 20 | plt.plot(x, np.asarray(numbers[name])) 21 | return [logger.title + '(' + name + ')' for name in names] 22 | 23 | class Logger(object): 24 | '''Save training process to log file with simple plot function.''' 25 | def __init__(self, fpath, title=None, resume=False): 26 | self.file = None 27 | self.resume = resume 28 | self.title = '' if title == None else title 29 | if fpath is not None: 30 | if resume: 31 | self.file = open(fpath, 'r') 32 | name = self.file.readline() 33 | self.names = name.rstrip().split('\t') 34 | self.numbers = {} 35 | for _, name in enumerate(self.names): 36 | self.numbers[name] = [] 37 | 38 | for numbers in self.file: 39 | numbers = numbers.rstrip().split('\t') 40 | for i in range(0, len(numbers)): 41 | self.numbers[self.names[i]].append(numbers[i]) 42 | self.file.close() 43 | self.file = open(fpath, 'a') 44 | else: 45 | self.file = open(fpath, 'w') 46 | 47 | def set_names(self, names): 48 | if self.resume: 49 | pass 50 | # initialize numbers as empty list 51 | self.numbers = {} 52 | self.names = names 53 | for _, name in enumerate(self.names): 54 | self.file.write(name) 55 | self.file.write('\t') 56 | self.numbers[name] = [] 57 | self.file.write('\n') 58 | self.file.flush() 59 | 60 | 61 | def append(self, numbers): 62 | assert len(self.names) == len(numbers), 'Numbers do not match names' 63 | for index, num in enumerate(numbers): 64 | self.file.write("{0:.6f}".format(num)) 65 | self.file.write('\t') 66 | self.numbers[self.names[index]].append(num) 67 | self.file.write('\n') 68 | self.file.flush() 69 | 70 | def plot(self, names=None): 71 | names = self.names if names == None else names 72 | numbers = self.numbers 73 | for _, name in enumerate(names): 74 | x = np.arange(len(numbers[name])) 75 | plt.plot(x, np.asarray(numbers[name])) 76 | plt.legend([self.title + '(' + name + ')' for name in names]) 77 | plt.grid(True) 78 | 79 | def close(self): 80 | if self.file is not None: 81 | self.file.close() 82 | 83 | class LoggerMonitor(object): 84 | '''Load and visualize multiple logs.''' 85 | def __init__ (self, paths): 86 | '''paths is a distionary with {name:filepath} pair''' 87 | self.loggers = [] 88 | for title, path in paths.items(): 89 | logger = Logger(path, title=title, resume=True) 90 | self.loggers.append(logger) 91 | 92 | def plot(self, names=None): 93 | plt.figure() 94 | plt.subplot(121) 95 | legend_text = [] 96 | for logger in self.loggers: 97 | legend_text += plot_overlap(logger, names) 98 | plt.legend(legend_text, bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.) 99 | plt.grid(True) 100 | 101 | if __name__ == '__main__': 102 | # # Example 103 | # logger = Logger('test.txt') 104 | # logger.set_names(['Train loss', 'Valid loss','Test loss']) 105 | 106 | # length = 100 107 | # t = np.arange(length) 108 | # train_loss = np.exp(-t / 10.0) + np.random.rand(length) * 0.1 109 | # valid_loss = np.exp(-t / 10.0) + np.random.rand(length) * 0.1 110 | # test_loss = np.exp(-t / 10.0) + np.random.rand(length) * 0.1 111 | 112 | # for i in range(0, length): 113 | # logger.append([train_loss[i], valid_loss[i], test_loss[i]]) 114 | # logger.plot() 115 | 116 | # Example: logger monitor 117 | paths = { 118 | 'resadvnet20':'/home/wyang/code/pytorch-classification/checkpoint/cifar10/resadvnet20/log.txt', 119 | 'resadvnet32':'/home/wyang/code/pytorch-classification/checkpoint/cifar10/resadvnet32/log.txt', 120 | 'resadvnet44':'/home/wyang/code/pytorch-classification/checkpoint/cifar10/resadvnet44/log.txt', 121 | } 122 | 123 | field = ['Valid Acc.'] 124 | 125 | monitor = LoggerMonitor(paths) 126 | monitor.plot(names=field) 127 | savefig('test.eps') -------------------------------------------------------------------------------- /pointnet2_ops_lib/pointnet2_ops/_ext-src/src/interpolate_gpu.cu: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "cuda_utils.h" 6 | 7 | // input: unknown(b, n, 3) known(b, m, 3) 8 | // output: dist2(b, n, 3), idx(b, n, 3) 9 | __global__ void three_nn_kernel(int b, int n, int m, 10 | const float *__restrict__ unknown, 11 | const float *__restrict__ known, 12 | float *__restrict__ dist2, 13 | int *__restrict__ idx) { 14 | int batch_index = blockIdx.x; 15 | unknown += batch_index * n * 3; 16 | known += batch_index * m * 3; 17 | dist2 += batch_index * n * 3; 18 | idx += batch_index * n * 3; 19 | 20 | int index = threadIdx.x; 21 | int stride = blockDim.x; 22 | for (int j = index; j < n; j += stride) { 23 | float ux = unknown[j * 3 + 0]; 24 | float uy = unknown[j * 3 + 1]; 25 | float uz = unknown[j * 3 + 2]; 26 | 27 | double best1 = 1e40, best2 = 1e40, best3 = 1e40; 28 | int besti1 = 0, besti2 = 0, besti3 = 0; 29 | for (int k = 0; k < m; ++k) { 30 | float x = known[k * 3 + 0]; 31 | float y = known[k * 3 + 1]; 32 | float z = known[k * 3 + 2]; 33 | float d = (ux - x) * (ux - x) + (uy - y) * (uy - y) + (uz - z) * (uz - z); 34 | if (d < best1) { 35 | best3 = best2; 36 | besti3 = besti2; 37 | best2 = best1; 38 | besti2 = besti1; 39 | best1 = d; 40 | besti1 = k; 41 | } else if (d < best2) { 42 | best3 = best2; 43 | besti3 = besti2; 44 | best2 = d; 45 | besti2 = k; 46 | } else if (d < best3) { 47 | best3 = d; 48 | besti3 = k; 49 | } 50 | } 51 | dist2[j * 3 + 0] = best1; 52 | dist2[j * 3 + 1] = best2; 53 | dist2[j * 3 + 2] = best3; 54 | 55 | idx[j * 3 + 0] = besti1; 56 | idx[j * 3 + 1] = besti2; 57 | idx[j * 3 + 2] = besti3; 58 | } 59 | } 60 | 61 | void three_nn_kernel_wrapper(int b, int n, int m, const float *unknown, 62 | const float *known, float *dist2, int *idx) { 63 | cudaStream_t stream = at::cuda::getCurrentCUDAStream(); 64 | three_nn_kernel<<>>(b, n, m, unknown, known, 65 | dist2, idx); 66 | 67 | CUDA_CHECK_ERRORS(); 68 | } 69 | 70 | // input: points(b, c, m), idx(b, n, 3), weight(b, n, 3) 71 | // output: out(b, c, n) 72 | __global__ void three_interpolate_kernel(int b, int c, int m, int n, 73 | const float *__restrict__ points, 74 | const int *__restrict__ idx, 75 | const float *__restrict__ weight, 76 | float *__restrict__ out) { 77 | int batch_index = blockIdx.x; 78 | points += batch_index * m * c; 79 | 80 | idx += batch_index * n * 3; 81 | weight += batch_index * n * 3; 82 | 83 | out += batch_index * n * c; 84 | 85 | const int index = threadIdx.y * blockDim.x + threadIdx.x; 86 | const int stride = blockDim.y * blockDim.x; 87 | for (int i = index; i < c * n; i += stride) { 88 | const int l = i / n; 89 | const int j = i % n; 90 | float w1 = weight[j * 3 + 0]; 91 | float w2 = weight[j * 3 + 1]; 92 | float w3 = weight[j * 3 + 2]; 93 | 94 | int i1 = idx[j * 3 + 0]; 95 | int i2 = idx[j * 3 + 1]; 96 | int i3 = idx[j * 3 + 2]; 97 | 98 | out[i] = points[l * m + i1] * w1 + points[l * m + i2] * w2 + 99 | points[l * m + i3] * w3; 100 | } 101 | } 102 | 103 | void three_interpolate_kernel_wrapper(int b, int c, int m, int n, 104 | const float *points, const int *idx, 105 | const float *weight, float *out) { 106 | cudaStream_t stream = at::cuda::getCurrentCUDAStream(); 107 | three_interpolate_kernel<<>>( 108 | b, c, m, n, points, idx, weight, out); 109 | 110 | CUDA_CHECK_ERRORS(); 111 | } 112 | 113 | // input: grad_out(b, c, n), idx(b, n, 3), weight(b, n, 3) 114 | // output: grad_points(b, c, m) 115 | 116 | __global__ void three_interpolate_grad_kernel( 117 | int b, int c, int n, int m, const float *__restrict__ grad_out, 118 | const int *__restrict__ idx, const float *__restrict__ weight, 119 | float *__restrict__ grad_points) { 120 | int batch_index = blockIdx.x; 121 | grad_out += batch_index * n * c; 122 | idx += batch_index * n * 3; 123 | weight += batch_index * n * 3; 124 | grad_points += batch_index * m * c; 125 | 126 | const int index = threadIdx.y * blockDim.x + threadIdx.x; 127 | const int stride = blockDim.y * blockDim.x; 128 | for (int i = index; i < c * n; i += stride) { 129 | const int l = i / n; 130 | const int j = i % n; 131 | float w1 = weight[j * 3 + 0]; 132 | float w2 = weight[j * 3 + 1]; 133 | float w3 = weight[j * 3 + 2]; 134 | 135 | int i1 = idx[j * 3 + 0]; 136 | int i2 = idx[j * 3 + 1]; 137 | int i3 = idx[j * 3 + 2]; 138 | 139 | atomicAdd(grad_points + l * m + i1, grad_out[i] * w1); 140 | atomicAdd(grad_points + l * m + i2, grad_out[i] * w2); 141 | atomicAdd(grad_points + l * m + i3, grad_out[i] * w3); 142 | } 143 | } 144 | 145 | void three_interpolate_grad_kernel_wrapper(int b, int c, int n, int m, 146 | const float *grad_out, 147 | const int *idx, const float *weight, 148 | float *grad_points) { 149 | cudaStream_t stream = at::cuda::getCurrentCUDAStream(); 150 | three_interpolate_grad_kernel<<>>( 151 | b, c, n, m, grad_out, idx, weight, grad_points); 152 | 153 | CUDA_CHECK_ERRORS(); 154 | } 155 | -------------------------------------------------------------------------------- /part_segmentation/util/data_util.py: -------------------------------------------------------------------------------- 1 | import glob 2 | import h5py 3 | import numpy as np 4 | from torch.utils.data import Dataset 5 | import os 6 | import json 7 | os.environ["HDF5_USE_FILE_LOCKING"] = "FALSE" 8 | 9 | 10 | def load_data(partition): 11 | all_data = [] 12 | all_label = [] 13 | for h5_name in glob.glob('./data/modelnet40_ply_hdf5_2048/ply_data_%s*.h5' % partition): 14 | f = h5py.File(h5_name) 15 | data = f['data'][:].astype('float32') 16 | label = f['label'][:].astype('int64') 17 | f.close() 18 | all_data.append(data) 19 | all_label.append(label) 20 | all_data = np.concatenate(all_data, axis=0) 21 | all_label = np.concatenate(all_label, axis=0) 22 | return all_data, all_label 23 | 24 | 25 | def pc_normalize(pc): 26 | centroid = np.mean(pc, axis=0) 27 | pc = pc - centroid 28 | m = np.max(np.sqrt(np.sum(pc ** 2, axis=1))) 29 | pc = pc / m 30 | return pc 31 | 32 | 33 | def translate_pointcloud(pointcloud): 34 | xyz1 = np.random.uniform(low=2./3., high=3./2., size=[3]) 35 | xyz2 = np.random.uniform(low=-0.2, high=0.2, size=[3]) 36 | 37 | translated_pointcloud = np.add(np.multiply(pointcloud, xyz1), xyz2).astype('float32') 38 | return translated_pointcloud 39 | 40 | 41 | def jitter_pointcloud(pointcloud, sigma=0.01, clip=0.02): 42 | N, C = pointcloud.shape 43 | pointcloud += np.clip(sigma * np.random.randn(N, C), -1*clip, clip) 44 | return pointcloud 45 | 46 | 47 | # =========== ModelNet40 ================= 48 | class ModelNet40(Dataset): 49 | def __init__(self, num_points, partition='train'): 50 | self.data, self.label = load_data(partition) 51 | self.num_points = num_points 52 | self.partition = partition # Here the new given partition will cover the 'train' 53 | 54 | def __getitem__(self, item): # indice of the pts or label 55 | pointcloud = self.data[item][:self.num_points] 56 | label = self.label[item] 57 | if self.partition == 'train': 58 | # pointcloud = pc_normalize(pointcloud) # you can try to add it or not to train our model 59 | pointcloud = translate_pointcloud(pointcloud) 60 | np.random.shuffle(pointcloud) # shuffle the order of pts 61 | return pointcloud, label 62 | 63 | def __len__(self): 64 | return self.data.shape[0] 65 | 66 | 67 | # =========== ShapeNet Part ================= 68 | class PartNormalDataset(Dataset): 69 | def __init__(self, npoints=2500, split='train', normalize=False): 70 | self.npoints = npoints 71 | self.root = './data/shapenetcore_partanno_segmentation_benchmark_v0_normal' 72 | self.catfile = os.path.join(self.root, 'synsetoffset2category.txt') 73 | self.cat = {} 74 | self.normalize = normalize 75 | 76 | with open(self.catfile, 'r') as f: 77 | for line in f: 78 | ls = line.strip().split() 79 | self.cat[ls[0]] = ls[1] 80 | self.cat = {k: v for k, v in self.cat.items()} 81 | 82 | self.meta = {} 83 | with open(os.path.join(self.root, 'train_test_split', 'shuffled_train_file_list.json'), 'r') as f: 84 | train_ids = set([str(d.split('/')[2]) for d in json.load(f)]) 85 | with open(os.path.join(self.root, 'train_test_split', 'shuffled_val_file_list.json'), 'r') as f: 86 | val_ids = set([str(d.split('/')[2]) for d in json.load(f)]) 87 | with open(os.path.join(self.root, 'train_test_split', 'shuffled_test_file_list.json'), 'r') as f: 88 | test_ids = set([str(d.split('/')[2]) for d in json.load(f)]) 89 | for item in self.cat: 90 | self.meta[item] = [] 91 | dir_point = os.path.join(self.root, self.cat[item]) 92 | fns = sorted(os.listdir(dir_point)) 93 | 94 | if split == 'trainval': 95 | fns = [fn for fn in fns if ((fn[0:-4] in train_ids) or (fn[0:-4] in val_ids))] 96 | elif split == 'train': 97 | fns = [fn for fn in fns if fn[0:-4] in train_ids] 98 | elif split == 'val': 99 | fns = [fn for fn in fns if fn[0:-4] in val_ids] 100 | elif split == 'test': 101 | fns = [fn for fn in fns if fn[0:-4] in test_ids] 102 | else: 103 | print('Unknown split: %s. Exiting..' % (split)) 104 | exit(-1) 105 | 106 | for fn in fns: 107 | token = (os.path.splitext(os.path.basename(fn))[0]) 108 | self.meta[item].append(os.path.join(dir_point, token + '.txt')) 109 | 110 | self.datapath = [] 111 | for item in self.cat: 112 | for fn in self.meta[item]: 113 | self.datapath.append((item, fn)) 114 | 115 | self.classes = dict(zip(self.cat, range(len(self.cat)))) 116 | # Mapping from category ('Chair') to a list of int [10,11,12,13] as segmentation labels 117 | self.seg_classes = {'Earphone': [16, 17, 18], 'Motorbike': [30, 31, 32, 33, 34, 35], 'Rocket': [41, 42, 43], 118 | 'Car': [8, 9, 10, 11], 'Laptop': [28, 29], 'Cap': [6, 7], 'Skateboard': [44, 45, 46], 119 | 'Mug': [36, 37], 'Guitar': [19, 20, 21], 'Bag': [4, 5], 'Lamp': [24, 25, 26, 27], 120 | 'Table': [47, 48, 49], 'Airplane': [0, 1, 2, 3], 'Pistol': [38, 39, 40], 121 | 'Chair': [12, 13, 14, 15], 'Knife': [22, 23]} 122 | 123 | self.cache = {} # from index to (point_set, cls, seg) tuple 124 | self.cache_size = 20000 125 | 126 | def __getitem__(self, index): 127 | if index in self.cache: 128 | point_set, normal, seg, cls = self.cache[index] 129 | else: 130 | fn = self.datapath[index] 131 | cat = self.datapath[index][0] 132 | cls = self.classes[cat] 133 | cls = np.array([cls]).astype(np.int32) 134 | data = np.loadtxt(fn[1]).astype(np.float32) 135 | point_set = data[:, 0:3] 136 | normal = data[:, 3:6] 137 | seg = data[:, -1].astype(np.int32) 138 | if len(self.cache) < self.cache_size: 139 | self.cache[index] = (point_set, normal, seg, cls) 140 | 141 | if self.normalize: 142 | point_set = pc_normalize(point_set) 143 | 144 | choice = np.random.choice(len(seg), self.npoints, replace=True) 145 | 146 | # resample 147 | # note that the number of points in some points clouds is less than 2048, thus use random.choice 148 | # remember to use the same seed during train and test for a getting stable result 149 | point_set = point_set[choice, :] 150 | seg = seg[choice] 151 | normal = normal[choice, :] 152 | 153 | return point_set, cls, seg, normal 154 | 155 | def __len__(self): 156 | return len(self.datapath) 157 | 158 | 159 | if __name__ == '__main__': 160 | train = PartNormalDataset(npoints=2048, split='trainval', normalize=False) 161 | test = PartNormalDataset(npoints=2048, split='test', normalize=False) 162 | for data, label, _, _ in train: 163 | print(data.shape) 164 | print(label.shape) 165 | -------------------------------------------------------------------------------- /classification_ModelNet40/utils/misc.py: -------------------------------------------------------------------------------- 1 | '''Some helper functions for PyTorch, including: 2 | - get_mean_and_std: calculate the mean and std value of dataset. 3 | - msr_init: net parameter initialization. 4 | - progress_bar: progress bar mimic xlua.progress. 5 | ''' 6 | import errno 7 | import os 8 | import sys 9 | import time 10 | import math 11 | import torch 12 | import shutil 13 | import numpy as np 14 | import random 15 | import torch.nn.functional as F 16 | 17 | 18 | import torch.nn as nn 19 | import torch.nn.init as init 20 | from torch.autograd import Variable 21 | 22 | __all__ = ['get_mean_and_std', 'init_params', 'mkdir_p', 'AverageMeter', 23 | 'progress_bar','save_model',"save_args","set_seed", "IOStream", "cal_loss"] 24 | 25 | 26 | def get_mean_and_std(dataset): 27 | '''Compute the mean and std value of dataset.''' 28 | dataloader = trainloader = torch.utils.data.DataLoader(dataset, batch_size=1, shuffle=True, num_workers=2) 29 | 30 | mean = torch.zeros(3) 31 | std = torch.zeros(3) 32 | print('==> Computing mean and std..') 33 | for inputs, targets in dataloader: 34 | for i in range(3): 35 | mean[i] += inputs[:,i,:,:].mean() 36 | std[i] += inputs[:,i,:,:].std() 37 | mean.div_(len(dataset)) 38 | std.div_(len(dataset)) 39 | return mean, std 40 | 41 | def init_params(net): 42 | '''Init layer parameters.''' 43 | for m in net.modules(): 44 | if isinstance(m, nn.Conv2d): 45 | init.kaiming_normal(m.weight, mode='fan_out') 46 | if m.bias: 47 | init.constant(m.bias, 0) 48 | elif isinstance(m, nn.BatchNorm2d): 49 | init.constant(m.weight, 1) 50 | init.constant(m.bias, 0) 51 | elif isinstance(m, nn.Linear): 52 | init.normal(m.weight, std=1e-3) 53 | if m.bias: 54 | init.constant(m.bias, 0) 55 | 56 | def mkdir_p(path): 57 | '''make dir if not exist''' 58 | try: 59 | os.makedirs(path) 60 | except OSError as exc: # Python >2.5 61 | if exc.errno == errno.EEXIST and os.path.isdir(path): 62 | pass 63 | else: 64 | raise 65 | 66 | class AverageMeter(object): 67 | """Computes and stores the average and current value 68 | Imported from https://github.com/pytorch/examples/blob/master/imagenet/main.py#L247-L262 69 | """ 70 | def __init__(self): 71 | self.reset() 72 | 73 | def reset(self): 74 | self.val = 0 75 | self.avg = 0 76 | self.sum = 0 77 | self.count = 0 78 | 79 | def update(self, val, n=1): 80 | self.val = val 81 | self.sum += val * n 82 | self.count += n 83 | self.avg = self.sum / self.count 84 | 85 | 86 | 87 | TOTAL_BAR_LENGTH = 65. 88 | last_time = time.time() 89 | begin_time = last_time 90 | def progress_bar(current, total, msg=None): 91 | global last_time, begin_time 92 | if current == 0: 93 | begin_time = time.time() # Reset for new bar. 94 | 95 | cur_len = int(TOTAL_BAR_LENGTH*current/total) 96 | rest_len = int(TOTAL_BAR_LENGTH - cur_len) - 1 97 | 98 | sys.stdout.write(' [') 99 | for i in range(cur_len): 100 | sys.stdout.write('=') 101 | sys.stdout.write('>') 102 | for i in range(rest_len): 103 | sys.stdout.write('.') 104 | sys.stdout.write(']') 105 | 106 | cur_time = time.time() 107 | step_time = cur_time - last_time 108 | last_time = cur_time 109 | tot_time = cur_time - begin_time 110 | 111 | L = [] 112 | L.append(' Step: %s' % format_time(step_time)) 113 | L.append(' | Tot: %s' % format_time(tot_time)) 114 | if msg: 115 | L.append(' | ' + msg) 116 | 117 | msg = ''.join(L) 118 | sys.stdout.write(msg) 119 | # for i in range(term_width-int(TOTAL_BAR_LENGTH)-len(msg)-3): 120 | # sys.stdout.write(' ') 121 | 122 | # Go back to the center of the bar. 123 | # for i in range(term_width-int(TOTAL_BAR_LENGTH/2)+2): 124 | # sys.stdout.write('\b') 125 | sys.stdout.write(' %d/%d ' % (current+1, total)) 126 | 127 | if current < total-1: 128 | sys.stdout.write('\r') 129 | else: 130 | sys.stdout.write('\n') 131 | sys.stdout.flush() 132 | 133 | 134 | def format_time(seconds): 135 | days = int(seconds / 3600/24) 136 | seconds = seconds - days*3600*24 137 | hours = int(seconds / 3600) 138 | seconds = seconds - hours*3600 139 | minutes = int(seconds / 60) 140 | seconds = seconds - minutes*60 141 | secondsf = int(seconds) 142 | seconds = seconds - secondsf 143 | millis = int(seconds*1000) 144 | 145 | f = '' 146 | i = 1 147 | if days > 0: 148 | f += str(days) + 'D' 149 | i += 1 150 | if hours > 0 and i <= 2: 151 | f += str(hours) + 'h' 152 | i += 1 153 | if minutes > 0 and i <= 2: 154 | f += str(minutes) + 'm' 155 | i += 1 156 | if secondsf > 0 and i <= 2: 157 | f += str(secondsf) + 's' 158 | i += 1 159 | if millis > 0 and i <= 2: 160 | f += str(millis) + 'ms' 161 | i += 1 162 | if f == '': 163 | f = '0ms' 164 | return f 165 | 166 | 167 | def save_model(net, epoch, path, acc, is_best, **kwargs): 168 | state = { 169 | 'net': net.state_dict(), 170 | 'epoch': epoch, 171 | 'acc': acc 172 | } 173 | for key, value in kwargs.items(): 174 | state[key] = value 175 | filepath = os.path.join(path, "last_checkpoint.pth") 176 | torch.save(state, filepath) 177 | if is_best: 178 | shutil.copyfile(filepath, os.path.join(path, 'best_checkpoint.pth')) 179 | 180 | 181 | 182 | def save_args(args): 183 | file = open(os.path.join(args.checkpoint, 'args.txt'), "w") 184 | for k, v in vars(args).items(): 185 | file.write(f"{k}:\t {v}\n") 186 | file.close() 187 | 188 | 189 | 190 | def set_seed(seed=None): 191 | if seed is None: 192 | return 193 | random.seed(seed) 194 | os.environ['PYTHONHASHSEED'] = ("%s" % seed) 195 | np.random.seed(seed) 196 | torch.manual_seed(seed) 197 | torch.cuda.manual_seed(seed) 198 | torch.cuda.manual_seed_all(seed) 199 | torch.backends.cudnn.benchmark = False 200 | torch.backends.cudnn.deterministic = True 201 | 202 | 203 | 204 | # create a file and write the text into it 205 | class IOStream(): 206 | def __init__(self, path): 207 | self.f = open(path, 'a') 208 | 209 | def cprint(self, text): 210 | print(text) 211 | self.f.write(text+'\n') 212 | self.f.flush() 213 | 214 | def close(self): 215 | self.f.close() 216 | 217 | 218 | def cal_loss(pred, gold, smoothing=True): 219 | ''' Calculate cross entropy loss, apply label smoothing if needed. ''' 220 | 221 | gold = gold.contiguous().view(-1) 222 | 223 | if smoothing: 224 | eps = 0.2 225 | n_class = pred.size(1) 226 | 227 | one_hot = torch.zeros_like(pred).scatter(1, gold.view(-1, 1), 1) 228 | one_hot = one_hot * (1 - eps) + (1 - one_hot) * eps / (n_class - 1) 229 | log_prb = F.log_softmax(pred, dim=1) 230 | 231 | loss = -(one_hot * log_prb).sum(dim=1).mean() 232 | else: 233 | loss = F.cross_entropy(pred, gold, reduction='mean') 234 | 235 | return loss 236 | -------------------------------------------------------------------------------- /classification_ScanObjectNN/utils/misc.py: -------------------------------------------------------------------------------- 1 | '''Some helper functions for PyTorch, including: 2 | - get_mean_and_std: calculate the mean and std value of dataset. 3 | - msr_init: net parameter initialization. 4 | - progress_bar: progress bar mimic xlua.progress. 5 | ''' 6 | import errno 7 | import os 8 | import sys 9 | import time 10 | import math 11 | import torch 12 | import shutil 13 | import numpy as np 14 | import random 15 | import torch.nn.functional as F 16 | 17 | 18 | import torch.nn as nn 19 | import torch.nn.init as init 20 | from torch.autograd import Variable 21 | 22 | __all__ = ['get_mean_and_std', 'init_params', 'mkdir_p', 'AverageMeter', 23 | 'progress_bar','save_model',"save_args","set_seed", "IOStream", "cal_loss"] 24 | 25 | 26 | def get_mean_and_std(dataset): 27 | '''Compute the mean and std value of dataset.''' 28 | dataloader = trainloader = torch.utils.data.DataLoader(dataset, batch_size=1, shuffle=True, num_workers=2) 29 | 30 | mean = torch.zeros(3) 31 | std = torch.zeros(3) 32 | print('==> Computing mean and std..') 33 | for inputs, targets in dataloader: 34 | for i in range(3): 35 | mean[i] += inputs[:,i,:,:].mean() 36 | std[i] += inputs[:,i,:,:].std() 37 | mean.div_(len(dataset)) 38 | std.div_(len(dataset)) 39 | return mean, std 40 | 41 | def init_params(net): 42 | '''Init layer parameters.''' 43 | for m in net.modules(): 44 | if isinstance(m, nn.Conv2d): 45 | init.kaiming_normal(m.weight, mode='fan_out') 46 | if m.bias: 47 | init.constant(m.bias, 0) 48 | elif isinstance(m, nn.BatchNorm2d): 49 | init.constant(m.weight, 1) 50 | init.constant(m.bias, 0) 51 | elif isinstance(m, nn.Linear): 52 | init.normal(m.weight, std=1e-3) 53 | if m.bias: 54 | init.constant(m.bias, 0) 55 | 56 | def mkdir_p(path): 57 | '''make dir if not exist''' 58 | try: 59 | os.makedirs(path) 60 | except OSError as exc: # Python >2.5 61 | if exc.errno == errno.EEXIST and os.path.isdir(path): 62 | pass 63 | else: 64 | raise 65 | 66 | class AverageMeter(object): 67 | """Computes and stores the average and current value 68 | Imported from https://github.com/pytorch/examples/blob/master/imagenet/main.py#L247-L262 69 | """ 70 | def __init__(self): 71 | self.reset() 72 | 73 | def reset(self): 74 | self.val = 0 75 | self.avg = 0 76 | self.sum = 0 77 | self.count = 0 78 | 79 | def update(self, val, n=1): 80 | self.val = val 81 | self.sum += val * n 82 | self.count += n 83 | self.avg = self.sum / self.count 84 | 85 | 86 | 87 | TOTAL_BAR_LENGTH = 65. 88 | last_time = time.time() 89 | begin_time = last_time 90 | def progress_bar(current, total, msg=None): 91 | global last_time, begin_time 92 | if current == 0: 93 | begin_time = time.time() # Reset for new bar. 94 | 95 | cur_len = int(TOTAL_BAR_LENGTH*current/total) 96 | rest_len = int(TOTAL_BAR_LENGTH - cur_len) - 1 97 | 98 | sys.stdout.write(' [') 99 | for i in range(cur_len): 100 | sys.stdout.write('=') 101 | sys.stdout.write('>') 102 | for i in range(rest_len): 103 | sys.stdout.write('.') 104 | sys.stdout.write(']') 105 | 106 | cur_time = time.time() 107 | step_time = cur_time - last_time 108 | last_time = cur_time 109 | tot_time = cur_time - begin_time 110 | 111 | L = [] 112 | L.append(' Step: %s' % format_time(step_time)) 113 | L.append(' | Tot: %s' % format_time(tot_time)) 114 | if msg: 115 | L.append(' | ' + msg) 116 | 117 | msg = ''.join(L) 118 | sys.stdout.write(msg) 119 | # for i in range(term_width-int(TOTAL_BAR_LENGTH)-len(msg)-3): 120 | # sys.stdout.write(' ') 121 | 122 | # Go back to the center of the bar. 123 | # for i in range(term_width-int(TOTAL_BAR_LENGTH/2)+2): 124 | # sys.stdout.write('\b') 125 | sys.stdout.write(' %d/%d ' % (current+1, total)) 126 | 127 | if current < total-1: 128 | sys.stdout.write('\r') 129 | else: 130 | sys.stdout.write('\n') 131 | sys.stdout.flush() 132 | 133 | 134 | def format_time(seconds): 135 | days = int(seconds / 3600/24) 136 | seconds = seconds - days*3600*24 137 | hours = int(seconds / 3600) 138 | seconds = seconds - hours*3600 139 | minutes = int(seconds / 60) 140 | seconds = seconds - minutes*60 141 | secondsf = int(seconds) 142 | seconds = seconds - secondsf 143 | millis = int(seconds*1000) 144 | 145 | f = '' 146 | i = 1 147 | if days > 0: 148 | f += str(days) + 'D' 149 | i += 1 150 | if hours > 0 and i <= 2: 151 | f += str(hours) + 'h' 152 | i += 1 153 | if minutes > 0 and i <= 2: 154 | f += str(minutes) + 'm' 155 | i += 1 156 | if secondsf > 0 and i <= 2: 157 | f += str(secondsf) + 's' 158 | i += 1 159 | if millis > 0 and i <= 2: 160 | f += str(millis) + 'ms' 161 | i += 1 162 | if f == '': 163 | f = '0ms' 164 | return f 165 | 166 | 167 | def save_model(net, epoch, path, acc, is_best, **kwargs): 168 | state = { 169 | 'net': net.state_dict(), 170 | 'epoch': epoch, 171 | 'acc': acc 172 | } 173 | for key, value in kwargs.items(): 174 | state[key] = value 175 | filepath = os.path.join(path, "last_checkpoint.pth") 176 | torch.save(state, filepath) 177 | if is_best: 178 | shutil.copyfile(filepath, os.path.join(path, 'best_checkpoint.pth')) 179 | 180 | 181 | 182 | def save_args(args): 183 | file = open(os.path.join(args.checkpoint, 'args.txt'), "w") 184 | for k, v in vars(args).items(): 185 | file.write(f"{k}:\t {v}\n") 186 | file.close() 187 | 188 | 189 | 190 | def set_seed(seed=None): 191 | if seed is None: 192 | return 193 | random.seed(seed) 194 | os.environ['PYTHONHASHSEED'] = ("%s" % seed) 195 | np.random.seed(seed) 196 | torch.manual_seed(seed) 197 | torch.cuda.manual_seed(seed) 198 | torch.cuda.manual_seed_all(seed) 199 | torch.backends.cudnn.benchmark = False 200 | torch.backends.cudnn.deterministic = True 201 | 202 | 203 | 204 | # create a file and write the text into it 205 | class IOStream(): 206 | def __init__(self, path): 207 | self.f = open(path, 'a') 208 | 209 | def cprint(self, text): 210 | print(text) 211 | self.f.write(text+'\n') 212 | self.f.flush() 213 | 214 | def close(self): 215 | self.f.close() 216 | 217 | 218 | def cal_loss(pred, gold, smoothing=True): 219 | ''' Calculate cross entropy loss, apply label smoothing if needed. ''' 220 | 221 | gold = gold.contiguous().view(-1) 222 | 223 | if smoothing: 224 | eps = 0.2 225 | n_class = pred.size(1) 226 | 227 | one_hot = torch.zeros_like(pred).scatter(1, gold.view(-1, 1), 1) 228 | one_hot = one_hot * (1 - eps) + (1 - one_hot) * eps / (n_class - 1) 229 | log_prb = F.log_softmax(pred, dim=1) 230 | 231 | loss = -(one_hot * log_prb).sum(dim=1).mean() 232 | else: 233 | loss = F.cross_entropy(pred, gold, reduction='mean') 234 | 235 | return loss 236 | -------------------------------------------------------------------------------- /pointnet2_ops_lib/pointnet2_ops/pointnet2_modules.py: -------------------------------------------------------------------------------- 1 | from typing import List, Optional, Tuple 2 | 3 | import torch 4 | import torch.nn as nn 5 | import torch.nn.functional as F 6 | from pointnet2_ops import pointnet2_utils 7 | 8 | 9 | def build_shared_mlp(mlp_spec: List[int], bn: bool = True): 10 | layers = [] 11 | for i in range(1, len(mlp_spec)): 12 | layers.append( 13 | nn.Conv2d(mlp_spec[i - 1], mlp_spec[i], kernel_size=1, bias=not bn) 14 | ) 15 | if bn: 16 | layers.append(nn.BatchNorm2d(mlp_spec[i])) 17 | layers.append(nn.ReLU(True)) 18 | 19 | return nn.Sequential(*layers) 20 | 21 | 22 | class _PointnetSAModuleBase(nn.Module): 23 | def __init__(self): 24 | super(_PointnetSAModuleBase, self).__init__() 25 | self.npoint = None 26 | self.groupers = None 27 | self.mlps = None 28 | 29 | def forward( 30 | self, xyz: torch.Tensor, features: Optional[torch.Tensor] 31 | ) -> Tuple[torch.Tensor, torch.Tensor]: 32 | r""" 33 | Parameters 34 | ---------- 35 | xyz : torch.Tensor 36 | (B, N, 3) tensor of the xyz coordinates of the features 37 | features : torch.Tensor 38 | (B, C, N) tensor of the descriptors of the the features 39 | 40 | Returns 41 | ------- 42 | new_xyz : torch.Tensor 43 | (B, npoint, 3) tensor of the new features' xyz 44 | new_features : torch.Tensor 45 | (B, \sum_k(mlps[k][-1]), npoint) tensor of the new_features descriptors 46 | """ 47 | 48 | new_features_list = [] 49 | 50 | xyz_flipped = xyz.transpose(1, 2).contiguous() 51 | new_xyz = ( 52 | pointnet2_utils.gather_operation( 53 | xyz_flipped, pointnet2_utils.furthest_point_sample(xyz, self.npoint) 54 | ) 55 | .transpose(1, 2) 56 | .contiguous() 57 | if self.npoint is not None 58 | else None 59 | ) 60 | 61 | for i in range(len(self.groupers)): 62 | new_features = self.groupers[i]( 63 | xyz, new_xyz, features 64 | ) # (B, C, npoint, nsample) 65 | 66 | new_features = self.mlps[i](new_features) # (B, mlp[-1], npoint, nsample) 67 | new_features = F.max_pool2d( 68 | new_features, kernel_size=[1, new_features.size(3)] 69 | ) # (B, mlp[-1], npoint, 1) 70 | new_features = new_features.squeeze(-1) # (B, mlp[-1], npoint) 71 | 72 | new_features_list.append(new_features) 73 | 74 | return new_xyz, torch.cat(new_features_list, dim=1) 75 | 76 | 77 | class PointnetSAModuleMSG(_PointnetSAModuleBase): 78 | r"""Pointnet set abstrction layer with multiscale grouping 79 | 80 | Parameters 81 | ---------- 82 | npoint : int 83 | Number of features 84 | radii : list of float32 85 | list of radii to group with 86 | nsamples : list of int32 87 | Number of samples in each ball query 88 | mlps : list of list of int32 89 | Spec of the pointnet before the global max_pool for each scale 90 | bn : bool 91 | Use batchnorm 92 | """ 93 | 94 | def __init__(self, npoint, radii, nsamples, mlps, bn=True, use_xyz=True): 95 | # type: (PointnetSAModuleMSG, int, List[float], List[int], List[List[int]], bool, bool) -> None 96 | super(PointnetSAModuleMSG, self).__init__() 97 | 98 | assert len(radii) == len(nsamples) == len(mlps) 99 | 100 | self.npoint = npoint 101 | self.groupers = nn.ModuleList() 102 | self.mlps = nn.ModuleList() 103 | for i in range(len(radii)): 104 | radius = radii[i] 105 | nsample = nsamples[i] 106 | self.groupers.append( 107 | pointnet2_utils.QueryAndGroup(radius, nsample, use_xyz=use_xyz) 108 | if npoint is not None 109 | else pointnet2_utils.GroupAll(use_xyz) 110 | ) 111 | mlp_spec = mlps[i] 112 | if use_xyz: 113 | mlp_spec[0] += 3 114 | 115 | self.mlps.append(build_shared_mlp(mlp_spec, bn)) 116 | 117 | 118 | class PointnetSAModule(PointnetSAModuleMSG): 119 | r"""Pointnet set abstrction layer 120 | 121 | Parameters 122 | ---------- 123 | npoint : int 124 | Number of features 125 | radius : float 126 | Radius of ball 127 | nsample : int 128 | Number of samples in the ball query 129 | mlp : list 130 | Spec of the pointnet before the global max_pool 131 | bn : bool 132 | Use batchnorm 133 | """ 134 | 135 | def __init__( 136 | self, mlp, npoint=None, radius=None, nsample=None, bn=True, use_xyz=True 137 | ): 138 | # type: (PointnetSAModule, List[int], int, float, int, bool, bool) -> None 139 | super(PointnetSAModule, self).__init__( 140 | mlps=[mlp], 141 | npoint=npoint, 142 | radii=[radius], 143 | nsamples=[nsample], 144 | bn=bn, 145 | use_xyz=use_xyz, 146 | ) 147 | 148 | 149 | class PointnetFPModule(nn.Module): 150 | r"""Propigates the features of one set to another 151 | 152 | Parameters 153 | ---------- 154 | mlp : list 155 | Pointnet module parameters 156 | bn : bool 157 | Use batchnorm 158 | """ 159 | 160 | def __init__(self, mlp, bn=True): 161 | # type: (PointnetFPModule, List[int], bool) -> None 162 | super(PointnetFPModule, self).__init__() 163 | self.mlp = build_shared_mlp(mlp, bn=bn) 164 | 165 | def forward(self, unknown, known, unknow_feats, known_feats): 166 | # type: (PointnetFPModule, torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor) -> torch.Tensor 167 | r""" 168 | Parameters 169 | ---------- 170 | unknown : torch.Tensor 171 | (B, n, 3) tensor of the xyz positions of the unknown features 172 | known : torch.Tensor 173 | (B, m, 3) tensor of the xyz positions of the known features 174 | unknow_feats : torch.Tensor 175 | (B, C1, n) tensor of the features to be propigated to 176 | known_feats : torch.Tensor 177 | (B, C2, m) tensor of features to be propigated 178 | 179 | Returns 180 | ------- 181 | new_features : torch.Tensor 182 | (B, mlp[-1], n) tensor of the features of the unknown features 183 | """ 184 | 185 | if known is not None: 186 | dist, idx = pointnet2_utils.three_nn(unknown, known) 187 | dist_recip = 1.0 / (dist + 1e-8) 188 | norm = torch.sum(dist_recip, dim=2, keepdim=True) 189 | weight = dist_recip / norm 190 | 191 | interpolated_feats = pointnet2_utils.three_interpolate( 192 | known_feats, idx, weight 193 | ) 194 | else: 195 | interpolated_feats = known_feats.expand( 196 | *(known_feats.size()[0:2] + [unknown.size(1)]) 197 | ) 198 | 199 | if unknow_feats is not None: 200 | new_features = torch.cat( 201 | [interpolated_feats, unknow_feats], dim=1 202 | ) # (B, C2 + C1, n) 203 | else: 204 | new_features = interpolated_feats 205 | 206 | new_features = new_features.unsqueeze(-1) 207 | new_features = self.mlp(new_features) 208 | 209 | return new_features.squeeze(-1) 210 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rethinking Network Design and Local Geometry in Point Cloud: A Simple Residual MLP Framework (ICLR 2022) 2 | 3 | 4 | 5 | [![PWC](https://img.shields.io/endpoint.svg?url=https://paperswithcode.com/badge/rethinking-network-design-and-local-geometry-1/3d-point-cloud-classification-on-modelnet40)](https://paperswithcode.com/sota/3d-point-cloud-classification-on-modelnet40?p=rethinking-network-design-and-local-geometry-1) 6 | [![PWC](https://img.shields.io/endpoint.svg?url=https://paperswithcode.com/badge/rethinking-network-design-and-local-geometry-1/3d-point-cloud-classification-on-scanobjectnn)](https://paperswithcode.com/sota/3d-point-cloud-classification-on-scanobjectnn?p=rethinking-network-design-and-local-geometry-1) 7 | 8 | 9 | [![github](https://img.shields.io/github/stars/ma-xu/pointMLP-pytorch?style=social)](https://github.com/ma-xu/pointMLP-pytorch) 10 | 11 | 12 |
13 | 14 | 15 | 16 |
17 | 18 | [open review](https://openreview.net/forum?id=3Pbra-_u76D) | [arXiv](https://arxiv.org/abs/2202.07123) | Primary contact: [Xu Ma](mailto:ma.xu1@northeastern.edu) 19 | 20 |
21 | 22 |
23 | 24 | Overview of one stage in PointMLP. Given an input point cloud, PointMLP progressively extracts local features using residual point MLP blocks. In each stage, we first transform the local point using a geometric affine module, and then local points are extracted before and after aggregation, respectively. By repeating multiple stages, PointMLP progressively enlarges the receptive field and models entire point cloud geometric information. 25 | 26 | 27 | ## BibTeX 28 | 29 | @article{ma2022rethinking, 30 | title={Rethinking network design and local geometry in point cloud: A simple residual MLP framework}, 31 | author={Ma, Xu and Qin, Can and You, Haoxuan and Ran, Haoxi and Fu, Yun}, 32 | journal={arXiv preprint arXiv:2202.07123}, 33 | year={2022} 34 | } 35 | 36 | ## Model Zoo 37 | 38 | **Questions on ModelNet40 classification results (a common issue for ModelNet40 dataset in the community)** 39 | 40 | The performance on ModelNet40 of almost all methods are not stable, see (https://github.com/CVMI-Lab/PAConv/issues/9#issuecomment-873371422).
41 | If you run the same codes for several times, you will get different results (even with fixed seed).
42 | The best way to reproduce the results is to test with a pretrained model for ModelNet40.
43 | Also, the randomness of ModelNet40 is our motivation to experiment on ScanObjectNN, and to report the mean/std results of several runs. 44 | 45 | 46 | 47 | ------ 48 | 49 | The codes/models/logs for submission version (without bug fixed) can be found here [commit:d2b8dbaa](http://github.com/13952522076/pointMLP-pytorch/tree/d2b8dbaa06eb6176b222dcf2ad248f8438582026). 50 | 51 | On ModelNet40, fixed pointMLP achieves a result of **91.5% mAcc** and **94.1% OA** without voting, logs and pretrained models can be found [[here]](https://web.northeastern.edu/smilelab/xuma/pointMLP/checkpoints/fixstd/modelnet40/pointMLP-20220209053148-404/). 52 | 53 | On ScanObjectNN, fixed pointMLP achieves a result of **84.4% mAcc** and **86.1% OA** without voting, logs and pretrained models can be found [[here]](https://web.northeastern.edu/smilelab/xuma/pointMLP/checkpoints/fixstd/scanobjectnn/pointMLP-20220204021453/). Fixed pointMLP-elite achieves a result of **81.7% mAcc** and **84.1% OA** without voting, logs and pretrained models can be found [[here]](https://web.northeastern.edu/smilelab/xuma/pointMLP/checkpoints/fixstd/scanobjectnn/model313Elite-20220220015842-2956/). 54 | 55 | Stay tuned. More elite versions and voting results will be uploaded. 56 | 57 | 58 | 59 | ## News & Updates: 60 | 61 | - [x] **Apr/24/2024**: University server is down. Update the ScanobjectNN dataset link. 62 | - [x] fix the uncomplete utils in partseg by Mar/10, caused by error uplaoded folder. 63 | - [x] upload test code for ModelNet40 64 | - [x] update std bug (unstable testing in previous version) 65 | - [x] paper/codes release 66 | 67 | :point_right::point_right::point_right:**NOTE:** The codes/models/logs for submission version (without bug fixed) can be found here [commit:d2b8dbaa](http://github.com/13952522076/pointMLP-pytorch/tree/d2b8dbaa06eb6176b222dcf2ad248f8438582026). 68 | 69 | 70 | 71 | 72 | ## Install 73 | 74 | ```bash 75 | # step 1. clone this repo 76 | git clone https://github.com/ma-xu/pointMLP-pytorch.git 77 | cd pointMLP-pytorch 78 | 79 | # step 2. create a conda virtual environment and activate it 80 | conda env create 81 | conda activate pointmlp 82 | ``` 83 | 84 | ```bash 85 | # Optional solution for step 2: install libs step by step 86 | conda create -n pointmlp python=3.7 -y 87 | conda activate pointmlp 88 | conda install pytorch==1.10.1 torchvision==0.11.2 cudatoolkit=10.2 -c pytorch -y 89 | # if you are using Ampere GPUs (e.g., A100 and 30X0), please install compatible Pytorch and CUDA versions, like: 90 | # pip install torch==1.8.1+cu111 torchvision==0.9.1+cu111 torchaudio==0.8.1 -f https://download.pytorch.org/whl/torch_stable.html 91 | pip install cycler einops h5py pyyaml==5.4.1 scikit-learn==0.24.2 scipy tqdm matplotlib==3.4.2 92 | pip install pointnet2_ops_lib/. 93 | ``` 94 | 95 | 96 | ## Useage 97 | 98 | ### Classification ModelNet40 99 | **Train**: The dataset will be automatically downloaded, run following command to train. 100 | 101 | By default, it will create a folder named "checkpoints/{modelName}-{msg}-{randomseed}", which includes args.txt, best_checkpoint.pth, last_checkpoint.pth, log.txt, out.txt. 102 | ```bash 103 | cd classification_ModelNet40 104 | # train pointMLP 105 | python main.py --model pointMLP 106 | # train pointMLP-elite 107 | python main.py --model pointMLPElite 108 | # please add other paramemters as you wish. 109 | ``` 110 | 111 | 112 | To conduct voting testing, run 113 | ```bash 114 | # please modify the msg accrodingly 115 | python voting.py --model pointMLP --msg demo 116 | ``` 117 | 118 | 119 | ### Classification ScanObjectNN 120 | 121 | The dataset will be automatically downloaded 122 | 123 | - Train pointMLP/pointMLPElite 124 | ```bash 125 | cd classification_ScanObjectNN 126 | # train pointMLP 127 | python main.py --model pointMLP 128 | # train pointMLP-elite 129 | python main.py --model pointMLPElite 130 | # please add other paramemters as you wish. 131 | ``` 132 | By default, it will create a fold named "checkpoints/{modelName}-{msg}-{randomseed}", which includes args.txt, best_checkpoint.pth, last_checkpoint.pth, log.txt, out.txt. 133 | 134 | 135 | ### Part segmentation 136 | 137 | - Make data folder and download the dataset 138 | ```bash 139 | cd part_segmentation 140 | mkdir data 141 | cd data 142 | wget https://shapenet.cs.stanford.edu/media/shapenetcore_partanno_segmentation_benchmark_v0_normal.zip --no-check-certificate 143 | unzip shapenetcore_partanno_segmentation_benchmark_v0_normal.zip 144 | ``` 145 | 146 | - Train pointMLP 147 | ```bash 148 | # train pointMLP 149 | python main.py --model pointMLP 150 | # please add other paramemters as you wish. 151 | ``` 152 | 153 | 154 | ## Acknowledgment 155 | 156 | Our implementation is mainly based on the following codebases. We gratefully thank the authors for their wonderful works. 157 | 158 | [CurveNet](https://github.com/tiangexiang/CurveNet), 159 | [PAConv](https://github.com/CVMI-Lab/PAConv), 160 | [GDANet](https://github.com/mutianxu/GDANet), 161 | [Pointnet2_PyTorch](https://github.com/erikwijmans/Pointnet2_PyTorch) 162 | 163 | ## LICENSE 164 | PointMLP is under the Apache-2.0 license. 165 | 166 | 167 | 168 | 169 | 170 | 171 | -------------------------------------------------------------------------------- /pointnet2_ops_lib/pointnet2_ops/_ext-src/src/sampling_gpu.cu: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "cuda_utils.h" 5 | 6 | // input: points(b, c, n) idx(b, m) 7 | // output: out(b, c, m) 8 | __global__ void gather_points_kernel(int b, int c, int n, int m, 9 | const float *__restrict__ points, 10 | const int *__restrict__ idx, 11 | float *__restrict__ out) { 12 | for (int i = blockIdx.x; i < b; i += gridDim.x) { 13 | for (int l = blockIdx.y; l < c; l += gridDim.y) { 14 | for (int j = threadIdx.x; j < m; j += blockDim.x) { 15 | int a = idx[i * m + j]; 16 | out[(i * c + l) * m + j] = points[(i * c + l) * n + a]; 17 | } 18 | } 19 | } 20 | } 21 | 22 | void gather_points_kernel_wrapper(int b, int c, int n, int npoints, 23 | const float *points, const int *idx, 24 | float *out) { 25 | gather_points_kernel<<>>(b, c, n, npoints, 27 | points, idx, out); 28 | 29 | CUDA_CHECK_ERRORS(); 30 | } 31 | 32 | // input: grad_out(b, c, m) idx(b, m) 33 | // output: grad_points(b, c, n) 34 | __global__ void gather_points_grad_kernel(int b, int c, int n, int m, 35 | const float *__restrict__ grad_out, 36 | const int *__restrict__ idx, 37 | float *__restrict__ grad_points) { 38 | for (int i = blockIdx.x; i < b; i += gridDim.x) { 39 | for (int l = blockIdx.y; l < c; l += gridDim.y) { 40 | for (int j = threadIdx.x; j < m; j += blockDim.x) { 41 | int a = idx[i * m + j]; 42 | atomicAdd(grad_points + (i * c + l) * n + a, 43 | grad_out[(i * c + l) * m + j]); 44 | } 45 | } 46 | } 47 | } 48 | 49 | void gather_points_grad_kernel_wrapper(int b, int c, int n, int npoints, 50 | const float *grad_out, const int *idx, 51 | float *grad_points) { 52 | gather_points_grad_kernel<<>>( 54 | b, c, n, npoints, grad_out, idx, grad_points); 55 | 56 | CUDA_CHECK_ERRORS(); 57 | } 58 | 59 | __device__ void __update(float *__restrict__ dists, int *__restrict__ dists_i, 60 | int idx1, int idx2) { 61 | const float v1 = dists[idx1], v2 = dists[idx2]; 62 | const int i1 = dists_i[idx1], i2 = dists_i[idx2]; 63 | dists[idx1] = max(v1, v2); 64 | dists_i[idx1] = v2 > v1 ? i2 : i1; 65 | } 66 | 67 | // Input dataset: (b, n, 3), tmp: (b, n) 68 | // Ouput idxs (b, m) 69 | template 70 | __global__ void furthest_point_sampling_kernel( 71 | int b, int n, int m, const float *__restrict__ dataset, 72 | float *__restrict__ temp, int *__restrict__ idxs) { 73 | if (m <= 0) return; 74 | __shared__ float dists[block_size]; 75 | __shared__ int dists_i[block_size]; 76 | 77 | int batch_index = blockIdx.x; 78 | dataset += batch_index * n * 3; 79 | temp += batch_index * n; 80 | idxs += batch_index * m; 81 | 82 | int tid = threadIdx.x; 83 | const int stride = block_size; 84 | 85 | int old = 0; 86 | if (threadIdx.x == 0) idxs[0] = old; 87 | 88 | __syncthreads(); 89 | for (int j = 1; j < m; j++) { 90 | int besti = 0; 91 | float best = -1; 92 | float x1 = dataset[old * 3 + 0]; 93 | float y1 = dataset[old * 3 + 1]; 94 | float z1 = dataset[old * 3 + 2]; 95 | for (int k = tid; k < n; k += stride) { 96 | float x2, y2, z2; 97 | x2 = dataset[k * 3 + 0]; 98 | y2 = dataset[k * 3 + 1]; 99 | z2 = dataset[k * 3 + 2]; 100 | float mag = (x2 * x2) + (y2 * y2) + (z2 * z2); 101 | if (mag <= 1e-3) continue; 102 | 103 | float d = 104 | (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1) + (z2 - z1) * (z2 - z1); 105 | 106 | float d2 = min(d, temp[k]); 107 | temp[k] = d2; 108 | besti = d2 > best ? k : besti; 109 | best = d2 > best ? d2 : best; 110 | } 111 | dists[tid] = best; 112 | dists_i[tid] = besti; 113 | __syncthreads(); 114 | 115 | if (block_size >= 512) { 116 | if (tid < 256) { 117 | __update(dists, dists_i, tid, tid + 256); 118 | } 119 | __syncthreads(); 120 | } 121 | if (block_size >= 256) { 122 | if (tid < 128) { 123 | __update(dists, dists_i, tid, tid + 128); 124 | } 125 | __syncthreads(); 126 | } 127 | if (block_size >= 128) { 128 | if (tid < 64) { 129 | __update(dists, dists_i, tid, tid + 64); 130 | } 131 | __syncthreads(); 132 | } 133 | if (block_size >= 64) { 134 | if (tid < 32) { 135 | __update(dists, dists_i, tid, tid + 32); 136 | } 137 | __syncthreads(); 138 | } 139 | if (block_size >= 32) { 140 | if (tid < 16) { 141 | __update(dists, dists_i, tid, tid + 16); 142 | } 143 | __syncthreads(); 144 | } 145 | if (block_size >= 16) { 146 | if (tid < 8) { 147 | __update(dists, dists_i, tid, tid + 8); 148 | } 149 | __syncthreads(); 150 | } 151 | if (block_size >= 8) { 152 | if (tid < 4) { 153 | __update(dists, dists_i, tid, tid + 4); 154 | } 155 | __syncthreads(); 156 | } 157 | if (block_size >= 4) { 158 | if (tid < 2) { 159 | __update(dists, dists_i, tid, tid + 2); 160 | } 161 | __syncthreads(); 162 | } 163 | if (block_size >= 2) { 164 | if (tid < 1) { 165 | __update(dists, dists_i, tid, tid + 1); 166 | } 167 | __syncthreads(); 168 | } 169 | 170 | old = dists_i[0]; 171 | if (tid == 0) idxs[j] = old; 172 | } 173 | } 174 | 175 | void furthest_point_sampling_kernel_wrapper(int b, int n, int m, 176 | const float *dataset, float *temp, 177 | int *idxs) { 178 | unsigned int n_threads = opt_n_threads(n); 179 | 180 | cudaStream_t stream = at::cuda::getCurrentCUDAStream(); 181 | 182 | switch (n_threads) { 183 | case 512: 184 | furthest_point_sampling_kernel<512> 185 | <<>>(b, n, m, dataset, temp, idxs); 186 | break; 187 | case 256: 188 | furthest_point_sampling_kernel<256> 189 | <<>>(b, n, m, dataset, temp, idxs); 190 | break; 191 | case 128: 192 | furthest_point_sampling_kernel<128> 193 | <<>>(b, n, m, dataset, temp, idxs); 194 | break; 195 | case 64: 196 | furthest_point_sampling_kernel<64> 197 | <<>>(b, n, m, dataset, temp, idxs); 198 | break; 199 | case 32: 200 | furthest_point_sampling_kernel<32> 201 | <<>>(b, n, m, dataset, temp, idxs); 202 | break; 203 | case 16: 204 | furthest_point_sampling_kernel<16> 205 | <<>>(b, n, m, dataset, temp, idxs); 206 | break; 207 | case 8: 208 | furthest_point_sampling_kernel<8> 209 | <<>>(b, n, m, dataset, temp, idxs); 210 | break; 211 | case 4: 212 | furthest_point_sampling_kernel<4> 213 | <<>>(b, n, m, dataset, temp, idxs); 214 | break; 215 | case 2: 216 | furthest_point_sampling_kernel<2> 217 | <<>>(b, n, m, dataset, temp, idxs); 218 | break; 219 | case 1: 220 | furthest_point_sampling_kernel<1> 221 | <<>>(b, n, m, dataset, temp, idxs); 222 | break; 223 | default: 224 | furthest_point_sampling_kernel<512> 225 | <<>>(b, n, m, dataset, temp, idxs); 226 | } 227 | 228 | CUDA_CHECK_ERRORS(); 229 | } 230 | -------------------------------------------------------------------------------- /classification_ModelNet40/voting.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import os 3 | import datetime 4 | import torch 5 | import torch.nn.parallel 6 | import torch.backends.cudnn as cudnn 7 | import torch.optim 8 | import torch.utils.data 9 | import torch.utils.data.distributed 10 | from torch.utils.data import DataLoader 11 | import models as models 12 | from utils import progress_bar, IOStream 13 | from data import ModelNet40 14 | import sklearn.metrics as metrics 15 | from helper import cal_loss 16 | import numpy as np 17 | import torch.nn.functional as F 18 | 19 | model_names = sorted(name for name in models.__dict__ 20 | if callable(models.__dict__[name])) 21 | 22 | 23 | def parse_args(): 24 | """Parameters""" 25 | parser = argparse.ArgumentParser('training') 26 | parser.add_argument('-c', '--checkpoint', type=str, metavar='PATH', 27 | help='path to save checkpoint (default: checkpoint)') 28 | parser.add_argument('--msg', type=str, help='message after checkpoint') 29 | parser.add_argument('--batch_size', type=int, default=32, help='batch size in training') 30 | parser.add_argument('--model', default='model31A', help='model name [default: pointnet_cls]') 31 | parser.add_argument('--num_classes', default=40, type=int, choices=[10, 40], help='training on ModelNet10/40') 32 | parser.add_argument('--num_points', type=int, default=1024, help='Point Number') 33 | parser.add_argument('--seed', type=int, help='random seed (default: 1)') 34 | 35 | # Voting evaluation, referring: https://github.com/CVMI-Lab/PAConv/blob/main/obj_cls/eval_voting.py 36 | parser.add_argument('--NUM_PEPEAT', type=int, default=300) 37 | parser.add_argument('--NUM_VOTE', type=int, default=10) 38 | 39 | parser.add_argument('--validate', action='store_true', help='Validate the original testing result.') 40 | return parser.parse_args() 41 | 42 | 43 | class PointcloudScale(object): # input random scaling 44 | def __init__(self, scale_low=2. / 3., scale_high=3. / 2.): 45 | self.scale_low = scale_low 46 | self.scale_high = scale_high 47 | 48 | def __call__(self, pc): 49 | bsize = pc.size()[0] 50 | for i in range(bsize): 51 | xyz1 = np.random.uniform(low=self.scale_low, high=self.scale_high, size=[3]) 52 | pc[i, :, 0:3] = torch.mul(pc[i, :, 0:3], torch.from_numpy(xyz1).float().cuda()) 53 | 54 | return pc 55 | 56 | 57 | def main(): 58 | args = parse_args() 59 | print(f"args: {args}") 60 | os.environ["HDF5_USE_FILE_LOCKING"] = "FALSE" 61 | if args.seed is None: 62 | args.seed = np.random.randint(1, 10000) 63 | print(f"random seed is set to {args.seed}, the speed will slow down.") 64 | torch.manual_seed(args.seed) 65 | np.random.seed(args.seed) 66 | torch.cuda.manual_seed_all(args.seed) 67 | torch.cuda.manual_seed(args.seed) 68 | torch.set_printoptions(10) 69 | torch.backends.cudnn.benchmark = False 70 | torch.backends.cudnn.deterministic = True 71 | os.environ['PYTHONHASHSEED'] = str(args.seed) 72 | if torch.cuda.is_available(): 73 | device = 'cuda' 74 | else: 75 | device = 'cpu' 76 | print(f"==> Using device: {device}") 77 | if args.msg is None: 78 | message = str(datetime.datetime.now().strftime('-%Y%m%d%H%M%S')) 79 | else: 80 | message = "-" + args.msg 81 | args.checkpoint = 'checkpoints/' + args.model + message 82 | 83 | print('==> Preparing data..') 84 | test_loader = DataLoader(ModelNet40(partition='test', num_points=args.num_points), num_workers=4, 85 | batch_size=args.batch_size // 2, shuffle=False, drop_last=False) 86 | # Model 87 | print('==> Building model..') 88 | net = models.__dict__[args.model]() 89 | criterion = cal_loss 90 | net = net.to(device) 91 | checkpoint_path = os.path.join(args.checkpoint, 'best_checkpoint.pth') 92 | checkpoint = torch.load(checkpoint_path, map_location=torch.device('cpu')) 93 | # criterion = criterion.to(device) 94 | if device == 'cuda': 95 | net = torch.nn.DataParallel(net) 96 | cudnn.benchmark = True 97 | net.load_state_dict(checkpoint['net']) 98 | 99 | if args.validate: 100 | test_out = validate(net, test_loader, criterion, device) 101 | print(f"Vanilla out: {test_out}") 102 | print(f"Note 1: Please also load the random seed parameter (if forgot, see out.txt).\n" 103 | f"Note 2: This result may vary little on different GPUs (and number of GPUs), we tested 2080Ti, P100, and V100.\n" 104 | f"[note : Original result is achieved with V100 GPUs.]\n\n\n") 105 | # Interestingly, we get original best_test_acc on 4 V100 gpus, but this model is trained on one V100 gpu. 106 | # On different GPUs, and different number of GPUs, both OA and mean_acc vary a little. 107 | # Also, the batch size also affect the testing results, could not understand. 108 | 109 | print(f"===> start voting evaluation...") 110 | voting(net, test_loader, device, args) 111 | 112 | 113 | def validate(net, testloader, criterion, device): 114 | net.eval() 115 | test_loss = 0 116 | correct = 0 117 | total = 0 118 | test_true = [] 119 | test_pred = [] 120 | time_cost = datetime.datetime.now() 121 | with torch.no_grad(): 122 | for batch_idx, (data, label) in enumerate(testloader): 123 | data, label = data.to(device), label.to(device).squeeze() 124 | data = data.permute(0, 2, 1) 125 | logits = net(data) 126 | loss = criterion(logits, label) 127 | test_loss += loss.item() 128 | preds = logits.max(dim=1)[1] 129 | test_true.append(label.cpu().numpy()) 130 | test_pred.append(preds.detach().cpu().numpy()) 131 | total += label.size(0) 132 | correct += preds.eq(label).sum().item() 133 | progress_bar(batch_idx, len(testloader), 'Loss: %.3f | Acc: %.3f%% (%d/%d)' 134 | % (test_loss / (batch_idx + 1), 100. * correct / total, correct, total)) 135 | 136 | time_cost = int((datetime.datetime.now() - time_cost).total_seconds()) 137 | test_true = np.concatenate(test_true) 138 | test_pred = np.concatenate(test_pred) 139 | return { 140 | "loss": float("%.3f" % (test_loss / (batch_idx + 1))), 141 | "acc": float("%.3f" % (100. * metrics.accuracy_score(test_true, test_pred))), 142 | "acc_avg": float("%.3f" % (100. * metrics.balanced_accuracy_score(test_true, test_pred))), 143 | "time": time_cost 144 | } 145 | 146 | 147 | def voting(net, testloader, device, args): 148 | name = '/evaluate_voting' + str(datetime.datetime.now().strftime('-%Y%m%d%H%M%S')) + 'seed_' + str( 149 | args.seed) + '.log' 150 | io = IOStream(args.checkpoint + name) 151 | io.cprint(str(args)) 152 | 153 | net.eval() 154 | best_acc = 0 155 | best_mean_acc = 0 156 | # pointscale = PointcloudScale(scale_low=0.8, scale_high=1.18) # set the range of scaling 157 | # pointscale = PointcloudScale() 158 | pointscale = PointcloudScale(scale_low=0.85, scale_high=1.15) 159 | 160 | for i in range(args.NUM_PEPEAT): 161 | test_true = [] 162 | test_pred = [] 163 | 164 | for batch_idx, (data, label) in enumerate(testloader): 165 | data, label = data.to(device), label.to(device).squeeze() 166 | pred = 0 167 | for v in range(args.NUM_VOTE): 168 | new_data = data 169 | # batch_size = data.size()[0] 170 | if v > 0: 171 | new_data.data = pointscale(new_data.data) 172 | with torch.no_grad(): 173 | pred += F.softmax(net(new_data.permute(0, 2, 1)), dim=1) # sum 10 preds 174 | pred /= args.NUM_VOTE # avg the preds! 175 | label = label.view(-1) 176 | pred_choice = pred.max(dim=1)[1] 177 | test_true.append(label.cpu().numpy()) 178 | test_pred.append(pred_choice.detach().cpu().numpy()) 179 | test_true = np.concatenate(test_true) 180 | test_pred = np.concatenate(test_pred) 181 | test_acc = 100. * metrics.accuracy_score(test_true, test_pred) 182 | test_mean_acc = 100. * metrics.balanced_accuracy_score(test_true, test_pred) 183 | if test_acc > best_acc: 184 | best_acc = test_acc 185 | if test_mean_acc > best_mean_acc: 186 | best_mean_acc = test_mean_acc 187 | outstr = 'Voting %d, test acc: %.3f, test mean acc: %.3f, [current best(all_acc: %.3f mean_acc: %.3f)]' % \ 188 | (i, test_acc, test_mean_acc, best_acc, best_mean_acc) 189 | io.cprint(outstr) 190 | 191 | final_outstr = 'Final voting test acc: %.6f,' % (best_acc * 100) 192 | io.cprint(final_outstr) 193 | 194 | 195 | if __name__ == '__main__': 196 | main() 197 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /classification_ScanObjectNN/main.py: -------------------------------------------------------------------------------- 1 | """ 2 | for training with resume functions. 3 | Usage: 4 | python main.py --model PointNet --msg demo 5 | or 6 | CUDA_VISIBLE_DEVICES=0 nohup python main.py --model PointNet --msg demo > nohup/PointNet_demo.out & 7 | """ 8 | import argparse 9 | import os 10 | import logging 11 | import datetime 12 | import torch 13 | import torch.nn.parallel 14 | import torch.backends.cudnn as cudnn 15 | import torch.optim 16 | import torch.utils.data 17 | import torch.utils.data.distributed 18 | from torch.utils.data import DataLoader 19 | import models as models 20 | from utils import Logger, mkdir_p, progress_bar, save_model, save_args, cal_loss 21 | from ScanObjectNN import ScanObjectNN 22 | from torch.optim.lr_scheduler import CosineAnnealingLR 23 | import sklearn.metrics as metrics 24 | import numpy as np 25 | 26 | 27 | def parse_args(): 28 | """Parameters""" 29 | parser = argparse.ArgumentParser('training') 30 | parser.add_argument('-c', '--checkpoint', type=str, metavar='PATH', 31 | help='path to save checkpoint (default: checkpoint)') 32 | parser.add_argument('--msg', type=str, help='message after checkpoint') 33 | parser.add_argument('--batch_size', type=int, default=32, help='batch size in training') 34 | parser.add_argument('--model', default='PointNet', help='model name [default: pointnet_cls]') 35 | parser.add_argument('--num_classes', default=15, type=int, help='default value for classes of ScanObjectNN') 36 | parser.add_argument('--epoch', default=200, type=int, help='number of epoch in training') 37 | parser.add_argument('--num_points', type=int, default=1024, help='Point Number') 38 | parser.add_argument('--learning_rate', default=0.01, type=float, help='learning rate in training') 39 | parser.add_argument('--weight_decay', type=float, default=1e-4, help='decay rate') 40 | parser.add_argument('--smoothing', action='store_true', default=False, help='loss smoothing') 41 | parser.add_argument('--seed', type=int, help='random seed') 42 | parser.add_argument('--workers', default=4, type=int, help='workers') 43 | return parser.parse_args() 44 | 45 | 46 | def main(): 47 | args = parse_args() 48 | os.environ["HDF5_USE_FILE_LOCKING"] = "FALSE" 49 | if args.seed is not None: 50 | torch.manual_seed(args.seed) 51 | if torch.cuda.is_available(): 52 | device = 'cuda' 53 | if args.seed is not None: 54 | torch.cuda.manual_seed(args.seed) 55 | else: 56 | device = 'cpu' 57 | time_str = str(datetime.datetime.now().strftime('-%Y%m%d%H%M%S')) 58 | if args.msg is None: 59 | message = time_str 60 | else: 61 | message = "-" + args.msg 62 | args.checkpoint = 'checkpoints/' + args.model + message 63 | if not os.path.isdir(args.checkpoint): 64 | mkdir_p(args.checkpoint) 65 | 66 | screen_logger = logging.getLogger("Model") 67 | screen_logger.setLevel(logging.INFO) 68 | formatter = logging.Formatter('%(message)s') 69 | file_handler = logging.FileHandler(os.path.join(args.checkpoint, "out.txt")) 70 | file_handler.setLevel(logging.INFO) 71 | file_handler.setFormatter(formatter) 72 | screen_logger.addHandler(file_handler) 73 | 74 | def printf(str): 75 | screen_logger.info(str) 76 | print(str) 77 | 78 | # Model 79 | printf(f"args: {args}") 80 | printf('==> Building model..') 81 | net = models.__dict__[args.model](num_classes=args.num_classes) 82 | criterion = cal_loss 83 | net = net.to(device) 84 | # criterion = criterion.to(device) 85 | if device == 'cuda': 86 | net = torch.nn.DataParallel(net) 87 | cudnn.benchmark = True 88 | 89 | best_test_acc = 0. # best test accuracy 90 | best_train_acc = 0. 91 | best_test_acc_avg = 0. 92 | best_train_acc_avg = 0. 93 | best_test_loss = float("inf") 94 | best_train_loss = float("inf") 95 | start_epoch = 0 # start from epoch 0 or last checkpoint epoch 96 | optimizer_dict = None 97 | 98 | if not os.path.isfile(os.path.join(args.checkpoint, "last_checkpoint.pth")): 99 | save_args(args) 100 | logger = Logger(os.path.join(args.checkpoint, 'log.txt'), title="ModelNet" + args.model) 101 | logger.set_names(["Epoch-Num", 'Learning-Rate', 102 | 'Train-Loss', 'Train-acc-B', 'Train-acc', 103 | 'Valid-Loss', 'Valid-acc-B', 'Valid-acc']) 104 | else: 105 | printf(f"Resuming last checkpoint from {args.checkpoint}") 106 | checkpoint_path = os.path.join(args.checkpoint, "last_checkpoint.pth") 107 | checkpoint = torch.load(checkpoint_path) 108 | net.load_state_dict(checkpoint['net']) 109 | start_epoch = checkpoint['epoch'] 110 | best_test_acc = checkpoint['best_test_acc'] 111 | best_train_acc = checkpoint['best_train_acc'] 112 | best_test_acc_avg = checkpoint['best_test_acc_avg'] 113 | best_train_acc_avg = checkpoint['best_train_acc_avg'] 114 | best_test_loss = checkpoint['best_test_loss'] 115 | best_train_loss = checkpoint['best_train_loss'] 116 | logger = Logger(os.path.join(args.checkpoint, 'log.txt'), title="ModelNet" + args.model, resume=True) 117 | optimizer_dict = checkpoint['optimizer'] 118 | 119 | printf('==> Preparing data..') 120 | train_loader = DataLoader(ScanObjectNN(partition='training', num_points=args.num_points), num_workers=args.workers, 121 | batch_size=args.batch_size, shuffle=True, drop_last=True) 122 | test_loader = DataLoader(ScanObjectNN(partition='test', num_points=args.num_points), num_workers=args.workers, 123 | batch_size=args.batch_size, shuffle=True, drop_last=False) 124 | 125 | optimizer = torch.optim.SGD(net.parameters(), lr=args.learning_rate, momentum=0.9, weight_decay=args.weight_decay) 126 | if optimizer_dict is not None: 127 | optimizer.load_state_dict(optimizer_dict) 128 | scheduler = CosineAnnealingLR(optimizer, args.epoch, eta_min=args.learning_rate / 100, last_epoch=start_epoch - 1) 129 | 130 | for epoch in range(start_epoch, args.epoch): 131 | printf('Epoch(%d/%s) Learning Rate %s:' % (epoch + 1, args.epoch, optimizer.param_groups[0]['lr'])) 132 | train_out = train(net, train_loader, optimizer, criterion, device) # {"loss", "acc", "acc_avg", "time"} 133 | test_out = validate(net, test_loader, criterion, device) 134 | scheduler.step() 135 | 136 | if test_out["acc"] > best_test_acc: 137 | best_test_acc = test_out["acc"] 138 | is_best = True 139 | else: 140 | is_best = False 141 | 142 | best_test_acc = test_out["acc"] if (test_out["acc"] > best_test_acc) else best_test_acc 143 | best_train_acc = train_out["acc"] if (train_out["acc"] > best_train_acc) else best_train_acc 144 | best_test_acc_avg = test_out["acc_avg"] if (test_out["acc_avg"] > best_test_acc_avg) else best_test_acc_avg 145 | best_train_acc_avg = train_out["acc_avg"] if (train_out["acc_avg"] > best_train_acc_avg) else best_train_acc_avg 146 | best_test_loss = test_out["loss"] if (test_out["loss"] < best_test_loss) else best_test_loss 147 | best_train_loss = train_out["loss"] if (train_out["loss"] < best_train_loss) else best_train_loss 148 | 149 | save_model( 150 | net, epoch, path=args.checkpoint, acc=test_out["acc"], is_best=is_best, 151 | best_test_acc=best_test_acc, # best test accuracy 152 | best_train_acc=best_train_acc, 153 | best_test_acc_avg=best_test_acc_avg, 154 | best_train_acc_avg=best_train_acc_avg, 155 | best_test_loss=best_test_loss, 156 | best_train_loss=best_train_loss, 157 | optimizer=optimizer.state_dict() 158 | ) 159 | logger.append([epoch, optimizer.param_groups[0]['lr'], 160 | train_out["loss"], train_out["acc_avg"], train_out["acc"], 161 | test_out["loss"], test_out["acc_avg"], test_out["acc"]]) 162 | printf( 163 | f"Training loss:{train_out['loss']} acc_avg:{train_out['acc_avg']}% acc:{train_out['acc']}% time:{train_out['time']}s") 164 | printf( 165 | f"Testing loss:{test_out['loss']} acc_avg:{test_out['acc_avg']}% " 166 | f"acc:{test_out['acc']}% time:{test_out['time']}s [best test acc: {best_test_acc}%] \n\n") 167 | logger.close() 168 | 169 | printf(f"++++++++" * 2 + "Final results" + "++++++++" * 2) 170 | printf(f"++ Last Train time: {train_out['time']} | Last Test time: {test_out['time']} ++") 171 | printf(f"++ Best Train loss: {best_train_loss} | Best Test loss: {best_test_loss} ++") 172 | printf(f"++ Best Train acc_B: {best_train_acc_avg} | Best Test acc_B: {best_test_acc_avg} ++") 173 | printf(f"++ Best Train acc: {best_train_acc} | Best Test acc: {best_test_acc} ++") 174 | printf(f"++++++++" * 5) 175 | 176 | 177 | def train(net, trainloader, optimizer, criterion, device): 178 | net.train() 179 | train_loss = 0 180 | correct = 0 181 | total = 0 182 | train_pred = [] 183 | train_true = [] 184 | time_cost = datetime.datetime.now() 185 | for batch_idx, (data, label) in enumerate(trainloader): 186 | data, label = data.to(device), label.to(device).squeeze() 187 | data = data.permute(0, 2, 1) # so, the input data shape is [batch, 3, 1024] 188 | optimizer.zero_grad() 189 | logits = net(data) 190 | loss = criterion(logits, label) 191 | loss.backward() 192 | optimizer.step() 193 | train_loss += loss.item() 194 | preds = logits.max(dim=1)[1] 195 | 196 | train_true.append(label.cpu().numpy()) 197 | train_pred.append(preds.detach().cpu().numpy()) 198 | 199 | total += label.size(0) 200 | correct += preds.eq(label).sum().item() 201 | 202 | progress_bar(batch_idx, len(trainloader), 'Loss: %.3f | Acc: %.3f%% (%d/%d)' 203 | % (train_loss / (batch_idx + 1), 100. * correct / total, correct, total)) 204 | 205 | time_cost = int((datetime.datetime.now() - time_cost).total_seconds()) 206 | train_true = np.concatenate(train_true) 207 | train_pred = np.concatenate(train_pred) 208 | return { 209 | "loss": float("%.3f" % (train_loss / (batch_idx + 1))), 210 | "acc": float("%.3f" % (100. * metrics.accuracy_score(train_true, train_pred))), 211 | "acc_avg": float("%.3f" % (100. * metrics.balanced_accuracy_score(train_true, train_pred))), 212 | "time": time_cost 213 | } 214 | 215 | 216 | def validate(net, testloader, criterion, device): 217 | net.eval() 218 | test_loss = 0 219 | correct = 0 220 | total = 0 221 | test_true = [] 222 | test_pred = [] 223 | time_cost = datetime.datetime.now() 224 | with torch.no_grad(): 225 | for batch_idx, (data, label) in enumerate(testloader): 226 | data, label = data.to(device), label.to(device).squeeze() 227 | data = data.permute(0, 2, 1) 228 | logits = net(data) 229 | loss = criterion(logits, label) 230 | test_loss += loss.item() 231 | preds = logits.max(dim=1)[1] 232 | test_true.append(label.cpu().numpy()) 233 | test_pred.append(preds.detach().cpu().numpy()) 234 | total += label.size(0) 235 | correct += preds.eq(label).sum().item() 236 | progress_bar(batch_idx, len(testloader), 'Loss: %.3f | Acc: %.3f%% (%d/%d)' 237 | % (test_loss / (batch_idx + 1), 100. * correct / total, correct, total)) 238 | 239 | time_cost = int((datetime.datetime.now() - time_cost).total_seconds()) 240 | test_true = np.concatenate(test_true) 241 | test_pred = np.concatenate(test_pred) 242 | return { 243 | "loss": float("%.3f" % (test_loss / (batch_idx + 1))), 244 | "acc": float("%.3f" % (100. * metrics.accuracy_score(test_true, test_pred))), 245 | "acc_avg": float("%.3f" % (100. * metrics.balanced_accuracy_score(test_true, test_pred))), 246 | "time": time_cost 247 | } 248 | 249 | 250 | if __name__ == '__main__': 251 | main() 252 | -------------------------------------------------------------------------------- /pointnet2_ops_lib/pointnet2_ops/pointnet2_utils.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | import warnings 4 | from torch.autograd import Function 5 | from typing import * 6 | 7 | try: 8 | import pointnet2_ops._ext as _ext 9 | except ImportError: 10 | from torch.utils.cpp_extension import load 11 | import glob 12 | import os.path as osp 13 | import os 14 | 15 | warnings.warn("Unable to load pointnet2_ops cpp extension. JIT Compiling.") 16 | 17 | _ext_src_root = osp.join(osp.dirname(__file__), "_ext-src") 18 | _ext_sources = glob.glob(osp.join(_ext_src_root, "src", "*.cpp")) + glob.glob( 19 | osp.join(_ext_src_root, "src", "*.cu") 20 | ) 21 | _ext_headers = glob.glob(osp.join(_ext_src_root, "include", "*")) 22 | 23 | os.environ["TORCH_CUDA_ARCH_LIST"] = "3.7+PTX;5.0;6.0;6.1;6.2;7.0;7.5" 24 | _ext = load( 25 | "_ext", 26 | sources=_ext_sources, 27 | extra_include_paths=[osp.join(_ext_src_root, "include")], 28 | extra_cflags=["-O3"], 29 | extra_cuda_cflags=["-O3", "-Xfatbin", "-compress-all"], 30 | with_cuda=True, 31 | ) 32 | 33 | 34 | class FurthestPointSampling(Function): 35 | @staticmethod 36 | def forward(ctx, xyz, npoint): 37 | # type: (Any, torch.Tensor, int) -> torch.Tensor 38 | r""" 39 | Uses iterative furthest point sampling to select a set of npoint features that have the largest 40 | minimum distance 41 | 42 | Parameters 43 | ---------- 44 | xyz : torch.Tensor 45 | (B, N, 3) tensor where N > npoint 46 | npoint : int32 47 | number of features in the sampled set 48 | 49 | Returns 50 | ------- 51 | torch.Tensor 52 | (B, npoint) tensor containing the set 53 | """ 54 | out = _ext.furthest_point_sampling(xyz, npoint) 55 | 56 | ctx.mark_non_differentiable(out) 57 | 58 | return out 59 | 60 | @staticmethod 61 | def backward(ctx, grad_out): 62 | return () 63 | 64 | 65 | furthest_point_sample = FurthestPointSampling.apply 66 | 67 | 68 | class GatherOperation(Function): 69 | @staticmethod 70 | def forward(ctx, features, idx): 71 | # type: (Any, torch.Tensor, torch.Tensor) -> torch.Tensor 72 | r""" 73 | 74 | Parameters 75 | ---------- 76 | features : torch.Tensor 77 | (B, C, N) tensor 78 | 79 | idx : torch.Tensor 80 | (B, npoint) tensor of the features to gather 81 | 82 | Returns 83 | ------- 84 | torch.Tensor 85 | (B, C, npoint) tensor 86 | """ 87 | 88 | ctx.save_for_backward(idx, features) 89 | 90 | return _ext.gather_points(features, idx) 91 | 92 | @staticmethod 93 | def backward(ctx, grad_out): 94 | idx, features = ctx.saved_tensors 95 | N = features.size(2) 96 | 97 | grad_features = _ext.gather_points_grad(grad_out.contiguous(), idx, N) 98 | return grad_features, None 99 | 100 | 101 | gather_operation = GatherOperation.apply 102 | 103 | 104 | class ThreeNN(Function): 105 | @staticmethod 106 | def forward(ctx, unknown, known): 107 | # type: (Any, torch.Tensor, torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor] 108 | r""" 109 | Find the three nearest neighbors of unknown in known 110 | Parameters 111 | ---------- 112 | unknown : torch.Tensor 113 | (B, n, 3) tensor of known features 114 | known : torch.Tensor 115 | (B, m, 3) tensor of unknown features 116 | 117 | Returns 118 | ------- 119 | dist : torch.Tensor 120 | (B, n, 3) l2 distance to the three nearest neighbors 121 | idx : torch.Tensor 122 | (B, n, 3) index of 3 nearest neighbors 123 | """ 124 | dist2, idx = _ext.three_nn(unknown, known) 125 | dist = torch.sqrt(dist2) 126 | 127 | ctx.mark_non_differentiable(dist, idx) 128 | 129 | return dist, idx 130 | 131 | @staticmethod 132 | def backward(ctx, grad_dist, grad_idx): 133 | return () 134 | 135 | 136 | three_nn = ThreeNN.apply 137 | 138 | 139 | class ThreeInterpolate(Function): 140 | @staticmethod 141 | def forward(ctx, features, idx, weight): 142 | # type(Any, torch.Tensor, torch.Tensor, torch.Tensor) -> Torch.Tensor 143 | r""" 144 | Performs weight linear interpolation on 3 features 145 | Parameters 146 | ---------- 147 | features : torch.Tensor 148 | (B, c, m) Features descriptors to be interpolated from 149 | idx : torch.Tensor 150 | (B, n, 3) three nearest neighbors of the target features in features 151 | weight : torch.Tensor 152 | (B, n, 3) weights 153 | 154 | Returns 155 | ------- 156 | torch.Tensor 157 | (B, c, n) tensor of the interpolated features 158 | """ 159 | ctx.save_for_backward(idx, weight, features) 160 | 161 | return _ext.three_interpolate(features, idx, weight) 162 | 163 | @staticmethod 164 | def backward(ctx, grad_out): 165 | # type: (Any, torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor] 166 | r""" 167 | Parameters 168 | ---------- 169 | grad_out : torch.Tensor 170 | (B, c, n) tensor with gradients of ouputs 171 | 172 | Returns 173 | ------- 174 | grad_features : torch.Tensor 175 | (B, c, m) tensor with gradients of features 176 | 177 | None 178 | 179 | None 180 | """ 181 | idx, weight, features = ctx.saved_tensors 182 | m = features.size(2) 183 | 184 | grad_features = _ext.three_interpolate_grad( 185 | grad_out.contiguous(), idx, weight, m 186 | ) 187 | 188 | return grad_features, torch.zeros_like(idx), torch.zeros_like(weight) 189 | 190 | 191 | three_interpolate = ThreeInterpolate.apply 192 | 193 | 194 | class GroupingOperation(Function): 195 | @staticmethod 196 | def forward(ctx, features, idx): 197 | # type: (Any, torch.Tensor, torch.Tensor) -> torch.Tensor 198 | r""" 199 | 200 | Parameters 201 | ---------- 202 | features : torch.Tensor 203 | (B, C, N) tensor of features to group 204 | idx : torch.Tensor 205 | (B, npoint, nsample) tensor containing the indicies of features to group with 206 | 207 | Returns 208 | ------- 209 | torch.Tensor 210 | (B, C, npoint, nsample) tensor 211 | """ 212 | ctx.save_for_backward(idx, features) 213 | 214 | return _ext.group_points(features, idx) 215 | 216 | @staticmethod 217 | def backward(ctx, grad_out): 218 | # type: (Any, torch.tensor) -> Tuple[torch.Tensor, torch.Tensor] 219 | r""" 220 | 221 | Parameters 222 | ---------- 223 | grad_out : torch.Tensor 224 | (B, C, npoint, nsample) tensor of the gradients of the output from forward 225 | 226 | Returns 227 | ------- 228 | torch.Tensor 229 | (B, C, N) gradient of the features 230 | None 231 | """ 232 | idx, features = ctx.saved_tensors 233 | N = features.size(2) 234 | 235 | grad_features = _ext.group_points_grad(grad_out.contiguous(), idx, N) 236 | 237 | return grad_features, torch.zeros_like(idx) 238 | 239 | 240 | grouping_operation = GroupingOperation.apply 241 | 242 | 243 | class BallQuery(Function): 244 | @staticmethod 245 | def forward(ctx, radius, nsample, xyz, new_xyz): 246 | # type: (Any, float, int, torch.Tensor, torch.Tensor) -> torch.Tensor 247 | r""" 248 | 249 | Parameters 250 | ---------- 251 | radius : float 252 | radius of the balls 253 | nsample : int 254 | maximum number of features in the balls 255 | xyz : torch.Tensor 256 | (B, N, 3) xyz coordinates of the features 257 | new_xyz : torch.Tensor 258 | (B, npoint, 3) centers of the ball query 259 | 260 | Returns 261 | ------- 262 | torch.Tensor 263 | (B, npoint, nsample) tensor with the indicies of the features that form the query balls 264 | """ 265 | output = _ext.ball_query(new_xyz, xyz, radius, nsample) 266 | 267 | ctx.mark_non_differentiable(output) 268 | 269 | return output 270 | 271 | @staticmethod 272 | def backward(ctx, grad_out): 273 | return () 274 | 275 | 276 | ball_query = BallQuery.apply 277 | 278 | 279 | class QueryAndGroup(nn.Module): 280 | r""" 281 | Groups with a ball query of radius 282 | 283 | Parameters 284 | --------- 285 | radius : float32 286 | Radius of ball 287 | nsample : int32 288 | Maximum number of features to gather in the ball 289 | """ 290 | 291 | def __init__(self, radius, nsample, use_xyz=True): 292 | # type: (QueryAndGroup, float, int, bool) -> None 293 | super(QueryAndGroup, self).__init__() 294 | self.radius, self.nsample, self.use_xyz = radius, nsample, use_xyz 295 | 296 | def forward(self, xyz, new_xyz, features=None): 297 | # type: (QueryAndGroup, torch.Tensor. torch.Tensor, torch.Tensor) -> Tuple[Torch.Tensor] 298 | r""" 299 | Parameters 300 | ---------- 301 | xyz : torch.Tensor 302 | xyz coordinates of the features (B, N, 3) 303 | new_xyz : torch.Tensor 304 | centriods (B, npoint, 3) 305 | features : torch.Tensor 306 | Descriptors of the features (B, C, N) 307 | 308 | Returns 309 | ------- 310 | new_features : torch.Tensor 311 | (B, 3 + C, npoint, nsample) tensor 312 | """ 313 | 314 | idx = ball_query(self.radius, self.nsample, xyz, new_xyz) 315 | xyz_trans = xyz.transpose(1, 2).contiguous() 316 | grouped_xyz = grouping_operation(xyz_trans, idx) # (B, 3, npoint, nsample) 317 | grouped_xyz -= new_xyz.transpose(1, 2).unsqueeze(-1) 318 | 319 | if features is not None: 320 | grouped_features = grouping_operation(features, idx) 321 | if self.use_xyz: 322 | new_features = torch.cat( 323 | [grouped_xyz, grouped_features], dim=1 324 | ) # (B, C + 3, npoint, nsample) 325 | else: 326 | new_features = grouped_features 327 | else: 328 | assert ( 329 | self.use_xyz 330 | ), "Cannot have not features and not use xyz as a feature!" 331 | new_features = grouped_xyz 332 | 333 | return new_features 334 | 335 | 336 | class GroupAll(nn.Module): 337 | r""" 338 | Groups all features 339 | 340 | Parameters 341 | --------- 342 | """ 343 | 344 | def __init__(self, use_xyz=True): 345 | # type: (GroupAll, bool) -> None 346 | super(GroupAll, self).__init__() 347 | self.use_xyz = use_xyz 348 | 349 | def forward(self, xyz, new_xyz, features=None): 350 | # type: (GroupAll, torch.Tensor, torch.Tensor, torch.Tensor) -> Tuple[torch.Tensor] 351 | r""" 352 | Parameters 353 | ---------- 354 | xyz : torch.Tensor 355 | xyz coordinates of the features (B, N, 3) 356 | new_xyz : torch.Tensor 357 | Ignored 358 | features : torch.Tensor 359 | Descriptors of the features (B, C, N) 360 | 361 | Returns 362 | ------- 363 | new_features : torch.Tensor 364 | (B, C + 3, 1, N) tensor 365 | """ 366 | 367 | grouped_xyz = xyz.transpose(1, 2).unsqueeze(2) 368 | if features is not None: 369 | grouped_features = features.unsqueeze(2) 370 | if self.use_xyz: 371 | new_features = torch.cat( 372 | [grouped_xyz, grouped_features], dim=1 373 | ) # (B, 3 + C, 1, N) 374 | else: 375 | new_features = grouped_features 376 | else: 377 | new_features = grouped_xyz 378 | 379 | return new_features 380 | -------------------------------------------------------------------------------- /classification_ModelNet40/main.py: -------------------------------------------------------------------------------- 1 | """ 2 | Usage: 3 | python main.py --model PointMLP --msg demo 4 | """ 5 | import argparse 6 | import os 7 | import logging 8 | import datetime 9 | import torch 10 | import torch.nn.parallel 11 | import torch.backends.cudnn as cudnn 12 | import torch.optim 13 | import torch.utils.data 14 | import torch.utils.data.distributed 15 | from torch.utils.data import DataLoader 16 | import models as models 17 | from utils import Logger, mkdir_p, progress_bar, save_model, save_args, cal_loss 18 | from data import ModelNet40 19 | from torch.optim.lr_scheduler import CosineAnnealingLR 20 | import sklearn.metrics as metrics 21 | import numpy as np 22 | 23 | 24 | def parse_args(): 25 | """Parameters""" 26 | parser = argparse.ArgumentParser('training') 27 | parser.add_argument('-c', '--checkpoint', type=str, metavar='PATH', 28 | help='path to save checkpoint (default: checkpoint)') 29 | parser.add_argument('--msg', type=str, help='message after checkpoint') 30 | parser.add_argument('--batch_size', type=int, default=32, help='batch size in training') 31 | parser.add_argument('--model', default='PointNet', help='model name [default: pointnet_cls]') 32 | parser.add_argument('--epoch', default=300, type=int, help='number of epoch in training') 33 | parser.add_argument('--num_points', type=int, default=1024, help='Point Number') 34 | parser.add_argument('--learning_rate', default=0.1, type=float, help='learning rate in training') 35 | parser.add_argument('--min_lr', default=0.005, type=float, help='min lr') 36 | parser.add_argument('--weight_decay', type=float, default=2e-4, help='decay rate') 37 | parser.add_argument('--seed', type=int, help='random seed') 38 | parser.add_argument('--workers', default=8, type=int, help='workers') 39 | return parser.parse_args() 40 | 41 | 42 | def main(): 43 | args = parse_args() 44 | if args.seed is None: 45 | args.seed = np.random.randint(1, 10000) 46 | os.environ["HDF5_USE_FILE_LOCKING"] = "FALSE" 47 | 48 | assert torch.cuda.is_available(), "Please ensure codes are executed in cuda." 49 | device = 'cuda' 50 | if args.seed is not None: 51 | torch.manual_seed(args.seed) 52 | np.random.seed(args.seed) 53 | torch.cuda.manual_seed_all(args.seed) 54 | torch.cuda.manual_seed(args.seed) 55 | torch.set_printoptions(10) 56 | torch.backends.cudnn.benchmark = False 57 | torch.backends.cudnn.deterministic = True 58 | os.environ['PYTHONHASHSEED'] = str(args.seed) 59 | time_str = str(datetime.datetime.now().strftime('-%Y%m%d%H%M%S')) 60 | if args.msg is None: 61 | message = time_str 62 | else: 63 | message = "-" + args.msg 64 | args.checkpoint = 'checkpoints/' + args.model + message + '-' + str(args.seed) 65 | if not os.path.isdir(args.checkpoint): 66 | mkdir_p(args.checkpoint) 67 | 68 | screen_logger = logging.getLogger("Model") 69 | screen_logger.setLevel(logging.INFO) 70 | formatter = logging.Formatter('%(message)s') 71 | file_handler = logging.FileHandler(os.path.join(args.checkpoint, "out.txt")) 72 | file_handler.setLevel(logging.INFO) 73 | file_handler.setFormatter(formatter) 74 | screen_logger.addHandler(file_handler) 75 | 76 | def printf(str): 77 | screen_logger.info(str) 78 | print(str) 79 | 80 | # Model 81 | printf(f"args: {args}") 82 | printf('==> Building model..') 83 | net = models.__dict__[args.model]() 84 | criterion = cal_loss 85 | net = net.to(device) 86 | # criterion = criterion.to(device) 87 | if device == 'cuda': 88 | net = torch.nn.DataParallel(net) 89 | cudnn.benchmark = True 90 | 91 | best_test_acc = 0. # best test accuracy 92 | best_train_acc = 0. 93 | best_test_acc_avg = 0. 94 | best_train_acc_avg = 0. 95 | best_test_loss = float("inf") 96 | best_train_loss = float("inf") 97 | start_epoch = 0 # start from epoch 0 or last checkpoint epoch 98 | optimizer_dict = None 99 | 100 | if not os.path.isfile(os.path.join(args.checkpoint, "last_checkpoint.pth")): 101 | save_args(args) 102 | logger = Logger(os.path.join(args.checkpoint, 'log.txt'), title="ModelNet" + args.model) 103 | logger.set_names(["Epoch-Num", 'Learning-Rate', 104 | 'Train-Loss', 'Train-acc-B', 'Train-acc', 105 | 'Valid-Loss', 'Valid-acc-B', 'Valid-acc']) 106 | else: 107 | printf(f"Resuming last checkpoint from {args.checkpoint}") 108 | checkpoint_path = os.path.join(args.checkpoint, "last_checkpoint.pth") 109 | checkpoint = torch.load(checkpoint_path) 110 | net.load_state_dict(checkpoint['net']) 111 | start_epoch = checkpoint['epoch'] 112 | best_test_acc = checkpoint['best_test_acc'] 113 | best_train_acc = checkpoint['best_train_acc'] 114 | best_test_acc_avg = checkpoint['best_test_acc_avg'] 115 | best_train_acc_avg = checkpoint['best_train_acc_avg'] 116 | best_test_loss = checkpoint['best_test_loss'] 117 | best_train_loss = checkpoint['best_train_loss'] 118 | logger = Logger(os.path.join(args.checkpoint, 'log.txt'), title="ModelNet" + args.model, resume=True) 119 | optimizer_dict = checkpoint['optimizer'] 120 | 121 | printf('==> Preparing data..') 122 | train_loader = DataLoader(ModelNet40(partition='train', num_points=args.num_points), num_workers=args.workers, 123 | batch_size=args.batch_size, shuffle=True, drop_last=True) 124 | test_loader = DataLoader(ModelNet40(partition='test', num_points=args.num_points), num_workers=args.workers, 125 | batch_size=args.batch_size // 2, shuffle=False, drop_last=False) 126 | 127 | optimizer = torch.optim.SGD(net.parameters(), lr=args.learning_rate, momentum=0.9, weight_decay=args.weight_decay) 128 | if optimizer_dict is not None: 129 | optimizer.load_state_dict(optimizer_dict) 130 | scheduler = CosineAnnealingLR(optimizer, args.epoch, eta_min=args.min_lr, last_epoch=start_epoch - 1) 131 | 132 | for epoch in range(start_epoch, args.epoch): 133 | printf('Epoch(%d/%s) Learning Rate %s:' % (epoch + 1, args.epoch, optimizer.param_groups[0]['lr'])) 134 | train_out = train(net, train_loader, optimizer, criterion, device) # {"loss", "acc", "acc_avg", "time"} 135 | test_out = validate(net, test_loader, criterion, device) 136 | scheduler.step() 137 | 138 | if test_out["acc"] > best_test_acc: 139 | best_test_acc = test_out["acc"] 140 | is_best = True 141 | else: 142 | is_best = False 143 | 144 | best_test_acc = test_out["acc"] if (test_out["acc"] > best_test_acc) else best_test_acc 145 | best_train_acc = train_out["acc"] if (train_out["acc"] > best_train_acc) else best_train_acc 146 | best_test_acc_avg = test_out["acc_avg"] if (test_out["acc_avg"] > best_test_acc_avg) else best_test_acc_avg 147 | best_train_acc_avg = train_out["acc_avg"] if (train_out["acc_avg"] > best_train_acc_avg) else best_train_acc_avg 148 | best_test_loss = test_out["loss"] if (test_out["loss"] < best_test_loss) else best_test_loss 149 | best_train_loss = train_out["loss"] if (train_out["loss"] < best_train_loss) else best_train_loss 150 | 151 | save_model( 152 | net, epoch, path=args.checkpoint, acc=test_out["acc"], is_best=is_best, 153 | best_test_acc=best_test_acc, # best test accuracy 154 | best_train_acc=best_train_acc, 155 | best_test_acc_avg=best_test_acc_avg, 156 | best_train_acc_avg=best_train_acc_avg, 157 | best_test_loss=best_test_loss, 158 | best_train_loss=best_train_loss, 159 | optimizer=optimizer.state_dict() 160 | ) 161 | logger.append([epoch, optimizer.param_groups[0]['lr'], 162 | train_out["loss"], train_out["acc_avg"], train_out["acc"], 163 | test_out["loss"], test_out["acc_avg"], test_out["acc"]]) 164 | printf( 165 | f"Training loss:{train_out['loss']} acc_avg:{train_out['acc_avg']}% acc:{train_out['acc']}% time:{train_out['time']}s") 166 | printf( 167 | f"Testing loss:{test_out['loss']} acc_avg:{test_out['acc_avg']}% " 168 | f"acc:{test_out['acc']}% time:{test_out['time']}s [best test acc: {best_test_acc}%] \n\n") 169 | logger.close() 170 | 171 | printf(f"++++++++" * 2 + "Final results" + "++++++++" * 2) 172 | printf(f"++ Last Train time: {train_out['time']} | Last Test time: {test_out['time']} ++") 173 | printf(f"++ Best Train loss: {best_train_loss} | Best Test loss: {best_test_loss} ++") 174 | printf(f"++ Best Train acc_B: {best_train_acc_avg} | Best Test acc_B: {best_test_acc_avg} ++") 175 | printf(f"++ Best Train acc: {best_train_acc} | Best Test acc: {best_test_acc} ++") 176 | printf(f"++++++++" * 5) 177 | 178 | 179 | def train(net, trainloader, optimizer, criterion, device): 180 | net.train() 181 | train_loss = 0 182 | correct = 0 183 | total = 0 184 | train_pred = [] 185 | train_true = [] 186 | time_cost = datetime.datetime.now() 187 | for batch_idx, (data, label) in enumerate(trainloader): 188 | data, label = data.to(device), label.to(device).squeeze() 189 | data = data.permute(0, 2, 1) # so, the input data shape is [batch, 3, 1024] 190 | optimizer.zero_grad() 191 | logits = net(data) 192 | loss = criterion(logits, label) 193 | loss.backward() 194 | torch.nn.utils.clip_grad_norm_(net.parameters(), 1) 195 | optimizer.step() 196 | train_loss += loss.item() 197 | preds = logits.max(dim=1)[1] 198 | 199 | train_true.append(label.cpu().numpy()) 200 | train_pred.append(preds.detach().cpu().numpy()) 201 | 202 | total += label.size(0) 203 | correct += preds.eq(label).sum().item() 204 | 205 | progress_bar(batch_idx, len(trainloader), 'Loss: %.3f | Acc: %.3f%% (%d/%d)' 206 | % (train_loss / (batch_idx + 1), 100. * correct / total, correct, total)) 207 | 208 | time_cost = int((datetime.datetime.now() - time_cost).total_seconds()) 209 | train_true = np.concatenate(train_true) 210 | train_pred = np.concatenate(train_pred) 211 | return { 212 | "loss": float("%.3f" % (train_loss / (batch_idx + 1))), 213 | "acc": float("%.3f" % (100. * metrics.accuracy_score(train_true, train_pred))), 214 | "acc_avg": float("%.3f" % (100. * metrics.balanced_accuracy_score(train_true, train_pred))), 215 | "time": time_cost 216 | } 217 | 218 | 219 | def validate(net, testloader, criterion, device): 220 | net.eval() 221 | test_loss = 0 222 | correct = 0 223 | total = 0 224 | test_true = [] 225 | test_pred = [] 226 | time_cost = datetime.datetime.now() 227 | with torch.no_grad(): 228 | for batch_idx, (data, label) in enumerate(testloader): 229 | data, label = data.to(device), label.to(device).squeeze() 230 | data = data.permute(0, 2, 1) 231 | logits = net(data) 232 | loss = criterion(logits, label) 233 | test_loss += loss.item() 234 | preds = logits.max(dim=1)[1] 235 | test_true.append(label.cpu().numpy()) 236 | test_pred.append(preds.detach().cpu().numpy()) 237 | total += label.size(0) 238 | correct += preds.eq(label).sum().item() 239 | progress_bar(batch_idx, len(testloader), 'Loss: %.3f | Acc: %.3f%% (%d/%d)' 240 | % (test_loss / (batch_idx + 1), 100. * correct / total, correct, total)) 241 | 242 | time_cost = int((datetime.datetime.now() - time_cost).total_seconds()) 243 | test_true = np.concatenate(test_true) 244 | test_pred = np.concatenate(test_pred) 245 | return { 246 | "loss": float("%.3f" % (test_loss / (batch_idx + 1))), 247 | "acc": float("%.3f" % (100. * metrics.accuracy_score(test_true, test_pred))), 248 | "acc_avg": float("%.3f" % (100. * metrics.balanced_accuracy_score(test_true, test_pred))), 249 | "time": time_cost 250 | } 251 | 252 | 253 | if __name__ == '__main__': 254 | main() 255 | -------------------------------------------------------------------------------- /classification_ModelNet40/models/pointmlp.py: -------------------------------------------------------------------------------- 1 | 2 | import torch 3 | import torch.nn as nn 4 | import torch.nn.functional as F 5 | # from torch import einsum 6 | # from einops import rearrange, repeat 7 | 8 | 9 | from pointnet2_ops import pointnet2_utils 10 | 11 | 12 | def get_activation(activation): 13 | if activation.lower() == 'gelu': 14 | return nn.GELU() 15 | elif activation.lower() == 'rrelu': 16 | return nn.RReLU(inplace=True) 17 | elif activation.lower() == 'selu': 18 | return nn.SELU(inplace=True) 19 | elif activation.lower() == 'silu': 20 | return nn.SiLU(inplace=True) 21 | elif activation.lower() == 'hardswish': 22 | return nn.Hardswish(inplace=True) 23 | elif activation.lower() == 'leakyrelu': 24 | return nn.LeakyReLU(inplace=True) 25 | else: 26 | return nn.ReLU(inplace=True) 27 | 28 | 29 | def square_distance(src, dst): 30 | """ 31 | Calculate Euclid distance between each two points. 32 | src^T * dst = xn * xm + yn * ym + zn * zm; 33 | sum(src^2, dim=-1) = xn*xn + yn*yn + zn*zn; 34 | sum(dst^2, dim=-1) = xm*xm + ym*ym + zm*zm; 35 | dist = (xn - xm)^2 + (yn - ym)^2 + (zn - zm)^2 36 | = sum(src**2,dim=-1)+sum(dst**2,dim=-1)-2*src^T*dst 37 | Input: 38 | src: source points, [B, N, C] 39 | dst: target points, [B, M, C] 40 | Output: 41 | dist: per-point square distance, [B, N, M] 42 | """ 43 | B, N, _ = src.shape 44 | _, M, _ = dst.shape 45 | dist = -2 * torch.matmul(src, dst.permute(0, 2, 1)) 46 | dist += torch.sum(src ** 2, -1).view(B, N, 1) 47 | dist += torch.sum(dst ** 2, -1).view(B, 1, M) 48 | return dist 49 | 50 | 51 | def index_points(points, idx): 52 | """ 53 | Input: 54 | points: input points data, [B, N, C] 55 | idx: sample index data, [B, S] 56 | Return: 57 | new_points:, indexed points data, [B, S, C] 58 | """ 59 | device = points.device 60 | B = points.shape[0] 61 | view_shape = list(idx.shape) 62 | view_shape[1:] = [1] * (len(view_shape) - 1) 63 | repeat_shape = list(idx.shape) 64 | repeat_shape[0] = 1 65 | batch_indices = torch.arange(B, dtype=torch.long).to(device).view(view_shape).repeat(repeat_shape) 66 | new_points = points[batch_indices, idx, :] 67 | return new_points 68 | 69 | 70 | def farthest_point_sample(xyz, npoint): 71 | """ 72 | Input: 73 | xyz: pointcloud data, [B, N, 3] 74 | npoint: number of samples 75 | Return: 76 | centroids: sampled pointcloud index, [B, npoint] 77 | """ 78 | device = xyz.device 79 | B, N, C = xyz.shape 80 | centroids = torch.zeros(B, npoint, dtype=torch.long).to(device) 81 | distance = torch.ones(B, N).to(device) * 1e10 82 | farthest = torch.randint(0, N, (B,), dtype=torch.long).to(device) 83 | batch_indices = torch.arange(B, dtype=torch.long).to(device) 84 | for i in range(npoint): 85 | centroids[:, i] = farthest 86 | centroid = xyz[batch_indices, farthest, :].view(B, 1, 3) 87 | dist = torch.sum((xyz - centroid) ** 2, -1) 88 | distance = torch.min(distance, dist) 89 | farthest = torch.max(distance, -1)[1] 90 | return centroids 91 | 92 | 93 | def query_ball_point(radius, nsample, xyz, new_xyz): 94 | """ 95 | Input: 96 | radius: local region radius 97 | nsample: max sample number in local region 98 | xyz: all points, [B, N, 3] 99 | new_xyz: query points, [B, S, 3] 100 | Return: 101 | group_idx: grouped points index, [B, S, nsample] 102 | """ 103 | device = xyz.device 104 | B, N, C = xyz.shape 105 | _, S, _ = new_xyz.shape 106 | group_idx = torch.arange(N, dtype=torch.long).to(device).view(1, 1, N).repeat([B, S, 1]) 107 | sqrdists = square_distance(new_xyz, xyz) 108 | group_idx[sqrdists > radius ** 2] = N 109 | group_idx = group_idx.sort(dim=-1)[0][:, :, :nsample] 110 | group_first = group_idx[:, :, 0].view(B, S, 1).repeat([1, 1, nsample]) 111 | mask = group_idx == N 112 | group_idx[mask] = group_first[mask] 113 | return group_idx 114 | 115 | 116 | def knn_point(nsample, xyz, new_xyz): 117 | """ 118 | Input: 119 | nsample: max sample number in local region 120 | xyz: all points, [B, N, C] 121 | new_xyz: query points, [B, S, C] 122 | Return: 123 | group_idx: grouped points index, [B, S, nsample] 124 | """ 125 | sqrdists = square_distance(new_xyz, xyz) 126 | _, group_idx = torch.topk(sqrdists, nsample, dim=-1, largest=False, sorted=False) 127 | return group_idx 128 | 129 | 130 | class LocalGrouper(nn.Module): 131 | def __init__(self, channel, groups, kneighbors, use_xyz=True, normalize="center", **kwargs): 132 | """ 133 | Give xyz[b,p,3] and fea[b,p,d], return new_xyz[b,g,3] and new_fea[b,g,k,d] 134 | :param groups: groups number 135 | :param kneighbors: k-nerighbors 136 | :param kwargs: others 137 | """ 138 | super(LocalGrouper, self).__init__() 139 | self.groups = groups 140 | self.kneighbors = kneighbors 141 | self.use_xyz = use_xyz 142 | if normalize is not None: 143 | self.normalize = normalize.lower() 144 | else: 145 | self.normalize = None 146 | if self.normalize not in ["center", "anchor"]: 147 | print(f"Unrecognized normalize parameter (self.normalize), set to None. Should be one of [center, anchor].") 148 | self.normalize = None 149 | if self.normalize is not None: 150 | add_channel=3 if self.use_xyz else 0 151 | self.affine_alpha = nn.Parameter(torch.ones([1,1,1,channel + add_channel])) 152 | self.affine_beta = nn.Parameter(torch.zeros([1, 1, 1, channel + add_channel])) 153 | 154 | def forward(self, xyz, points): 155 | B, N, C = xyz.shape 156 | S = self.groups 157 | xyz = xyz.contiguous() # xyz [btach, points, xyz] 158 | 159 | # fps_idx = torch.multinomial(torch.linspace(0, N - 1, steps=N).repeat(B, 1).to(xyz.device), num_samples=self.groups, replacement=False).long() 160 | # fps_idx = farthest_point_sample(xyz, self.groups).long() 161 | fps_idx = pointnet2_utils.furthest_point_sample(xyz, self.groups).long() # [B, npoint] 162 | new_xyz = index_points(xyz, fps_idx) # [B, npoint, 3] 163 | new_points = index_points(points, fps_idx) # [B, npoint, d] 164 | 165 | idx = knn_point(self.kneighbors, xyz, new_xyz) 166 | # idx = query_ball_point(radius, nsample, xyz, new_xyz) 167 | grouped_xyz = index_points(xyz, idx) # [B, npoint, k, 3] 168 | grouped_points = index_points(points, idx) # [B, npoint, k, d] 169 | if self.use_xyz: 170 | grouped_points = torch.cat([grouped_points, grouped_xyz],dim=-1) # [B, npoint, k, d+3] 171 | if self.normalize is not None: 172 | if self.normalize =="center": 173 | mean = torch.mean(grouped_points, dim=2, keepdim=True) 174 | if self.normalize =="anchor": 175 | mean = torch.cat([new_points, new_xyz],dim=-1) if self.use_xyz else new_points 176 | mean = mean.unsqueeze(dim=-2) # [B, npoint, 1, d+3] 177 | std = torch.std((grouped_points-mean).reshape(B,-1),dim=-1,keepdim=True).unsqueeze(dim=-1).unsqueeze(dim=-1) 178 | grouped_points = (grouped_points-mean)/(std + 1e-5) 179 | grouped_points = self.affine_alpha*grouped_points + self.affine_beta 180 | 181 | new_points = torch.cat([grouped_points, new_points.view(B, S, 1, -1).repeat(1, 1, self.kneighbors, 1)], dim=-1) 182 | return new_xyz, new_points 183 | 184 | 185 | class ConvBNReLU1D(nn.Module): 186 | def __init__(self, in_channels, out_channels, kernel_size=1, bias=True, activation='relu'): 187 | super(ConvBNReLU1D, self).__init__() 188 | self.act = get_activation(activation) 189 | self.net = nn.Sequential( 190 | nn.Conv1d(in_channels=in_channels, out_channels=out_channels, kernel_size=kernel_size, bias=bias), 191 | nn.BatchNorm1d(out_channels), 192 | self.act 193 | ) 194 | 195 | def forward(self, x): 196 | return self.net(x) 197 | 198 | 199 | class ConvBNReLURes1D(nn.Module): 200 | def __init__(self, channel, kernel_size=1, groups=1, res_expansion=1.0, bias=True, activation='relu'): 201 | super(ConvBNReLURes1D, self).__init__() 202 | self.act = get_activation(activation) 203 | self.net1 = nn.Sequential( 204 | nn.Conv1d(in_channels=channel, out_channels=int(channel * res_expansion), 205 | kernel_size=kernel_size, groups=groups, bias=bias), 206 | nn.BatchNorm1d(int(channel * res_expansion)), 207 | self.act 208 | ) 209 | if groups > 1: 210 | self.net2 = nn.Sequential( 211 | nn.Conv1d(in_channels=int(channel * res_expansion), out_channels=channel, 212 | kernel_size=kernel_size, groups=groups, bias=bias), 213 | nn.BatchNorm1d(channel), 214 | self.act, 215 | nn.Conv1d(in_channels=channel, out_channels=channel, 216 | kernel_size=kernel_size, bias=bias), 217 | nn.BatchNorm1d(channel), 218 | ) 219 | else: 220 | self.net2 = nn.Sequential( 221 | nn.Conv1d(in_channels=int(channel * res_expansion), out_channels=channel, 222 | kernel_size=kernel_size, bias=bias), 223 | nn.BatchNorm1d(channel) 224 | ) 225 | 226 | def forward(self, x): 227 | return self.act(self.net2(self.net1(x)) + x) 228 | 229 | 230 | class PreExtraction(nn.Module): 231 | def __init__(self, channels, out_channels, blocks=1, groups=1, res_expansion=1, bias=True, 232 | activation='relu', use_xyz=True): 233 | """ 234 | input: [b,g,k,d]: output:[b,d,g] 235 | :param channels: 236 | :param blocks: 237 | """ 238 | super(PreExtraction, self).__init__() 239 | in_channels = 3+2*channels if use_xyz else 2*channels 240 | self.transfer = ConvBNReLU1D(in_channels, out_channels, bias=bias, activation=activation) 241 | operation = [] 242 | for _ in range(blocks): 243 | operation.append( 244 | ConvBNReLURes1D(out_channels, groups=groups, res_expansion=res_expansion, 245 | bias=bias, activation=activation) 246 | ) 247 | self.operation = nn.Sequential(*operation) 248 | 249 | def forward(self, x): 250 | b, n, s, d = x.size() # torch.Size([32, 512, 32, 6]) 251 | x = x.permute(0, 1, 3, 2) 252 | x = x.reshape(-1, d, s) 253 | x = self.transfer(x) 254 | batch_size, _, _ = x.size() 255 | x = self.operation(x) # [b, d, k] 256 | x = F.adaptive_max_pool1d(x, 1).view(batch_size, -1) 257 | x = x.reshape(b, n, -1).permute(0, 2, 1) 258 | return x 259 | 260 | 261 | class PosExtraction(nn.Module): 262 | def __init__(self, channels, blocks=1, groups=1, res_expansion=1, bias=True, activation='relu'): 263 | """ 264 | input[b,d,g]; output[b,d,g] 265 | :param channels: 266 | :param blocks: 267 | """ 268 | super(PosExtraction, self).__init__() 269 | operation = [] 270 | for _ in range(blocks): 271 | operation.append( 272 | ConvBNReLURes1D(channels, groups=groups, res_expansion=res_expansion, bias=bias, activation=activation) 273 | ) 274 | self.operation = nn.Sequential(*operation) 275 | 276 | def forward(self, x): # [b, d, g] 277 | return self.operation(x) 278 | 279 | 280 | class Model(nn.Module): 281 | def __init__(self, points=1024, class_num=40, embed_dim=64, groups=1, res_expansion=1.0, 282 | activation="relu", bias=True, use_xyz=True, normalize="center", 283 | dim_expansion=[2, 2, 2, 2], pre_blocks=[2, 2, 2, 2], pos_blocks=[2, 2, 2, 2], 284 | k_neighbors=[32, 32, 32, 32], reducers=[2, 2, 2, 2], **kwargs): 285 | super(Model, self).__init__() 286 | self.stages = len(pre_blocks) 287 | self.class_num = class_num 288 | self.points = points 289 | self.embedding = ConvBNReLU1D(3, embed_dim, bias=bias, activation=activation) 290 | assert len(pre_blocks) == len(k_neighbors) == len(reducers) == len(pos_blocks) == len(dim_expansion), \ 291 | "Please check stage number consistent for pre_blocks, pos_blocks k_neighbors, reducers." 292 | self.local_grouper_list = nn.ModuleList() 293 | self.pre_blocks_list = nn.ModuleList() 294 | self.pos_blocks_list = nn.ModuleList() 295 | last_channel = embed_dim 296 | anchor_points = self.points 297 | for i in range(len(pre_blocks)): 298 | out_channel = last_channel * dim_expansion[i] 299 | pre_block_num = pre_blocks[i] 300 | pos_block_num = pos_blocks[i] 301 | kneighbor = k_neighbors[i] 302 | reduce = reducers[i] 303 | anchor_points = anchor_points // reduce 304 | # append local_grouper_list 305 | local_grouper = LocalGrouper(last_channel, anchor_points, kneighbor, use_xyz, normalize) # [b,g,k,d] 306 | self.local_grouper_list.append(local_grouper) 307 | # append pre_block_list 308 | pre_block_module = PreExtraction(last_channel, out_channel, pre_block_num, groups=groups, 309 | res_expansion=res_expansion, 310 | bias=bias, activation=activation, use_xyz=use_xyz) 311 | self.pre_blocks_list.append(pre_block_module) 312 | # append pos_block_list 313 | pos_block_module = PosExtraction(out_channel, pos_block_num, groups=groups, 314 | res_expansion=res_expansion, bias=bias, activation=activation) 315 | self.pos_blocks_list.append(pos_block_module) 316 | 317 | last_channel = out_channel 318 | 319 | self.act = get_activation(activation) 320 | self.classifier = nn.Sequential( 321 | nn.Linear(last_channel, 512), 322 | nn.BatchNorm1d(512), 323 | self.act, 324 | nn.Dropout(0.5), 325 | nn.Linear(512, 256), 326 | nn.BatchNorm1d(256), 327 | self.act, 328 | nn.Dropout(0.5), 329 | nn.Linear(256, self.class_num) 330 | ) 331 | 332 | def forward(self, x): 333 | xyz = x.permute(0, 2, 1) 334 | batch_size, _, _ = x.size() 335 | x = self.embedding(x) # B,D,N 336 | for i in range(self.stages): 337 | # Give xyz[b, p, 3] and fea[b, p, d], return new_xyz[b, g, 3] and new_fea[b, g, k, d] 338 | xyz, x = self.local_grouper_list[i](xyz, x.permute(0, 2, 1)) # [b,g,3] [b,g,k,d] 339 | x = self.pre_blocks_list[i](x) # [b,d,g] 340 | x = self.pos_blocks_list[i](x) # [b,d,g] 341 | 342 | x = F.adaptive_max_pool1d(x, 1).squeeze(dim=-1) 343 | x = self.classifier(x) 344 | return x 345 | 346 | 347 | 348 | 349 | def pointMLP(num_classes=40, **kwargs) -> Model: 350 | return Model(points=1024, class_num=num_classes, embed_dim=64, groups=1, res_expansion=1.0, 351 | activation="relu", bias=False, use_xyz=False, normalize="anchor", 352 | dim_expansion=[2, 2, 2, 2], pre_blocks=[2, 2, 2, 2], pos_blocks=[2, 2, 2, 2], 353 | k_neighbors=[24, 24, 24, 24], reducers=[2, 2, 2, 2], **kwargs) 354 | 355 | 356 | def pointMLPElite(num_classes=40, **kwargs) -> Model: 357 | return Model(points=1024, class_num=num_classes, embed_dim=32, groups=1, res_expansion=0.25, 358 | activation="relu", bias=False, use_xyz=False, normalize="anchor", 359 | dim_expansion=[2, 2, 2, 1], pre_blocks=[1, 1, 2, 1], pos_blocks=[1, 1, 2, 1], 360 | k_neighbors=[24,24,24,24], reducers=[2, 2, 2, 2], **kwargs) 361 | 362 | if __name__ == '__main__': 363 | data = torch.rand(2, 3, 1024) 364 | print("===> testing pointMLP ...") 365 | model = pointMLP() 366 | out = model(data) 367 | print(out.shape) 368 | 369 | --------------------------------------------------------------------------------