├── .clang-format ├── .github ├── ISSUE_TEMPLATE │ ├── 0-build-issue-report.yml │ ├── 1-bug-report.yml │ ├── 2-questions.yml │ ├── 3-feature-request.yml │ └── config.yml ├── config.yml └── workflows │ ├── style.yml │ └── ubuntu.yml ├── .gitignore ├── .style.yapf ├── CHANGELOG.md ├── LICENSE ├── Makefile ├── README.md ├── SECURITY.md ├── __init__.py ├── ci ├── check_style.py └── run_ci.sh ├── data └── demo │ ├── fragment.pcd │ └── fragment.ply ├── docs ├── howtos.md ├── images │ ├── getting_started_ml_visualizer.gif │ ├── tensorboard_demo_scene.jpg │ ├── tensorboard_monkey.jpg │ ├── tensorboard_small_scale.gif │ ├── visualizer_BoundingBoxes.png │ ├── visualizer_custom_lut.png │ ├── visualizer_int_attr.png │ ├── visualizer_predictions.gif │ └── visualizer_random_color_attr.png ├── openvino.md ├── tensorboard.md └── tutorial │ └── notebook │ ├── add_own_dataset.rst │ ├── index.rst │ ├── train_ss_model_using_pytorch.rst │ └── train_ss_model_using_tensorflow.rst ├── examples ├── tensorboard_pytorch.py ├── tensorboard_tf.py ├── util.py ├── vis_pred.py └── visualize.py ├── ml3d ├── __init__.py ├── configs │ ├── __init__.py │ ├── default_cfgs │ │ ├── kpconv.yml │ │ ├── object_detection.yml │ │ ├── parislille3d.yml │ │ ├── randlanet.yml │ │ ├── s3dis.yml │ │ ├── semantic3d.yml │ │ ├── semantic_segmentation.yml │ │ ├── semantickitti.yml │ │ ├── shapenet.yml │ │ └── toronto3d.yml │ ├── kpconv_parislille3d.yml │ ├── kpconv_s3dis.yml │ ├── kpconv_semantic3d.yml │ ├── kpconv_semantickitti.yml │ ├── kpconv_toronto3d.yml │ ├── pointpillars_argoverse.yml │ ├── pointpillars_kitti.yml │ ├── pointpillars_lyft.yml │ ├── pointpillars_nuscenes.yml │ ├── pointpillars_waymo.yml │ ├── pointrcnn_kitti.yml │ ├── pointtransformer_s3dis.yml │ ├── pvcnn_s3dis.yml │ ├── randlanet_pandaset.yml │ ├── randlanet_parislille3d.yml │ ├── randlanet_s3dis.yml │ ├── randlanet_semantic3d.yml │ ├── randlanet_semantickitti.yml │ ├── randlanet_toronto3d.yml │ ├── sparseconvunet_scannet.yml │ └── torch_to_tf.yml ├── datasets │ ├── __init__.py │ ├── _resources │ │ ├── download_paris_lille3d.sh │ │ ├── download_semantic3d.sh │ │ ├── download_toronto3d.sh │ │ ├── lyft │ │ │ ├── test.txt │ │ │ ├── train.txt │ │ │ └── val.txt │ │ ├── s3dis_annotation_paths.txt │ │ ├── scannet │ │ │ ├── scannetv2-labels.combined.tsv │ │ │ ├── scannetv2_test.txt │ │ │ ├── scannetv2_train.txt │ │ │ └── scannetv2_val.txt │ │ └── semantic-kitti.yaml │ ├── argoverse.py │ ├── augment │ │ ├── __init__.py │ │ └── augmentation.py │ ├── base_dataset.py │ ├── customdataset.py │ ├── inference_dummy.py │ ├── kitti.py │ ├── lyft.py │ ├── matterport_objects.py │ ├── nuscenes.py │ ├── pandaset.py │ ├── parislille3d.py │ ├── s3dis.py │ ├── samplers │ │ ├── __init__.py │ │ ├── semseg_random.py │ │ └── semseg_spatially_regular.py │ ├── scannet.py │ ├── semantic3d.py │ ├── semantickitti.py │ ├── shapenet.py │ ├── sunrgbd.py │ ├── toronto3d.py │ ├── tumfacade.py │ ├── utils │ │ ├── __init__.py │ │ ├── bev_box.py │ │ ├── dataprocessing.py │ │ ├── operations.py │ │ ├── semantic-kitti.yaml │ │ └── transforms.py │ └── waymo.py ├── metrics │ ├── __init__.py │ └── mAP.py ├── tf │ ├── __init__.py │ ├── dataloaders │ │ ├── __init__.py │ │ └── tf_dataloader.py │ ├── models │ │ ├── __init__.py │ │ ├── base_model.py │ │ ├── base_model_objdet.py │ │ ├── kpconv.py │ │ ├── network_blocks.py │ │ ├── openvino_model.py │ │ ├── point_pillars.py │ │ ├── point_rcnn.py │ │ ├── point_transformer.py │ │ ├── pvcnn.py │ │ ├── randlanet.py │ │ ├── sparseconvnet.py │ │ └── utils │ │ │ ├── __init__.py │ │ │ ├── kernels │ │ │ ├── __init__.py │ │ │ └── kernel_points.py │ │ │ └── openvino_ext │ │ │ └── front │ │ │ └── tf │ │ │ └── min.py │ ├── modules │ │ ├── __init__.py │ │ ├── losses │ │ │ ├── __init__.py │ │ │ ├── cross_entropy.py │ │ │ ├── focal_loss.py │ │ │ ├── semseg_loss.py │ │ │ └── smooth_L1.py │ │ ├── metrics │ │ │ ├── __init__.py │ │ │ └── semseg_metric.py │ │ ├── optimizers │ │ │ └── __init__.py │ │ ├── pointnet.py │ │ └── schedulers │ │ │ ├── __init__.py │ │ │ ├── bn_momentum_scheduler.py │ │ │ ├── cosine_warmup_scheduler.py │ │ │ └── lr_one_cycle_scheduler.py │ ├── pipelines │ │ ├── __init__.py │ │ ├── base_pipeline.py │ │ ├── object_detection.py │ │ └── semantic_segmentation.py │ └── utils │ │ ├── __init__.py │ │ ├── objdet_helper.py │ │ ├── pointnet │ │ ├── pointnet2_modules.py │ │ ├── pointnet2_utils.py │ │ └── tf_utils.py │ │ ├── roipool3d │ │ └── roipool3d_utils.py │ │ └── tf_utils.py ├── torch │ ├── __init__.py │ ├── dataloaders │ │ ├── __init__.py │ │ ├── concat_batcher.py │ │ ├── default_batcher.py │ │ ├── torch_dataloader.py │ │ └── torch_sampler.py │ ├── models │ │ ├── __init__.py │ │ ├── base_model.py │ │ ├── base_model_objdet.py │ │ ├── kpconv.py │ │ ├── openvino_model.py │ │ ├── point_pillars.py │ │ ├── point_rcnn.py │ │ ├── point_transformer.py │ │ ├── pvcnn.py │ │ ├── randlanet.py │ │ └── sparseconvnet.py │ ├── modules │ │ ├── __init__.py │ │ ├── losses │ │ │ ├── __init__.py │ │ │ ├── cross_entropy.py │ │ │ ├── focal_loss.py │ │ │ ├── semseg_loss.py │ │ │ └── smooth_L1.py │ │ ├── metrics │ │ │ ├── __init__.py │ │ │ └── semseg_metric.py │ │ ├── optimizers │ │ │ ├── __init__.py │ │ │ └── optim_wrapper.py │ │ ├── pointnet.py │ │ └── schedulers │ │ │ ├── __init__.py │ │ │ ├── bn_momentum_scheduler.py │ │ │ ├── cosine_warmup_scheduler.py │ │ │ └── lr_one_cycle_scheduler.py │ ├── pipelines │ │ ├── __init__.py │ │ ├── base_pipeline.py │ │ ├── object_detection.py │ │ └── semantic_segmentation.py │ └── utils │ │ ├── __init__.py │ │ ├── objdet_helper.py │ │ ├── pointnet │ │ ├── pointnet2_modules.py │ │ ├── pointnet2_utils.py │ │ └── pytorch_utils.py │ │ ├── roipool3d │ │ ├── __init__.py │ │ └── roipool3d_utils.py │ │ └── torch_utils.py ├── utils │ ├── __init__.py │ ├── builder.py │ ├── config.py │ ├── dataset_helper.py │ ├── log.py │ └── registry.py └── vis │ ├── __init__.py │ ├── boundingbox.py │ ├── colormap.py │ ├── labellut.py │ └── visualizer.py ├── model_zoo.md ├── requirements-openvino.txt ├── requirements-tensorflow.txt ├── requirements-torch-cuda.txt ├── requirements-torch-cxx11-abi.txt ├── requirements-torch.txt ├── requirements.txt ├── scripts ├── README.md ├── collect_bboxes.py ├── demo_api_train.py ├── demo_datasets.py ├── demo_obj_det.py ├── download_datasets │ ├── download_kitti.sh │ ├── download_lyft.sh │ ├── download_parislille3d.sh │ ├── download_semantic3d.sh │ ├── download_semantickitti.sh │ ├── download_shapenet.sh │ ├── download_sunrgbd.sh │ └── download_toronto3d.sh ├── preprocess_argoverse.py ├── preprocess_lyft.py ├── preprocess_nuscenes.py ├── preprocess_scannet.py ├── preprocess_semantic3d.py ├── preprocess_sunrgbd.py ├── preprocess_waymo.py ├── run_pipeline.py └── train_scripts │ ├── kpconv_kitti.sh │ ├── kpconv_paris.sh │ ├── kpconv_s3dis.sh │ ├── kpconv_semantic3d.sh │ ├── kpconv_toronto.sh │ ├── pointpillars_kitti.sh │ ├── pointpillars_waymo.sh │ ├── randlanet_kitti.sh │ ├── randlanet_paris.sh │ ├── randlanet_s3dis.sh │ ├── randlanet_semantic3d.sh │ └── randlanet_toronto.sh ├── set_open3d_ml_root.sh ├── setup.py ├── tests ├── test_integration.py └── test_models.py └── version.txt /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Google 2 | IndentWidth: 4 3 | ColumnLimit: 80 4 | UseTab: Never 5 | Standard: c++14 6 | ContinuationIndentWidth: 8 7 | AccessModifierOffset: -4 8 | BinPackParameters: false 9 | SortIncludes: true 10 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/0-build-issue-report.yml: -------------------------------------------------------------------------------- 1 | name: Report an Installation or Build Issue 2 | description: I have trouble installing or compiling Open3D-ML. 3 | title: "Summarize the issue and your environments (e.g., \"Cannot import ml3d on Ubuntu 20.04 with CUDA\")" 4 | labels: [build/install issue] 5 | 6 | body: 7 | 8 | - type: checkboxes 9 | attributes: 10 | label: "Checklist" 11 | options: 12 | - label: "I have searched for [similar issues](https://github.com/isl-org/Open3D-ML/issues)." 13 | required: true 14 | - label: "I have tested with the [latest development wheel](http://www.open3d.org/docs/latest/getting_started.html#development-version-pip)." 15 | required: true 16 | - label: "I have checked the [release documentation](http://www.open3d.org/docs/release/) and the [latest documentation](http://www.open3d.org/docs/latest/) (for `main` branch)." 17 | required: true 18 | 19 | - type: textarea 20 | attributes: 21 | label: "Steps to reproduce the issue" 22 | description: > 23 | Please provide step-by-step instructions on how to reproduce the issue. 24 | Describe the installation method (e.g. building from source or pip). 25 | Providing *detailed* instructions is crucial for other to help with 26 | the issues. Here is an example for reference you may modify. 27 | value: > 28 | #### I first cloned Open3D-ML by: 29 | 30 | ``` 31 | 32 | git clone https://github.com/isl-org/Open3D-ML.git 33 | 34 | cd Open3D-ML 35 | 36 | ``` 37 | 38 | 39 | #### Then, I set OPEN3D_ML_ROOT with: 40 | 41 | ``` 42 | 43 | source set_open3d_ml_root.sh 44 | 45 | 46 | ``` 47 | 48 | validations: 49 | required: true 50 | 51 | - type: textarea 52 | attributes: 53 | label: "Error message" 54 | description: > 55 | Provide the *full* error message. It is even better to provide your 56 | terminal commands and the full terminal outputs. If you are 57 | reporting a segfault, please include a debugger backtrace. 58 | placeholder: | 59 | Building OpenBLAS with LAPACK from source 60 | CMake Error at 3rdparty/find_dependencies.cmake:1227 (message): 61 | gfortran is required to compile LAPACK from source. On Ubuntu, please 62 | install by `apt install gfortran`. On macOS, please install by `brew 63 | install gfortran`. 64 | Call Stack (most recent call first): 65 | CMakeLists.txt:446 (include) 66 | 67 | Here's the full terminal output with my commands: 68 | (you may drag and drop a .txt file in `Additional information`) 69 | render: shell 70 | validations: 71 | required: true 72 | 73 | - type: textarea 74 | attributes: 75 | label: "Open3D, Python and System information" 76 | value: > 77 | - Operating system: (e.g. OSX 10.15, Ubuntu 18.04, Windows 10 64-bit) 78 | 79 | - Python version: (e.g. Python 3.8 / output from `import sys print(sys.version)`) 80 | 81 | - Open3D version: (output from python: `print(open3d.__version__)`) 82 | 83 | - System type: (x84 / arm64 / apple-silicon / jetson / rpi) 84 | 85 | - Is this remote workstation?: yes or no 86 | 87 | - How did you install Open3D?: (e.g. pip, conda, build from source) 88 | 89 | - Compiler version (if built from source): (e.g. gcc 7.5, clang 7.0) 90 | render: markdown 91 | validations: 92 | required: true 93 | 94 | - type: textarea 95 | attributes: 96 | label: "Additional information" 97 | description: > 98 | Please add any additional information that could help us diagnose the 99 | problem better. Provide screenshots if applicable. You may attach 100 | log files, generated wheel, or any other files, that could be helpful. 101 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/1-bug-report.yml: -------------------------------------------------------------------------------- 1 | name: Report a Bug 2 | description: I found a possible bug while using Open3D-ML. 3 | title: "Summarize the bug (e.g., \"Segmentation Fault while training RandLANet on S3DIS\")" 4 | labels: [bug] 5 | 6 | body: 7 | 8 | - type: checkboxes 9 | attributes: 10 | label: "Checklist" 11 | options: 12 | - label: "I have searched for [similar issues](https://github.com/isl-org/Open3D-ML/issues)." 13 | required: true 14 | - label: "I have tested with the [latest development wheel](http://www.open3d.org/docs/latest/getting_started.html#development-version-pip)." 15 | required: true 16 | - label: "I have checked the [release documentation](http://www.open3d.org/docs/release/) and the [latest documentation](http://www.open3d.org/docs/latest/) (for `main` branch)." 17 | required: true 18 | 19 | - type: textarea 20 | attributes: 21 | label: "Describe the issue" 22 | description: > 23 | Provide the *detailed* description of the issue you are facing. Include 24 | references to any documentation or issues you referred. 25 | placeholder: | 26 | I have been trying to train RandLANet on S3DIS dataset using tensorflow 27 | pipeline. However, I get segmentation fault after few epochs. I am using 28 | the default config present in ml3d/configs/ 29 | validations: 30 | required: true 31 | 32 | - type: textarea 33 | attributes: 34 | label: "Steps to reproduce the bug" 35 | description: > 36 | Please provide step-by-step instructions and full source code to reproduce 37 | the bug. The instructions shall be self-contained. 38 | placeholder: | 39 | << your code here >> 40 | 41 | import os 42 | import sys 43 | import open3d.ml as _ml3d 44 | import open3d.ml.tf as ml3d 45 | 46 | cfg_file = "/content/Desktop/point_pillars/Open3D-ML/ml3d/configs/pointpillars_kitti.yml" 47 | cfg = _ml3d.utils.Config.load_from_file(cfg_file) 48 | 49 | model = ml3d.models.PointPillars(**cfg.model) #ml3d.models.PointPillars(**cfg.model) 50 | 51 | datapath = "/content/Desktop/KITTI_DATASET/KITTI_PTCLOUD_DATA/data_object_velodyne" 52 | 53 | dataset = ml3d.datasets.KITTI(dataset_path=datapath, use_cache = True) 54 | 55 | pipeline = ml3d.pipelines.ObjectDetection(model=model, dataset = dataset, **cfg.pipeline) 56 | 57 | pipeline.run_train() 58 | 59 | render: python 60 | validations: 61 | required: true 62 | 63 | - type: textarea 64 | attributes: 65 | label: "Error message" 66 | description: > 67 | Please include the *full* error message, if any. You may submit/attach 68 | the entire terminal output with the error message. If you are reporting a 69 | segfault please include a debugger backtrace. 70 | placeholder: | 71 | << Full error message >> 72 | validations: 73 | required: false 74 | 75 | - type: textarea 76 | attributes: 77 | label: "Expected behavior" 78 | description: > 79 | A clear and concise description of what you expected to happen. 80 | 81 | - type: textarea 82 | attributes: 83 | label: "Open3D, Python and System information" 84 | value: > 85 | - Operating system: (e.g. OSX 10.15, Ubuntu 18.04, Windows 10 64-bit) 86 | 87 | - Python version: (e.g. Python 3.8 / output from `import sys print(sys.version)`) 88 | 89 | - Open3D version: (output from python: `print(open3d.__version__)`) 90 | 91 | - System type: (x84 / arm64 / apple-silicon / jetson / rpi) 92 | 93 | - Is this remote workstation?: yes or no 94 | 95 | - How did you install Open3D?: (e.g. pip, conda, build from source) 96 | 97 | - Compiler version (if built from source): (e.g. gcc 7.5, clang 7.0) 98 | render: markdown 99 | validations: 100 | required: true 101 | 102 | - type: textarea 103 | attributes: 104 | label: "Additional information" 105 | description: > 106 | Please add any additional information that could help us diagnose the 107 | problem better. Provide screenshots if applicable. You may attach 108 | log files, generated wheel, or any other files, that could be helpful. 109 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/2-questions.yml: -------------------------------------------------------------------------------- 1 | name: Ask a Question 2 | description: Ask a question on how to use Open3D-ML, how to implement something, etc. 3 | title: "Summarize the question. (e.g., \"How to do inference on custom dataset ? \")" 4 | labels: [question] 5 | 6 | body: 7 | 8 | - type: checkboxes 9 | attributes: 10 | label: "Checklist" 11 | options: 12 | - label: "I have searched for [similar issues](https://github.com/isl-org/Open3D-ML/issues)." 13 | required: true 14 | - label: "I have tested with the [latest development wheel](http://www.open3d.org/docs/latest/getting_started.html#development-version-pip)." 15 | required: true 16 | - label: "I have checked the [release documentation](http://www.open3d.org/docs/release/) and the [latest documentation](http://www.open3d.org/docs/latest/) (for `main` branch)." 17 | required: true 18 | 19 | - type: textarea 20 | attributes: 21 | label: "My Question" 22 | description: > 23 | Provide the *detailed* description of your query. Kindly attach code 24 | snippets, screen-shots, data files used in your application, etc. 25 | validations: 26 | required: true 27 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/3-feature-request.yml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: New feature request. Request to add some new functionality or improve the existing ones. 3 | title: "Summarize your request. (e.g., \"Request to add more models in Open3D-ML model zoo\")" 4 | labels: [feature request] 5 | 6 | body: 7 | 8 | - type: checkboxes 9 | attributes: 10 | label: "Checklist" 11 | options: 12 | - label: "I have searched for [similar issues](https://github.com/isl-org/Open3D-ML/issues)." 13 | required: true 14 | - label: "I have tested with the [latest development wheel](http://www.open3d.org/docs/latest/getting_started.html#development-version-pip)." 15 | required: true 16 | - label: "I have checked the [release documentation](http://www.open3d.org/docs/release/) and the [latest documentation](http://www.open3d.org/docs/latest/) (for `main` branch)." 17 | required: true 18 | 19 | - type: textarea 20 | attributes: 21 | description: > 22 | Describe what feature you'd like to have in Open3D-ML. 23 | label: "Proposed new feature or change" 24 | validations: 25 | required: true 26 | 27 | - type: textarea 28 | attributes: 29 | label: "References" 30 | description: Please share some references that can help us know more about this feature and try to implement the same. For ex. research paper, open-source codes, stackoverflow, etc. 31 | 32 | - type: textarea 33 | attributes: 34 | label: "Additional information" 35 | description: Add any other context or screenshots about the feature request here. 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | contact_links: 2 | - name: Links to Open3D Documentation and Tutorials 3 | url: http://www.open3d.org/docs/latest/ 4 | about: "Read the documentation and tutorials for Open3D." 5 | - name: Link to Open3D Discord Server 6 | url: https://discord.gg/D35BGvn 7 | about: "Get in touch with the Open3D community on Discord." 8 | -------------------------------------------------------------------------------- /.github/config.yml: -------------------------------------------------------------------------------- 1 | # Configuration for update-docs - https://github.com/behaviorbot/update-docs 2 | 3 | # Comment to be posted to on PRs that don't update the changelog 4 | updateDocsComment: > 5 | Thanks for submitting this pull request! The maintainers of this repository would appreciate if you could update the **CHANGELOG.md** based on your changes. 6 | 7 | updateDocsWhiteList: 8 | - documentation 9 | 10 | updateDocsTargetFiles: 11 | - CHANGELOG.md 12 | -------------------------------------------------------------------------------- /.github/workflows/style.yml: -------------------------------------------------------------------------------- 1 | name: Style check 2 | permissions: {} 3 | 4 | on: 5 | workflow_dispatch: 6 | push: 7 | branches: [main, dev] 8 | pull_request: 9 | types: [opened, reopened, synchronize] 10 | 11 | jobs: 12 | style-check: 13 | permissions: 14 | contents: read 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Checkout source code 18 | uses: actions/checkout@v4 19 | with: 20 | submodules: true 21 | - name: Set up Python version 22 | uses: actions/setup-python@v5 23 | with: 24 | python-version: '3.10' 25 | - name: Install dependencies 26 | run: | 27 | python -m pip install -U clang-format==10.0.1.1 yapf==0.30.0 nbformat pydocstyle==6.0.0 28 | - name: Run style check 29 | run: | 30 | python ci/check_style.py --verbose 31 | - name: Run docstring style check 32 | run: | 33 | pydocstyle --convention=google --add-ignore=D1,D205,D415,D212 . 34 | -------------------------------------------------------------------------------- /.github/workflows/ubuntu.yml: -------------------------------------------------------------------------------- 1 | name: Ubuntu CI 2 | permissions: {} 3 | 4 | on: 5 | workflow_dispatch: 6 | push: 7 | branches: [main, dev] 8 | pull_request: 9 | types: [opened, reopened, synchronize] 10 | 11 | concurrency: 12 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} 13 | cancel-in-progress: true 14 | 15 | env: 16 | NPROC: 3 17 | 18 | jobs: 19 | ubuntu: 20 | permissions: 21 | contents: read 22 | runs-on: ubuntu-22.04 23 | strategy: 24 | fail-fast: false 25 | steps: 26 | - name: Checkout source code 27 | uses: actions/checkout@v4 28 | - name: Setup cache 29 | uses: actions/cache@v3 30 | with: 31 | # Ref: https://github.com/apache/incubator-mxnet/pull/18459/files 32 | path: ~/.ccache 33 | # We include the commit sha in the cache key, as new cache entries are 34 | # only created if there is no existing entry for the key yet. 35 | key: ${{ runner.os }}-ccache-${{ github.sha }} 36 | # Restore any ccache cache entry, if none for 37 | # ${{ runner.os }}-ccache-${{ github.sha }} exists. 38 | # Common prefix will be used so that ccache can be used across commits. 39 | restore-keys: | 40 | ${{ runner.os }}-ccache 41 | - name: Set up Python version 42 | uses: actions/setup-python@v5 43 | with: 44 | python-version: "3.11" 45 | # Pre-installed packages: https://github.com/actions/runner-images/tree/main/images 46 | - name: Install ccache 47 | run: | 48 | sudo apt-get --yes install ccache 49 | ccache -M 2G # GitHub's total cache limit is 5GB for all OSes. 50 | - name: Config and build 51 | run: | 52 | PATH=/usr/lib/ccache:$PATH 53 | ccache -s 54 | ./ci/run_ci.sh 55 | ccache -s 56 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | .DS_Store 3 | __pycache__ 4 | # Distribution / packaging 5 | .Python 6 | env/ 7 | build/ 8 | develop-eggs/ 9 | dist/ 10 | downloads/ 11 | eggs/ 12 | .eggs/ 13 | **/lib/ 14 | **/build/ 15 | **/lib64/ 16 | *.egg-info 17 | .installed.cfg 18 | *.egg 19 | *linux-gnu.so 20 | *darwin.so 21 | *.so 22 | # 23 | logs/ 24 | test/ 25 | test_kpconv/ 26 | kernels/ 27 | **/.fuse* 28 | train_log/ 29 | -------------------------------------------------------------------------------- /.style.yapf: -------------------------------------------------------------------------------- 1 | [style] 2 | based_on_style = google 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.15 2 | * Add support for Images and their visualization (#366) 3 | * Add RandLANet MIT licensed implementation (#454) 4 | * Refactor config parsing (#460) 5 | * Update pl3d, s3dis, scannet and semantic3d datasets to BaseDataSplit (#451) 6 | * Fix missing axis_align_matrix in Scannet preprocesing (#456) 7 | * Fix OpenVINOModel in Torch pipeline (#447) 8 | * Intel OpenVINO backend (#406) 9 | * Downgrade torch on Mac to 1.8.1 (#436) (#439) 10 | * Add bounding boxes to Visualizer DataModel (#431) 11 | 12 | ## 0.14 13 | * Added CHANGELOG 14 | * Added RandLA-Net's License (PR #423) 15 | * Fixed wrong label order in Visualizer (PR #422) 16 | * Fixed confusion_matrix bug for predictions with fewer classes than gt (PR #421) 17 | * Fixed cyclic dependency in PVCNN (PR #420) 18 | * Update PyTorch 1.8.1 -> 1.8.2 and TensorFlow 2.5.1 -> 2.5.2 (PR #416) 19 | * Added Tensorboard Plugin support (PR #405) 20 | * Use new numpy random number API (PR #395) 21 | * Added Point-Voxel Convolution Model (PR #394) 22 | * Added Point-Transformers Model (PR #327) 23 | * Python 3.9 support (PR #304) 24 | 25 | ## 0.13 26 | * Added TensorFlow pipeline for PointRCNN (PR #246) 27 | * Added PyTorch pipeline for PointRCNN (PR #237) 28 | * Added prefetching and memory pinning (PR #236) 29 | * Added training pipelines for SparseConvUnet (torch and tf) (PR #233) 30 | * Added SparseConvUnet model for TensorFlow (PR #228) 31 | * Support for CUDA 11.0 (PR #224) 32 | * Added SparseConvUnet model for PyTorch (PR #223) 33 | * Added inference pipeline for PointRCNN (PR #218) 34 | * Added SunRGBD dataset support (PR #215) 35 | * Added ScanNet dataset support (PR #212) 36 | * Added bounding box support in S3DIS dataset (PR #210) 37 | 38 | ## 0.12 39 | * Added Data Augmentation pipeline (PR #178) 40 | * Added metrics for PointPillars (PR #172) 41 | * Added PointPillars training pipeline for TensorFlow (PR #171) 42 | * Added PointPillars training pipeline for PyTorch (PR #170) 43 | * Added PointPillars model for TensorFlow (PR #159) 44 | * Added ShapeNet dataset support (PR #157) 45 | * Added Argoverse3D dataset support (PR #155) 46 | * Added PointPillars inference pipeline (PR #153) 47 | * Added dataset download scripts (PR #145) 48 | * Added Lyft dataset support (PR #138) 49 | * Added NuScenes dataset support (PR #137) 50 | * Added Waymo dataset support (PR #136) 51 | * Added KITTI dataset support (PR #128) 52 | * Added training and inference pipeline for Semantic Segmentation (TensorFlow) (PR #38) 53 | * Added visualizer (PR #33) 54 | * Added KPConv model (PR #22) 55 | * Added Tensorflow Dataloader (PR #17) 56 | * Added support for Custom dataset (PR #11) 57 | * Added Toronto3D dataset support (PR #10) 58 | * Added ParisLille3D dataset support (PR #9) 59 | * Added Semantic3D dataset support (PR #8) 60 | * Added dataloader class (PR #6) 61 | * Added support for S3DIS dataset (PR #5) 62 | * Added logging mechanism (PR #3) 63 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | The MIT License (MIT) 3 | 4 | Open3D: www.open3d.org 5 | Copyright (c) 2020 www.open3d.org 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | 3 | check-style: 4 | python ./ci/check_style.py 5 | 6 | apply-style: 7 | python ./ci/check_style.py --do_apply_style 8 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | Intel is committed to rapidly addressing security vulnerabilities affecting our customers and providing clear guidance on the solution, impact, severity and mitigation. 3 | 4 | ## Reporting a Vulnerability 5 | Please report any security vulnerabilities in this project utilizing the guidelines [here](https://www.intel.com/content/www/us/en/security-center/vulnerability-handling-guidelines.html). -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isl-org/Open3D-ML/b64b51445bf2fdb17f7de4fd7a1c383ac238a3dc/__init__.py -------------------------------------------------------------------------------- /ci/run_ci.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | NPROC=${NPROC:?'env var must be set to number of available CPUs.'} 6 | PIP_VER="23.2.1" 7 | 8 | echo 1. Prepare the Open3D-ML repo and install dependencies 9 | echo 10 | export PATH_TO_OPEN3D_ML="$PWD" 11 | echo "$PATH_TO_OPEN3D_ML" 12 | # the build system of the main repo expects a main branch. make sure main exists 13 | git checkout -b main || true 14 | python -m pip install -U pip==$PIP_VER 15 | python -m pip install -r requirements.txt \ 16 | -r requirements-torch.txt 17 | # -r requirements-tensorflow.txt # TF disabled on Linux (Open3D PR#6288) 18 | # -r requirements-openvino.txt # Numpy version conflict with TF 2.8.2 19 | cd .. 20 | python -m pip install -U Cython 21 | 22 | echo 2. clone Open3D and install dependencies 23 | echo 24 | git clone --branch main https://github.com/isl-org/Open3D.git 25 | 26 | ./Open3D/util/install_deps_ubuntu.sh assume-yes 27 | python -m pip install -r Open3D/python/requirements.txt \ 28 | -r Open3D/python/requirements_style.txt \ 29 | -r Open3D/python/requirements_test.txt 30 | 31 | echo 3. Configure for bundling the Open3D-ML part 32 | echo 33 | mkdir Open3D/build 34 | pushd Open3D/build 35 | # TF disabled on Linux (Open3D PR#6288) 36 | cmake -DBUNDLE_OPEN3D_ML=ON \ 37 | -DOPEN3D_ML_ROOT="${PATH_TO_OPEN3D_ML}" \ 38 | -DGLIBCXX_USE_CXX11_ABI=OFF \ 39 | -DBUILD_TENSORFLOW_OPS=OFF \ 40 | -DBUILD_PYTORCH_OPS=ON \ 41 | -DBUILD_GUI=ON \ 42 | -DBUILD_UNIT_TESTS=OFF \ 43 | -DBUILD_BENCHMARKS=OFF \ 44 | -DBUILD_EXAMPLES=OFF \ 45 | .. 46 | 47 | echo 4. Build and install wheel 48 | echo 49 | make -j"$NPROC" install-pip-package 50 | 51 | echo 5. run examples/tests in the Open3D-ML repo outside of the repo directory to 52 | echo make sure that the installed package works. 53 | echo 54 | popd 55 | mkdir test_workdir 56 | pushd test_workdir 57 | mv "$PATH_TO_OPEN3D_ML/tests" . 58 | echo Add --randomly-seed=SEED to the test command to reproduce test order. 59 | python -m pytest tests 60 | 61 | echo "... now do the same but in dev mode by setting OPEN3D_ML_ROOT" 62 | echo 63 | export OPEN3D_ML_ROOT="$PATH_TO_OPEN3D_ML" 64 | echo Add --randomly-seed=SEED to the test command to reproduce test order. 65 | python -m pytest tests 66 | unset OPEN3D_ML_ROOT 67 | 68 | popd 69 | -------------------------------------------------------------------------------- /data/demo/fragment.pcd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isl-org/Open3D-ML/b64b51445bf2fdb17f7de4fd7a1c383ac238a3dc/data/demo/fragment.pcd -------------------------------------------------------------------------------- /data/demo/fragment.ply: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isl-org/Open3D-ML/b64b51445bf2fdb17f7de4fd7a1c383ac238a3dc/data/demo/fragment.ply -------------------------------------------------------------------------------- /docs/images/getting_started_ml_visualizer.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isl-org/Open3D-ML/b64b51445bf2fdb17f7de4fd7a1c383ac238a3dc/docs/images/getting_started_ml_visualizer.gif -------------------------------------------------------------------------------- /docs/images/tensorboard_demo_scene.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isl-org/Open3D-ML/b64b51445bf2fdb17f7de4fd7a1c383ac238a3dc/docs/images/tensorboard_demo_scene.jpg -------------------------------------------------------------------------------- /docs/images/tensorboard_monkey.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isl-org/Open3D-ML/b64b51445bf2fdb17f7de4fd7a1c383ac238a3dc/docs/images/tensorboard_monkey.jpg -------------------------------------------------------------------------------- /docs/images/tensorboard_small_scale.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isl-org/Open3D-ML/b64b51445bf2fdb17f7de4fd7a1c383ac238a3dc/docs/images/tensorboard_small_scale.gif -------------------------------------------------------------------------------- /docs/images/visualizer_BoundingBoxes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isl-org/Open3D-ML/b64b51445bf2fdb17f7de4fd7a1c383ac238a3dc/docs/images/visualizer_BoundingBoxes.png -------------------------------------------------------------------------------- /docs/images/visualizer_custom_lut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isl-org/Open3D-ML/b64b51445bf2fdb17f7de4fd7a1c383ac238a3dc/docs/images/visualizer_custom_lut.png -------------------------------------------------------------------------------- /docs/images/visualizer_int_attr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isl-org/Open3D-ML/b64b51445bf2fdb17f7de4fd7a1c383ac238a3dc/docs/images/visualizer_int_attr.png -------------------------------------------------------------------------------- /docs/images/visualizer_predictions.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isl-org/Open3D-ML/b64b51445bf2fdb17f7de4fd7a1c383ac238a3dc/docs/images/visualizer_predictions.gif -------------------------------------------------------------------------------- /docs/images/visualizer_random_color_attr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isl-org/Open3D-ML/b64b51445bf2fdb17f7de4fd7a1c383ac238a3dc/docs/images/visualizer_random_color_attr.png -------------------------------------------------------------------------------- /docs/openvino.md: -------------------------------------------------------------------------------- 1 | # OpenVINO backend 2 | 3 | Open3D-ML allows to use [Intel 4 | OpenVINO](https://github.com/openvinotoolkit/openvino) as an optional backend for deep learning models inference. 5 | 6 | ## Install 7 | 8 | Install a compatible version of OpenVINO with: 9 | 10 | ```sh 11 | pip install -r requirements-openvino.txt 12 | ``` 13 | 14 | ## Usage 15 | 16 | To enable OpenVINO, wrap a model in `ml3d.models.OpenVINOModel` class. In example, 17 | 18 | ```python 19 | net = ml3d.models.PointPillars(**cfg.model, device='cpu') 20 | net = ml3d.models.OpenVINOModel(net) 21 | ``` 22 | 23 | Then use `net` as usual. 24 | 25 | ## Supported hardware 26 | 27 | OpenVINO supports Intel CPUs, GPUs and VPUs. By default, model is executed on CPU. 28 | To switch between devices, use `net.to` option: 29 | 30 | ```python 31 | net.to("cpu") # CPU device (default) 32 | net.to("gpu") # GPU device 33 | net.to("myriad") # VPU device 34 | ``` 35 | 36 | ## Supported models 37 | 38 | * `RandLA-Net` (tf, torch) 39 | * `KPConv` (tf, torch) 40 | * `PointPillars` (torch) 41 | -------------------------------------------------------------------------------- /docs/tutorial/notebook/index.rst: -------------------------------------------------------------------------------- 1 | .. _jupyter_notebooks: 2 | 3 | Jupyter Notebooks 4 | =================================================================== 5 | 6 | The following tutorials will demonstrate how you can create Jupyter Notebooks that can train a semantic segmentation using TensorFlow and PyTorch. 7 | 8 | .. toctree:: 9 | 10 | train_ss_model_using_pytorch 11 | train_ss_model_using_tensorflow 12 | add_own_dataset.rst 13 | 14 | -------------------------------------------------------------------------------- /examples/util.py: -------------------------------------------------------------------------------- 1 | # ---------------------------------------------------------------------------- 2 | # - Open3D: www.open3d.org - 3 | # ---------------------------------------------------------------------------- 4 | # The MIT License (MIT) 5 | # 6 | # Copyright (c) 2018-2021 www.open3d.org 7 | # 8 | # Permission is hereby granted, free of charge, to any person obtaining a copy 9 | # of this software and associated documentation files (the "Software"), to deal 10 | # in the Software without restriction, including without limitation the rights 11 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | # copies of the Software, and to permit persons to whom the Software is 13 | # furnished to do so, subject to the following conditions: 14 | # 15 | # The above copyright notice and this permission notice shall be included in 16 | # all copies or substantial portions of the Software. 17 | # 18 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 24 | # IN THE SOFTWARE. 25 | # ---------------------------------------------------------------------------- 26 | from os import listdir 27 | from os.path import isfile, join, dirname, abspath, exists 28 | import urllib.request 29 | import tempfile 30 | import shutil 31 | 32 | 33 | def ensure_demo_data(): 34 | """Check if demo data is present. Else download from github.""" 35 | # Very basic check for demo data. Delete the folder and re-run if data is 36 | # corrupted. 37 | DEMO_DATA_DIR = join(dirname(abspath(__file__)), "demo_data") 38 | DEMO_DATA_URL = "https://github.com/isl-org/open3d_downloads/releases/download/open3d-ml/open3dml_demo_data.zip" 39 | if exists(DEMO_DATA_DIR) and {'KITTI', 'SemanticKITTI'}.issubset( 40 | listdir(DEMO_DATA_DIR)): 41 | return DEMO_DATA_DIR 42 | print(f"Demo data not found in {DEMO_DATA_DIR}. Downloading...") 43 | with tempfile.TemporaryDirectory() as dl_dir: 44 | dl_filename = join(dl_dir, "demo_data.zip") 45 | with urllib.request.urlopen(DEMO_DATA_URL) as response, open( 46 | dl_filename, 'wb') as dl_file: 47 | shutil.copyfileobj(response, dl_file) 48 | shutil.unpack_archive(dl_filename, DEMO_DATA_DIR) 49 | 50 | return DEMO_DATA_DIR 51 | -------------------------------------------------------------------------------- /ml3d/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = [] 2 | -------------------------------------------------------------------------------- /ml3d/configs/__init__.py: -------------------------------------------------------------------------------- 1 | """Config files for ml3d.""" 2 | -------------------------------------------------------------------------------- /ml3d/configs/default_cfgs/kpconv.yml: -------------------------------------------------------------------------------- 1 | name: KPFCNN 2 | ckpt_path: # path/to/your/checkpoint 3 | KP_extent: 1.2 4 | KP_influence: linear 5 | aggregation_mode: sum 6 | architecture: 7 | - simple 8 | - resnetb 9 | - resnetb_strided 10 | - resnetb 11 | - resnetb 12 | - resnetb_strided 13 | - resnetb 14 | - resnetb 15 | - resnetb_strided 16 | - resnetb 17 | - resnetb 18 | - resnetb_strided 19 | - resnetb 20 | - nearest_upsample 21 | - unary 22 | - nearest_upsample 23 | - unary 24 | - nearest_upsample 25 | - unary 26 | - nearest_upsample 27 | - unary 28 | augment_color: 0.8 29 | augment_noise: 0.001 30 | augment_rotation: vertical 31 | augment_scale_anisotropic: true 32 | augment_scale_max: 1.2 33 | augment_scale_min: 0.8 34 | augment_symmetries: 35 | - true 36 | - false 37 | - false 38 | batch_limit: 50000 39 | batch_norm_momentum: 0.02 40 | batch_num: 30 41 | batcher: ConcatBatcher 42 | conv_radius: 2.5 43 | deform_fitting_mode: point2point 44 | deform_fitting_power: 1.0 45 | deform_radius: 6.0 46 | density_parameter: 5.0 47 | first_features_dim: 128 48 | first_subsampling_dl: 0.06 49 | fixed_kernel_points: center 50 | ignored_label_inds: [] 51 | in_features_dim: 2 # with colors : 5, without colors : 2 52 | in_points_dim: 3 53 | in_radius: 4.0 54 | lbl_values: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] 55 | max_in_points: 20000 56 | modulated: false 57 | num_classes: 19 # number of valid classes. 58 | num_kernel_points: 15 59 | num_layers: 5 60 | repulse_extent: 1.2 61 | use_batch_norm: true 62 | val_batch_num: 30 63 | -------------------------------------------------------------------------------- /ml3d/configs/default_cfgs/object_detection.yml: -------------------------------------------------------------------------------- 1 | batch_size: 2 2 | val_batch_size: 1 3 | test_batch_size: 1 4 | max_epoch: 200 5 | save_ckpt_freq: 5 6 | main_log_dir: ./logs 7 | train_sum_dir: train_log 8 | device: gpu 9 | # Open3D for Tensorboard summary (3D data) 10 | summary: 11 | # Record summary in these stages (from train, valid, test) 12 | record_for: [] 13 | # Subsample point cloud if n_pts exceeds this value. Empty => save all 14 | # points in the summary. 15 | max_pts: 16 | # Only write input point cloud in the first epoch. In other epochs, use 17 | # reference to the first step. Do not use if each epoch has a different 18 | # order of minibatches. 19 | use_reference: false 20 | # Write at most this many initial samples from each batch 21 | max_outputs: 1 22 | -------------------------------------------------------------------------------- /ml3d/configs/default_cfgs/parislille3d.yml: -------------------------------------------------------------------------------- 1 | name: ParisLille3D 2 | dataset_path: # path/to/your/dataset 3 | cache_dir: ./logs/cache 4 | class_weights: [65075320, 33014819, 656096, 61715, 296523, 4052947, 172132,4212295, 10599237] 5 | ignored_label_inds: 6 | - 0 7 | num_points: 65536 8 | test_result_folder: ./test 9 | val_files: 10 | - Lille2.ply 11 | use_cache: False 12 | -------------------------------------------------------------------------------- /ml3d/configs/default_cfgs/randlanet.yml: -------------------------------------------------------------------------------- 1 | name: RandLANet 2 | batcher: DefaultBatcher 3 | ckpt_path: # path/to/your/checkpoint 4 | dim_feature: 8 5 | dim_input: 3 # 3 + feature dimension. 6 | dim_output: 7 | - 16 8 | - 64 9 | - 128 10 | - 256 11 | grid_size: 0.06 12 | ignored_label_inds: [] 13 | k_n: 16 14 | num_classes: 19 15 | num_layers: 4 16 | num_points: 45056 17 | sub_sampling_ratio: 18 | - 4 19 | - 4 20 | - 4 21 | - 4 -------------------------------------------------------------------------------- /ml3d/configs/default_cfgs/s3dis.yml: -------------------------------------------------------------------------------- 1 | name: S3DIS 2 | dataset_path: # path/to/your/dataset 3 | cache_dir: ./logs/cache 4 | class_weights: [3370714, 2856755, 4919229, 318158, 375640, 5 | 478001, 974733, 650464, 791496, 88727, 1284130, 229758, 2272837] 6 | dataset_path: ../dataset/S3DIS/ 7 | ignored_label_inds: [] 8 | num_points: 40960 9 | test_area_idx: 3 10 | test_result_folder: ./test 11 | use_cache: False 12 | -------------------------------------------------------------------------------- /ml3d/configs/default_cfgs/semantic3d.yml: -------------------------------------------------------------------------------- 1 | name: Semantic3D 2 | dataset_path: # path/to/your/dataset 3 | cache_dir: .logs/cache/ 4 | class_weights: [5181602, 5012952, 6830086, 1311528, 10476365, 946982, 334860, 269353, 269353] 5 | ignored_label_inds: 6 | - 0 7 | num_points: 65536 8 | test_result_folder: ./test 9 | use_cache: true 10 | val_files: 11 | - bildstein_station3_xyz_intensity_rgb 12 | - sg27_station2_intensity_rgb 13 | -------------------------------------------------------------------------------- /ml3d/configs/default_cfgs/semantic_segmentation.yml: -------------------------------------------------------------------------------- 1 | batch_size: 2 2 | val_batch_size: 1 3 | test_batch_size: 1 4 | max_epoch: 100 5 | learning_rate: 0.01 6 | lr_decays: 0.95 7 | deform_lr_factor: 0.1 8 | save_ckpt_freq: 5 9 | adam_lr: 0.01 10 | scheduler_gamma: 0.95 11 | momentum: 0.98 12 | main_log_dir: ./logs 13 | train_sum_dir: train_log 14 | device: gpu 15 | # Open3D for Tensorboard summary (3D data) 16 | summary: 17 | # Record summary in these stages (from train, valid, test) 18 | record_for: [] 19 | # Subsample point cloud if n_pts exceeds this value. Empty => save all 20 | # points in the summary. 21 | max_pts: 22 | # Only write input point cloud in the first epoch. In other epochs, use 23 | # reference to the first step. Do not use if each epoch has a different 24 | # order of minibatches. Do not use for RandLaNet or KPConv. 25 | use_reference: false 26 | # Write at most this many initial samples from each batch 27 | max_outputs: 1 28 | -------------------------------------------------------------------------------- /ml3d/configs/default_cfgs/semantickitti.yml: -------------------------------------------------------------------------------- 1 | name: SemanticKITTI 2 | dataset_path: # path/to/your/dataset 3 | cache_dir: ./logs/cache 4 | use_cache: false 5 | class_weights: [55437630, 320797, 541736, 2578735, 3274484, 552662, 6 | 184064, 78858, 240942562, 17294618, 170599734, 6369672, 230413074, 101130274, 7 | 476491114, 9833174, 129609852, 4506626, 1168181] 8 | test_result_folder: ./test 9 | test_split: ['11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21'] 10 | training_split: ['00', '01', '02', '03', '04', '05', '06', '07', '09', '10'] 11 | validation_split: ['08'] 12 | all_split: ['00', '01', '02', '03', '04', '05', '06', '07', '09', 13 | '08', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21'] 14 | -------------------------------------------------------------------------------- /ml3d/configs/default_cfgs/shapenet.yml: -------------------------------------------------------------------------------- 1 | name: ShapeNet 2 | dataset_path: # path/to/your/dataset 3 | cache_dir: .logs/cache/ 4 | class_weights: [2690, 76, 55, 1824, 3746, 69, 787, 392, 1546, 445, 202, 184, 275, 66, 152, 5266] 5 | ignored_label_inds: [] 6 | num_points: 17775 7 | test_result_folder: ./test 8 | use_cache: false 9 | -------------------------------------------------------------------------------- /ml3d/configs/default_cfgs/toronto3d.yml: -------------------------------------------------------------------------------- 1 | name: Toronto3D 2 | dataset_path: # path/to/your/dataset 3 | cache_dir: ./logs/cache 4 | class_weights: [41697357, 1745448, 6572572, 19136493, 674897, 897825, 4634634, 374721] 5 | ignored_label_inds: 6 | - 0 7 | num_points: 65536 8 | test_files: 9 | - L002.ply 10 | test_result_folder: ./test 11 | train_files: 12 | - L001.ply 13 | - L003.ply 14 | - L004.ply 15 | val_files: 16 | - L002.ply 17 | use_cache: False 18 | -------------------------------------------------------------------------------- /ml3d/configs/kpconv_parislille3d.yml: -------------------------------------------------------------------------------- 1 | dataset: 2 | name: ParisLille3D 3 | dataset_path: # path/to/your/dataset 4 | cache_dir: ./logs/cache 5 | class_weights: [65075320, 33014819, 656096, 61715, 296523, 4052947, 172132,4212295, 10599237] 6 | ignored_label_inds: 7 | - 0 8 | test_result_folder: ./test 9 | val_files: 10 | - Lille2.ply 11 | use_cache: True 12 | steps_per_epoch_train: 40 13 | steps_per_epoch_valid: 10 14 | sampler: 15 | name: SemSegSpatiallyRegularSampler 16 | model: 17 | name: KPFCNN 18 | ckpt_path: # path/to/your/checkpoint 19 | KP_extent: 1.0 20 | KP_influence: linear 21 | aggregation_mode: sum 22 | architecture: [ 23 | 'simple', 24 | 'resnetb', 25 | 'resnetb_strided', 26 | 'resnetb', 27 | 'resnetb_strided', 28 | 'resnetb_deformable', 29 | 'resnetb_deformable_strided', 30 | 'resnetb_deformable', 31 | 'resnetb_deformable_strided', 32 | 'resnetb_deformable', 33 | 'nearest_upsample', 34 | 'unary', 35 | 'nearest_upsample', 36 | 'unary', 37 | 'nearest_upsample', 38 | 'unary', 39 | 'nearest_upsample', 40 | 'unary'] 41 | reduce_fc: true 42 | augment_color: 0.8 43 | augment_noise: 0.01 44 | augment_rotation: vertical 45 | augment_scale_anisotropic: true 46 | augment_scale_max: 1.1 47 | augment_scale_min: 0.9 48 | augment_symmetries: 49 | - true 50 | - false 51 | - false 52 | batch_limit: 20000 53 | grad_clip_norm: 100.0 54 | batch_norm_momentum: 0.98 55 | batcher: ConcatBatcher 56 | conv_radius: 2.5 57 | deform_fitting_mode: point2point 58 | deform_fitting_power: 1.0 59 | deform_radius: 6.0 60 | density_parameter: 5.0 61 | first_features_dim: 128 62 | first_subsampling_dl: 0.08 63 | fixed_kernel_points: center 64 | ignored_label_inds: [0] 65 | in_features_dim: 1 66 | in_points_dim: 3 67 | l_relu: 0.2 68 | in_radius: 4.0 69 | lbl_values: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 70 | min_in_points: 10000 71 | max_in_points: 17000 72 | modulated: false 73 | num_classes: 9 74 | num_kernel_points: 15 75 | num_layers: 5 76 | repulse_extent: 1.2 77 | use_batch_norm: true 78 | pipeline: 79 | name: SemanticSegmentation 80 | adam_lr: 0.01 81 | momentum: 0.98 82 | batch_size: 1 83 | learning_rate: 0.01 84 | lr_decays: 0.98477 85 | deform_lr_factor: 0.1 86 | main_log_dir: ./logs 87 | max_epoch: 100 88 | save_ckpt_freq: 5 89 | scheduler_gamma: 0.95 90 | test_batch_size: 1 91 | train_sum_dir: train_log 92 | val_batch_size: 1 93 | weight_decay: 0.001 94 | summary: 95 | record_for: [] 96 | max_pts: 97 | use_reference: false 98 | max_outputs: 1 99 | -------------------------------------------------------------------------------- /ml3d/configs/kpconv_s3dis.yml: -------------------------------------------------------------------------------- 1 | dataset: 2 | name: S3DIS 3 | dataset_path: # path/to/your/dataset 4 | cache_dir: ./logs/cache 5 | class_weights: [3370714, 2856755, 4919229, 318158, 375640, 6 | 478001, 974733, 650464, 791496, 88727, 1284130, 229758, 2272837] 7 | ignored_label_inds: [] 8 | num_points: 40960 9 | test_area_idx: 3 10 | test_result_folder: ./test 11 | use_cache: false 12 | model: 13 | name: KPFCNN 14 | ckpt_path: # path/to/your/checkpoint 15 | KP_extent: 1.2 16 | KP_influence: linear 17 | aggregation_mode: sum 18 | architecture: 19 | - simple 20 | - resnetb 21 | - resnetb_strided 22 | - resnetb 23 | - resnetb 24 | - resnetb_strided 25 | - resnetb 26 | - resnetb 27 | - resnetb_strided 28 | - resnetb 29 | - resnetb 30 | - resnetb_strided 31 | - resnetb 32 | - nearest_upsample 33 | - unary 34 | - nearest_upsample 35 | - unary 36 | - nearest_upsample 37 | - unary 38 | - nearest_upsample 39 | - unary 40 | augment_color: 1.0 41 | augment_noise: 0.001 42 | augment_rotation: vertical 43 | augment_scale_anisotropic: true 44 | augment_scale_max: 1.1 45 | augment_scale_min: 0.9 46 | augment_symmetries: 47 | - true 48 | - false 49 | - false 50 | batch_limit: 20000 51 | batch_norm_momentum: 0.98 52 | batcher: ConcatBatcher 53 | conv_radius: 2.5 54 | deform_fitting_mode: point2point 55 | deform_fitting_power: 1.0 56 | deform_radius: 6.0 57 | density_parameter: 5.0 58 | first_features_dim: 128 59 | first_subsampling_dl: 0.04 60 | fixed_kernel_points: center 61 | ignored_label_inds: [] 62 | in_features_dim: 5 63 | in_points_dim: 3 64 | in_radius: 1.5 65 | lbl_values: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] 66 | max_in_points: 20000 67 | modulated: false 68 | num_classes: 13 69 | num_kernel_points: 15 70 | num_layers: 5 71 | repulse_extent: 1.2 72 | use_batch_norm: true 73 | t_normalize: 74 | method: linear 75 | normalize_points: False 76 | feat_bias: 0 77 | feat_scale: 255 78 | pipeline: 79 | name: SemanticSegmentation 80 | adam_lr: 0.01 81 | batch_size: 4 82 | learning_rate: 0.01 83 | lr_decays: 0.98477 84 | deform_lr_factor: 0.1 85 | main_log_dir: ./logs 86 | max_epoch: 800 87 | momentum: 0.98 88 | save_ckpt_freq: 10 89 | scheduler_gamma: 0.98477 90 | test_batch_size: 4 91 | train_sum_dir: train_log 92 | val_batch_size: 4 93 | weight_decay: 0.001 94 | summary: 95 | record_for: [] 96 | max_pts: 97 | use_reference: false 98 | max_outputs: 1 99 | -------------------------------------------------------------------------------- /ml3d/configs/kpconv_semantic3d.yml: -------------------------------------------------------------------------------- 1 | dataset: 2 | name: Semantic3D 3 | dataset_path: # path/to/your/dataset 4 | cache_dir: ./logs/cache_small3d/ 5 | class_weights: [5181602, 5012952, 6830086, 1311528, 10476365, 946982, 334860, 269353] 6 | ignored_label_inds: 7 | - 0 8 | test_result_folder: ./test 9 | use_cache: true 10 | val_files: 11 | - bildstein_station1_xyz_intensity_rgb 12 | - domfountain_station1_xyz_intensity_rgb 13 | steps_per_epoch_train: 500 14 | steps_per_epoch_valid: 50 15 | model: 16 | name: KPFCNN 17 | ckpt_path: # path/to/your/checkpoint 18 | KP_extent: 1.2 19 | KP_influence: linear 20 | aggregation_mode: sum 21 | architecture: ['simple', 22 | 'resnetb', 23 | 'resnetb_strided', 24 | 'resnetb', 25 | 'resnetb_strided', 26 | 'resnetb', 27 | 'resnetb_strided', 28 | 'resnetb', 29 | 'resnetb_strided', 30 | 'resnetb', 31 | 'nearest_upsample', 32 | 'unary', 33 | 'nearest_upsample', 34 | 'unary', 35 | 'nearest_upsample', 36 | 'unary', 37 | 'nearest_upsample', 38 | 'unary'] 39 | augment_color: 1.0 40 | augment_noise: 0.001 41 | augment_rotation: vertical 42 | augment_scale_anisotropic: true 43 | augment_scale_max: 1.1 44 | augment_scale_min: 0.9 45 | augment_symmetries: 46 | - true 47 | - false 48 | - false 49 | batch_limit: 10000 50 | batch_norm_momentum: 0.98 51 | batcher: ConcatBatcher 52 | conv_radius: 2.5 53 | deform_fitting_mode: point2point 54 | deform_fitting_power: 1.0 55 | deform_radius: 6.0 56 | density_parameter: 5.0 57 | first_features_dim: 128 58 | first_subsampling_dl: 0.06 59 | fixed_kernel_points: center 60 | ignored_label_inds: [0] 61 | in_features_dim: 4 62 | in_points_dim: 3 63 | in_radius: 6.0 64 | lbl_values: [0, 1, 2, 3, 4, 5, 6, 7, 8] 65 | min_in_points: 5000 66 | max_in_points: 10000 67 | modulated: false 68 | num_classes: 8 69 | num_kernel_points: 15 70 | num_layers: 5 71 | repulse_extent: 1.2 72 | use_batch_norm: true 73 | pipeline: 74 | name: SemanticSegmentation 75 | adam_lr: 0.01 76 | batch_size: 1 77 | learning_rate: 0.01 78 | lr_decays: 0.98477 79 | deform_lr_factor: 0.1 80 | main_log_dir: ./logs 81 | max_epoch: 500 82 | save_ckpt_freq: 5 83 | scheduler_gamma: 0.95 84 | test_batch_size: 1 85 | train_sum_dir: train_log 86 | val_batch_size: 1 87 | weight_decay: 0.001 88 | summary: 89 | record_for: [] 90 | max_pts: 91 | use_reference: false 92 | max_outputs: 1 93 | -------------------------------------------------------------------------------- /ml3d/configs/kpconv_semantickitti.yml: -------------------------------------------------------------------------------- 1 | dataset: 2 | name: SemanticKITTI 3 | # dataset_path: path/to/your/datasets 4 | class_weights: [55437630, 320797, 541736, 2578735, 3274484, 552662, 184064, 5 | 78858, 240942562, 17294618, 170599734, 6369672, 230413074, 101130274, 6 | 476491114, 9833174, 129609852, 4506626, 1168181] 7 | test_result_folder: ./test 8 | test_split: ['11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21'] 9 | training_split: ['00', '01', '02', '03', '04', '05', '06', '07', '09', '10'] 10 | all_split: ['00', '01', '02', '03', '04', '05', '06', '07', '09', 11 | '08', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21'] 12 | validation_split: ['08'] 13 | use_cache: true 14 | validation_split: 15 | - '08' 16 | model: 17 | name: KPFCNN 18 | ckpt_path: # path/to/your/checkpoint 19 | KP_extent: 1.2 20 | KP_influence: linear 21 | aggregation_mode: sum 22 | architecture: 23 | - simple 24 | - resnetb 25 | - resnetb_strided 26 | - resnetb 27 | - resnetb 28 | - resnetb_strided 29 | - resnetb 30 | - resnetb 31 | - resnetb_strided 32 | - resnetb 33 | - resnetb 34 | - resnetb_strided 35 | - resnetb 36 | - nearest_upsample 37 | - unary 38 | - nearest_upsample 39 | - unary 40 | - nearest_upsample 41 | - unary 42 | - nearest_upsample 43 | - unary 44 | augment_color: 0.8 45 | augment_noise: 0.001 46 | augment_rotation: vertical 47 | augment_scale_anisotropic: true 48 | augment_scale_max: 1.2 49 | augment_scale_min: 0.8 50 | augment_symmetries: 51 | - true 52 | - false 53 | - false 54 | batch_limit: 50000 55 | batch_norm_momentum: 0.98 56 | batcher: ConcatBatcher 57 | conv_radius: 2.5 58 | deform_fitting_mode: point2point 59 | deform_fitting_power: 1.0 60 | deform_radius: 6.0 61 | density_parameter: 5.0 62 | first_features_dim: 128 63 | first_subsampling_dl: 0.06 64 | fixed_kernel_points: center 65 | ignored_label_inds: 66 | - 0 67 | in_features_dim: 2 68 | in_points_dim: 3 69 | in_radius: 4.0 70 | lbl_values: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] 71 | min_in_points: 10000 72 | max_in_points: 20000 73 | modulated: false 74 | num_classes: 19 75 | num_kernel_points: 15 76 | num_layers: 5 77 | repulse_extent: 1.2 78 | use_batch_norm: true 79 | pipeline: 80 | name: SemanticSegmentation 81 | adam_lr: 0.01 82 | batch_size: 1 83 | learning_rate: 0.01 84 | lr_decays: 0.98477 85 | deform_lr_factor: 0.1 86 | main_log_dir: ./logs 87 | max_epoch: 800 88 | momentum: 0.98 89 | save_ckpt_freq: 3 90 | scheduler_gamma: 0.98477 91 | test_batch_size: 1 92 | train_sum_dir: train_log 93 | val_batch_size: 1 94 | weight_decay: 0.001 95 | summary: 96 | record_for: [] 97 | max_pts: 98 | use_reference: false 99 | max_outputs: 1 100 | -------------------------------------------------------------------------------- /ml3d/configs/kpconv_toronto3d.yml: -------------------------------------------------------------------------------- 1 | dataset: 2 | name: Toronto3D 3 | dataset_path: # path/to/your/dataset 4 | cache_dir: ./logs/cache 5 | class_weights: [35391894., 1449308., 14650919., 18252779., 6 | 589856., 743579., 4311631., 356463.] 7 | ignored_label_inds: 8 | - 0 9 | test_files: 10 | - L002.ply 11 | test_result_folder: ./test 12 | train_files: 13 | - L001.ply 14 | - L003.ply 15 | - L004.ply 16 | val_files: 17 | - L002.ply 18 | use_cache: true 19 | sampler: 20 | name: SemSegSpatiallyRegularSampler 21 | steps_per_epoch_train: 100 22 | steps_per_epoch_valid: 10 23 | model: 24 | name: KPFCNN 25 | ckpt_path: # path/to/your/checkpoint 26 | KP_extent: 1.0 27 | KP_influence: linear 28 | aggregation_mode: sum 29 | architecture: [ 30 | 'simple', 'resnetb', 31 | 'resnetb_strided', 'resnetb', 32 | 'resnetb_strided', 'resnetb', 33 | 'resnetb_strided', 'resnetb', 34 | 'resnetb_strided', 'resnetb', 35 | 'nearest_upsample', 'unary', 36 | 'nearest_upsample', 'unary', 37 | 'nearest_upsample', 'unary', 38 | 'nearest_upsample', 'unary' 39 | ] 40 | augment_color: 1 41 | augment_noise: 0.0001 42 | augment_rotation: vertical 43 | augment_scale_anisotropic: false 44 | augment_scale_max: 1.1 45 | augment_scale_min: 0.9 46 | grad_clip_norm: 100.0 47 | augment_symmetries: 48 | - true 49 | - false 50 | - false 51 | batch_limit: 10000 52 | batch_norm_momentum: 0.98 53 | batcher: ConcatBatcher 54 | conv_radius: 2.5 55 | deform_fitting_mode: point2point 56 | deform_fitting_power: 1.0 57 | deform_radius: 6.0 58 | density_parameter: 5.0 59 | first_features_dim: 128 60 | first_subsampling_dl: 0.08 61 | fixed_kernel_points: center 62 | ignored_label_inds: 63 | - 0 64 | in_features_dim: 1 65 | in_points_dim: 3 66 | in_radius: 4.0 67 | lbl_values: [0, 1, 2, 3, 4, 5, 6, 7, 8] 68 | min_in_points: 5000 69 | max_in_points: 10000 70 | modulated: false 71 | num_classes: 8 72 | num_kernel_points: 15 73 | num_layers: 5 74 | repulse_extent: 1.2 75 | use_batch_norm: true 76 | reduce_fc: true 77 | l_relu: 0.2 78 | t_normalize: 79 | method: linear 80 | normalize_points: False 81 | feat_bias: 0 82 | feat_scale: 255 83 | pipeline: 84 | name: SemanticSegmentation 85 | momentum: 0.98 86 | adam_lr: 0.01 87 | batch_size: 1 88 | learning_rate: 0.01 89 | lr_decays: 0.98477 90 | deform_lr_factor: 0.1 91 | weight_decay: 0.001 92 | main_log_dir: ./logs 93 | max_epoch: 1000 94 | save_ckpt_freq: 5 95 | scheduler_gamma: 0.95 96 | test_batch_size: 1 97 | train_sum_dir: train_log 98 | val_batch_size: 1 99 | summary: 100 | record_for: [] 101 | max_pts: 102 | use_reference: false 103 | max_outputs: 1 104 | -------------------------------------------------------------------------------- /ml3d/configs/pointpillars_argoverse.yml: -------------------------------------------------------------------------------- 1 | dataset: 2 | name: Argoverse 3 | dataset_path: # path/to/your/dataset 4 | cache_dir: ./logs/cache 5 | steps_per_epoch_train: 5000 6 | steps_per_epoch_valid: 1000 7 | 8 | model: 9 | name: PointPillars 10 | ckpt_path: # path/to/your/checkpoint 11 | 12 | batcher: "ignore" 13 | 14 | point_cloud_range: [-50, -50, -5, 50, 50, 3.0] 15 | classes: ['VEHICLE', 'BUS', 'TRAILER','PEDESTRIAN', 'BICYCLE'] 16 | 17 | loss: 18 | focal: 19 | gamma: 2.0 20 | alpha: 0.25 21 | loss_weight: 1.0 22 | smooth_l1: 23 | beta: 0.11 24 | loss_weight: 2.0 25 | cross_entropy: 26 | loss_weight: 0.2 27 | 28 | voxelize: 29 | max_num_points: 20 30 | voxel_size: &vsize 31 | [0.25, 0.25, 8] 32 | max_voxels: [30000, 40000] 33 | 34 | voxel_encoder: 35 | in_channels: 3 36 | feat_channels: [64, 64] 37 | voxel_size: *vsize 38 | 39 | scatter: 40 | in_channels: 64 41 | output_shape: [400, 400] 42 | 43 | backbone: 44 | in_channels: 64 45 | out_channels: [64, 128, 256] 46 | layer_nums: [3, 5, 5] 47 | layer_strides: [2, 2, 2] 48 | 49 | neck: 50 | in_channels: [64, 128, 256] 51 | out_channels: [128, 128, 128] 52 | upsample_strides: [1, 2, 4] 53 | use_conv_for_no_stride: false 54 | 55 | head: 56 | in_channels: 384 57 | feat_channels: 384 58 | nms_pre: 1000 59 | score_thr: 0.05 60 | ranges: [ 61 | [-49.6, -49.6, -1.80032795, 49.6, 49.6, -1.80032795], 62 | [-49.6, -49.6, -1.74440365, 49.6, 49.6, -1.74440365], 63 | [-49.6, -49.6, -1.68526504, 49.6, 49.6, -1.68526504], 64 | [-49.6, -49.6, -1.61785072, 49.6, 49.6, -1.61785072], 65 | [-49.6, -49.6, -1.763965, 49.6, 49.6, -1.763965], 66 | ] 67 | sizes: [ 68 | [1.95017717, 4.60718145, 1.72270761], # car 69 | [2.4560939, 6.73778078, 2.73004906], # truck 70 | [2.87427237, 12.01320693, 3.81509561], # trailer 71 | [0.66344886, 0.7256437, 1.75748069], # pedestrian 72 | [2.49008838, 0.48578221, 0.98297065], # barrier 73 | ] 74 | rotations: [0, 1.57] 75 | iou_thr: [[0.3, 0.6]] 76 | dir_offset: 0.7854 77 | 78 | augment: 79 | PointShuffle: True 80 | ObjectRangeFilter: 81 | point_cloud_range: [-50, -50, -5, 50, 50, 3.0] 82 | 83 | 84 | pipeline: 85 | name: ObjectDetection 86 | test_compute_metric: true 87 | batch_size: 6 88 | val_batch_size: 1 89 | test_batch_size: 1 90 | save_ckpt_freq: 5 91 | max_epoch: 200 92 | main_log_dir: ./logs 93 | train_sum_dir: train_log 94 | grad_clip_norm: 2 95 | 96 | optimizer: 97 | lr: 0.001 98 | betas: [0.95, 0.99] 99 | weight_decay: 0.01 100 | 101 | # evaluation properties 102 | overlaps: [0.7] 103 | difficulties: [0] 104 | summary: 105 | record_for: [] 106 | max_pts: 107 | use_reference: false 108 | max_outputs: 1 109 | -------------------------------------------------------------------------------- /ml3d/configs/pointpillars_kitti.yml: -------------------------------------------------------------------------------- 1 | dataset: 2 | name: KITTI 3 | dataset_path: # path/to/your/dataset 4 | cache_dir: ./logs/cache 5 | steps_per_epoch_train: 5000 6 | 7 | model: 8 | name: PointPillars 9 | ckpt_path: # path/to/your/checkpoint 10 | 11 | batcher: "ignore" 12 | 13 | point_cloud_range: [0, -39.68, -3, 69.12, 39.68, 1] 14 | classes: ['Pedestrian', 'Cyclist', 'Car'] 15 | 16 | loss: 17 | focal: 18 | gamma: 2.0 19 | alpha: 0.25 20 | loss_weight: 1.0 21 | smooth_l1: 22 | beta: 0.11 23 | loss_weight: 2.0 24 | cross_entropy: 25 | loss_weight: 0.2 26 | 27 | voxelize: 28 | max_num_points: 32 29 | voxel_size: &vsize 30 | [0.16, 0.16, 4] 31 | max_voxels: [16000, 40000] 32 | 33 | voxel_encoder: 34 | in_channels: 4 35 | feat_channels: [64] 36 | voxel_size: *vsize 37 | 38 | scatter: 39 | in_channels: 64 40 | output_shape: [496, 432] 41 | 42 | backbone: 43 | in_channels: 64 44 | out_channels: [64, 128, 256] 45 | layer_nums: [3, 5, 5] 46 | layer_strides: [2, 2, 2] 47 | 48 | neck: 49 | in_channels: [64, 128, 256] 50 | out_channels: [128, 128, 128] 51 | upsample_strides: [1, 2, 4] 52 | use_conv_for_no_stride: false 53 | 54 | head: 55 | in_channels: 384 56 | feat_channels: 384 57 | nms_pre: 100 58 | score_thr: 0.1 59 | ranges: [ 60 | [0, -39.68, -0.6, 70.4, 39.68, -0.6], 61 | [0, -39.68, -0.6, 70.4, 39.68, -0.6], 62 | [0, -39.68, -1.78, 70.4, 39.68, -1.78] 63 | ] 64 | sizes: [[0.6, 0.8, 1.73], [0.6, 1.76, 1.73], [1.6, 3.9, 1.56]] 65 | rotations: [0, 1.57] 66 | iou_thr: [[0.35, 0.5], [0.35, 0.5], [0.45, 0.6]] 67 | 68 | augment: 69 | PointShuffle: True 70 | ObjectRangeFilter: 71 | point_cloud_range: [0, -39.68, -3, 69.12, 39.68, 1] 72 | ObjectSample: 73 | min_points_dict: 74 | Car: 5 75 | Pedestrian: 10 76 | Cyclist: 10 77 | sample_dict: 78 | Car: 15 79 | Pedestrian: 10 80 | Cyclist: 10 81 | 82 | 83 | pipeline: 84 | name: ObjectDetection 85 | test_compute_metric: true 86 | batch_size: 6 87 | val_batch_size: 1 88 | test_batch_size: 1 89 | save_ckpt_freq: 5 90 | max_epoch: 200 91 | main_log_dir: ./logs 92 | train_sum_dir: train_log 93 | grad_clip_norm: 2 94 | 95 | optimizer: 96 | lr: 0.001 97 | betas: [0.95, 0.99] 98 | weight_decay: 0.01 99 | 100 | # evaluation properties 101 | overlaps: [0.5, 0.5, 0.7] 102 | similar_classes: { 103 | Van: Car, 104 | Person_sitting: Pedestrian 105 | } 106 | difficulties: [0, 1, 2] 107 | summary: 108 | record_for: [] 109 | max_pts: 110 | use_reference: false 111 | max_outputs: 1 112 | -------------------------------------------------------------------------------- /ml3d/configs/pointpillars_lyft.yml: -------------------------------------------------------------------------------- 1 | dataset: 2 | name: Lyft 3 | dataset_path: # path/to/your/dataset 4 | cache_dir: ./logs/cache 5 | steps_per_epoch_train: 5000 6 | 7 | model: 8 | name: PointPillars 9 | ckpt_path: # path/to/your/checkpoint 10 | 11 | batcher: "ignore" 12 | 13 | point_cloud_range: [-80, -80, -5, 80, 80, 3.0] 14 | classes: [ 15 | 'car', 'truck', 'bus', 'emergency_vehicle', 'other_vehicle', 'motorcycle', 16 | 'bicycle', 'pedestrian', 'animal' 17 | ] 18 | 19 | loss: 20 | focal: 21 | gamma: 2.0 22 | alpha: 0.25 23 | loss_weight: 1.0 24 | smooth_l1: 25 | beta: 0.11 26 | loss_weight: 2.0 27 | cross_entropy: 28 | loss_weight: 0.2 29 | 30 | voxelize: 31 | max_num_points: 20 32 | voxel_size: &vsize 33 | [0.25, 0.25, 8] 34 | max_voxels: [60000, 60000] 35 | 36 | voxel_encoder: 37 | in_channels: 4 38 | feat_channels: [64] 39 | voxel_size: *vsize 40 | 41 | scatter: 42 | in_channels: 64 43 | output_shape: [640, 640] 44 | 45 | backbone: 46 | in_channels: 64 47 | out_channels: [64, 128, 256] 48 | layer_nums: [3, 5, 5] 49 | layer_strides: [2, 2, 2] 50 | 51 | neck: 52 | in_channels: [64, 128, 256] 53 | out_channels: [128, 128, 128] 54 | upsample_strides: [1, 2, 4] 55 | use_conv_for_no_stride: false 56 | 57 | head: 58 | in_channels: 384 59 | feat_channels: 384 60 | nms_pre: 1000 61 | score_thr: 0.05 62 | ranges: [ 63 | [-80, -80, -1.0715024, 80, 80, -1.0715024], 64 | [-80, -80, -0.3033737, 80, 80, -0.3033737], 65 | [-80, -80, -0.3519405, 80, 80, -0.3519405], 66 | [-80, -80, -0.8871424, 80, 80, -0.8871424], 67 | [-80, -80, -0.6276341, 80, 80, -0.6276341], 68 | [-80, -80, -1.3220503, 80, 80, -1.3220503], 69 | [-80, -80, -1.0709302, 80, 80, -1.0709302], 70 | [-80, -80, -0.9122268, 80, 80, -0.9122268], 71 | [-80, -80, -1.8012227, 80, 80, -1.8012227] 72 | ] 73 | sizes: [ 74 | [1.92, 4.75, 1.71], # car 75 | [2.84, 10.24, 3.44], # truck 76 | [2.92, 12.70, 3.42], # bus 77 | [2.42, 6.52, 2.34], # emergency vehicle 78 | [2.75, 8.17, 3.20], # other vehicle 79 | [0.96, 2.35, 1.59], # motorcycle 80 | [0.63, 1.76, 1.44], # bicycle 81 | [0.76, 0.80, 1.76], # pedestrian 82 | [0.35, 0.73, 0.50] # animal 83 | ] 84 | rotations: [0, 1.57] 85 | iou_thr: [[0.3, 0.6]] 86 | dir_offset: 0.7854 87 | 88 | augment: 89 | PointShuffle: True 90 | ObjectRangeFilter: 91 | point_cloud_range: [-80, -80, -5, 80, 80, 3.0] 92 | 93 | 94 | pipeline: 95 | name: ObjectDetection 96 | test_compute_metric: true 97 | batch_size: 6 98 | val_batch_size: 1 99 | test_batch_size: 1 100 | save_ckpt_freq: 5 101 | max_epoch: 200 102 | main_log_dir: ./logs 103 | train_sum_dir: train_log 104 | grad_clip_norm: 2 105 | 106 | optimizer: 107 | lr: 0.001 108 | betas: [0.95, 0.99] 109 | weight_decay: 0.01 110 | 111 | # evaluation properties 112 | overlaps: [0.7] 113 | difficulties: [0] 114 | summary: 115 | record_for: [] 116 | max_pts: 117 | use_reference: false 118 | max_outputs: 1 119 | -------------------------------------------------------------------------------- /ml3d/configs/pointpillars_nuscenes.yml: -------------------------------------------------------------------------------- 1 | dataset: 2 | name: NuScenes 3 | dataset_path: # path/to/your/dataset 4 | cache_dir: ./logs/cache 5 | steps_per_epoch_train: 5000 6 | 7 | model: 8 | name: PointPillars 9 | ckpt_path: # path/to/your/checkpoint 10 | 11 | batcher: "ignore" 12 | 13 | point_cloud_range: [-50, -50, -5, 50, 50, 3.0] 14 | classes: [ 15 | 'car', 'truck', 'trailer', 'bicycle', 16 | 'pedestrian', 'traffic_cone', 'barrier' 17 | ] 18 | 19 | loss: 20 | focal: 21 | gamma: 2.0 22 | alpha: 0.25 23 | loss_weight: 1.0 24 | smooth_l1: 25 | beta: 0.11 26 | loss_weight: 2.0 27 | cross_entropy: 28 | loss_weight: 0.2 29 | 30 | voxelize: 31 | max_num_points: 20 32 | voxel_size: &vsize 33 | [0.25, 0.25, 8] 34 | max_voxels: [30000, 40000] 35 | 36 | voxel_encoder: 37 | in_channels: 4 38 | feat_channels: [64, 64] 39 | voxel_size: *vsize 40 | 41 | scatter: 42 | in_channels: 64 43 | output_shape: [400, 400] 44 | 45 | backbone: 46 | in_channels: 64 47 | out_channels: [64, 128, 256] 48 | layer_nums: [3, 5, 5] 49 | layer_strides: [2, 2, 2] 50 | 51 | neck: 52 | in_channels: [64, 128, 256] 53 | out_channels: [128, 128, 128] 54 | upsample_strides: [1, 2, 4] 55 | use_conv_for_no_stride: false 56 | 57 | head: 58 | in_channels: 384 59 | feat_channels: 384 60 | nms_pre: 1000 61 | score_thr: 0.05 62 | ranges: [ 63 | [-49.6, -49.6, -1.80032795, 49.6, 49.6, -1.80032795], 64 | [-49.6, -49.6, -1.74440365, 49.6, 49.6, -1.74440365], 65 | [-49.6, -49.6, -1.68526504, 49.6, 49.6, -1.68526504], 66 | [-49.6, -49.6, -1.67339111, 49.6, 49.6, -1.67339111], 67 | [-49.6, -49.6, -1.61785072, 49.6, 49.6, -1.61785072], 68 | [-49.6, -49.6, -1.80984986, 49.6, 49.6, -1.80984986], 69 | [-49.6, -49.6, -1.763965, 49.6, 49.6, -1.763965], 70 | ] 71 | sizes: [ 72 | [1.95017717, 4.60718145, 1.72270761], # car 73 | [2.4560939, 6.73778078, 2.73004906], # truck 74 | [2.87427237, 12.01320693, 3.81509561], # trailer 75 | [0.60058911, 1.68452161, 1.27192197], # bicycle 76 | [0.66344886, 0.7256437, 1.75748069], # pedestrian 77 | [0.39694519, 0.40359262, 1.06232151], # traffic_cone 78 | [2.49008838, 0.48578221, 0.98297065], # barrier 79 | ] 80 | rotations: [0, 1.57] 81 | iou_thr: [[0.3, 0.6]] 82 | dir_offset: 0.7854 83 | 84 | augment: 85 | PointShuffle: True 86 | ObjectRangeFilter: 87 | point_cloud_range: [-50, -50, -5, 50, 50, 3.0] 88 | 89 | 90 | pipeline: 91 | name: ObjectDetection 92 | test_compute_metric: true 93 | batch_size: 6 94 | val_batch_size: 1 95 | test_batch_size: 1 96 | save_ckpt_freq: 5 97 | max_epoch: 200 98 | main_log_dir: ./logs 99 | train_sum_dir: train_log 100 | grad_clip_norm: 2 101 | 102 | optimizer: 103 | lr: 0.001 104 | betas: [0.95, 0.99] 105 | weight_decay: 0.01 106 | 107 | # evaluation properties 108 | overlaps: [0.7] 109 | difficulties: [0] 110 | summary: 111 | record_for: [] 112 | max_pts: 113 | use_reference: false 114 | max_outputs: 1 115 | -------------------------------------------------------------------------------- /ml3d/configs/pointpillars_waymo.yml: -------------------------------------------------------------------------------- 1 | dataset: 2 | name: Waymo 3 | dataset_path: # path/to/your/dataset 4 | cache_dir: ./logs/cache 5 | steps_per_epoch_train: 4000 6 | 7 | model: 8 | name: PointPillars 9 | ckpt_path: # path/to/your/checkpoint 10 | 11 | batcher: "ignore" 12 | 13 | point_cloud_range: [-74.88, -74.88, -2, 74.88, 74.88, 4] 14 | classes: ['VEHICLE', 'PEDESTRIAN', 'CYCLIST'] 15 | 16 | loss: 17 | focal: 18 | gamma: 2.0 19 | alpha: 0.25 20 | loss_weight: 1.0 21 | smooth_l1: 22 | beta: 0.11 23 | loss_weight: 2.0 24 | cross_entropy: 25 | loss_weight: 0.2 26 | 27 | voxelize: 28 | max_num_points: 20 29 | voxel_size: &vsize 30 | [0.32, 0.32, 6] 31 | max_voxels: [32000, 32000] 32 | 33 | voxel_encoder: 34 | in_channels: 4 35 | feat_channels: [64] 36 | voxel_size: *vsize 37 | 38 | scatter: 39 | in_channels: 64 40 | output_shape: [468, 468] 41 | 42 | backbone: 43 | in_channels: 64 44 | out_channels: [64, 128, 256] 45 | layer_nums: [3, 5, 5] 46 | layer_strides: [1, 2, 2] 47 | 48 | neck: 49 | in_channels: [64, 128, 256] 50 | out_channels: [128, 128, 128] 51 | upsample_strides: [1, 2, 4] 52 | use_conv_for_no_stride: false 53 | 54 | head: 55 | in_channels: 384 56 | feat_channels: 384 57 | nms_pre: 4096 58 | score_thr: 0.1 59 | ranges: [ 60 | [-74.88, -74.88, -0.0345, 74.88, 74.88, -0.0345], 61 | [-74.88, -74.88, -0.1188, 74.88, 74.88, -0.1188], 62 | [-74.88, -74.88, 0, 74.88, 74.88, 0], 63 | ] 64 | sizes: [ 65 | [2.08, 4.73, 1.77], # VEHICLE 66 | [0.84, 1.81, 1.77], # CYCLIST 67 | [0.84, 0.91, 1.74] # PEDESTRIAN 68 | ] 69 | dir_offset: 0.7854 70 | rotations: [0, 1.57] 71 | iou_thr: [[0.4, 0.55], [0.3, 0.5], [0.3, 0.5]] 72 | 73 | augment: 74 | PointShuffle: True 75 | ObjectRangeFilter: 76 | point_cloud_range: [-74.88, -74.88, -2, 74.88, 74.88, 4] 77 | ObjectSample: 78 | min_points_dict: 79 | VEHICLE: 5 80 | PEDESTRIAN: 10 81 | CYCLIST: 10 82 | sample_dict: 83 | VEHICLE: 15 84 | PEDESTRIAN: 10 85 | CYCLIST: 10 86 | 87 | 88 | pipeline: 89 | name: ObjectDetection 90 | test_compute_metric: true 91 | batch_size: 6 92 | val_batch_size: 6 93 | test_batch_size: 1 94 | save_ckpt_freq: 5 95 | max_epoch: 200 96 | main_log_dir: ./logs 97 | train_sum_dir: train_log 98 | grad_clip_norm: 2 99 | 100 | optimizer: 101 | lr: 0.001 102 | betas: [0.95, 0.99] 103 | weight_decay: 0.01 104 | 105 | # evaluation properties 106 | overlaps: [0.5, 0.5, 0.5] 107 | difficulties: [0, 1, 2] 108 | summary: 109 | record_for: [] 110 | max_pts: 111 | use_reference: false 112 | max_outputs: 1 113 | -------------------------------------------------------------------------------- /ml3d/configs/pointrcnn_kitti.yml: -------------------------------------------------------------------------------- 1 | dataset: 2 | name: KITTI 3 | dataset_path: # path/to/your/dataset 4 | cache_dir: ./logs/cache 5 | 6 | model: 7 | name: PointRCNN 8 | ckpt_path: # path/to/your/checkpoint 9 | batcher: "ignore" # for TF, does use the batcher of the model class 10 | 11 | point_cloud_range: [0, -40, -1, 70.4, 40, 3] 12 | classes: ['Car'] 13 | 14 | npoint: 16384 15 | score_thres: 0.3 16 | 17 | augment: 18 | PointShuffle: True 19 | ObjectRangeFilter: False 20 | ObjectSample: 21 | min_points_dict: 22 | Car: 50 23 | sample_dict: 24 | Car: 15 25 | 26 | rpn: # First module for bottom-up 3D proposal generation 27 | backbone: # backbone based on PointNet++ 28 | in_channels: 0 # only xyz, no features 29 | SA_config: # multiscale pointnet config 30 | npoints: [4096, 1024, 256, 64] 31 | radius: [[0.1, 0.5], [0.5, 1.0], [1.0, 2.0], [2.0, 4.0]] 32 | nsample: [[16, 32], [16, 32], [16, 32], [16, 32]] 33 | mlps: [[[16, 16, 32], [32, 32, 64]], 34 | [[64, 64, 128], [64, 96, 128]], 35 | [[128, 196, 256], [128, 196, 256]], 36 | [[256, 256, 512], [256, 384, 512]]] 37 | fp_mlps: [[128, 128], [256, 256], [512, 512], [512, 512]] 38 | 39 | cls_in_ch: 128 40 | cls_out_ch: [128] 41 | reg_in_ch: 128 42 | reg_out_ch: [128] 43 | db_ratio: 0.5 44 | 45 | focal_loss: 46 | gamma: 2.0 47 | alpha: 0.25 48 | loss_weight: 1.0 49 | 50 | loss_weight: [1.0, 1.0] 51 | 52 | head: # config of bin-based loss 53 | loc_xz_fine: True # False for pre-trained 54 | loc_scope: 3.0 55 | loc_bin_size: 0.5 56 | num_head_bin: 12 57 | nms_pre: 9000 58 | nms_post: 512 59 | nms_thres: 0.85 60 | nms_post_val: 100 61 | nms_thres_val: 0.8 62 | mean_size: [1.52563191462, 1.62856739989, 3.88311640418] 63 | 64 | rcnn: # Second module for refinement of result 65 | in_channels: 128 66 | xyz_up_layer: [128, 128] 67 | cls_out_ch: [256, 256] 68 | reg_out_ch: [256, 256] 69 | 70 | SA_config: # multiscale pointnet config 71 | npoints: [128, 32, -1] 72 | radius: [0.2, 0.4, 100] 73 | nsample: [64, 64, 64] 74 | mlps: [[128, 128, 128], 75 | [128, 128, 256], 76 | [256, 256, 512]] 77 | 78 | head: 79 | # config of bin-based loss 80 | loc_xz_fine: True 81 | get_y_by_bin: False 82 | loc_y_scope: 0.5 83 | loc_y_bin_size: 0.25 84 | get_ry_fine: True 85 | loc_scope: 1.5 86 | loc_bin_size: 0.5 87 | num_head_bin: 9 88 | mean_size: [1.52563191462, 1.62856739989, 3.88311640418] 89 | post_process: False 90 | nms_thres: 0.1 91 | 92 | target_head: # mapping of gt to pred boxes 93 | pool_extra_width: 1.0 94 | num_points: 512 95 | reg_fg_thresh: 0.55 96 | cls_fg_thresh: 0.6 97 | cls_bg_thresh: 0.45 98 | cls_bg_thresh_lo: 0.05 99 | fg_ratio: 0.5 100 | roi_per_image: 100 101 | aug_rot_range: 18 102 | hard_bg_ratio: 0.8 103 | roi_fg_aug_times: 10 104 | 105 | 106 | pipeline: 107 | name: ObjectDetection 108 | test_compute_metric: true 109 | batch_size: 1 #4 110 | max_epoch: 200 111 | val_batch_size: 1 #4 112 | test_batch_size: 1 113 | save_ckpt_freq: 5 114 | main_log_dir: ./logs 115 | train_sum_dir: train_log 116 | grad_clip_norm: 2 117 | 118 | optimizer: 119 | lr: 0.002 120 | betas: [0.9, 0.99] 121 | weight_decay: 0.5 122 | moms: [0.95, 0.85] 123 | div_factor: 10.0 124 | pct_start: 0.4 125 | bn_decay_step_list: [1000] 126 | bn_decay: 0.5 127 | bnm_clip: 0.01 128 | bn_momentum: 0.1 129 | warmup_epoch: 1 130 | warmup_min: 0.0002 131 | 132 | # evaluation properties 133 | overlaps: [0.7] 134 | similar_classes: { 135 | Van: Car 136 | } 137 | difficulties: [0, 1, 2] 138 | summary: 139 | record_for: [] 140 | max_pts: 141 | use_reference: false 142 | max_outputs: 1 143 | -------------------------------------------------------------------------------- /ml3d/configs/pointtransformer_s3dis.yml: -------------------------------------------------------------------------------- 1 | dataset: 2 | name: S3DIS 3 | dataset_path: # path/to/your/dataset 4 | cache_dir: ./logs/cache 5 | class_weights: [3370714, 2856755, 4919229, 318158, 375640, 6 | 478001, 974733, 650464, 791496, 88727, 1284130, 229758, 2272837] 7 | ignored_label_inds: [] 8 | num_points: 40960 9 | test_area_idx: 5 10 | test_result_folder: ./test 11 | use_cache: false 12 | model: 13 | name: PointTransformer 14 | batcher: ConcatBatcher 15 | ckpt_path: # path/to/your/checkpoint 16 | in_channels: 6 17 | blocks: [2, 3, 4, 6, 3] 18 | num_classes: 13 19 | voxel_size: 0.04 20 | max_voxels: 50000 21 | ignored_label_inds: [-1] 22 | augment: 23 | rotate: 24 | method: vertical 25 | scale: 26 | min_s: 0.95 27 | max_s: 1.05 28 | noise: 29 | noise_std: 0.005 30 | ChromaticAutoContrast: 31 | randomize_blend_factor: True 32 | blend_factor: 0.2 33 | ChromaticTranslation: 34 | trans_range_ratio: 0.05 35 | ChromaticJitter: 36 | std: 0.01 37 | HueSaturationTranslation: 38 | hue_max: 0.5 39 | saturation_max: 0.2 40 | pipeline: 41 | name: SemanticSegmentation 42 | optimizer: 43 | lr: 0.02 44 | momentum: 0.9 45 | weight_decay: 0.0001 46 | batch_size: 3 47 | learning_rate: 0.01 48 | main_log_dir: ./logs 49 | max_epoch: 512 50 | save_ckpt_freq: 5 51 | scheduler_gamma: 0.99 52 | test_batch_size: 1 53 | train_sum_dir: train_log 54 | val_batch_size: 3 55 | summary: 56 | record_for: [] 57 | max_pts: 58 | use_reference: false 59 | max_outputs: 1 60 | -------------------------------------------------------------------------------- /ml3d/configs/pvcnn_s3dis.yml: -------------------------------------------------------------------------------- 1 | dataset: 2 | name: S3DIS 3 | dataset_path: # path/to/your/dataset 4 | cache_dir: ./logs/cache 5 | class_weights: [3370714, 2856755, 4919229, 318158, 375640, 6 | 478001, 974733, 650464, 791496, 88727, 1284130, 229758, 2272837] 7 | ignored_label_inds: [] 8 | num_points: 40960 9 | test_area_idx: 5 10 | test_result_folder: ./test 11 | use_cache: False 12 | model: 13 | name: PVCNN 14 | batcher: DefaultBatcher 15 | ckpt_path: # path/to/your/checkpoint 16 | num_classes: 13 17 | num_points: 40960 18 | extra_feature_channels: 6 19 | width_multiplier: 1 20 | voxel_resolution_multiplier: 2 21 | ignored_label_inds: [-1] 22 | augment: None 23 | pipeline: 24 | name: SemanticSegmentation 25 | optimizer: 26 | lr: 0.001 27 | weight_decay: 0.00001 28 | betas: [0.9, 0.999] 29 | batch_size: 4 30 | main_log_dir: ./logs 31 | max_epoch: 256 32 | save_ckpt_freq: 5 33 | scheduler_gamma: 0.99 34 | test_batch_size: 1 35 | train_sum_dir: train_log 36 | val_batch_size: 4 37 | summary: 38 | record_for: [] 39 | max_pts: 40 | use_reference: false 41 | max_outputs: 1 42 | -------------------------------------------------------------------------------- /ml3d/configs/randlanet_pandaset.yml: -------------------------------------------------------------------------------- 1 | dataset: 2 | name: Pandaset 3 | dataset_path: path_to_dataset 4 | cache_dir: ./logs/cache 5 | test_result_folder: './logs/test' 6 | training_split: [ '001', '002', '003', '005', '011', '013', '015', '016', 7 | '017', '019', '021', '023', '024', '027', '028', '029', 8 | '030', '032', '033', '034', '035', '037', '038', '039', 9 | '040', '041', '042', '043', '044', '046', '052', '053', 10 | '054', '056', '057', '058', '064', '065', '066', '067', 11 | '070', '071', '072', '073', '077', '078', '080', '084', 12 | '088', '089', '090', '094', '095', '097', '098', '101', 13 | '102', '103', '105', '106', '109', '110', '112', '113' 14 | ] 15 | test_split: ['115', '116', '117', '119', '120', '124', '139', '149', '158'] 16 | validation_split: ['122', '123'] 17 | use_cache: true 18 | sampler: 19 | name: 'SemSegRandomSampler' 20 | model: 21 | name: RandLANet 22 | batcher: DefaultBatcher 23 | num_classes: 39 24 | num_points: 81920 25 | num_neighbors: 16 26 | framework: torch 27 | num_layers: 4 28 | ignored_label_inds: [0] 29 | sub_sampling_ratio: [4, 4, 4, 4] 30 | in_channels: 3 31 | dim_features: 8 32 | dim_output: [16, 64, 128, 256] 33 | grid_size: 0.06 34 | pipeline: 35 | name: SemanticSegmentation 36 | max_epoch: 50 37 | save_ckpt_freq: 5 38 | device: gpu 39 | optimizer: 40 | lr: 0.001 41 | batch_size: 4 42 | main_log_dir: './logs' 43 | logs_dir: './logs' 44 | scheduler_gamma: 0.9886 45 | test_batch_size: 2 46 | train_sum_dir: './logs/training_log' 47 | val_batch_size: 2 48 | -------------------------------------------------------------------------------- /ml3d/configs/randlanet_parislille3d.yml: -------------------------------------------------------------------------------- 1 | dataset: 2 | name: ParisLille3D 3 | dataset_path: # path/to/your/dataset 4 | cache_dir: ./logs/cache 5 | class_weights: [53033221, 25821560, 546190, 54417, 6 | 180638, 3998129, 160899, 3441844, 9681606] 7 | ignored_label_inds: 8 | - 0 9 | num_points: 65536 10 | test_result_folder: ./test 11 | val_files: 12 | - Lille2.ply 13 | use_cache: true 14 | steps_per_epoch_train: 50 15 | steps_per_epoch_valid: 10 16 | sampler: 17 | name: SemSegSpatiallyRegularSampler 18 | model: 19 | name: RandLANet 20 | batcher: DefaultBatcher 21 | ckpt_path: # path/to/your/checkpoint 22 | num_neighbors: 16 23 | num_layers: 5 24 | num_points: 65536 25 | num_classes: 9 26 | ignored_label_inds: [0] 27 | sub_sampling_ratio: [4, 4, 4, 4, 2] 28 | in_channels: 3 29 | dim_features: 8 30 | dim_output: [16, 64, 128, 256, 512] 31 | grid_size: 0.06 32 | weight_decay: 0.001 33 | augment: 34 | recenter: 35 | dim: [0, 1, 2] 36 | pipeline: 37 | name: SemanticSegmentation 38 | optimizer: 39 | lr: 0.001 40 | batch_size: 2 41 | main_log_dir: ./logs 42 | max_epoch: 100 43 | save_ckpt_freq: 5 44 | scheduler_gamma: 0.9886 45 | test_batch_size: 1 46 | train_sum_dir: train_log 47 | val_batch_size: 2 48 | summary: 49 | record_for: [] 50 | max_pts: 51 | use_reference: false 52 | max_outputs: 1 53 | -------------------------------------------------------------------------------- /ml3d/configs/randlanet_s3dis.yml: -------------------------------------------------------------------------------- 1 | dataset: 2 | name: S3DIS 3 | dataset_path: # path/to/your/dataset 4 | cache_dir: ./logs/cache 5 | class_weights: [3370714, 2856755, 4919229, 318158, 375640, 6 | 478001, 974733, 650464, 791496, 88727, 1284130, 229758, 2272837] 7 | ignored_label_inds: [] 8 | num_points: 40960 9 | test_area_idx: 3 10 | test_result_folder: ./test 11 | use_cache: False 12 | model: 13 | name: RandLANet 14 | batcher: DefaultBatcher 15 | ckpt_path: # path/to/your/checkpoint 16 | num_neighbors: 16 17 | num_layers: 5 18 | num_points: 40960 19 | num_classes: 13 20 | ignored_label_inds: [] 21 | sub_sampling_ratio: [4, 4, 4, 4, 2] 22 | in_channels: 6 23 | dim_features: 8 24 | dim_output: [16, 64, 128, 256, 512] 25 | grid_size: 0.04 26 | augment: 27 | recenter: 28 | dim: [0, 1] 29 | rotate: 30 | method: vertical 31 | scale: 32 | min_s: 0.9 33 | max_s: 1.1 34 | noise: 35 | noise_std: 0.001 36 | pipeline: 37 | name: SemanticSegmentation 38 | optimizer: 39 | lr: 0.001 40 | batch_size: 2 41 | main_log_dir: ./logs 42 | max_epoch: 200 43 | save_ckpt_freq: 3 44 | scheduler_gamma: 0.99 45 | test_batch_size: 3 46 | train_sum_dir: train_log 47 | val_batch_size: 2 48 | summary: 49 | record_for: [] 50 | max_pts: 51 | use_reference: false 52 | max_outputs: 1 53 | -------------------------------------------------------------------------------- /ml3d/configs/randlanet_semantic3d.yml: -------------------------------------------------------------------------------- 1 | dataset: 2 | name: Semantic3D 3 | dataset_path: # path/to/your/dataset 4 | cache_dir: ./logs/cache_small3d/ 5 | class_weights: [5181602, 5012952, 6830086, 1311528, 10476365, 946982, 334860, 269353] 6 | ignored_label_inds: [0] 7 | num_points: 65536 8 | test_result_folder: ./test 9 | use_cache: true 10 | val_files: 11 | - bildstein_station1_xyz_intensity_rgb 12 | - domfountain_station1_xyz_intensity_rgb 13 | steps_per_epoch_train: 500 14 | steps_per_epoch_valid: 10 15 | model: 16 | name: RandLANet 17 | batcher: DefaultBatcher 18 | ckpt_path: # path/to/your/checkpoint 19 | num_neighbors: 16 20 | num_layers: 5 21 | num_points: 65536 22 | num_classes: 8 23 | ignored_label_inds: [0] 24 | sub_sampling_ratio: [4, 4, 4, 4, 2] 25 | in_channels: 6 26 | dim_features: 8 27 | dim_output: [16, 64, 128, 256, 512] 28 | grid_size: 0.06 29 | augment: 30 | recenter: 31 | dim: [0, 1] 32 | normalize: 33 | feat: 34 | method: linear 35 | bias: 0 36 | scale: 255 37 | rotate: 38 | method: vertical 39 | scale: 40 | min_s: 0.9 41 | max_s: 1.1 42 | noise: 43 | noise_std: 0.001 44 | pipeline: 45 | name: SemanticSegmentation 46 | optimizer: 47 | lr: 0.001 48 | batch_size: 2 49 | main_log_dir: ./logs 50 | max_epoch: 100 51 | save_ckpt_freq: 5 52 | scheduler_gamma: 0.9886 53 | test_batch_size: 1 54 | train_sum_dir: train_log 55 | val_batch_size: 2 56 | summary: 57 | record_for: [] 58 | max_pts: 59 | use_reference: false 60 | max_outputs: 1 61 | -------------------------------------------------------------------------------- /ml3d/configs/randlanet_semantickitti.yml: -------------------------------------------------------------------------------- 1 | dataset: 2 | name: SemanticKITTI 3 | dataset_path: # path/to/your/dataset 4 | cache_dir: ./logs/cache 5 | class_weights: [55437630, 320797, 541736, 2578735, 3274484, 552662, 184064, 6 | 78858, 240942562, 17294618, 170599734, 6369672, 230413074, 101130274, 7 | 476491114, 9833174, 129609852, 4506626, 1168181] 8 | test_result_folder: ./test 9 | test_split: ['11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21'] 10 | training_split: ['00', '01', '02', '03', '04', '05', '06', '07', '09', '10'] 11 | all_split: ['00', '01', '02', '03', '04', '05', '06', '07', '09', 12 | '08', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21'] 13 | validation_split: ['08'] 14 | use_cache: true 15 | sampler: 16 | name: 'SemSegRandomSampler' 17 | model: 18 | name: RandLANet 19 | batcher: DefaultBatcher 20 | ckpt_path: # path/to/your/checkpoint 21 | num_neighbors: 16 22 | num_layers: 4 23 | num_points: 45056 24 | num_classes: 19 25 | ignored_label_inds: [0] 26 | sub_sampling_ratio: [4, 4, 4, 4] 27 | in_channels: 3 28 | dim_features: 8 29 | dim_output: [16, 64, 128, 256] 30 | grid_size: 0.06 31 | augment: 32 | recenter: 33 | dim: [0, 1] 34 | pipeline: 35 | name: SemanticSegmentation 36 | optimizer: 37 | lr: 0.001 38 | batch_size: 4 39 | main_log_dir: ./logs 40 | max_epoch: 100 41 | save_ckpt_freq: 5 42 | scheduler_gamma: 0.9886 43 | test_batch_size: 1 44 | train_sum_dir: train_log 45 | val_batch_size: 2 46 | summary: 47 | record_for: [] 48 | max_pts: 49 | use_reference: false 50 | max_outputs: 1 51 | -------------------------------------------------------------------------------- /ml3d/configs/randlanet_toronto3d.yml: -------------------------------------------------------------------------------- 1 | dataset: 2 | name: Toronto3D 3 | cache_dir: ./logs/cache 4 | dataset_path: # path/to/your/dataset 5 | class_weights: [41697357, 1745448, 6572572, 19136493, 674897, 897825, 4634634, 374721] 6 | ignored_label_inds: 7 | - 0 8 | num_classes: 8 9 | num_points: 65536 10 | test_files: 11 | - L002.ply 12 | test_result_folder: ./test 13 | train_files: 14 | - L001.ply 15 | - L003.ply 16 | - L004.ply 17 | use_cache: true 18 | val_files: 19 | - L002.ply 20 | steps_per_epoch_train: 100 21 | steps_per_epoch_valid: 10 22 | model: 23 | name: RandLANet 24 | batcher: DefaultBatcher 25 | ckpt_path: # path/to/your/checkpoint 26 | num_neighbors: 16 27 | num_layers: 5 28 | num_points: 65536 29 | num_classes: 8 30 | ignored_label_inds: [0] 31 | sub_sampling_ratio: [4, 4, 4, 4, 2] 32 | in_channels: 6 33 | dim_features: 8 34 | dim_output: [16, 64, 128, 256, 512] 35 | grid_size: 0.05 36 | augment: 37 | recenter: 38 | dim: [0, 1, 2] 39 | normalize: 40 | points: 41 | method: linear 42 | pipeline: 43 | name: SemanticSegmentation 44 | optimizer: 45 | lr: 0.001 46 | batch_size: 2 47 | main_log_dir: ./logs 48 | max_epoch: 200 49 | save_ckpt_freq: 5 50 | scheduler_gamma: 0.99 51 | test_batch_size: 1 52 | train_sum_dir: train_log 53 | val_batch_size: 2 54 | summary: 55 | record_for: [] 56 | max_pts: 57 | use_reference: false 58 | max_outputs: 1 59 | -------------------------------------------------------------------------------- /ml3d/configs/sparseconvunet_scannet.yml: -------------------------------------------------------------------------------- 1 | dataset: 2 | name: Scannet 3 | dataset_path: # path/to/your/dataset 4 | cache_dir: ./logs/cache 5 | class_weights: [52539651, 40780759, 6961796, 4665739, 13155460, 4172698, 6368886, 7869501, 6780748, 4418153, 883498, 883847, 3330654, 3637628, 939700, 593984, 547679, 460448, 567893, 6490881] 6 | test_result_folder: ./test 7 | use_cache: False 8 | sampler: 9 | name: 'SemSegRandomSampler' 10 | model: 11 | name: SparseConvUnet 12 | batcher: ConcatBatcher 13 | ckpt_path: # path/to/your/checkpoint 14 | multiplier: 32 15 | voxel_size: 0.02 16 | residual_blocks: True 17 | conv_block_reps: 1 18 | in_channels: 3 19 | num_classes: 20 20 | grid_size: 4096 21 | ignored_label_inds: [-1] 22 | augment: 23 | rotate: 24 | method: vertical 25 | scale: 26 | min_s: 0.9 27 | max_s: 1.1 28 | noise: 29 | noise_std: 0.01 30 | RandomDropout: 31 | dropout_ratio: 0.2 32 | RandomHorizontalFlip: 33 | axes: [0, 1] 34 | ChromaticAutoContrast: 35 | randomize_blend_factor: True 36 | blend_factor: 0.5 37 | ChromaticTranslation: 38 | trans_range_ratio: 0.1 39 | ChromaticJitter: 40 | std: 0.05 41 | pipeline: 42 | name: SemanticSegmentation 43 | optimizer: 44 | lr: 0.001 45 | betas: [0.9, 0.999] 46 | batch_size: 8 47 | main_log_dir: ./logs 48 | max_epoch: 512 49 | save_ckpt_freq: 5 50 | scheduler_gamma: 0.99 51 | test_batch_size: 1 52 | train_sum_dir: train_log 53 | val_batch_size: 8 54 | summary: 55 | record_for: [] 56 | max_pts: 57 | use_reference: false 58 | max_outputs: 1 59 | -------------------------------------------------------------------------------- /ml3d/configs/torch_to_tf.yml: -------------------------------------------------------------------------------- 1 | dataset: 2 | model: 3 | pipeline: 4 | lr: learning_rate 5 | betas: [beta_1, beta_2] 6 | eps: epsilon -------------------------------------------------------------------------------- /ml3d/datasets/__init__.py: -------------------------------------------------------------------------------- 1 | """I/O, attributes, and processing for different datasets.""" 2 | 3 | from .semantickitti import SemanticKITTI 4 | from .s3dis import S3DIS 5 | from .parislille3d import ParisLille3D 6 | from .toronto3d import Toronto3D 7 | from .customdataset import Custom3D 8 | from .semantic3d import Semantic3D 9 | from .inference_dummy import InferenceDummySplit 10 | from .samplers import SemSegRandomSampler, SemSegSpatiallyRegularSampler 11 | from . import utils 12 | from . import augment 13 | from . import samplers 14 | 15 | from .kitti import KITTI 16 | from .nuscenes import NuScenes 17 | from .waymo import Waymo 18 | from .lyft import Lyft 19 | from .shapenet import ShapeNet 20 | from .argoverse import Argoverse 21 | from .scannet import Scannet 22 | from .sunrgbd import SunRGBD 23 | from .matterport_objects import MatterportObjects 24 | from .tumfacade import TUMFacade 25 | 26 | __all__ = [ 27 | 'SemanticKITTI', 'S3DIS', 'Toronto3D', 'ParisLille3D', 'Semantic3D', 28 | 'Custom3D', 'utils', 'augment', 'samplers', 'KITTI', 'Waymo', 'NuScenes', 29 | 'Lyft', 'ShapeNet', 'SemSegRandomSampler', 'InferenceDummySplit', 30 | 'SemSegSpatiallyRegularSampler', 'Argoverse', 'Scannet', 'SunRGBD', 31 | 'MatterportObjects', 'TUMFacade' 32 | ] 33 | -------------------------------------------------------------------------------- /ml3d/datasets/_resources/download_paris_lille3d.sh: -------------------------------------------------------------------------------- 1 | export BASE_DIR="/home/sanskar_agrawal/data/test/Paris_Lille3D" 2 | 3 | mkdir -p $BASE_DIR 4 | 5 | export url_train="https://cloud.mines-paristech.fr/index.php/s/JhIxgyt0ALgRZ1O/download?path=%2F&files=training_10_classes" 6 | export url_test="https://cloud.mines-paristech.fr/index.php/s/JhIxgyt0ALgRZ1O/download?path=%2F&files=test_10_classes" 7 | 8 | wget -c -N -O $BASE_DIR'/training_10_classes.zip' $url_train 9 | wget -c -N -O $BASE_DIR'/test_10_classes.zip' $url_test 10 | 11 | cd $BASE_DIR 12 | 13 | unzip test_10_classes.zip 14 | unzip train_10_classes.zip 15 | 16 | rm test_10_classes.zip 17 | rm train_10_classes.zip 18 | 19 | -------------------------------------------------------------------------------- /ml3d/datasets/_resources/download_toronto3d.sh: -------------------------------------------------------------------------------- 1 | export BASE_DIR="/home/sanskar_agrawal/data/test/" 2 | export url="https://xx9lca.sn.files.1drv.com/y4mUm9-LiY3vULTW79zlB3xp0wzCPASzteId4wdUZYpzWiw6Jp4IFoIs6ADjLREEk1-IYH8KRGdwFZJrPlIebwytHBYVIidsCwkHhW39aQkh3Vh0OWWMAcLVxYwMTjXwDxHl-CDVDau420OG4iMiTzlsK_RTC_ypo3z-Adf-h0gp2O8j5bOq-2TZd9FD1jPLrkf3759rB-BWDGFskF3AsiB3g" 3 | 4 | mkdir -p $BASE_DIR 5 | 6 | wget -c -N -O $BASE_DIR'/Toronto_3D.zip' $url 7 | 8 | cd $BASE_DIR 9 | 10 | unzip Toronto_3D.zip 11 | 12 | rm Toronto_3D.zip 13 | -------------------------------------------------------------------------------- /ml3d/datasets/_resources/lyft/val.txt: -------------------------------------------------------------------------------- 1 | host-a004-lidar0-1233080749298771736-1233080774198118416 2 | host-a004-lidar0-1232905197298264546-1232905222198133856 3 | host-a011-lidar0-1232732468299489666-1232732493199050666 4 | host-a101-lidar0-1241561147998866622-1241561172899320654 5 | host-a006-lidar0-1237322885198285226-1237322910098576786 6 | host-a004-lidar0-1233963848198981116-1233963873098642176 7 | host-a011-lidar0-1232752543198025666-1232752568099126026 8 | host-a004-lidar0-1232842367198056546-1232842392097783226 9 | host-a004-lidar0-1233615989298293586-1233616014198854636 10 | host-a011-lidar0-1233965426299054906-1233965451199121906 11 | host-a011-lidar0-1236104034298928316-1236104059198988026 12 | host-a007-lidar0-1233946614199227636-1233946639098289666 13 | host-a015-lidar0-1235423696198069636-1235423721098551296 14 | host-a004-lidar0-1233014843199117706-1233014868098023786 15 | host-a011-lidar0-1236093962299300416-1236093987199363346 16 | host-a011-lidar0-1234639296198260986-1234639321099417316 17 | host-a011-lidar0-1233524871199389346-1233524896098591466 18 | host-a011-lidar0-1235933781298838116-1235933806199517736 19 | host-a011-lidar0-1233965312298542226-1233965337198958586 20 | host-a011-lidar0-1233090567199118316-1233090592098933996 21 | host-a007-lidar0-1233621256298511876-1233621281197988026 22 | host-a007-lidar0-1233079617197863906-1233079642098533586 23 | host-a015-lidar0-1236112516098396876-1236112540999028556 24 | host-a008-lidar0-1236016333197799906-1236016358099063636 25 | host-a101-lidar0-1240710366399037786-1240710391298976894 26 | host-a102-lidar0-1242755350298764586-1242755375198787666 27 | host-a101-lidar0-1240877587199107226-1240877612099413030 28 | host-a101-lidar0-1242583745399163026-1242583770298821706 29 | host-a011-lidar0-1232817034199342856-1232817059098800346 30 | host-a004-lidar0-1232905117299287546-1232905142198246226 -------------------------------------------------------------------------------- /ml3d/datasets/_resources/scannet/scannetv2_test.txt: -------------------------------------------------------------------------------- 1 | scene0707_00 2 | scene0708_00 3 | scene0709_00 4 | scene0710_00 5 | scene0711_00 6 | scene0712_00 7 | scene0713_00 8 | scene0714_00 9 | scene0715_00 10 | scene0716_00 11 | scene0717_00 12 | scene0718_00 13 | scene0719_00 14 | scene0720_00 15 | scene0721_00 16 | scene0722_00 17 | scene0723_00 18 | scene0724_00 19 | scene0725_00 20 | scene0726_00 21 | scene0727_00 22 | scene0728_00 23 | scene0729_00 24 | scene0730_00 25 | scene0731_00 26 | scene0732_00 27 | scene0733_00 28 | scene0734_00 29 | scene0735_00 30 | scene0736_00 31 | scene0737_00 32 | scene0738_00 33 | scene0739_00 34 | scene0740_00 35 | scene0741_00 36 | scene0742_00 37 | scene0743_00 38 | scene0744_00 39 | scene0745_00 40 | scene0746_00 41 | scene0747_00 42 | scene0748_00 43 | scene0749_00 44 | scene0750_00 45 | scene0751_00 46 | scene0752_00 47 | scene0753_00 48 | scene0754_00 49 | scene0755_00 50 | scene0756_00 51 | scene0757_00 52 | scene0758_00 53 | scene0759_00 54 | scene0760_00 55 | scene0761_00 56 | scene0762_00 57 | scene0763_00 58 | scene0764_00 59 | scene0765_00 60 | scene0766_00 61 | scene0767_00 62 | scene0768_00 63 | scene0769_00 64 | scene0770_00 65 | scene0771_00 66 | scene0772_00 67 | scene0773_00 68 | scene0774_00 69 | scene0775_00 70 | scene0776_00 71 | scene0777_00 72 | scene0778_00 73 | scene0779_00 74 | scene0780_00 75 | scene0781_00 76 | scene0782_00 77 | scene0783_00 78 | scene0784_00 79 | scene0785_00 80 | scene0786_00 81 | scene0787_00 82 | scene0788_00 83 | scene0789_00 84 | scene0790_00 85 | scene0791_00 86 | scene0792_00 87 | scene0793_00 88 | scene0794_00 89 | scene0795_00 90 | scene0796_00 91 | scene0797_00 92 | scene0798_00 93 | scene0799_00 94 | scene0800_00 95 | scene0801_00 96 | scene0802_00 97 | scene0803_00 98 | scene0804_00 99 | scene0805_00 100 | scene0806_00 101 | -------------------------------------------------------------------------------- /ml3d/datasets/augment/__init__.py: -------------------------------------------------------------------------------- 1 | """Dataset augmentation classes with functions such as random rotation, translation, and scaling.""" 2 | 3 | from .augmentation import SemsegAugmentation, ObjdetAugmentation 4 | -------------------------------------------------------------------------------- /ml3d/datasets/inference_dummy.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from .base_dataset import BaseDatasetSplit 4 | from ..utils import DATASET, get_module 5 | 6 | log = logging.getLogger(__name__) 7 | 8 | 9 | class InferenceDummySplit(BaseDatasetSplit): 10 | 11 | def __init__(self, inference_data): 12 | self.split = 'test' 13 | self.inference_data = inference_data 14 | self.cfg = {} 15 | sampler_cls = get_module('sampler', 'SemSegSpatiallyRegularSampler') 16 | self.sampler = sampler_cls(self) 17 | 18 | def __len__(self): 19 | return 1 20 | 21 | def get_data(self, idx): 22 | return self.inference_data 23 | 24 | def get_attr(self, idx): 25 | pc_path = 'inference_data' 26 | split = self.split 27 | attr = {'idx': 0, 'name': 'inference', 'path': pc_path, 'split': split} 28 | return attr 29 | 30 | 31 | DATASET._register_module(InferenceDummySplit) 32 | -------------------------------------------------------------------------------- /ml3d/datasets/samplers/__init__.py: -------------------------------------------------------------------------------- 1 | """Various algorithms for sampling points from input point clouds.""" 2 | 3 | from .semseg_random import SemSegRandomSampler 4 | from .semseg_spatially_regular import SemSegSpatiallyRegularSampler 5 | 6 | __all__ = ['SemSegRandomSampler', 'SemSegSpatiallyRegularSampler'] 7 | -------------------------------------------------------------------------------- /ml3d/datasets/samplers/semseg_random.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import random 3 | 4 | from ...utils import SAMPLER 5 | 6 | 7 | class SemSegRandomSampler(object): 8 | """Random sampler for semantic segmentation datasets.""" 9 | 10 | def __init__(self, dataset): 11 | self.dataset = dataset 12 | self.length = len(dataset) 13 | self.split = self.dataset.split 14 | 15 | def __len__(self): 16 | return self.length 17 | 18 | def initialize_with_dataloader(self, dataloader): 19 | self.length = len(dataloader) 20 | 21 | def get_cloud_sampler(self): 22 | 23 | def gen(): 24 | ids = np.random.permutation(self.length) 25 | for i in ids: 26 | yield i 27 | 28 | return gen() 29 | 30 | @staticmethod 31 | def get_point_sampler(): 32 | 33 | def _random_centered_gen(**kwargs): 34 | pc = kwargs.get('pc', None) 35 | num_points = kwargs.get('num_points', None) 36 | search_tree = kwargs.get('search_tree', None) 37 | if pc is None or num_points is None or search_tree is None: 38 | raise KeyError("Please provide pc, num_points, and search_tree \ 39 | for point_sampler in SemSegRandomSampler") 40 | 41 | center_idx = np.random.choice(len(pc), 1) 42 | center_point = pc[center_idx, :].reshape(1, -1) 43 | 44 | if (pc.shape[0] < num_points): 45 | diff = num_points - pc.shape[0] 46 | idxs = np.array(range(pc.shape[0])) 47 | idxs = list(idxs) + list(random.choices(idxs, k=diff)) 48 | idxs = np.asarray(idxs) 49 | else: 50 | idxs = search_tree.query(center_point, k=num_points)[1][0] 51 | random.shuffle(idxs) 52 | pc = pc[idxs] 53 | return pc, idxs, center_point 54 | 55 | return _random_centered_gen 56 | 57 | 58 | SAMPLER._register_module(SemSegRandomSampler) 59 | -------------------------------------------------------------------------------- /ml3d/datasets/utils/__init__.py: -------------------------------------------------------------------------------- 1 | """Utilities for processing data, such as normalization and cropping.""" 2 | 3 | from .dataprocessing import DataProcessing 4 | from .transforms import trans_normalize, trans_augment, trans_crop_pc, ObjdetAugmentation 5 | from .operations import create_3D_rotations, get_min_bbox 6 | from .bev_box import BEVBox3D 7 | 8 | __all__ = [ 9 | 'DataProcessing', 'trans_normalize', 'create_3D_rotations', 'trans_augment', 10 | 'trans_crop_pc', 'BEVBox3D' 11 | ] 12 | -------------------------------------------------------------------------------- /ml3d/metrics/__init__.py: -------------------------------------------------------------------------------- 1 | import open3d 2 | 3 | if open3d.core.cuda.device_count() > 0: 4 | # Open3D is built with CUDA and the machine has a CUDA device. 5 | from open3d.ml.contrib import iou_bev_cuda as iou_bev 6 | from open3d.ml.contrib import iou_3d_cuda as iou_3d 7 | else: 8 | from open3d.ml.contrib import iou_bev_cpu as iou_bev 9 | from open3d.ml.contrib import iou_3d_cpu as iou_3d 10 | 11 | from .mAP import precision_3d, mAP 12 | 13 | __all__ = ['precision_3d', 'mAP', 'iou_bev', 'iou_3d'] 14 | -------------------------------------------------------------------------------- /ml3d/tf/__init__.py: -------------------------------------------------------------------------------- 1 | from .pipelines import * 2 | from .models import * 3 | from .dataloaders import * 4 | from .modules import * 5 | -------------------------------------------------------------------------------- /ml3d/tf/dataloaders/__init__.py: -------------------------------------------------------------------------------- 1 | """Dataloader for TensorFlow.""" 2 | 3 | from .tf_dataloader import TFDataloader 4 | __all__ = ['TFDataloader'] 5 | -------------------------------------------------------------------------------- /ml3d/tf/models/__init__.py: -------------------------------------------------------------------------------- 1 | """Tensorflow network models.""" 2 | 3 | from .randlanet import RandLANet 4 | from .kpconv import KPFCNN 5 | from .point_pillars import PointPillars 6 | from .sparseconvnet import SparseConvUnet 7 | from .point_rcnn import PointRCNN 8 | from .point_transformer import PointTransformer 9 | from .pvcnn import PVCNN 10 | 11 | __all__ = [ 12 | 'RandLANet', 'KPFCNN', 'PointPillars', 'SparseConvUnet', 'PointRCNN', 13 | 'PointTransformer', 'PVCNN' 14 | ] 15 | 16 | try: 17 | from .openvino_model import OpenVINOModel 18 | __all__.append("OpenVINOModel") 19 | except Exception: 20 | pass 21 | -------------------------------------------------------------------------------- /ml3d/tf/models/base_model.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import tensorflow as tf 3 | from abc import ABC, abstractmethod 4 | 5 | # use relative import for being compatible with Open3d main repo 6 | from ...utils import Config 7 | 8 | 9 | class BaseModel(ABC, tf.keras.Model): 10 | """Base class for models. 11 | 12 | All models must inherit from this class and implement all functions to be 13 | used with a pipeline. 14 | 15 | Args: 16 | **kwargs: Configuration of the model as keyword arguments. 17 | """ 18 | 19 | def __init__(self, **kwargs): 20 | super().__init__() 21 | self.cfg = Config(kwargs) 22 | self.rng = np.random.default_rng(kwargs.get('seed', None)) 23 | 24 | def get_loss(self, Loss, results, inputs): 25 | """Computes the loss given the network input and outputs. 26 | 27 | Args: 28 | Loss: A loss object. 29 | results: This is the output of the model. 30 | inputs: This is the input to the model. 31 | 32 | Returns: 33 | Returns the loss value. 34 | """ 35 | return 36 | 37 | @abstractmethod 38 | def get_optimizer(self, cfg_pipeline): 39 | """Returns an optimizer object for the model. 40 | 41 | Args: 42 | cfg_pipeline: A Config object with the configuration of the pipeline. 43 | 44 | Returns: 45 | Returns a new optimizer object. 46 | """ 47 | return 48 | 49 | @abstractmethod 50 | def preprocess(self, data, attr): 51 | """Data preprocessing function. 52 | 53 | This function is called before training to preprocess the data from a 54 | dataset. 55 | 56 | Args: 57 | data: A sample from the dataset. 58 | attr: The corresponding attributes. 59 | 60 | Returns: 61 | Returns the preprocessed data 62 | """ 63 | return 64 | 65 | @abstractmethod 66 | def transform(self, *args): 67 | """Transform function for the point cloud and features. 68 | 69 | Args: 70 | args: A list of tf Tensors. 71 | """ 72 | return [] 73 | 74 | @abstractmethod 75 | def inference_begin(self, data): 76 | """Function called right before running inference. 77 | 78 | Args: 79 | data: A data from the dataset. 80 | """ 81 | return 82 | 83 | @abstractmethod 84 | def inference_preprocess(self): 85 | """This function prepares the inputs for the model. 86 | 87 | Returns: 88 | The inputs to be consumed by the call() function of the model. 89 | """ 90 | return 91 | 92 | @abstractmethod 93 | def inference_end(self, results): 94 | """This function is called after the inference. 95 | 96 | This function can be implemented to apply post-processing on the 97 | network outputs. 98 | 99 | Args: 100 | results: The model outputs as returned by the call() function. 101 | Post-processing is applied on this object. 102 | 103 | Returns: 104 | Returns True if the inference is complete and otherwise False. 105 | Returning False can be used to implement inference for large point 106 | clouds which require multiple passes. 107 | """ 108 | return 109 | -------------------------------------------------------------------------------- /ml3d/tf/models/base_model_objdet.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import yaml 3 | import tensorflow as tf 4 | from os.path import join, exists, dirname, abspath 5 | from abc import ABC, abstractmethod 6 | 7 | # use relative import for being compatible with Open3d main repo 8 | from ...utils import Config 9 | 10 | 11 | class BaseModel(ABC, tf.keras.Model): 12 | """Base class for models. 13 | 14 | All models must inherit from this class and implement all functions to be 15 | used with a pipeline. 16 | 17 | Args: 18 | **kwargs: Configuration of the model as keyword arguments. 19 | """ 20 | 21 | def __init__(self, **kwargs): 22 | super().__init__() 23 | self.cfg = Config(kwargs) 24 | self.rng = np.random.default_rng(kwargs.get('seed', None)) 25 | 26 | def loss(self, results, inputs): 27 | """Computes the loss given the network input and outputs. 28 | 29 | Args: 30 | Loss: A loss object. 31 | results: This is the output of the model. 32 | inputs: This is the input to the model. 33 | 34 | Returns: 35 | Returns the loss value. 36 | """ 37 | return 38 | 39 | @abstractmethod 40 | def get_optimizer(self, cfg_pipeline): 41 | """Returns an optimizer object for the model. 42 | 43 | Args: 44 | cfg_pipeline: A Config object with the configuration of the pipeline. 45 | 46 | Returns: 47 | Returns a new optimizer object. 48 | """ 49 | return 50 | 51 | @abstractmethod 52 | def preprocess(self, data, attr): 53 | """Data preprocessing function. 54 | 55 | This function is called before training to preprocess the data from a 56 | dataset. 57 | 58 | Args: 59 | data: A sample from the dataset. 60 | attr: The corresponding attributes. 61 | 62 | Returns: 63 | Returns the preprocessed data 64 | """ 65 | return 66 | 67 | @abstractmethod 68 | def transform(self, *args): 69 | """Transform function for the point cloud and features. 70 | 71 | Args: 72 | args: A list of tf Tensors. 73 | """ 74 | return [] 75 | 76 | @abstractmethod 77 | def inference_end(self, results, attr=None): 78 | """This function is called after the inference. 79 | 80 | This function can be implemented to apply post-processing on the 81 | network outputs. 82 | 83 | Args: 84 | results: The model outputs as returned by the call() function. 85 | Post-processing is applied on this object. 86 | 87 | Returns: 88 | Returns True if the inference is complete and otherwise False. 89 | Returning False can be used to implement inference for large point 90 | clouds which require multiple passes. 91 | """ 92 | return 93 | -------------------------------------------------------------------------------- /ml3d/tf/models/openvino_model.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import subprocess 4 | 5 | import numpy as np 6 | 7 | from openvino.inference_engine import IECore 8 | 9 | 10 | class OpenVINOModel: 11 | """Class providing OpenVINO backend for TensorFlow models. 12 | 13 | Class performs conversion to OpenVINO IR format using Model Optimizer tool. 14 | """ 15 | 16 | def __init__(self, base_model): 17 | self.ie = IECore() 18 | self.exec_net = None 19 | self.base_model = base_model 20 | self.input_names = None 21 | self.input_ids = None 22 | self.device = "CPU" 23 | 24 | def _read_tf_model(self, inputs): 25 | # Dump a model in SavedModel format 26 | self.base_model.save('model') 27 | 28 | # Read input signatures (names, shapes) 29 | signatures = self.base_model._get_save_spec() 30 | 31 | self.input_names = [] 32 | self.input_ids = [] 33 | input_shapes = [] 34 | for inp in signatures: 35 | inp_idx = int(inp.name[inp.name.find('_') + 1:]) - 1 36 | shape = list(inputs[inp_idx].shape) 37 | 38 | if np.prod(shape) == 0: 39 | continue 40 | 41 | self.input_names.append(inp.name) 42 | self.input_ids.append(inp_idx) 43 | input_shapes.append(str(shape)) 44 | 45 | # Run Model Optimizer to get IR 46 | subprocess.run([ 47 | sys.executable, 48 | '-m', 49 | 'mo', 50 | '--saved_model_dir=model', 51 | '--input_shape', 52 | ','.join(input_shapes), 53 | '--input', 54 | ','.join(self.input_names), 55 | '--extension', 56 | os.path.join(os.path.dirname(__file__), 'utils', 'openvino_ext'), 57 | ], 58 | check=True) 59 | 60 | self.exec_net = self.ie.load_network('saved_model.xml', self.device) 61 | 62 | try: 63 | os.remove('saved_model.xml') 64 | os.remove('saved_model.bin') 65 | os.rmdir('model') 66 | except Exception: 67 | pass 68 | 69 | def __call__(self, inputs): 70 | if self.exec_net is None: 71 | self._read_tf_model(inputs) 72 | 73 | tensors = {} 74 | for idx, name in zip(self.input_ids, self.input_names): 75 | tensors[name] = inputs[idx] 76 | 77 | output = self.exec_net.infer(tensors) 78 | output = next(iter(output.values())) 79 | return output 80 | 81 | def to(self, device): 82 | self.device = device.upper() 83 | -------------------------------------------------------------------------------- /ml3d/tf/models/utils/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /ml3d/tf/models/utils/kernels/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /ml3d/tf/models/utils/openvino_ext/front/tf/min.py: -------------------------------------------------------------------------------- 1 | from mo.front.extractor import FrontExtractorOp 2 | from extensions.ops.ReduceOps import ReduceMin 3 | from mo.graph.graph import Node 4 | 5 | 6 | class MinFrontExtractor(FrontExtractorOp): 7 | """Support class which enables missed Min operation in OpenVINO. 8 | 9 | TODO: will be fixed in next OpenVINO release (2022.1) 10 | """ 11 | 12 | op = 'Min' 13 | enabled = True 14 | 15 | @classmethod 16 | def extract(cls, node: Node): 17 | ReduceMin.update_node_stat(node, 18 | {'keep_dims': node.pb.attr['keep_dims'].b}) 19 | return cls.enabled 20 | -------------------------------------------------------------------------------- /ml3d/tf/modules/__init__.py: -------------------------------------------------------------------------------- 1 | """Functional modules for tensorflow.""" 2 | 3 | from .losses import * 4 | from .metrics import * 5 | -------------------------------------------------------------------------------- /ml3d/tf/modules/losses/__init__.py: -------------------------------------------------------------------------------- 1 | """Loss modules""" 2 | 3 | from .semseg_loss import SemSegLoss 4 | from .cross_entropy import CrossEntropyLoss 5 | from .focal_loss import FocalLoss 6 | from .smooth_L1 import SmoothL1Loss 7 | 8 | __all__ = ['SemSegLoss', 'CrossEntropyLoss', 'FocalLoss', 'SmoothL1Loss'] 9 | -------------------------------------------------------------------------------- /ml3d/tf/modules/losses/cross_entropy.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | 3 | 4 | class CrossEntropyLoss(tf.Module): 5 | """CrossEntropyLoss.""" 6 | 7 | def __init__(self, loss_weight=1.0): 8 | """CrossEntropyLoss. 9 | 10 | Args: 11 | loss_weight (float, optional): Weight of the loss. Defaults to 1.0. 12 | """ 13 | super(CrossEntropyLoss, self).__init__() 14 | self.loss_weight = loss_weight 15 | 16 | self.loss_fn = tf.keras.losses.SparseCategoricalCrossentropy( 17 | from_logits=True, reduction=tf.keras.losses.Reduction.NONE) 18 | 19 | def __call__(self, 20 | cls_score, 21 | label, 22 | weight=None, 23 | avg_factor=None, 24 | **kwargs): 25 | """Forward function. 26 | 27 | Args: 28 | cls_score (tf.Tensor): The prediction. 29 | label (tf.Tensor): The learning label of the prediction. 30 | weight (tf.Tensor, optional): Sample-wise loss weight. 31 | avg_factor (int, optional): Average factor that is used to average 32 | the loss. Defaults to None. 33 | 34 | Returns: 35 | tf.Tensor: The calculated loss 36 | """ 37 | if weight is not None: 38 | loss = self.loss_fn(label, cls_score, sample_weight=weight) 39 | else: 40 | loss = self.loss_fn(label, cls_score) 41 | 42 | loss = loss * self.loss_weight 43 | 44 | if avg_factor: 45 | return tf.reduce_sum(loss) / avg_factor 46 | else: 47 | return tf.reduce_mean(loss) 48 | -------------------------------------------------------------------------------- /ml3d/tf/modules/losses/focal_loss.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | 3 | 4 | class FocalLoss(tf.Module): 5 | """`Focal Loss `_ 6 | 7 | Args: 8 | gamma (float, optional): The gamma for calculating the modulating 9 | factor. Defaults to 2.0. 10 | alpha (float, optional): A balanced form for Focal Loss. 11 | Defaults to 0.25. 12 | loss_weight (float, optional): Weight of loss. Defaults to 1.0. 13 | """ 14 | 15 | def __init__(self, gamma=2.0, alpha=0.25, loss_weight=1.0): 16 | super(FocalLoss, self).__init__() 17 | self.gamma = gamma 18 | self.alpha = alpha 19 | self.loss_weight = loss_weight 20 | 21 | def __call__(self, pred, target, weight=None, avg_factor=None): 22 | 23 | pred_sigmoid = tf.math.sigmoid(pred) 24 | 25 | if len(pred.shape) > 1: 26 | target = tf.one_hot(target, int(pred.shape[-1])) 27 | target = tf.cast(target, pred.dtype) 28 | 29 | pt = (1 - pred_sigmoid) * target + pred_sigmoid * (1 - target) 30 | 31 | focal_weight = (self.alpha * target + (1 - self.alpha) * 32 | (1 - target)) * tf.pow(pt, self.gamma) 33 | 34 | loss = tf.nn.sigmoid_cross_entropy_with_logits(target, 35 | pred) * focal_weight 36 | 37 | if weight is not None: 38 | loss = loss * weight 39 | 40 | loss = loss * self.loss_weight 41 | 42 | if avg_factor: 43 | return tf.reduce_sum(loss) / avg_factor 44 | else: 45 | return tf.reduce_mean(loss) 46 | -------------------------------------------------------------------------------- /ml3d/tf/modules/losses/semseg_loss.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | from ....datasets.utils import DataProcessing as DP 3 | 4 | 5 | class SemSegLoss(object): 6 | """Loss functions for semantic segmentation.""" 7 | 8 | def __init__(self, pipeline, model, dataset): 9 | super(SemSegLoss, self).__init__() 10 | # weighted_CrossEntropyLoss 11 | self.num_classes = model.cfg.num_classes 12 | self.ignored_label_inds = model.cfg.ignored_label_inds 13 | self.class_weights = None 14 | 15 | if 'class_weights' in dataset.cfg.keys() and len( 16 | dataset.cfg.class_weights) != 0: 17 | weights = DP.get_class_weights(dataset.cfg.class_weights) 18 | self.class_weights = tf.convert_to_tensor(weights, dtype=tf.float32) 19 | 20 | def weighted_CrossEntropyLoss(self, logits, labels): 21 | if self.class_weights is None: 22 | return tf.reduce_mean( 23 | tf.nn.sparse_softmax_cross_entropy_with_logits(labels, logits)) 24 | else: 25 | # calculate the weighted cross entropy according to the inverse frequency 26 | one_hot_labels = tf.one_hot(labels, depth=self.num_classes) 27 | weights = tf.reduce_sum(self.class_weights * one_hot_labels, axis=1) 28 | unweighted_losses = tf.nn.softmax_cross_entropy_with_logits( 29 | logits=logits, labels=one_hot_labels) 30 | weighted_losses = unweighted_losses * weights 31 | output_loss = tf.reduce_mean(weighted_losses) 32 | 33 | return output_loss 34 | 35 | def filter_valid_label(self, scores, labels): 36 | """Filter out invalid points.""" 37 | logits = tf.reshape(scores, [-1, self.num_classes]) 38 | labels = tf.reshape(labels, [-1]) 39 | 40 | # Boolean mask of points that should be ignored 41 | ignored_bool = tf.zeros_like(labels, dtype=tf.bool) 42 | for ign_label in self.ignored_label_inds: 43 | ignored_bool = tf.logical_or(ignored_bool, 44 | tf.equal(labels, ign_label)) 45 | 46 | # Collect logits and labels that are not ignored 47 | valid_idx = tf.squeeze(tf.where(tf.logical_not(ignored_bool))) 48 | valid_logits = tf.gather(logits, valid_idx, axis=0) 49 | valid_labels_init = tf.gather(labels, valid_idx, axis=0) 50 | 51 | # Reduce label values in the range of logit shape 52 | reducing_list = tf.range(self.num_classes, dtype=tf.int32) 53 | inserted_value = tf.zeros((1,), dtype=tf.int32) 54 | for ign_label in self.ignored_label_inds: 55 | if ign_label >= 0: 56 | reducing_list = tf.concat([ 57 | reducing_list[:ign_label], inserted_value, 58 | reducing_list[ign_label:] 59 | ], 0) 60 | 61 | valid_labels = tf.gather(reducing_list, valid_labels_init) 62 | 63 | return valid_logits, valid_labels 64 | -------------------------------------------------------------------------------- /ml3d/tf/modules/losses/smooth_L1.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | 3 | 4 | class SmoothL1Loss(tf.Module): 5 | """Smooth L1 loss. 6 | 7 | Args: 8 | beta (float, optional): The threshold in the piecewise function. 9 | Defaults to 1.0. 10 | loss_weight (float, optional): The weight of loss. 11 | """ 12 | 13 | def __init__(self, beta=1.0, loss_weight=1.0): 14 | super(SmoothL1Loss, self).__init__() 15 | self.beta = beta 16 | self.loss_weight = loss_weight 17 | 18 | def __call__(self, pred, target, weight=None, avg_factor=None, **kwargs): 19 | """Forward function. 20 | 21 | Args: 22 | pred (tf.Tensor): The prediction. 23 | target (tf.Tensor): The learning target of the prediction. 24 | weight (tf.Tensor, optional): The weight of loss for each 25 | prediction. Defaults to None. 26 | avg_factor (int, optional): Average factor that is used to average 27 | the loss. Defaults to None. 28 | reduction_override (str, optional): The reduction method used to 29 | override the original reduction method of the loss. 30 | Defaults to None. 31 | """ 32 | assert pred.shape == target.shape and tf.size(target) > 0 33 | 34 | diff = tf.abs(pred - target) 35 | 36 | loss = tf.where(diff < self.beta, 0.5 * diff * diff / self.beta, 37 | diff - 0.5 * self.beta) 38 | 39 | if weight is not None: 40 | loss = loss * weight 41 | 42 | loss = loss * self.loss_weight 43 | 44 | if avg_factor: 45 | return tf.reduce_sum(loss) / avg_factor 46 | else: 47 | return tf.reduce_mean(loss) 48 | -------------------------------------------------------------------------------- /ml3d/tf/modules/metrics/__init__.py: -------------------------------------------------------------------------------- 1 | """Metrics for Semantic Segmentation. 2 | Includes accuracy, mIoU and confusion matrix. 3 | """ 4 | 5 | from .semseg_metric import SemSegMetric 6 | 7 | __all__ = ['SemSegMetric'] 8 | -------------------------------------------------------------------------------- /ml3d/tf/modules/metrics/semseg_metric.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import warnings 3 | 4 | 5 | class SemSegMetric(object): 6 | """Metrics for semantic segmentation. 7 | 8 | Accumulate confusion matrix over training loop and 9 | computes accuracy and mean IoU. 10 | """ 11 | 12 | def __init__(self): 13 | super(SemSegMetric, self).__init__() 14 | self.confusion_matrix = None 15 | self.num_classes = None 16 | 17 | def update(self, scores, labels): 18 | conf = self.get_confusion_matrix(scores, labels) 19 | if self.confusion_matrix is None: 20 | self.confusion_matrix = conf.copy() 21 | self.num_classes = conf.shape[0] 22 | else: 23 | assert self.confusion_matrix.shape == conf.shape 24 | self.confusion_matrix += conf 25 | 26 | def acc(self): 27 | """Compute the per-class accuracies and the overall accuracy. 28 | 29 | Args: 30 | scores (tf.FloatTensor, shape (B?, C, N): 31 | raw scores for each class. 32 | labels (tf.LongTensor, shape (B?, N)): 33 | ground truth labels. 34 | 35 | Returns: 36 | A list of floats of length num_classes+1. 37 | Consists of per class accuracy. Last item is Overall Accuracy. 38 | """ 39 | if self.confusion_matrix is None: 40 | return None 41 | 42 | accs = [] 43 | for label in range(self.num_classes): 44 | tp = np.longlong(self.confusion_matrix[label, label]) 45 | fn = np.longlong(self.confusion_matrix[label, :].sum()) - tp 46 | 47 | if tp + fn == 0: 48 | acc = float('nan') 49 | else: 50 | acc = tp / (tp + fn) 51 | 52 | accs.append(acc) 53 | 54 | accs.append(np.nanmean(accs)) 55 | 56 | return accs 57 | 58 | def iou(self): 59 | """Compute the per-class IoU and the mean IoU. 60 | 61 | Args: 62 | scores (tf.FloatTensor, shape (B?, C, N): 63 | raw scores for each class. 64 | labels (tf.LongTensor, shape (B?, N)): 65 | ground truth labels. 66 | 67 | Returns: 68 | A list of floats of length num_classes+1. 69 | Consists of per class IoU. Last item is mIoU. 70 | """ 71 | if self.confusion_matrix is None: 72 | return None 73 | 74 | ious = [] 75 | for label in range(self.num_classes): 76 | tp = np.longlong(self.confusion_matrix[label, label]) 77 | fn = np.longlong(self.confusion_matrix[label, :].sum()) - tp 78 | fp = np.longlong(self.confusion_matrix[:, label].sum()) - tp 79 | 80 | if tp + fp + fn == 0: 81 | iou = float('nan') 82 | else: 83 | iou = (tp) / (tp + fp + fn) 84 | 85 | ious.append(iou) 86 | 87 | ious.append(np.nanmean(ious)) 88 | 89 | return ious 90 | 91 | def reset(self): 92 | self.confusion_matrix = None 93 | 94 | @staticmethod 95 | def get_confusion_matrix(scores, labels): 96 | """Computes the confusion matrix of one batch 97 | 98 | Args: 99 | scores (tf.FloatTensor, shape (B?, N, C): 100 | raw scores for each class. 101 | labels (tf.LongTensor, shape (B?, N)): 102 | ground truth labels. 103 | 104 | Returns: 105 | Confusion matrix for current batch. 106 | """ 107 | C = scores.shape[-1] 108 | y_pred = scores.cpu().numpy().reshape(-1, C) # (N, C) 109 | y_pred = np.argmax(y_pred, axis=1) # (N,) 110 | 111 | y_true = labels.cpu().numpy().reshape(-1,) 112 | 113 | y = np.bincount(C * y_true + y_pred, minlength=C * C) 114 | 115 | if len(y) < C * C: 116 | y = np.concatenate([y, np.zeros((C * C - len(y)), dtype=np.long)]) 117 | else: 118 | if len(y) > C * C: 119 | warnings.warn( 120 | "Prediction has fewer classes than ground truth. This may affect accuracy." 121 | ) 122 | y = y[-(C * C):] # last c*c elements. 123 | 124 | y = y.reshape(C, C) 125 | 126 | return y 127 | -------------------------------------------------------------------------------- /ml3d/tf/modules/optimizers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isl-org/Open3D-ML/b64b51445bf2fdb17f7de4fd7a1c383ac238a3dc/ml3d/tf/modules/optimizers/__init__.py -------------------------------------------------------------------------------- /ml3d/tf/modules/schedulers/__init__.py: -------------------------------------------------------------------------------- 1 | from .bn_momentum_scheduler import BNMomentumScheduler 2 | from .lr_one_cycle_scheduler import OneCycleScheduler 3 | from .cosine_warmup_scheduler import CosineWarmupLR 4 | 5 | __all__ = ['BNMomentumScheduler', 'OneCycleScheduler', 'CosineWarmupLR'] 6 | -------------------------------------------------------------------------------- /ml3d/tf/modules/schedulers/bn_momentum_scheduler.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | 3 | 4 | def set_bn_momentum_default(bn_momentum): 5 | 6 | def fn(m): 7 | if isinstance(m, tf.keras.layers.BatchNormalization): 8 | m.momentum = bn_momentum 9 | 10 | return fn 11 | 12 | 13 | class BNMomentumScheduler(object): 14 | 15 | def __init__(self, 16 | model, 17 | bn_lambda, 18 | last_epoch=-1, 19 | setter=set_bn_momentum_default): 20 | if not isinstance(model, tf.keras.layers.Layer): 21 | raise RuntimeError( 22 | "Class '{}' is not a Tensorflow Keras Layer".format( 23 | type(model).__name__)) 24 | 25 | self.model = model 26 | self.setter = setter 27 | self.lmbd = bn_lambda 28 | 29 | self.step(last_epoch + 1) 30 | self.last_epoch = last_epoch 31 | 32 | def step(self, epoch=None): 33 | if epoch is None: 34 | epoch = self.last_epoch + 1 35 | 36 | self.last_epoch = epoch 37 | self.model.apply(self.setter(self.lmbd(epoch))) 38 | -------------------------------------------------------------------------------- /ml3d/tf/modules/schedulers/cosine_warmup_scheduler.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | import math 3 | 4 | 5 | class CosineWarmupLR(tf.keras.optimizers.schedules.LearningRateSchedule): 6 | 7 | def __init__(self, optimizer, T_max, eta_min=0, last_epoch=-1): 8 | self.T_max = T_max 9 | self.eta_min = eta_min 10 | super(CosineWarmupLR, self).__init__(optimizer, last_epoch) 11 | 12 | def get_lr(self): 13 | return [ 14 | self.eta_min + (base_lr - self.eta_min) * 15 | (1 - math.cos(math.pi * self.last_epoch / self.T_max)) / 2 16 | for base_lr in self.base_lrs 17 | ] 18 | -------------------------------------------------------------------------------- /ml3d/tf/modules/schedulers/lr_one_cycle_scheduler.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | import numpy as np 3 | 4 | 5 | class OneCycleScheduler(tf.keras.optimizers.schedules.LearningRateSchedule): 6 | """Scheduler class for cyclic learning rate scheduling. 7 | 8 | Args: 9 | total_step: number of steps for one cycle. 10 | lr_max: maximum cyclic learning rate. 11 | div_factor: factor by which initial learning starts. 12 | """ 13 | 14 | def __init__(self, total_step, lr_max=0.002, div_factor=10.0): 15 | 16 | self.lr_max = lr_max 17 | self.div_factor = div_factor 18 | self.total_step = total_step 19 | super(OneCycleScheduler, self).__init__() 20 | 21 | def __call__(self, step): 22 | lr_low = self.lr_max / self.div_factor 23 | 24 | angle = (np.pi / self.total_step * step) 25 | lr1 = tf.abs(lr_low + (self.lr_max - lr_low) * tf.math.sin(angle)) 26 | 27 | angle = (np.pi / self.total_step) * ( 28 | (step - self.total_step / 2) % self.total_step) 29 | 30 | lr2 = tf.abs(self.lr_max * tf.math.cos(angle)) 31 | 32 | lr = tf.where(step < self.total_step / 2, lr1, lr2) 33 | 34 | return lr 35 | -------------------------------------------------------------------------------- /ml3d/tf/pipelines/__init__.py: -------------------------------------------------------------------------------- 1 | """3D ML pipelines for tensorflow.""" 2 | 3 | from .semantic_segmentation import SemanticSegmentation 4 | from .object_detection import ObjectDetection 5 | 6 | __all__ = ['SemanticSegmentation', 'ObjectDetection'] 7 | -------------------------------------------------------------------------------- /ml3d/tf/pipelines/base_pipeline.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from abc import ABC, abstractmethod 3 | from os.path import join 4 | 5 | from ...utils import Config, make_dir 6 | 7 | 8 | class BasePipeline(ABC): 9 | """Base pipeline class.""" 10 | 11 | def __init__(self, model, dataset=None, **kwargs): 12 | """Initialize. 13 | 14 | Args: 15 | model: network 16 | dataset: dataset, or None for inference model 17 | device: 'gpu' or 'cpu' 18 | kwargs: 19 | 20 | Returns: 21 | class: The corresponding class. 22 | """ 23 | if kwargs['name'] is None: 24 | raise KeyError("Please give a name to the pipeline") 25 | 26 | self.cfg = Config(kwargs) 27 | self.name = self.cfg.name 28 | 29 | self.model = model 30 | self.dataset = dataset 31 | self.rng = np.random.default_rng(kwargs.get('seed', None)) 32 | 33 | make_dir(self.cfg.main_log_dir) 34 | dataset_name = dataset.name if dataset is not None else '' 35 | self.cfg.logs_dir = join( 36 | self.cfg.main_log_dir, 37 | model.__class__.__name__ + '_' + dataset_name + '_tf') 38 | make_dir(self.cfg.logs_dir) 39 | self.summary = {} 40 | self.cfg.setdefault('summary', {}) 41 | 42 | @abstractmethod 43 | def run_inference(self, data): 44 | """Run inference on a given data. 45 | 46 | Args: 47 | data: A raw data. 48 | 49 | Returns: 50 | Returns the inference results. 51 | """ 52 | return 53 | 54 | @abstractmethod 55 | def run_test(self): 56 | """Run testing on test sets.""" 57 | return 58 | 59 | @abstractmethod 60 | def run_train(self): 61 | """Run training on train sets.""" 62 | return 63 | -------------------------------------------------------------------------------- /ml3d/tf/utils/__init__.py: -------------------------------------------------------------------------------- 1 | """Utils for tensorflow networks.""" 2 | -------------------------------------------------------------------------------- /ml3d/tf/utils/roipool3d/roipool3d_utils.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | from tensorflow.python.framework import ops 3 | import open3d 4 | if open3d.core.cuda.device_count() > 0: 5 | from open3d.ml.tf.ops import roi_pool 6 | import numpy as np 7 | 8 | 9 | def enlarge_box3d(boxes3d, extra_width): 10 | """Enlarge 3D boxes. 11 | 12 | Args: 13 | boxes3d: (N, 7) [x, y, z, h, w, l, ry] 14 | extra_width: Extra width. 15 | """ 16 | trans = np.zeros((boxes3d.shape[-1],)) 17 | trans[1] = extra_width 18 | trans[3:6] = extra_width * 2 19 | 20 | return boxes3d + trans 21 | 22 | 23 | def roipool3d_gpu(pts, 24 | pts_feature, 25 | boxes3d, 26 | pool_extra_width, 27 | sampled_pt_num=512): 28 | """ 29 | :param pts: (B, N, 3) 30 | :param pts_feature: (B, N, C) 31 | :param boxes3d: (B, M, 7) 32 | :param pool_extra_width: float 33 | :param sampled_pt_num: int 34 | :return: 35 | pooled_features: (B, M, 512, 3 + C) 36 | pooled_empty_flag: (B, M) 37 | """ 38 | if not open3d.core.cuda.device_count() > 0: 39 | raise NotImplementedError 40 | 41 | batch_size = pts.shape[0] 42 | pooled_boxes3d = tf.reshape( 43 | enlarge_box3d(tf.reshape(boxes3d, (-1, 7)), pool_extra_width), 44 | (batch_size, -1, 7)) 45 | 46 | pooled_features, pooled_empty_flag = roi_pool(pts, pooled_boxes3d, 47 | pts_feature, sampled_pt_num) 48 | 49 | return pooled_features, pooled_empty_flag 50 | 51 | 52 | ops.NoGradient('Open3DRoiPool') 53 | -------------------------------------------------------------------------------- /ml3d/tf/utils/tf_utils.py: -------------------------------------------------------------------------------- 1 | import re 2 | import tensorflow as tf 3 | 4 | 5 | def atoi(text): 6 | return int(text) if text.isdigit() else text 7 | 8 | 9 | def natural_keys(text): 10 | return [atoi(c) for c in re.split('(\d+)', text)] 11 | 12 | 13 | def gen_CNN(channels, 14 | conv=tf.keras.layers.Conv1D, 15 | use_bias=True, 16 | activation=tf.keras.layers.ReLU, 17 | batch_norm=False): 18 | layers = [] 19 | for i in range(len(channels) - 1): 20 | in_size, out_size = channels[i:i + 2] 21 | layers.append( 22 | conv(out_size, 1, use_bias=use_bias, data_format="channels_first")) 23 | if batch_norm: 24 | layers.append( 25 | tf.keras.layers.BatchNormalization(axis=1, 26 | momentum=0.9, 27 | epsilon=1e-05)) 28 | if activation is not None: 29 | layers.append(activation()) 30 | 31 | return tf.keras.Sequential(layers) 32 | -------------------------------------------------------------------------------- /ml3d/torch/__init__.py: -------------------------------------------------------------------------------- 1 | from .pipelines import * 2 | from .models import * 3 | from .dataloaders import * 4 | from .modules import * 5 | -------------------------------------------------------------------------------- /ml3d/torch/dataloaders/__init__.py: -------------------------------------------------------------------------------- 1 | """Dataloader for PyTorch.""" 2 | 3 | from .torch_dataloader import TorchDataloader 4 | from .torch_sampler import get_sampler 5 | from .default_batcher import DefaultBatcher 6 | from .concat_batcher import ConcatBatcher 7 | 8 | __all__ = ['TorchDataloader', 'DefaultBatcher', 'ConcatBatcher', 'get_sampler'] 9 | -------------------------------------------------------------------------------- /ml3d/torch/dataloaders/default_batcher.py: -------------------------------------------------------------------------------- 1 | import re 2 | import collections 3 | import torch 4 | 5 | container_abcs = collections.abc 6 | string_classes = (str, bytes) 7 | np_str_obj_array_pattern = re.compile(r'[SaUO]') 8 | 9 | 10 | def default_convert(data): 11 | r"""Converts each NumPy array data field into a tensor""" 12 | elem_type = type(data) 13 | if isinstance(data, torch.Tensor): 14 | return data 15 | elif elem_type.__module__ == 'numpy' and elem_type.__name__ != 'str_' \ 16 | and elem_type.__name__ != 'string_': 17 | # array of string classes and object 18 | if elem_type.__name__ == 'ndarray' \ 19 | and np_str_obj_array_pattern.search(data.dtype.str) is not None: 20 | return data 21 | return torch.as_tensor(data) 22 | elif isinstance(data, collections.abc.Mapping): 23 | return {key: default_convert(data[key]) for key in data} 24 | elif isinstance(data, tuple) and hasattr(data, '_fields'): # namedtuple 25 | return elem_type(*(default_convert(d) for d in data)) 26 | elif isinstance(data, collections.abc.Sequence) and not isinstance( 27 | data, string_classes): 28 | return [default_convert(d) for d in data] 29 | else: 30 | return data 31 | 32 | 33 | default_collate_err_msg_format = ( 34 | "default_collate: batch must contain tensors, numpy arrays, numbers, " 35 | "dicts or lists; found {}") 36 | 37 | 38 | def default_collate(batch): 39 | r"""Puts each data field into a tensor with outer dimension batch size""" 40 | elem = batch[0] 41 | elem_type = type(elem) 42 | if isinstance(elem, torch.Tensor): 43 | out = None 44 | if torch.utils.data.get_worker_info() is not None: 45 | # If we're in a background process, concatenate directly into a 46 | # shared memory tensor to avoid an extra copy 47 | numel = sum([x.numel() for x in batch]) 48 | storage = elem.storage()._new_shared(numel) 49 | out = elem.new(storage) 50 | return torch.stack(batch, 0, out=out) 51 | elif elem_type.__module__ == 'numpy' and elem_type.__name__ != 'str_' \ 52 | and elem_type.__name__ != 'string_': 53 | if elem_type.__name__ == 'ndarray' or elem_type.__name__ == 'memmap': 54 | # array of string classes and object 55 | if np_str_obj_array_pattern.search(elem.dtype.str) is not None: 56 | raise TypeError( 57 | default_collate_err_msg_format.format(elem.dtype)) 58 | 59 | return default_collate([torch.as_tensor(b) for b in batch]) 60 | elif elem.shape == (): # scalars 61 | return torch.as_tensor(batch) 62 | elif isinstance(elem, float): 63 | return torch.tensor(batch, dtype=torch.float64) 64 | elif isinstance(elem, int): 65 | return torch.tensor(batch) 66 | elif isinstance(elem, string_classes): 67 | return batch 68 | elif isinstance(elem, collections.abc.Mapping): 69 | return {key: default_collate([d[key] for d in batch]) for key in elem} 70 | elif isinstance(elem, tuple) and hasattr(elem, '_fields'): # namedtuple 71 | return elem_type(*(default_collate(samples) for samples in zip(*batch))) 72 | elif isinstance(elem, collections.abc.Sequence): 73 | # check to make sure that the elements in batch have consistent size 74 | it = iter(batch) 75 | elem_size = len(next(it)) 76 | if not all(len(elem) == elem_size for elem in it): 77 | raise RuntimeError( 78 | 'each element in list of batch should be of equal size') 79 | transposed = zip(*batch) 80 | return [default_collate(samples) for samples in transposed] 81 | 82 | raise TypeError(default_collate_err_msg_format.format(elem_type)) 83 | 84 | 85 | class DefaultBatcher(object): 86 | """DefaultBatcher of PyTorch dataloader.""" 87 | 88 | def __init__(self): 89 | super(DefaultBatcher, self).__init__() 90 | 91 | def collate_fn(self, batch): 92 | batching_result = default_collate(batch) 93 | 94 | return batching_result 95 | -------------------------------------------------------------------------------- /ml3d/torch/dataloaders/torch_dataloader.py: -------------------------------------------------------------------------------- 1 | from tqdm import tqdm 2 | from torch.utils.data import Dataset 3 | 4 | from ...utils import Cache, get_hash 5 | 6 | 7 | class TorchDataloader(Dataset): 8 | """This class allows you to load datasets for a PyTorch framework. 9 | 10 | Example: 11 | This example loads the SemanticKITTI dataset using the Torch dataloader: 12 | 13 | import torch 14 | from torch.utils.data import Dataset, DataLoader 15 | train_split = TorchDataloader(dataset=dataset.get_split('training')) 16 | """ 17 | 18 | def __init__(self, 19 | dataset=None, 20 | preprocess=None, 21 | transform=None, 22 | sampler=None, 23 | use_cache=True, 24 | steps_per_epoch=None, 25 | cache_convert=None, 26 | **kwargs): 27 | """Initialize. 28 | 29 | Args: 30 | dataset: The 3D ML dataset class. You can use the base dataset, sample datasets , or a custom dataset. 31 | preprocess: The model's pre-process method. 32 | transform: The model's transform method. 33 | use_cache: Indicates if preprocessed data should be cached. 34 | steps_per_epoch: The number of steps per epoch that indicates the batches of samples to train. If it is None, then the step number will be the number of samples in the data. 35 | 36 | Returns: 37 | class: The corresponding class. 38 | """ 39 | self.dataset = dataset 40 | self.preprocess = preprocess 41 | self.steps_per_epoch = steps_per_epoch 42 | self.cache_convert = cache_convert 43 | 44 | if preprocess is not None and use_cache: 45 | cache_dir = getattr(dataset.cfg, 'cache_dir') 46 | assert cache_dir is not None, 'cache directory is not given' 47 | 48 | self.cache_convert = Cache(preprocess, 49 | cache_dir=cache_dir, 50 | cache_key=get_hash(repr(preprocess))) 51 | 52 | uncached = [ 53 | idx for idx in range(len(dataset)) if dataset.get_attr(idx) 54 | ['name'] not in self.cache_convert.cached_ids 55 | ] 56 | if len(uncached) > 0: 57 | for idx in tqdm(range(len(dataset)), desc='preprocess'): 58 | attr = dataset.get_attr(idx) 59 | name = attr['name'] 60 | if name in self.cache_convert.cached_ids: 61 | continue 62 | data = dataset.get_data(idx) 63 | # cache the data 64 | self.cache_convert(name, data, attr) 65 | 66 | self.transform = transform 67 | 68 | if sampler is not None: 69 | sampler.initialize_with_dataloader(self) 70 | 71 | def __getitem__(self, index): 72 | """Returns the item at index position (idx).""" 73 | dataset = self.dataset 74 | index = index % len(dataset) 75 | 76 | attr = dataset.get_attr(index) 77 | if self.cache_convert: 78 | data = self.cache_convert(attr['name']) 79 | elif self.preprocess: 80 | data = self.preprocess(dataset.get_data(index), attr) 81 | else: 82 | data = dataset.get_data(index) 83 | 84 | if self.transform is not None: 85 | data = self.transform(data, attr) 86 | 87 | inputs = {'data': data, 'attr': attr} 88 | 89 | return inputs 90 | 91 | def __len__(self): 92 | """Returns the number of steps for an epoch.""" 93 | if self.steps_per_epoch is not None: 94 | steps_per_epoch = self.steps_per_epoch 95 | else: 96 | steps_per_epoch = len(self.dataset) 97 | return steps_per_epoch 98 | -------------------------------------------------------------------------------- /ml3d/torch/dataloaders/torch_sampler.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from torch.utils.data import Sampler 3 | 4 | 5 | class TorchSamplerWrapper(Sampler): 6 | 7 | def __init__(self, sampler): 8 | self.sampler = sampler 9 | 10 | def __iter__(self): 11 | return self.sampler.get_cloud_sampler() 12 | 13 | def __len__(self): 14 | return len(self.sampler) 15 | 16 | 17 | def get_sampler(sampler): 18 | return TorchSamplerWrapper(sampler) 19 | -------------------------------------------------------------------------------- /ml3d/torch/models/__init__.py: -------------------------------------------------------------------------------- 1 | """Networks for torch.""" 2 | 3 | from .randlanet import RandLANet 4 | from .kpconv import KPFCNN 5 | from .point_pillars import PointPillars 6 | from .sparseconvnet import SparseConvUnet 7 | from .point_rcnn import PointRCNN 8 | from .point_transformer import PointTransformer 9 | from .pvcnn import PVCNN 10 | 11 | __all__ = [ 12 | 'RandLANet', 'KPFCNN', 'PointPillars', 'PointRCNN', 'SparseConvUnet', 13 | 'PointTransformer', 'PVCNN' 14 | ] 15 | 16 | try: 17 | from .openvino_model import OpenVINOModel 18 | __all__.append("OpenVINOModel") 19 | except Exception: 20 | pass 21 | -------------------------------------------------------------------------------- /ml3d/torch/models/base_model.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import torch 3 | from abc import ABC, abstractmethod 4 | 5 | # use relative import for being compatible with Open3d main repo 6 | from ...utils import Config 7 | from ...datasets.samplers import SemSegRandomSampler 8 | 9 | 10 | class BaseModel(ABC, torch.nn.Module): 11 | """Base dataset class.""" 12 | 13 | def __init__(self, **kwargs): 14 | """Initialize. 15 | 16 | Args: 17 | cfg (cfg object or str): cfg object or path to cfg file 18 | dataset_path (str): path to the dataset 19 | **kwargs (dict): Dict of args 20 | """ 21 | super().__init__() 22 | 23 | self.trans_point_sampler = SemSegRandomSampler.get_point_sampler() 24 | self.cfg = Config(kwargs) 25 | self.rng = np.random.default_rng(kwargs.get('seed', None)) 26 | 27 | @abstractmethod 28 | def get_loss(self, Loss, results, inputs, device): 29 | """Computes the loss given the network input and outputs. 30 | 31 | Args: 32 | Loss: A loss object. 33 | results: This is the output of the model. 34 | inputs: This is the input to the model. 35 | device: The torch device to be used. 36 | 37 | Returns: 38 | Returns the loss value. 39 | """ 40 | return 41 | 42 | @abstractmethod 43 | def get_optimizer(self, cfg_pipeline): 44 | """Returns an optimizer object for the model. 45 | 46 | Args: 47 | cfg_pipeline: A Config object with the configuration of the pipeline. 48 | 49 | Returns: 50 | Returns a new optimizer object. 51 | """ 52 | return 53 | 54 | @abstractmethod 55 | def preprocess(self, cfg_pipeline): 56 | """Data preprocessing function. 57 | 58 | This function is called before training to preprocess the data from a 59 | dataset. 60 | 61 | Args: 62 | data: A sample from the dataset. 63 | attr: The corresponding attributes. 64 | 65 | Returns: 66 | Returns the preprocessed data 67 | """ 68 | return 69 | 70 | @abstractmethod 71 | def transform(self, cfg_pipeline): 72 | """Transform function for the point cloud and features. 73 | 74 | Args: 75 | cfg_pipeline: config file for pipeline. 76 | """ 77 | return 78 | 79 | @abstractmethod 80 | def inference_begin(self, data): 81 | """Function called right before running inference. 82 | 83 | Args: 84 | data: A data from the dataset. 85 | """ 86 | return 87 | 88 | @abstractmethod 89 | def inference_preprocess(self): 90 | """This function prepares the inputs for the model. 91 | 92 | Returns: 93 | The inputs to be consumed by the call() function of the model. 94 | """ 95 | return 96 | 97 | @abstractmethod 98 | def inference_end(self, inputs, results): 99 | """This function is called after the inference. 100 | 101 | This function can be implemented to apply post-processing on the 102 | network outputs. 103 | 104 | Args: 105 | results: The model outputs as returned by the call() function. 106 | Post-processing is applied on this object. 107 | 108 | Returns: 109 | Returns True if the inference is complete and otherwise False. 110 | Returning False can be used to implement inference for large point 111 | clouds which require multiple passes. 112 | """ 113 | return 114 | -------------------------------------------------------------------------------- /ml3d/torch/models/base_model_objdet.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import yaml 3 | import torch 4 | from os.path import join, exists, dirname, abspath 5 | from abc import ABC, abstractmethod 6 | 7 | # use relative import for being compatible with Open3d main repo 8 | from ...utils import Config 9 | 10 | 11 | class BaseModel(ABC, torch.nn.Module): 12 | """Base dataset class.""" 13 | 14 | def __init__(self, **kwargs): 15 | """Initialize. 16 | 17 | Args: 18 | cfg (cfg object or str): cfg object or path to cfg file 19 | dataset_path (str): Path to the dataset 20 | **kwargs (dict): Dict of args 21 | """ 22 | super().__init__() 23 | 24 | self.cfg = Config(kwargs) 25 | self.rng = np.random.default_rng(kwargs.get('seed', None)) 26 | 27 | @abstractmethod 28 | def get_loss(self, results, inputs): 29 | """Computes the loss given the network input and outputs. 30 | 31 | Args: 32 | Loss: A loss object. 33 | results: This is the output of the model. 34 | inputs: This is the input to the model. 35 | 36 | Returns: 37 | Returns the loss value. 38 | """ 39 | return 40 | 41 | @abstractmethod 42 | def get_optimizer(self, cfg_pipeline): 43 | """Returns an optimizer object for the model. 44 | 45 | Args: 46 | cfg_pipeline: A Config object with the configuration of the pipeline. 47 | 48 | Returns: 49 | Returns a new optimizer object. 50 | """ 51 | return 52 | 53 | @abstractmethod 54 | def preprocess(self, cfg_pipeline): 55 | """Data preprocessing function. 56 | 57 | This function is called before training to preprocess the data from a 58 | dataset. 59 | 60 | Args: 61 | data: A sample from the dataset. 62 | attr: The corresponding attributes. 63 | 64 | Returns: 65 | Returns the preprocessed data 66 | """ 67 | return 68 | 69 | @abstractmethod 70 | def transform(self, cfg_pipeline): 71 | """Transform function for the point cloud and features. 72 | 73 | Args: 74 | cfg_pipeline: config file for pipeline. 75 | """ 76 | return 77 | 78 | @abstractmethod 79 | def inference_end(self, results, attr=None): 80 | """This function is called after the inference. 81 | 82 | This function can be implemented to apply post-processing on the 83 | network outputs. 84 | 85 | Args: 86 | results: The model outputs as returned by the call() function. 87 | Post-processing is applied on this object. 88 | 89 | Returns: 90 | Returns True if the inference is complete and otherwise False. 91 | Returning False can be used to implement inference for large point 92 | clouds which require multiple passes. 93 | """ 94 | return 95 | -------------------------------------------------------------------------------- /ml3d/torch/modules/__init__.py: -------------------------------------------------------------------------------- 1 | """Functional modules for torch.""" 2 | 3 | from .losses import * 4 | from .metrics import * 5 | -------------------------------------------------------------------------------- /ml3d/torch/modules/losses/__init__.py: -------------------------------------------------------------------------------- 1 | """Loss modules""" 2 | 3 | from .semseg_loss import filter_valid_label, SemSegLoss 4 | from .cross_entropy import CrossEntropyLoss 5 | from .focal_loss import FocalLoss 6 | from .smooth_L1 import SmoothL1Loss 7 | 8 | __all__ = [ 9 | 'filter_valid_label', 'SemSegLoss', 'CrossEntropyLoss', 'FocalLoss', 10 | 'SmoothL1Loss' 11 | ] 12 | -------------------------------------------------------------------------------- /ml3d/torch/modules/losses/cross_entropy.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | import torch.nn.functional as F 4 | 5 | 6 | def one_hot(index, classes): 7 | out_idx = torch.arange(classes, device=index.device) 8 | out_idx = torch.unsqueeze(out_idx, 0) 9 | index = torch.unsqueeze(index, -1) 10 | return (index == out_idx).float() 11 | 12 | 13 | class CrossEntropyLoss(nn.Module): 14 | """CrossEntropyLoss.""" 15 | 16 | def __init__(self, loss_weight=1.0): 17 | """CrossEntropyLoss. 18 | 19 | Args: 20 | loss_weight (float, optional): Weight of the loss. Defaults to 1.0. 21 | """ 22 | super(CrossEntropyLoss, self).__init__() 23 | self.loss_weight = loss_weight 24 | 25 | def forward(self, cls_score, label, weight=None, avg_factor=None, **kwargs): 26 | """Forward function. 27 | 28 | Args: 29 | cls_score (torch.Tensor): The prediction. 30 | label (torch.Tensor): The learning label of the prediction. 31 | weight (torch.Tensor, optional): Sample-wise loss weight. 32 | avg_factor (int, optional): Average factor that is used to average 33 | the loss. Defaults to None. 34 | 35 | Returns: 36 | torch.Tensor: The calculated loss 37 | """ 38 | loss = F.cross_entropy(cls_score, label, reduction='none') 39 | 40 | if weight is not None: 41 | loss = loss * weight 42 | 43 | loss = loss * self.loss_weight 44 | 45 | if avg_factor: 46 | return loss.sum() / avg_factor 47 | else: 48 | return loss.mean() 49 | -------------------------------------------------------------------------------- /ml3d/torch/modules/losses/focal_loss.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | import torch.nn.functional as F 4 | from torch.autograd import Variable 5 | 6 | 7 | def one_hot(index, classes): 8 | out_idx = torch.arange(classes, device=index.device) 9 | out_idx = torch.unsqueeze(out_idx, 0) 10 | index = torch.unsqueeze(index, -1) 11 | return (index == out_idx).float() 12 | 13 | 14 | class FocalLoss(nn.Module): 15 | """`Focal Loss `_ 16 | 17 | Args: 18 | gamma (float, optional): The gamma for calculating the modulating 19 | factor. Defaults to 2.0. 20 | alpha (float, optional): A balanced form for Focal Loss. 21 | Defaults to 0.25. 22 | loss_weight (float, optional): Weight of loss. Defaults to 1.0. 23 | """ 24 | 25 | def __init__(self, gamma=2.0, alpha=0.25, loss_weight=1.0): 26 | super(FocalLoss, self).__init__() 27 | self.gamma = gamma 28 | self.alpha = alpha 29 | self.loss_weight = loss_weight 30 | 31 | def forward(self, pred, target, weight=None, avg_factor=None): 32 | pred_sigmoid = pred.sigmoid() 33 | if len(pred.shape) > 1: 34 | target = one_hot(target, int(pred.shape[-1])) 35 | 36 | target = target.type_as(pred) 37 | 38 | pt = (1 - pred_sigmoid) * target + pred_sigmoid * (1 - target) 39 | focal_weight = (self.alpha * target + (1 - self.alpha) * 40 | (1 - target)) * pt.pow(self.gamma) 41 | loss = F.binary_cross_entropy_with_logits( 42 | pred, target, reduction='none') * focal_weight 43 | 44 | if weight is not None: 45 | loss = loss * weight 46 | 47 | loss = loss * self.loss_weight 48 | 49 | if avg_factor is None: 50 | return loss.mean() 51 | elif avg_factor > 0: 52 | return loss.sum() / avg_factor 53 | else: 54 | return loss 55 | -------------------------------------------------------------------------------- /ml3d/torch/modules/losses/semseg_loss.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | 4 | from ....datasets.utils import DataProcessing 5 | 6 | 7 | def filter_valid_label(scores, labels, num_classes, ignored_label_inds, device): 8 | """Loss functions for semantic segmentation.""" 9 | valid_scores = scores.reshape(-1, num_classes).to(device) 10 | valid_labels = labels.reshape(-1).to(device) 11 | 12 | ignored_bool = torch.zeros_like(valid_labels, dtype=torch.bool) 13 | for ign_label in ignored_label_inds: 14 | ignored_bool = torch.logical_or(ignored_bool, 15 | torch.eq(valid_labels, ign_label)) 16 | 17 | valid_idx = torch.where(torch.logical_not(ignored_bool))[0].to(device) 18 | 19 | valid_scores = torch.gather(valid_scores, 0, 20 | valid_idx.unsqueeze(-1).expand(-1, num_classes)) 21 | valid_labels = torch.gather(valid_labels, 0, valid_idx) 22 | 23 | # Reduce label values in the range of logit shape 24 | reducing_list = torch.arange(0, num_classes, dtype=torch.int64) 25 | inserted_value = torch.zeros([1], dtype=torch.int64) 26 | 27 | for ign_label in ignored_label_inds: 28 | if ign_label >= 0: 29 | 30 | reducing_list = torch.cat([ 31 | reducing_list[:ign_label], inserted_value, 32 | reducing_list[ign_label:] 33 | ], 0) 34 | valid_labels = torch.gather(reducing_list.to(device), 0, 35 | valid_labels.long()) 36 | 37 | return valid_scores, valid_labels 38 | 39 | 40 | class SemSegLoss(object): 41 | """Loss functions for semantic segmentation.""" 42 | 43 | def __init__(self, pipeline, model, dataset, device): 44 | super(SemSegLoss, self).__init__() 45 | # weighted_CrossEntropyLoss 46 | if 'class_weights' in dataset.cfg.keys() and len( 47 | dataset.cfg.class_weights) != 0: 48 | class_wt = DataProcessing.get_class_weights( 49 | dataset.cfg.class_weights) 50 | weights = torch.tensor(class_wt, dtype=torch.float, device=device) 51 | 52 | self.weighted_CrossEntropyLoss = nn.CrossEntropyLoss(weight=weights) 53 | else: 54 | self.weighted_CrossEntropyLoss = nn.CrossEntropyLoss() 55 | -------------------------------------------------------------------------------- /ml3d/torch/modules/losses/smooth_L1.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | 4 | 5 | class SmoothL1Loss(nn.Module): 6 | """Smooth L1 loss. 7 | 8 | Args: 9 | beta (float, optional): The threshold in the piecewise function. 10 | Defaults to 1.0. 11 | reduction (str, optional): The method to reduce the loss. 12 | Options are "none", "mean" and "sum". Defaults to "mean". 13 | loss_weight (float, optional): The weight of loss. 14 | """ 15 | 16 | def __init__(self, beta=1.0, loss_weight=1.0): 17 | super(SmoothL1Loss, self).__init__() 18 | self.beta = beta 19 | self.loss_weight = loss_weight 20 | 21 | def forward(self, pred, target, weight=None, avg_factor=None, **kwargs): 22 | """Forward function. 23 | 24 | Args: 25 | pred (torch.Tensor): The prediction. 26 | target (torch.Tensor): The learning target of the prediction. 27 | weight (torch.Tensor, optional): The weight of loss for each 28 | prediction. Defaults to None. 29 | avg_factor (int, optional): Average factor that is used to average 30 | the loss. Defaults to None. 31 | reduction_override (str, optional): The reduction method used to 32 | override the original reduction method of the loss. 33 | Defaults to None. 34 | """ 35 | assert pred.size() == target.size() and target.numel() > 0 36 | diff = torch.abs(pred - target) 37 | loss = torch.where(diff < self.beta, 0.5 * diff * diff / self.beta, 38 | diff - 0.5 * self.beta) 39 | if weight is not None: 40 | loss = loss * weight 41 | 42 | loss = loss * self.loss_weight 43 | 44 | if avg_factor: 45 | return loss.sum() / avg_factor 46 | else: 47 | return loss.mean() 48 | -------------------------------------------------------------------------------- /ml3d/torch/modules/metrics/__init__.py: -------------------------------------------------------------------------------- 1 | """Metrics for Semantic Segmentation. 2 | Includes accuracy, mIoU and confusion matrix. 3 | """ 4 | 5 | from .semseg_metric import SemSegMetric 6 | 7 | __all__ = ['SemSegMetric'] 8 | -------------------------------------------------------------------------------- /ml3d/torch/modules/optimizers/__init__.py: -------------------------------------------------------------------------------- 1 | from .optim_wrapper import OptimWrapper 2 | 3 | __all__ = ['OptimWrapper'] 4 | -------------------------------------------------------------------------------- /ml3d/torch/modules/schedulers/__init__.py: -------------------------------------------------------------------------------- 1 | from .bn_momentum_scheduler import BNMomentumScheduler 2 | from .lr_one_cycle_scheduler import OneCycleScheduler 3 | from .cosine_warmup_scheduler import CosineWarmupLR 4 | 5 | __all__ = ['BNMomentumScheduler', 'OneCycleScheduler', 'CosineWarmupLR'] 6 | -------------------------------------------------------------------------------- /ml3d/torch/modules/schedulers/bn_momentum_scheduler.py: -------------------------------------------------------------------------------- 1 | #***************************************************************************************/ 2 | # 3 | # Based on PointRCNN Library (MIT license): 4 | # https://github.com/sshaoshuai/PointRCNN 5 | # 6 | # Copyright (c) 2019 Shaoshuai Shi 7 | 8 | # Permission is hereby granted, free of charge, to any person obtaining a copy 9 | # of this software and associated documentation files (the "Software"), to deal 10 | # in the Software without restriction, including without limitation the rights 11 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | # copies of the Software, and to permit persons to whom the Software is 13 | # furnished to do so, subject to the following conditions: 14 | 15 | # The above copyright notice and this permission notice shall be included in all 16 | # copies or substantial portions of the Software. 17 | 18 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | # SOFTWARE. 25 | # 26 | #***************************************************************************************/ 27 | 28 | import torch.nn as nn 29 | 30 | 31 | def set_bn_momentum_default(bn_momentum): 32 | 33 | def fn(m): 34 | if isinstance(m, (nn.BatchNorm1d, nn.BatchNorm2d, nn.BatchNorm3d)): 35 | m.momentum = bn_momentum 36 | 37 | return fn 38 | 39 | 40 | class BNMomentumScheduler(object): 41 | 42 | def __init__(self, 43 | model, 44 | bn_lambda, 45 | last_epoch=-1, 46 | setter=set_bn_momentum_default): 47 | if not isinstance(model, nn.Module): 48 | raise RuntimeError("Class '{}' is not a PyTorch nn Module".format( 49 | type(model).__name__)) 50 | 51 | self.model = model 52 | self.setter = setter 53 | self.lmbd = bn_lambda 54 | 55 | self.step(last_epoch + 1) 56 | self.last_epoch = last_epoch 57 | 58 | def step(self, epoch=None): 59 | if epoch is None: 60 | epoch = self.last_epoch + 1 61 | 62 | self.last_epoch = epoch 63 | self.model.apply(self.setter(self.lmbd(epoch))) 64 | -------------------------------------------------------------------------------- /ml3d/torch/modules/schedulers/cosine_warmup_scheduler.py: -------------------------------------------------------------------------------- 1 | #***************************************************************************************/ 2 | # 3 | # Based on PointRCNN Library (MIT license): 4 | # https://github.com/sshaoshuai/PointRCNN 5 | # 6 | # Copyright (c) 2019 Shaoshuai Shi 7 | 8 | # Permission is hereby granted, free of charge, to any person obtaining a copy 9 | # of this software and associated documentation files (the "Software"), to deal 10 | # in the Software without restriction, including without limitation the rights 11 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | # copies of the Software, and to permit persons to whom the Software is 13 | # furnished to do so, subject to the following conditions: 14 | 15 | # The above copyright notice and this permission notice shall be included in all 16 | # copies or substantial portions of the Software. 17 | 18 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | # SOFTWARE. 25 | # 26 | #***************************************************************************************/ 27 | 28 | import torch.optim.lr_scheduler as lr_sched 29 | import math 30 | 31 | 32 | class CosineWarmupLR(lr_sched._LRScheduler): 33 | 34 | def __init__(self, optimizer, T_max, eta_min=0, last_epoch=-1): 35 | self.T_max = T_max 36 | self.eta_min = eta_min 37 | super(CosineWarmupLR, self).__init__(optimizer, last_epoch) 38 | 39 | def get_lr(self): 40 | return [ 41 | self.eta_min + (base_lr - self.eta_min) * 42 | (1 - math.cos(math.pi * self.last_epoch / self.T_max)) / 2 43 | for base_lr in self.base_lrs 44 | ] 45 | -------------------------------------------------------------------------------- /ml3d/torch/pipelines/__init__.py: -------------------------------------------------------------------------------- 1 | """3D ML pipelines for torch.""" 2 | 3 | from .semantic_segmentation import SemanticSegmentation 4 | from .object_detection import ObjectDetection 5 | 6 | __all__ = ['SemanticSegmentation', 'ObjectDetection'] 7 | -------------------------------------------------------------------------------- /ml3d/torch/pipelines/base_pipeline.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import yaml 3 | import torch 4 | from abc import ABC, abstractmethod 5 | 6 | from os.path import join, exists, dirname, abspath 7 | 8 | # use relative import for being compatible with Open3d main repo 9 | from ...utils import Config, make_dir 10 | 11 | 12 | class BasePipeline(ABC): 13 | """Base pipeline class.""" 14 | 15 | def __init__(self, 16 | model, 17 | dataset=None, 18 | device='cuda', 19 | distributed=False, 20 | **kwargs): 21 | """Initialize. 22 | 23 | Args: 24 | model: A network model. 25 | dataset: A dataset, or None for inference model. 26 | device: 'cuda' or 'cpu'. 27 | distributed: Whether to use multiple gpus. 28 | kwargs: 29 | 30 | Returns: 31 | class: The corresponding class. 32 | """ 33 | self.cfg = Config(kwargs) 34 | 35 | if kwargs['name'] is None: 36 | raise KeyError("Please give a name to the pipeline") 37 | self.name = self.cfg.name 38 | 39 | self.model = model 40 | self.dataset = dataset 41 | self.rng = np.random.default_rng(kwargs.get('seed', None)) 42 | 43 | self.distributed = distributed 44 | if self.distributed and self.name == "SemanticSegmentation": 45 | raise NotImplementedError( 46 | "Distributed training not implemented for SemanticSegmentation!" 47 | ) 48 | 49 | self.rank = kwargs.get('rank', 0) 50 | 51 | dataset_name = dataset.name if dataset is not None else '' 52 | self.cfg.logs_dir = join( 53 | self.cfg.main_log_dir, 54 | model.__class__.__name__ + '_' + dataset_name + '_torch') 55 | 56 | if self.rank == 0: 57 | make_dir(self.cfg.main_log_dir) 58 | make_dir(self.cfg.logs_dir) 59 | 60 | if device == 'cpu' or not torch.cuda.is_available(): 61 | if distributed: 62 | raise NotImplementedError( 63 | "Distributed training for CPU is not supported yet.") 64 | self.device = torch.device('cpu') 65 | else: 66 | if distributed: 67 | self.device = torch.device(device) 68 | print(f"Rank : {self.rank} using device : {self.device}") 69 | torch.cuda.set_device(self.device) 70 | else: 71 | self.device = torch.device('cuda') 72 | 73 | self.summary = {} 74 | self.cfg.setdefault('summary', {}) 75 | 76 | @abstractmethod 77 | def run_inference(self, data): 78 | """Run inference on a given data. 79 | 80 | Args: 81 | data: A raw data. 82 | 83 | Returns: 84 | Returns the inference results. 85 | """ 86 | return 87 | 88 | @abstractmethod 89 | def run_test(self): 90 | """Run testing on test sets.""" 91 | return 92 | 93 | @abstractmethod 94 | def run_train(self): 95 | """Run training on train sets.""" 96 | return 97 | -------------------------------------------------------------------------------- /ml3d/torch/utils/__init__.py: -------------------------------------------------------------------------------- 1 | """Utils for torch networks.""" 2 | 3 | from .torch_utils import latest_torch_ckpt 4 | 5 | __all__ = ['latest_torch_ckpt'] 6 | -------------------------------------------------------------------------------- /ml3d/torch/utils/roipool3d/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isl-org/Open3D-ML/b64b51445bf2fdb17f7de4fd7a1c383ac238a3dc/ml3d/torch/utils/roipool3d/__init__.py -------------------------------------------------------------------------------- /ml3d/torch/utils/roipool3d/roipool3d_utils.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import open3d 3 | if open3d.core.cuda.device_count() > 0: 4 | from open3d.ml.torch.ops import roi_pool 5 | import numpy as np 6 | 7 | 8 | def enlarge_box3d(boxes3d, extra_width): 9 | """Enlarge 3D box. 10 | 11 | Args: 12 | boxes3d: (N, 7) [x, y, z, h, w, l, ry] 13 | extra_width: extra width 14 | """ 15 | if isinstance(boxes3d, np.ndarray): 16 | large_boxes3d = boxes3d.copy() 17 | else: 18 | large_boxes3d = boxes3d.clone() 19 | large_boxes3d[:, 3:6] += extra_width * 2 20 | large_boxes3d[:, 1] += extra_width 21 | return large_boxes3d 22 | 23 | 24 | def roipool3d_gpu(pts, 25 | pts_feature, 26 | boxes3d, 27 | pool_extra_width, 28 | sampled_pt_num=512): 29 | """Roipool3D GPU. 30 | 31 | Args: 32 | pts: (B, N, 3) 33 | pts_feature: (B, N, C) 34 | boxes3d: (B, M, 7) 35 | pool_extra_width: float 36 | sampled_pt_num: int 37 | 38 | Returns: 39 | pooled_features: (B, M, 512, 3 + C) 40 | pooled_empty_flag: (B, M) 41 | """ 42 | if not open3d.core.cuda.device_count() > 0: 43 | raise NotImplementedError 44 | 45 | batch_size = pts.shape[0] 46 | pooled_boxes3d = enlarge_box3d(boxes3d.view(-1, 7), 47 | pool_extra_width).view(batch_size, -1, 7) 48 | 49 | pooled_features, pooled_empty_flag = roi_pool(pts.contiguous(), 50 | pooled_boxes3d.contiguous(), 51 | pts_feature.contiguous(), 52 | sampled_pt_num) 53 | 54 | return pooled_features, pooled_empty_flag 55 | -------------------------------------------------------------------------------- /ml3d/torch/utils/torch_utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | from torch import nn 4 | import torch.nn.functional as F 5 | 6 | 7 | def atoi(text): 8 | return int(text) if text.isdigit() else text 9 | 10 | 11 | def natural_keys(text): 12 | return [atoi(c) for c in re.split('(\d+)', text)] 13 | 14 | 15 | def latest_torch_ckpt(train_ckpt_dir): 16 | files = os.listdir(train_ckpt_dir) 17 | ckpt_list = [f for f in files if f.endswith('.pth')] 18 | if len(ckpt_list) == 0: 19 | return None 20 | ckpt_list.sort(key=natural_keys) 21 | 22 | ckpt_name = ckpt_list[-1] 23 | return os.path.join(train_ckpt_dir, ckpt_name) 24 | 25 | 26 | def gen_CNN(channels, 27 | conv=nn.Conv1d, 28 | bias=True, 29 | activation=nn.ReLU, 30 | batch_norm=None, 31 | instance_norm=None): 32 | layers = [] 33 | for i in range(len(channels) - 1): 34 | in_size, out_size = channels[i:i + 2] 35 | layers.append(conv(in_size, out_size, 1, bias=bias)) 36 | if batch_norm is not None: 37 | layers.append(batch_norm(out_size)) 38 | if activation is not None: 39 | layers.append(activation(inplace=True)) 40 | if instance_norm is not None: 41 | layers.append( 42 | instance_norm(out_size, affine=False, 43 | track_running_stats=False)) 44 | 45 | return nn.Sequential(*layers) 46 | -------------------------------------------------------------------------------- /ml3d/utils/__init__.py: -------------------------------------------------------------------------------- 1 | """Utils for 3D ML.""" 2 | 3 | from .config import Config 4 | from .log import LogRecord, get_runid, code2md 5 | from .builder import (MODEL, PIPELINE, DATASET, SAMPLER, get_module, 6 | convert_framework_name, convert_device_name) 7 | from .dataset_helper import get_hash, make_dir, Cache 8 | 9 | __all__ = [ 10 | 'Config', 'make_dir', 'LogRecord', 'MODEL', 'SAMPLER', 'PIPELINE', 11 | 'DATASET', 'get_module', 'convert_framework_name', 'get_hash', 'make_dir', 12 | 'Cache', 'convert_device_name' 13 | ] 14 | -------------------------------------------------------------------------------- /ml3d/utils/builder.py: -------------------------------------------------------------------------------- 1 | from .registry import Registry, get_from_name 2 | 3 | MODEL = Registry('model') 4 | DATASET = Registry('dataset') 5 | PIPELINE = Registry('pipeline') 6 | SAMPLER = Registry('sampler') 7 | 8 | 9 | def build(cfg, registry, args=None): 10 | return build_from_cfg(cfg, registry, args) 11 | 12 | 13 | def build_network(cfg): 14 | return build(cfg, NETWORK) 15 | 16 | 17 | def convert_device_name(device_type, device_ids): 18 | """Convert device to either cpu or cuda.""" 19 | gpu_names = ["gpu", "cuda"] 20 | cpu_names = ["cpu"] 21 | if device_type not in cpu_names + gpu_names: 22 | raise KeyError("the device should either " 23 | "be cuda or cpu but got {}".format(device_type)) 24 | assert type(device_ids) is list 25 | device_ids_new = [] 26 | for device in device_ids: 27 | device_ids_new.append(int(device)) 28 | 29 | if device_type in gpu_names: 30 | return "cuda", device_ids_new 31 | else: 32 | return "cpu", device_ids_new 33 | 34 | 35 | def convert_framework_name(framework): 36 | """Convert framework to either tf or torch.""" 37 | tf_names = ["tf", "tensorflow", "TF"] 38 | torch_names = ["torch", "pytorch", "PyTorch"] 39 | if framework not in tf_names + torch_names: 40 | raise KeyError("the framework should either " 41 | "be tf or torch but got {}".format(framework)) 42 | if framework in tf_names: 43 | return "tf" 44 | else: 45 | return "torch" 46 | 47 | 48 | def get_module(module_type, module_name, framework=None, **kwargs): 49 | """Fetch modules (pipeline, model, or) from registry.""" 50 | if module_type == 'pipeline': 51 | framework = convert_framework_name(framework) 52 | return get_from_name(module_name, PIPELINE, framework) 53 | 54 | elif module_type == "dataset": 55 | return get_from_name(module_name, DATASET, framework) 56 | 57 | elif module_type == "sampler": 58 | return get_from_name(module_name, SAMPLER, framework) 59 | 60 | elif module_type == "model": 61 | framework = convert_framework_name(framework) 62 | return get_from_name(module_name, MODEL, framework) 63 | else: 64 | raise KeyError("module type should be model, dataset, or pipeline but " 65 | "got {}".format(module_type)) 66 | -------------------------------------------------------------------------------- /ml3d/utils/dataset_helper.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | from typing import Callable 3 | import numpy as np 4 | 5 | from os import makedirs, listdir 6 | from os.path import exists, join, splitext 7 | 8 | 9 | def make_dir(folder_name): 10 | """Create a directory. 11 | 12 | If already exists, do nothing 13 | """ 14 | if not exists(folder_name): 15 | makedirs(folder_name) 16 | 17 | 18 | def get_hash(x: str): 19 | """Generate a hash from a string.""" 20 | h = hashlib.md5(x.encode()) 21 | return h.hexdigest() 22 | 23 | 24 | class Cache(object): 25 | """Cache converter for preprocessed data.""" 26 | 27 | def __init__(self, func: Callable, cache_dir: str, cache_key: str): 28 | """Initialize. 29 | 30 | Args: 31 | func: preprocess function of a model. 32 | cache_dir: directory to store the cache. 33 | cache_key: key of this cache 34 | Returns: 35 | class: The corresponding class. 36 | """ 37 | self.func = func 38 | self.cache_dir = join(cache_dir, cache_key) 39 | make_dir(self.cache_dir) 40 | self.cached_ids = [splitext(p)[0] for p in listdir(self.cache_dir)] 41 | 42 | def __call__(self, unique_id: str, *data): 43 | """Call the converter. If the cache exists, load and return the cache, 44 | otherwise run the preprocess function and store the cache. 45 | 46 | Args: 47 | unique_id: A unique key of this data. 48 | data: Input to the preprocess function. 49 | 50 | Returns: 51 | class: Preprocessed (cache) data. 52 | """ 53 | fpath = join(self.cache_dir, str('{}.npy'.format(unique_id))) 54 | 55 | if not exists(fpath): 56 | output = self.func(*data) 57 | 58 | self._write(output, fpath) 59 | self.cached_ids.append(unique_id) 60 | else: 61 | output = self._read(fpath) 62 | 63 | return self._read(fpath) 64 | 65 | def _write(self, x, fpath): 66 | np.save(fpath, x) 67 | 68 | def _read(self, fpath): 69 | return np.load(fpath, allow_pickle=True).item() 70 | -------------------------------------------------------------------------------- /ml3d/utils/log.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | from pathlib import Path 4 | 5 | 6 | class LogRecord(logging.LogRecord): 7 | """Class for logging information.""" 8 | 9 | def getMessage(self): 10 | msg = self.msg 11 | if self.args: 12 | if isinstance(self.args, dict): 13 | msg = msg.format(**self.args) 14 | else: 15 | msg = msg.format(*self.args) 16 | return msg 17 | 18 | 19 | def get_runid(path): 20 | """Get runid for an experiment.""" 21 | name = Path(path).name 22 | if not os.path.exists(Path(path).parent): 23 | return '00001' 24 | files = os.listdir(Path(path).parent) 25 | runid = 0 26 | for f in files: 27 | try: 28 | id, val = f.split('_', 1) 29 | runid = max(runid, int(id)) 30 | except: 31 | pass 32 | runid = str(runid + 1) 33 | runid = '0' * (5 - len(runid)) + runid 34 | return runid 35 | 36 | 37 | def code2md(code_text, language=None): 38 | """Format code as markdown for display (eg in tensorboard)""" 39 | four_spaces = ' ' 40 | code_md = four_spaces + code_text.replace(os.linesep, 41 | os.linesep + four_spaces) 42 | return code_md[:-4] 43 | -------------------------------------------------------------------------------- /ml3d/utils/registry.py: -------------------------------------------------------------------------------- 1 | import inspect 2 | 3 | 4 | class Registry(object): 5 | 6 | def __init__(self, name): 7 | self._name = name 8 | self._module_dict = dict() 9 | 10 | def get(self, key, framework): 11 | """Get the registry record. 12 | 13 | Args: 14 | key (str): The class name in string format. 15 | 16 | Returns: 17 | class: The corresponding class. 18 | """ 19 | if framework is None: 20 | return self._module_dict.get(key, None) 21 | else: 22 | if not isinstance(framework, str): 23 | raise TypeError("framework must be a string, " 24 | "either tf or torch, but got {}".format( 25 | type(framework))) 26 | return self._module_dict[framework].get(key, None) 27 | 28 | @property 29 | def name(self): 30 | return self._name 31 | 32 | @property 33 | def module_dict(self): 34 | return self._module_dict 35 | 36 | def _register_module(self, module_class, framework=None, module_name=None): 37 | if not inspect.isclass(module_class): 38 | raise TypeError("module must be a class, " 39 | "but got {}".format(type(module_class))) 40 | 41 | if module_name is None: 42 | module_name = module_class.__name__ 43 | if framework is None: 44 | self.module_dict[module_name] = module_class 45 | else: 46 | if not isinstance(framework, str): 47 | raise TypeError("framework must be a string, " 48 | "either tf or torch, but got {}".format( 49 | type(framework))) 50 | if framework in self.module_dict: 51 | self.module_dict[framework][module_name] = module_class 52 | else: 53 | self.module_dict[framework] = dict() 54 | self.module_dict[framework][module_name] = module_class 55 | 56 | def register_module(self, framework=None, name=None): 57 | 58 | def _register(cls): 59 | self._register_module(cls, framework=framework, module_name=name) 60 | 61 | return _register 62 | 63 | 64 | def get_from_name(module_name, registry, framework): 65 | """Build a module from config dict. 66 | 67 | Args: 68 | module_name (string): Name of the module. 69 | registry: The registry to search the type from. 70 | framework (string): Framework, one of 'tf' or 'torch' 71 | 72 | Returns: 73 | object: The constructed object. 74 | """ 75 | if not isinstance(module_name, str): 76 | raise TypeError("module_name must be a string".format( 77 | type(module_name))) 78 | if not isinstance(registry, Registry): 79 | raise TypeError("registry must be an Registry object, " 80 | "but got {}".format(type(module_name))) 81 | 82 | obj_cls = registry.get(module_name, framework) 83 | if obj_cls is None: 84 | raise KeyError("{} - {} is not in the {} registry".format( 85 | module_name, framework, registry.name)) 86 | 87 | return obj_cls 88 | -------------------------------------------------------------------------------- /ml3d/vis/__init__.py: -------------------------------------------------------------------------------- 1 | """Visualizer for 3D ML.""" 2 | 3 | from .boundingbox import * 4 | from .colormap import * 5 | from .visualizer import * 6 | -------------------------------------------------------------------------------- /ml3d/vis/colormap.py: -------------------------------------------------------------------------------- 1 | class Colormap: 2 | """This class is used to create a color map for visualization of points.""" 3 | 4 | class Point: 5 | """Initialize the class. 6 | 7 | Args: 8 | value: The scalar value index of the point. 9 | color: The color associated with the value. 10 | """ 11 | 12 | def __init__(self, value, color): 13 | assert (value >= 0.0) 14 | assert (value <= 1.0) 15 | 16 | self.value = value 17 | self.color = color 18 | 19 | def __repr__(self): 20 | """Represent the color and value in the colormap.""" 21 | return "Colormap.Point(" + str(self.value) + ", " + str( 22 | self.color) + ")" 23 | 24 | # The value of each Point must be greater than the previous 25 | # (e.g. [0.0, 0.1, 0.4, 1.0], not [0.0, 0.4, 0.1, 1.0] 26 | def __init__(self, points): 27 | self.points = points 28 | 29 | def calc_u_array(self, values, range_min, range_max): 30 | """Generate the basic array based on the minimum and maximum range passed.""" 31 | range_width = (range_max - range_min) 32 | return [ 33 | min(1.0, max(0.0, (v - range_min) / range_width)) for v in values 34 | ] 35 | 36 | # (This is done by the shader now) 37 | def calc_color_array(self, values, range_min, range_max): 38 | """Generate the color array based on the minimum and maximum range passed. 39 | 40 | Args: 41 | values: The index of values. 42 | range_min: The minimum value in the range. 43 | range_max: The maximum value in the range. 44 | 45 | Returns: 46 | An array of color index based on the range passed. 47 | """ 48 | u_array = self.calc_u_array(values, range_min, range_max) 49 | 50 | tex = [[1.0, 0.0, 1.0]] * 128 51 | n = float(len(tex) - 1) 52 | idx = 0 53 | for tex_idx in range(0, len(tex)): 54 | x = float(tex_idx) / n 55 | while idx < len(self.points) and x > self.points[idx].value: 56 | idx += 1 57 | 58 | if idx == 0: 59 | tex[tex_idx] = self.points[0].color 60 | elif idx == len(self.points): 61 | tex[tex_idx] = self.points[-1].color 62 | else: 63 | p0 = self.points[idx - 1] 64 | p1 = self.points[idx] 65 | dist = p1.value - p0.value 66 | # Calc weights between 0 and 1 67 | w0 = 1.0 - (x - p0.value) / dist 68 | w1 = (x - p0.value) / dist 69 | c = [ 70 | w0 * p0.color[0] + w1 * p1.color[0], 71 | w0 * p0.color[1] + w1 * p1.color[1], 72 | w0 * p0.color[2] + w1 * p1.color[2] 73 | ] 74 | tex[tex_idx] = c 75 | 76 | return [tex[int(u * n)] for u in u_array] 77 | 78 | # These are factory methods rather than class objects because 79 | # the user may modify the colormaps that are used. 80 | @staticmethod 81 | def make_greyscale(): 82 | """Generate a greyscale colormap.""" 83 | return Colormap([ 84 | Colormap.Point(0.0, [0.0, 0.0, 0.0]), 85 | Colormap.Point(1.0, [1.0, 1.0, 1.0]) 86 | ]) 87 | 88 | @staticmethod 89 | def make_rainbow(): 90 | """Generate the rainbow color array.""" 91 | return Colormap([ 92 | Colormap.Point(0.000, [0.0, 0.0, 1.0]), 93 | Colormap.Point(0.125, [0.0, 0.5, 1.0]), 94 | Colormap.Point(0.250, [0.0, 1.0, 1.0]), 95 | Colormap.Point(0.375, [0.0, 1.0, 0.5]), 96 | Colormap.Point(0.500, [0.0, 1.0, 0.0]), 97 | Colormap.Point(0.625, [0.5, 1.0, 0.0]), 98 | Colormap.Point(0.750, [1.0, 1.0, 0.0]), 99 | Colormap.Point(0.875, [1.0, 0.5, 0.0]), 100 | Colormap.Point(1.000, [1.0, 0.0, 0.0]) 101 | ]) 102 | -------------------------------------------------------------------------------- /ml3d/vis/labellut.py: -------------------------------------------------------------------------------- 1 | from colorsys import rgb_to_yiq 2 | 3 | 4 | class LabelLUT: 5 | """The class to manage look-up table for assigning colors to labels.""" 6 | 7 | class Label: 8 | 9 | def __init__(self, name, value, color): 10 | self.name = name 11 | self.value = value 12 | self.color = color 13 | 14 | Colors = [[0., 0., 0.], [0.96078431, 0.58823529, 0.39215686], 15 | [0.96078431, 0.90196078, 0.39215686], 16 | [0.58823529, 0.23529412, 0.11764706], 17 | [0.70588235, 0.11764706, 0.31372549], [1., 0., 0.], 18 | [0.11764706, 0.11764706, 1.], [0.78431373, 0.15686275, 1.], 19 | [0.35294118, 0.11764706, 0.58823529], [1., 0., 1.], 20 | [1., 0.58823529, 1.], [0.29411765, 0., 0.29411765], 21 | [0.29411765, 0., 0.68627451], [0., 0.78431373, 1.], 22 | [0.19607843, 0.47058824, 1.], [0., 0.68627451, 0.], 23 | [0., 0.23529412, 24 | 0.52941176], [0.31372549, 0.94117647, 0.58823529], 25 | [0.58823529, 0.94117647, 1.], [0., 0., 1.], [1.0, 1.0, 0.25], 26 | [0.5, 1.0, 0.25], [0.25, 1.0, 0.25], [0.25, 1.0, 0.5], 27 | [0.25, 1.0, 1.25], [0.25, 0.5, 1.25], [0.25, 0.25, 1.0], 28 | [0.125, 0.125, 0.125], [0.25, 0.25, 0.25], [0.375, 0.375, 0.375], 29 | [0.5, 0.5, 0.5], [0.625, 0.625, 0.625], [0.75, 0.75, 0.75], 30 | [0.875, 0.875, 0.875]] 31 | 32 | def __init__(self, label_to_names=None): 33 | """ 34 | Args: 35 | label_to_names: Initialize the colormap with this mapping from 36 | labels (int) to class names (str). 37 | """ 38 | self._next_color = 0 39 | self.labels = {} 40 | if label_to_names is not None: 41 | for val in sorted(label_to_names.keys()): 42 | self.add_label(label_to_names[val], val) 43 | 44 | def add_label(self, name, value, color=None): 45 | """Adds a label to the table. 46 | 47 | Example: 48 | The following sample creates a LUT with 3 labels:: 49 | 50 | lut = ml3d.vis.LabelLUT() 51 | lut.add_label('one', 1) 52 | lut.add_label('two', 2) 53 | lut.add_label('three', 3, [0,0,1]) # use blue for label 'three' 54 | 55 | Args: 56 | name: The label name as string. 57 | value: The value associated with the label. 58 | color: Optional RGB color. E.g., [0.2, 0.4, 1.0]. 59 | """ 60 | if color is None: 61 | if self._next_color >= len(self.Colors): 62 | color = [0.85, 1.0, 1.0] 63 | else: 64 | color = self.Colors[self._next_color] 65 | self._next_color += 1 66 | self.labels[value] = self.Label(name, value, color) 67 | 68 | @classmethod 69 | def get_colors(self, name='default', mode=None): 70 | """Return full list of colors in the lookup table. 71 | 72 | Args: 73 | name (str): Name of lookup table colormap. Only 'default' is 74 | supported. 75 | mode (str): Colormap mode. May be None (return as is), 'lightbg" to 76 | move the dark colors earlier in the list or 'darkbg' to move 77 | them later in the list. This will provide better visual 78 | discrimination for the earlier classes. 79 | 80 | Returns: 81 | List of colors (R, G, B) in the LUT. 82 | """ 83 | if mode is None: 84 | return self.Colors 85 | dark_colors = list( 86 | filter(lambda col: rgb_to_yiq(*col)[0] < 0.5, self.Colors)) 87 | light_colors = list( 88 | filter(lambda col: rgb_to_yiq(*col)[0] >= 0.5, self.Colors)) 89 | if mode == 'lightbg': 90 | return dark_colors + light_colors 91 | if mode == 'darkbg': 92 | return light_colors + dark_colors 93 | -------------------------------------------------------------------------------- /requirements-openvino.txt: -------------------------------------------------------------------------------- 1 | openvino-dev==2021.4.2 2 | -------------------------------------------------------------------------------- /requirements-tensorflow.txt: -------------------------------------------------------------------------------- 1 | tensorflow~=2.16.2 2 | -------------------------------------------------------------------------------- /requirements-torch-cuda.txt: -------------------------------------------------------------------------------- 1 | -f https://download.pytorch.org/whl/torch/ 2 | torch==2.2.2+cu121 3 | -f https://download.pytorch.org/whl/torchvision/ 4 | torchvision==0.17.2+cu121 5 | tensorboard 6 | -------------------------------------------------------------------------------- /requirements-torch-cxx11-abi.txt: -------------------------------------------------------------------------------- 1 | --extra-index-url https://download.pytorch.org/whl/ 2 | torch==2.2.2+cpu.cxx11.abi 3 | tensorboard 4 | -------------------------------------------------------------------------------- /requirements-torch.txt: -------------------------------------------------------------------------------- 1 | --extra-index-url https://download.pytorch.org/whl/cpu/ 2 | torch==2.2.2+cpu ; sys_platform != 'darwin' 3 | torchvision==0.17.2+cpu ; sys_platform != 'darwin' 4 | torch==2.2.2 ; sys_platform == 'darwin' 5 | torchvision==0.17.2 ; sys_platform == 'darwin' 6 | tensorboard 7 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | addict 2 | pillow>=9.3.0 3 | matplotlib>=3 4 | numpy>1.18 5 | pandas>=1.0 6 | pyyaml>=5.4.1 7 | scikit-learn>=0.21 8 | tqdm 9 | pyquaternion 10 | -------------------------------------------------------------------------------- /scripts/README.md: -------------------------------------------------------------------------------- 1 | # Scripts 2 | 3 | ## `run_pipeline.py` 4 | 5 | This script creates and trains a pipeline (SemanticSegmentation or ObjectDetection). 6 | To define the dataset you can pass the path to the dataset or the path to a 7 | config file as shown below. 8 | 9 | ```shell 10 | # Initialize a dataset using its path 11 | python scripts/run_pipeline.py {tf|torch} -p PIPELINE_NAME -m MODEL_NAME \ 12 | -d DATASET_NAME --dataset_path DATASET_PATH [optional arguments] 13 | 14 | # Initialize a dataset using its config file 15 | python scripts/run_pipeline.py {tf|torch} -p PIPELINE_NAME -m MODEL_NAME \ 16 | -d DATASET_NAME --cfg_dataset DATASET_CONFIG_FILE [optional arguments] 17 | 18 | ``` 19 | Alternatively, you can run the script using one single config file, which 20 | contains configs for dataset, model, and pipeline. 21 | ```shell 22 | python scripts/run_pipeline.py {tf|torch} -c CONFIG_FILE [optional arguments] 23 | ``` 24 | For further help, run `python scripts/run_pipeline.py --help`. 25 | ### Examples 26 | 27 | ```shell 28 | # Training on RandLANet and SemanticKITTI with torch. 29 | python scripts/run_pipeline.py torch -c ml3d/configs/randlanet_semantickitti.yml --dataset.dataset_path --pipeline SemanticSegmentation --dataset.use_cache True 30 | 31 | # Training on PointPillars and KITTI with torch. 32 | python scripts/run_pipeline.py torch -c ml3d/configs/pointpillars_kitti.yml --split test --dataset.dataset_path --pipeline ObjectDetection --dataset.use_cache True 33 | 34 | # Use a config file to train this model with tensorflow 35 | python scripts/run_pipeline.py tf -c ml3d/configs/kpconv_semantickitti.yml \ 36 | --dataset_path ../--pipeline.batch_size 2 37 | 38 | ``` 39 | 40 | Arguments can be 41 | - `-p, --pipeline`: pipeline name, SemanticSegmentation or ObjectDetection 42 | - `-m, --model`: model name (RnadLANet, KPConv) 43 | - `-d, --dataset`: dataset name (SemanticKITTI, Toronto3D, S3DIS, ParisLille3D, Semantic3D) 44 | - `-c, --c`: config file path (example config files are in in `ml3d/configs/`) 45 | - `--cfg_model`: path to the model's config file 46 | - `--cfg_pipeline`: path to the pipeline's config file 47 | - `--cfg_dataset`: path to the dataset's config file 48 | - `--cfg_model`: path to the model's config file 49 | - `--dataset_path`: path to the dataset 50 | - `--device`: `cpu` or `gpu` 51 | 52 | You can also add arbitrary arguments in the command line and the arguments will 53 | save in a dictionary and merge with dataset/model/pipeline's existing cfg. 54 | For example, `--foo abc` will add `{"foo": "abc"}`to the cfg dict. 55 | 56 | -------------------------------------------------------------------------------- /scripts/collect_bboxes.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import argparse 3 | import pickle 4 | import numpy as np 5 | import multiprocessing 6 | 7 | from tqdm import tqdm 8 | from os.path import join 9 | from open3d.ml.datasets import utils 10 | from open3d.ml import datasets 11 | 12 | 13 | def parse_args(): 14 | parser = argparse.ArgumentParser( 15 | description='Collect bounding boxes for augmentation.') 16 | parser.add_argument('--dataset_path', 17 | help='path to Dataset root', 18 | required=True) 19 | parser.add_argument( 20 | '--out_path', 21 | help='Output path to store pickle (default to dataet_path)', 22 | default=None, 23 | required=False) 24 | parser.add_argument('--dataset_type', 25 | help='Name of dataset class', 26 | default="KITTI", 27 | required=False) 28 | parser.add_argument('--num_cpus', 29 | help='Number of threads to use.', 30 | type=int, 31 | default=multiprocessing.cpu_count(), 32 | required=False) 33 | parser.add_argument( 34 | '--max_pc', 35 | help= 36 | 'Boxes from random N pointclouds will be saved. Default None(save from whole dataset).', 37 | type=int, 38 | default=None, 39 | required=False) 40 | 41 | args = parser.parse_args() 42 | 43 | dict_args = vars(args) 44 | for k in dict_args: 45 | v = dict_args[k] 46 | print("{}: {}".format(k, v) if v is not None else "{} not given". 47 | format(k)) 48 | 49 | return args 50 | 51 | 52 | def process_boxes(i): 53 | data = train.get_data(i) 54 | bbox = data['bounding_boxes'] 55 | flat_bbox = [box.to_xyzwhlr() for box in bbox] 56 | indices = utils.operations.points_in_box(data['point'], flat_bbox) 57 | bboxes = [] 58 | for i, box in enumerate(bbox): 59 | pts = data['point'][indices[:, i]] 60 | box.points_inside_box = pts 61 | bboxes.append(box) 62 | return bboxes 63 | 64 | 65 | if __name__ == '__main__': 66 | """Collect bboxes 67 | 68 | This script constructs a bbox dictionary for later data augmentation. 69 | 70 | Args: 71 | dataset_path (str): Directory to load dataset data. 72 | out_path (str): Directory to save pickle file (infos). 73 | dataset_type (str): Name of dataset object under `ml3d/datasets` to use to 74 | load the test data split from the dataset folder. Uses 75 | reflection to dynamically import this dataset object by name. 76 | Default: KITTI 77 | num_cpus (int): Number of threads to use. 78 | Default: All CPU cores. 79 | 80 | Example usage: 81 | 82 | python scripts/collect_bboxes.py --dataset_path /path/to/data --dataset_type MyCustomDataset 83 | """ 84 | 85 | logging.basicConfig( 86 | level=logging.INFO, 87 | format='%(levelname)s - %(asctime)s - %(module)s - %(message)s', 88 | ) 89 | 90 | args = parse_args() 91 | out_path = args.out_path 92 | if out_path is None: 93 | out_path = args.dataset_path 94 | classname = getattr(datasets, args.dataset_type) 95 | dataset = classname(args.dataset_path) 96 | train = dataset.get_split('train') 97 | max_pc = len(train) if args.max_pc is None else args.max_pc 98 | 99 | rng = np.random.default_rng() 100 | query_pc = range(len(train)) if max_pc >= len(train) else rng.choice( 101 | range(len(train)), max_pc, replace=False) 102 | 103 | print(f"Found {len(train)} traning samples, Using {max_pc}") 104 | print( 105 | f"Using {args.num_cpus} number of cpus, This may take a few minutes...") 106 | with multiprocessing.Pool(args.num_cpus) as p: 107 | bboxes = list(tqdm(p.imap(process_boxes, query_pc), 108 | total=len(query_pc))) 109 | bboxes = [e for l in bboxes for e in l] 110 | file = open(join(out_path, 'bboxes.pkl'), 'wb') 111 | pickle.dump(bboxes, file) 112 | print(f"Saved {len(bboxes)} boxes.") 113 | -------------------------------------------------------------------------------- /scripts/demo_api_train.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from open3d.ml.datasets import (SemanticKITTI, ParisLille3D, Semantic3D, S3DIS, 3 | Toronto3D) 4 | from open3d.ml.torch.pipelines import SemanticSegmentation 5 | from open3d.ml.torch.models import RandLANet 6 | from open3d.ml.utils import Config, get_module 7 | 8 | import argparse 9 | 10 | 11 | def parse_args(): 12 | parser = argparse.ArgumentParser( 13 | description='Demo for training and inference') 14 | parser.add_argument('--path_semantickitti', 15 | help='path to semantiSemanticKITTI', 16 | required=True) 17 | parser.add_argument('--path_ckpt_randlanet', 18 | help='path to RandLANet checkpoint') 19 | 20 | args, _ = parser.parse_known_args() 21 | 22 | dict_args = vars(args) 23 | for k in dict_args: 24 | v = dict_args[k] 25 | print("{}: {}".format(k, v) if v is not None else "{} not given". 26 | format(k)) 27 | 28 | return args 29 | 30 | 31 | def demo_train(args): 32 | # Initialize the training by passing parameters 33 | dataset = SemanticKITTI(args.path_semantickitti, use_cache=True) 34 | 35 | model = RandLANet(dim_input=3) 36 | 37 | pipeline = SemanticSegmentation(model=model, dataset=dataset, max_epoch=100) 38 | 39 | pipeline.run_train() 40 | 41 | 42 | def demo_inference(args): 43 | # Inference and test example 44 | from open3d.ml.tf.pipelines import SemanticSegmentation 45 | from open3d.ml.tf.models import RandLANet 46 | 47 | Pipeline = get_module("pipeline", "SemanticSegmentation", "tf") 48 | Model = get_module("model", "RandLANet", "tf") 49 | Dataset = get_module("dataset", "SemanticKITTI") 50 | 51 | RandLANet = Model(ckpt_path=args.path_ckpt_randlanet) 52 | 53 | # Initialize by specifying config file path 54 | SemanticKITTI = Dataset(args.path_semantickitti, use_cache=False) 55 | 56 | pipeline = Pipeline(model=RandLANet, dataset=SemanticKITTI) 57 | 58 | # inference 59 | # get data 60 | train_split = SemanticKITTI.get_split("train") 61 | data = train_split.get_data(0) 62 | # restore weights 63 | 64 | # run inference 65 | results = pipeline.run_inference(data) 66 | print(results) 67 | 68 | # test 69 | pipeline.run_test() 70 | 71 | 72 | if __name__ == '__main__': 73 | 74 | logging.basicConfig( 75 | level=logging.INFO, 76 | format='%(levelname)s - %(asctime)s - %(module)s - %(message)s', 77 | ) 78 | 79 | args = parse_args() 80 | demo_train(args) 81 | demo_inference(args) 82 | -------------------------------------------------------------------------------- /scripts/demo_datasets.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from open3d.ml.datasets import (SemanticKITTI, ParisLille3D, Semantic3D, S3DIS, 3 | Toronto3D) 4 | import argparse 5 | import numpy as np 6 | 7 | 8 | def parse_args(): 9 | parser = argparse.ArgumentParser(description='Read from datasets') 10 | parser.add_argument('--path_semantickitti', 11 | help='path to semantiSemanticKITTI') 12 | parser.add_argument('--path_semantick3d', help='path to Semantic3D') 13 | parser.add_argument('--path_parislille3d', help='path to ParisLille3D') 14 | parser.add_argument('--path_toronto3d', help='path to Toronto3D') 15 | parser.add_argument('--path_s3dis', help='path to S3DIS') 16 | 17 | args, _ = parser.parse_known_args() 18 | 19 | dict_args = vars(args) 20 | for k in dict_args: 21 | v = dict_args[k] 22 | print("{}: {}".format(k, v) if v is not None else "{} not given". 23 | format(k)) 24 | 25 | return args 26 | 27 | 28 | def demo_dataset(args): 29 | # read data from datasets 30 | datasets = [] 31 | if args.path_semantickitti is not None: 32 | datasets.append( 33 | SemanticKITTI(dataset_path=args.path_semantickitti, 34 | use_cache=False)) 35 | if args.path_parislille3d is not None: 36 | datasets.append( 37 | ParisLille3D(dataset_path=args.path_parislille3d, use_cache=False)) 38 | if args.path_toronto3d is not None: 39 | datasets.append( 40 | Toronto3D(dataset_path=args.path_toronto3d, use_cache=False)) 41 | if args.path_semantick3d is not None: 42 | datasets.append( 43 | Semantic3D(dataset_path=args.path_semantick3d, use_cache=False)) 44 | if args.path_s3dis is not None: 45 | datasets.append(S3DIS(dataset_path=args.path_s3dis, use_cache=False)) 46 | 47 | for dataset in datasets: 48 | print(dataset.name) 49 | cat_num = len(dataset.label_to_names) 50 | num_labels = np.zeros([cat_num]) 51 | 52 | split = dataset.get_split('train') 53 | for i in range(len(split)): 54 | data = split.get_data(i) 55 | labels = data['label'] 56 | for l in range(cat_num): 57 | num_labels[l] += (labels == l).sum() 58 | 59 | print(num_labels) 60 | 61 | for dataset in datasets: 62 | print(dataset.label_to_names) 63 | # print names of all pointcould 64 | split = dataset.get_split('test') 65 | for i in range(len(split)): 66 | attr = split.get_attr(i) 67 | print(attr['name']) 68 | 69 | split = dataset.get_split('train') 70 | for i in range(len(split)): 71 | data = split.get_data(i) 72 | print(data['point'].shape) 73 | 74 | 75 | if __name__ == '__main__': 76 | 77 | logging.basicConfig( 78 | level=logging.INFO, 79 | format='%(levelname)s - %(asctime)s - %(module)s - %(message)s', 80 | ) 81 | 82 | args = parse_args() 83 | demo_dataset(args) 84 | -------------------------------------------------------------------------------- /scripts/download_datasets/download_kitti.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "$#" -ne 1 ]; then 4 | echo "Please, provide the base directory to store the dataset." 5 | exit 1 6 | fi 7 | 8 | if ! command -v unzip &> /dev/null 9 | then 10 | echo "Error: unzip could not be found. Please, install it to continue" 11 | exit 12 | fi 13 | 14 | BASE_DIR="$1"/Kitti 15 | 16 | mkdir -p $BASE_DIR 17 | 18 | url_velodyne="https://s3.eu-central-1.amazonaws.com/avg-kitti/data_object_velodyne.zip" 19 | url_calib="https://s3.eu-central-1.amazonaws.com/avg-kitti/data_object_calib.zip" 20 | url_label="https://s3.eu-central-1.amazonaws.com/avg-kitti/data_object_label_2.zip" 21 | 22 | wget -c -N -O $BASE_DIR'/data_object_velodyne.zip' $url_velodyne 23 | wget -c -N -O $BASE_DIR'/data_object_calib.zip' $url_calib 24 | wget -c -N -O $BASE_DIR'/data_object_label_2.zip' $url_label 25 | 26 | cd $BASE_DIR 27 | 28 | unzip data_object_velodyne.zip 29 | unzip data_object_calib.zip 30 | unzip data_object_label_2.zip 31 | 32 | mkdir zip_files 33 | mv data_object_velodyne.zip zip_files 34 | mv data_object_calib.zip zip_files 35 | mv data_object_label_2.zip zip_files 36 | -------------------------------------------------------------------------------- /scripts/download_datasets/download_lyft.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "$#" -ne 1 ]; then 4 | echo "Please, provide the base directory to store the dataset." 5 | exit 1 6 | fi 7 | 8 | BASE_DIR="$1"/Lyft 9 | 10 | mkdir -p $BASE_DIR 11 | 12 | url_train="https://lyft-l5-datasets-public.s3-us-west-2.amazonaws.com/3d-object-detection/train.tar" 13 | url_test="https://lyft-l5-datasets-public.s3-us-west-2.amazonaws.com/3d-object-detection/test.tar" 14 | 15 | wget -c -N -O $BASE_DIR'/train.tar' $url_train 16 | wget -c -N -O $BASE_DIR'/test.tar' $url_test 17 | 18 | cd $BASE_DIR 19 | 20 | tar -xvf train.tar 21 | tar -xvf test.tar 22 | 23 | mkdir tar_files 24 | mv train.tar tar_files 25 | mv test.tar tar_files 26 | 27 | mkdir v1.01-train 28 | mkdir v1.01-test 29 | 30 | mv train_data v1.01-train/data 31 | mv train_images v1.01-train/images 32 | mv train_lidar v1.01-train/lidar 33 | mv train_maps v1.01-train/maps 34 | 35 | mv test_data v1.01-test/data 36 | mv test_images v1.01-test/images 37 | mv test_lidar v1.01-test/lidar 38 | mv test_maps v1.01-test/maps -------------------------------------------------------------------------------- /scripts/download_datasets/download_parislille3d.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "$#" -ne 1 ]; then 4 | echo "Please, provide the base directory to store the dataset." 5 | exit 1 6 | fi 7 | 8 | if ! command -v unzip &> /dev/null 9 | then 10 | echo "Error: unzip could not be found. Please, install it to continue" 11 | exit 12 | fi 13 | 14 | BASE_DIR="$1"/Paris_Lille3D 15 | 16 | mkdir -p $BASE_DIR 17 | 18 | export url_train="https://cloud.mines-paristech.fr/index.php/s/JhIxgyt0ALgRZ1O/download?path=%2F&files=training_10_classes" 19 | export url_test="https://cloud.mines-paristech.fr/index.php/s/JhIxgyt0ALgRZ1O/download?path=%2F&files=test_10_classes" 20 | 21 | wget -c -N -O $BASE_DIR'/training_10_classes.zip' $url_train 22 | wget -c -N -O $BASE_DIR'/test_10_classes.zip' $url_test 23 | 24 | cd $BASE_DIR 25 | 26 | unzip test_10_classes.zip 27 | unzip training_10_classes.zip 28 | 29 | mkdir -p $BASE_DIR/zip_files 30 | mv test_10_classes.zip $BASE_DIR/zip_files 31 | mv training_10_classes.zip $BASE_DIR/zip_files 32 | -------------------------------------------------------------------------------- /scripts/download_datasets/download_semantic3d.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "$#" -ne 1 ]; then 4 | echo "Please, provide the base directory to store the dataset." 5 | exit 1 6 | fi 7 | 8 | if ! command -v 7z &> /dev/null 9 | then 10 | echo "Error: 7z could not be found. Please, install it to continue." 11 | exit 12 | fi 13 | 14 | BASE_DIR="$1"/Semantic3D 15 | 16 | # Training data 17 | wget -c -N http://semantic3d.net/data/point-clouds/training1/bildstein_station1_xyz_intensity_rgb.7z -P $BASE_DIR 18 | wget -c -N http://semantic3d.net/data/point-clouds/training1/bildstein_station3_xyz_intensity_rgb.7z -P $BASE_DIR 19 | wget -c -N http://semantic3d.net/data/point-clouds/training1/bildstein_station5_xyz_intensity_rgb.7z -P $BASE_DIR 20 | wget -c -N http://semantic3d.net/data/point-clouds/training1/domfountain_station1_xyz_intensity_rgb.7z -P $BASE_DIR 21 | wget -c -N http://semantic3d.net/data/point-clouds/training1/domfountain_station2_xyz_intensity_rgb.7z -P $BASE_DIR/ 22 | wget -c -N http://semantic3d.net/data/point-clouds/training1/domfountain_station3_xyz_intensity_rgb.7z -P $BASE_DIR 23 | wget -c -N http://semantic3d.net/data/point-clouds/training1/neugasse_station1_xyz_intensity_rgb.7z -P $BASE_DIR 24 | wget -c -N http://semantic3d.net/data/point-clouds/training1/sg27_station1_intensity_rgb.7z -P $BASE_DIR 25 | wget -c -N http://semantic3d.net/data/point-clouds/training1/sg27_station2_intensity_rgb.7z -P $BASE_DIR 26 | wget -c -N http://semantic3d.net/data/point-clouds/training1/sg27_station4_intensity_rgb.7z -P $BASE_DIR/ 27 | wget -c -N http://semantic3d.net/data/point-clouds/training1/sg27_station5_intensity_rgb.7z -P $BASE_DIR 28 | wget -c -N http://semantic3d.net/data/point-clouds/training1/sg27_station9_intensity_rgb.7z -P $BASE_DIR 29 | wget -c -N http://semantic3d.net/data/point-clouds/training1/sg28_station4_intensity_rgb.7z -P $BASE_DIR 30 | wget -c -N http://semantic3d.net/data/point-clouds/training1/untermaederbrunnen_station1_xyz_intensity_rgb.7z -P $BASE_DIR 31 | wget -c -N http://semantic3d.net/data/point-clouds/training1/untermaederbrunnen_station3_xyz_intensity_rgb.7z -P $BASE_DIR/ 32 | wget -c -N http://semantic3d.net/data/sem8_labels_training.7z -P $BASE_DIR 33 | 34 | 35 | # Test data 36 | wget -c -N http://semantic3d.net/data/point-clouds/testing1/birdfountain_station1_xyz_intensity_rgb.7z -P $BASE_DIR 37 | wget -c -N http://semantic3d.net/data/point-clouds/testing1/castleblatten_station1_intensity_rgb.7z -P $BASE_DIR 38 | wget -c -N http://semantic3d.net/data/point-clouds/testing1/castleblatten_station5_xyz_intensity_rgb.7z -P $BASE_DIR 39 | wget -c -N http://semantic3d.net/data/point-clouds/testing1/marketplacefeldkirch_station1_intensity_rgb.7z -P $BASE_DIR 40 | wget -c -N http://semantic3d.net/data/point-clouds/testing1/marketplacefeldkirch_station4_intensity_rgb.7z -P $BASE_DIR 41 | wget -c -N http://semantic3d.net/data/point-clouds/testing1/marketplacefeldkirch_station7_intensity_rgb.7z -P $BASE_DIR 42 | wget -c -N http://semantic3d.net/data/point-clouds/testing1/sg27_station10_intensity_rgb.7z -P $BASE_DIR 43 | wget -c -N http://semantic3d.net/data/point-clouds/testing1/sg27_station3_intensity_rgb.7z -P $BASE_DIR 44 | wget -c -N http://semantic3d.net/data/point-clouds/testing1/sg27_station6_intensity_rgb.7z -P $BASE_DIR 45 | wget -c -N http://semantic3d.net/data/point-clouds/testing1/sg27_station8_intensity_rgb.7z -P $BASE_DIR 46 | wget -c -N http://semantic3d.net/data/point-clouds/testing1/sg28_station2_intensity_rgb.7z -P $BASE_DIR 47 | wget -c -N http://semantic3d.net/data/point-clouds/testing1/sg28_station5_xyz_intensity_rgb.7z -P $BASE_DIR 48 | wget -c -N http://semantic3d.net/data/point-clouds/testing1/stgallencathedral_station1_intensity_rgb.7z -P $BASE_DIR 49 | wget -c -N http://semantic3d.net/data/point-clouds/testing1/stgallencathedral_station3_intensity_rgb.7z -P $BASE_DIR 50 | wget -c -N http://semantic3d.net/data/point-clouds/testing1/stgallencathedral_station6_intensity_rgb.7z -P $BASE_DIR 51 | 52 | for entry in "$BASE_DIR"/*.7z 53 | do 54 | 7z x "$entry" -o$(dirname "$entry") -y 55 | done 56 | 57 | mv $BASE_DIR/station1_xyz_intensity_rgb.txt $BASE_DIR/neugasse_station1_xyz_intensity_rgb.txt 58 | 59 | # cleanup 60 | mkdir -p $BASE_DIR/zip_files 61 | mv $BASE_DIR/*.7z $BASE_DIR/zip_files 62 | 63 | -------------------------------------------------------------------------------- /scripts/download_datasets/download_semantickitti.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "$#" -ne 1 ]; then 4 | echo "Please, provide the base directory to store the dataset." 5 | exit 1 6 | fi 7 | 8 | if ! command -v unzip &> /dev/null 9 | then 10 | echo "Error: unzip could not be found. Please, install it to continue" 11 | exit 12 | fi 13 | 14 | BASE_DIR="$1"/SemanticKitti 15 | 16 | mkdir -p $BASE_DIR 17 | 18 | url_data="https://s3.eu-central-1.amazonaws.com/avg-kitti/data_odometry_velodyne.zip" 19 | url_label="http://semantic-kitti.org/assets/data_odometry_labels.zip" 20 | 21 | wget -c -N -O $BASE_DIR'/data_odometry_velodyne.zip' $url_data 22 | wget -c -N -O $BASE_DIR'/data_odometry_labels.zip' $url_label 23 | 24 | cd $BASE_DIR 25 | 26 | unzip data_odometry_velodyne.zip 27 | unzip data_odometry_labels.zip 28 | 29 | mkdir zip_files 30 | mv data_odometry_labels.zip zip_files 31 | mv data_odometry_velodyne.zip zip_files 32 | -------------------------------------------------------------------------------- /scripts/download_datasets/download_shapenet.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [[ "$#" -ne 1 ]]; then 4 | echo "Please, provide the base directory to store the dataset." 5 | exit 1 6 | fi 7 | 8 | if ! command -v unzip &> /dev/null 9 | then 10 | echo "Error: unzip could not be found. Please, install it to continue" 11 | exit 12 | fi 13 | 14 | BASE_DIR="$1"/ShapeNet 15 | 16 | mkdir -p ${BASE_DIR} 17 | 18 | export url="https://shapenet.cs.stanford.edu/ericyi/shapenetcore_partanno_segmentation_benchmark_v0.zip" 19 | 20 | wget -c -N -O ${BASE_DIR}'/shapenetcore_partanno_segmentation_benchmark_v0.zip' ${url} --no-check-certificate 21 | 22 | cd ${BASE_DIR} 23 | 24 | unzip shapenetcore_partanno_segmentation_benchmark_v0.zip 25 | 26 | mkdir -p ${BASE_DIR}/zip_files 27 | mv shapenetcore_partanno_segmentation_benchmark_v0.zip ${BASE_DIR}/zip_files 28 | -------------------------------------------------------------------------------- /scripts/download_datasets/download_sunrgbd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [[ "$#" -ne 1 ]]; then 4 | echo "Please, provide the base directory to store the dataset." 5 | exit 1 6 | fi 7 | 8 | if ! command -v unzip &> /dev/null 9 | then 10 | echo "Error: unzip could not be found. Please, install it to continue" 11 | exit 12 | fi 13 | 14 | BASE_DIR="$1"/sunrgbd 15 | 16 | mkdir -p ${BASE_DIR} 17 | 18 | url_sunrgbd="http://rgbd.cs.princeton.edu/data/SUNRGBD.zip" 19 | url_2dbb="http://rgbd.cs.princeton.edu/data/SUNRGBDMeta2DBB_v2.mat" 20 | url_3dbb="http://rgbd.cs.princeton.edu/data/SUNRGBDMeta3DBB_v2.mat" 21 | url_toolbox="http://rgbd.cs.princeton.edu/data/SUNRGBDtoolbox.zip" 22 | 23 | wget -c -N -O ${BASE_DIR}'/SUNRGBD.zip' ${url_sunrgbd} --no-check-certificate 24 | wget -c -N -O ${BASE_DIR}'/SUNRGBDMeta2DBB_v2.mat' ${url_2dbb} --no-check-certificate 25 | wget -c -N -O ${BASE_DIR}'/SUNRGBDMeta3DBB_v2.mat' ${url_3dbb} --no-check-certificate 26 | wget -c -N -O ${BASE_DIR}'/SUNRGBDtoolbox.zip' ${url_toolbox} --no-check-certificate 27 | 28 | cd ${BASE_DIR} 29 | 30 | unzip SUNRGBD.zip 31 | unzip SUNRGBDtoolbox.zip 32 | 33 | mkdir -p ${BASE_DIR}/zip_files 34 | mv SUNRGBDtoolbox.zip SUNRGBD.zip ${BASE_DIR}/zip_files 35 | -------------------------------------------------------------------------------- /scripts/download_datasets/download_toronto3d.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "$#" -ne 1 ]; then 4 | echo "Please, provide the base directory to store the dataset." 5 | exit 1 6 | fi 7 | 8 | if ! command -v unzip &> /dev/null 9 | then 10 | echo "Error: unzip could not be found. Please, install it to continue." 11 | exit 12 | fi 13 | 14 | BASE_DIR="$1"/Toronto3D 15 | 16 | export url="https://xx9lca.sn.files.1drv.com/y4mUm9-LiY3vULTW79zlB3xp0wzCPASzteId4wdUZYpzWiw6Jp4IFoIs6ADjLREEk1-IYH8KRGdwFZJrPlIebwytHBYVIidsCwkHhW39aQkh3Vh0OWWMAcLVxYwMTjXwDxHl-CDVDau420OG4iMiTzlsK_RTC_ypo3z-Adf-h0gp2O8j5bOq-2TZd9FD1jPLrkf3759rB-BWDGFskF3AsiB3g" 17 | 18 | mkdir -p $BASE_DIR 19 | 20 | wget -c -N -O $BASE_DIR'/Toronto_3D.zip' $url 21 | 22 | cd $BASE_DIR 23 | 24 | unzip -j Toronto_3D.zip 25 | 26 | # cleanup 27 | mkdir -p $BASE_DIR/zip_files 28 | mv Toronto_3D.zip $BASE_DIR/zip_files 29 | 30 | -------------------------------------------------------------------------------- /scripts/train_scripts/kpconv_kitti.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #SBATCH -p gpu 3 | #SBATCH -c 4 4 | #SBATCH --gres=gpu:1 5 | 6 | if [ "$#" -ne 2 ]; then 7 | echo "Please, provide the training framework: torch/tf and dataset path" 8 | exit 1 9 | fi 10 | 11 | cd ../.. 12 | python scripts/run_pipeline.py $1 -c ml3d/configs/kpconv_semantickitti.yml \ 13 | --dataset_path $2 14 | -------------------------------------------------------------------------------- /scripts/train_scripts/kpconv_paris.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #SBATCH -p gpu 3 | #SBATCH -c 4 4 | #SBATCH --gres=gpu:1 5 | 6 | if [ "$#" -ne 2 ]; then 7 | echo "Please, provide the training framework: torch/tf and dataset path" 8 | exit 1 9 | fi 10 | 11 | cd ../.. 12 | python scripts/run_pipeline.py $1 -c ml3d/configs/kpconv_parislille3d.yml \ 13 | --dataset_path $2 14 | -------------------------------------------------------------------------------- /scripts/train_scripts/kpconv_s3dis.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #SBATCH -p gpu 3 | #SBATCH -c 4 4 | #SBATCH --gres=gpu:1 5 | 6 | if [ "$#" -ne 2 ]; then 7 | echo "Please, provide the training framework: torch/tf and dataset path" 8 | exit 1 9 | fi 10 | 11 | cd ../.. 12 | python scripts/run_pipeline.py $1 -c ml3d/configs/kpconv_s3dis.yml \ 13 | --dataset_path $2 14 | -------------------------------------------------------------------------------- /scripts/train_scripts/kpconv_semantic3d.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #SBATCH -p gpu 3 | #SBATCH -c 4 4 | #SBATCH --gres=gpu:1 5 | 6 | if [ "$#" -ne 2 ]; then 7 | echo "Please, provide the training framework: torch/tf and dataset path" 8 | exit 1 9 | fi 10 | 11 | cd ../.. 12 | python scripts/run_pipeline.py $1 -c ml3d/configs/kpconv_semantic3d.yml \ 13 | --dataset_path $2 14 | -------------------------------------------------------------------------------- /scripts/train_scripts/kpconv_toronto.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #SBATCH -p gpu 3 | #SBATCH -c 4 4 | #SBATCH --gres=gpu:1 5 | 6 | if [ "$#" -ne 2 ]; then 7 | echo "Please, provide the training framework: torch/tf and dataset path" 8 | exit 1 9 | fi 10 | 11 | cd ../.. 12 | python scripts/run_pipeline.py $1 -c ml3d/configs/kpconv_toronto3d.yml \ 13 | --dataset_path $2 14 | -------------------------------------------------------------------------------- /scripts/train_scripts/pointpillars_kitti.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #SBATCH -p gpu 3 | #SBATCH -c 4 4 | #SBATCH --gres=gpu:1 5 | 6 | if [ "$#" -ne 2 ]; then 7 | echo "Please, provide the training framework: torch/tf and dataset path" 8 | exit 1 9 | fi 10 | 11 | cd ../.. 12 | python scripts/run_pipeline.py $1 -c ml3d/configs/pointpillars_kitti.yml \ 13 | --dataset_path $2 --pipeline ObjectDetection 14 | -------------------------------------------------------------------------------- /scripts/train_scripts/pointpillars_waymo.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Launch this script on each training node, for e.g. with SLURM. This example 3 | # launches 8 workers total: SLURM launches this on 2 nodes and run_pipline.py 4 | # launches 4 workers on each node. 5 | #SBATCH --job-name=objdet_2x4 6 | #SBATCH --gres=gpu:4 7 | #SBATCH --cpus-per-task=48 # scale by n_gpus_per_node 8 | #SBATCH --nodes=2 9 | #SBATCH --ntasks-per-node=1 10 | #SBATCH --mem=384G # scale by n_gpus_per_node 11 | #SBATCH --output="./slurm-%x-%j-%N.log" 12 | 13 | if [ "$#" -ne 2 ]; then 14 | echo "Please, provide the training framework: torch/tf and dataset path." 15 | exit 1 16 | fi 17 | 18 | # Use launch node as main, if not set 19 | export PRIMARY_ADDR=${PRIMARY_ADDR:-$SLURMD_NODENAME} 20 | export PRIMARY_PORT=${PRIMARY_PORT:-29500} 21 | # Use all available GPUs, if not set. Must be the same for ALL nodes. 22 | export DEVICE_IDS=${DEVICE_IDS:-$(nvidia-smi --list-gpus | cut -f2 -d' ' | tr ':\n' ' ')} 23 | export NODE_RANK=${NODE_RANK:-SLURM_NODEID} # Pass name of env var 24 | 25 | echo Started at: $(date) 26 | pushd ../.. 27 | # Launch on each node 28 | srun -l python scripts/run_pipeline.py "$1" -c ml3d/configs/pointpillars_waymo.yml \ 29 | --dataset_path "$2" --pipeline ObjectDetection \ 30 | --pipeline.num_workers 0 --pipeline.pin_memory False \ 31 | --pipeline.batch_size 4 --device_ids $DEVICE_IDS \ 32 | --backend nccl \ 33 | --nodes $SLURM_JOB_NUM_NODES \ 34 | --node_rank "$NODE_RANK" \ 35 | --host "$PRIMARY_ADDR" --port "$PRIMARY_PORT" 36 | 37 | echo Completed at: $(date) 38 | popd 39 | -------------------------------------------------------------------------------- /scripts/train_scripts/randlanet_kitti.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #SBATCH -p gpu 3 | #SBATCH -c 4 4 | #SBATCH --gres=gpu:1 5 | 6 | if [ "$#" -ne 2 ]; then 7 | echo "Please, provide the training framework: torch/tf and dataset path" 8 | exit 1 9 | fi 10 | 11 | cd ../.. 12 | python scripts/run_pipeline.py $1 -c ml3d/configs/randlanet_semantickitti.yml \ 13 | --dataset_path $2 14 | -------------------------------------------------------------------------------- /scripts/train_scripts/randlanet_paris.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #SBATCH -p gpu 3 | #SBATCH -c 4 4 | #SBATCH --gres=gpu:1 5 | 6 | if [ "$#" -ne 2 ]; then 7 | echo "Please, provide the training framework: torch/tf and dataset path" 8 | exit 1 9 | fi 10 | 11 | cd ../.. 12 | python scripts/run_pipeline.py $1 -c ml3d/configs/randlanet_parislille3d.yml \ 13 | --dataset_path $2 14 | -------------------------------------------------------------------------------- /scripts/train_scripts/randlanet_s3dis.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #SBATCH -p gpu 3 | #SBATCH -c 4 4 | #SBATCH --gres=gpu:1 5 | 6 | if [ "$#" -ne 2 ]; then 7 | echo "Please, provide the training framework: torch/tf and dataset path" 8 | exit 1 9 | fi 10 | 11 | cd ../.. 12 | python scripts/run_pipeline.py $1 -c ml3d/configs/randlanet_s3dis.yml \ 13 | --dataset_path $2 14 | -------------------------------------------------------------------------------- /scripts/train_scripts/randlanet_semantic3d.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #SBATCH -p gpu 3 | #SBATCH -c 4 4 | #SBATCH --gres=gpu:1 5 | 6 | 7 | if [ "$#" -ne 2 ]; then 8 | echo "Please, provide the training framework: torch/tf and dataset path" 9 | exit 1 10 | fi 11 | 12 | cd ../.. 13 | python scripts/run_pipeline.py $1 -c ml3d/configs/randlanet_semantic3d.yml \ 14 | --dataset_path $2 15 | -------------------------------------------------------------------------------- /scripts/train_scripts/randlanet_toronto.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #SBATCH -p gpu 3 | #SBATCH -c 4 4 | #SBATCH --gres=gpu:1 5 | 6 | if [ "$#" -ne 2 ]; then 7 | echo "Please, provide the training framework: torch/tf and dataset path" 8 | exit 1 9 | fi 10 | 11 | cd ../.. 12 | python scripts/run_pipeline.py $1 -c ml3d/configs/randlanet_toronto3d.yml \ 13 | --dataset_path $2 14 | -------------------------------------------------------------------------------- /set_open3d_ml_root.sh: -------------------------------------------------------------------------------- 1 | # Sets the env var OPEN3D_ML_ROOT to the directory of this file. 2 | # The open3d package will use this var to integrate ml3d into a common namespace. 3 | export OPEN3D_ML_ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 4 | if [[ $0 == $BASH_SOURCE ]]; then 5 | echo "source this script to set the OPEN3D_ML_ROOT env var." 6 | else 7 | echo "OPEN3D_ML_ROOT is now $OPEN3D_ML_ROOT" 8 | fi 9 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import glob 2 | import os 3 | import shutil 4 | from os import path 5 | from setuptools import find_packages, setup 6 | from typing import List 7 | import torch 8 | from torch.utils.cpp_extension import CUDA_HOME, CppExtension, CUDAExtension 9 | 10 | torch_ver = [int(x) for x in torch.__version__.split(".")[:2]] 11 | assert torch_ver >= [1, 4], "Requires PyTorch >= 1.4" 12 | 13 | if __name__ == '__main__': 14 | 15 | setup( 16 | name='ml3d', 17 | description='An extension of Open3D for 3D machine learning tasks', 18 | author='yi', 19 | packages=find_packages(exclude=('configs', 'tools', 'demo')), 20 | ) 21 | -------------------------------------------------------------------------------- /tests/test_integration.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import os 3 | import open3d as o3d 4 | 5 | if 'PATH_TO_OPEN3D_ML' in os.environ.keys(): 6 | base = os.environ['PATH_TO_OPEN3D_ML'] 7 | else: 8 | base = '.' 9 | # base = '../Open3D-ML' 10 | 11 | 12 | @pytest.mark.skipif("not o3d._build_config['BUILD_PYTORCH_OPS']") 13 | def test_integration_torch(): 14 | import torch 15 | import open3d.ml.torch as ml3d 16 | from open3d.ml.datasets import S3DIS 17 | from open3d.ml.utils import Config, get_module 18 | from open3d.ml.torch.models import RandLANet, KPFCNN 19 | from open3d.ml.torch.pipelines import SemanticSegmentation 20 | print(dir(ml3d)) 21 | 22 | config = base + '/ml3d/configs/randlanet_toronto3d.yml' 23 | cfg = Config.load_from_file(config) 24 | 25 | model = ml3d.models.RandLANet(**cfg.model) 26 | 27 | print(model) 28 | 29 | 30 | @pytest.mark.skipif("not o3d._build_config['BUILD_TENSORFLOW_OPS']") 31 | def test_integration_tf(): 32 | import tensorflow as tf 33 | import open3d.ml.tf as ml3d 34 | from open3d.ml.datasets import S3DIS 35 | from open3d.ml.utils import Config, get_module 36 | from open3d.ml.tf.models import RandLANet, KPFCNN 37 | from open3d.ml.tf.pipelines import SemanticSegmentation 38 | print(dir(ml3d)) 39 | 40 | config = base + '/ml3d/configs/randlanet_toronto3d.yml' 41 | cfg = Config.load_from_file(config) 42 | 43 | model = ml3d.models.RandLANet(**cfg.model) 44 | 45 | print(model) 46 | 47 | 48 | def test_integration_openvino(): 49 | try: 50 | from openvino.inference_engine import IECore 51 | except ImportError: 52 | return 53 | 54 | if o3d._build_config['BUILD_TORCH_OPS']: 55 | from open3d.ml.torch.models import OpenVINOModel 56 | if o3d._build_config['BUILD_TENSORFLOW_OPS']: 57 | from open3d.ml.tf.models import OpenVINOModel 58 | -------------------------------------------------------------------------------- /version.txt: -------------------------------------------------------------------------------- 1 | OPEN3D_VERSION_MAJOR 0 2 | OPEN3D_VERSION_MINOR 12 3 | OPEN3D_VERSION_PATCH 0 4 | --------------------------------------------------------------------------------