├── .gitignore ├── LICENSE ├── README.md ├── README_zh.md ├── data.py ├── image ├── DGCNN.jpg ├── partseg_colors.png ├── partseg_visu.png ├── semseg_s3dis_colors.png ├── semseg_s3dis_visu.png ├── semseg_scannet_colors.png └── semseg_scannet_visu.png ├── main_cls.py ├── main_partseg.py ├── main_semseg_s3dis.py ├── main_semseg_scannet.py ├── model.py ├── prepare_data ├── collect_indoor3d_data.py ├── data_prep_util.py ├── gen_indoor3d_h5.py ├── indoor3d_util.py ├── meta │ ├── all_data_label.txt │ ├── anno_paths.txt │ ├── class_names.txt │ ├── partseg_colors.txt │ └── semseg_colors.txt ├── scannetv2_seg_dataset_rgb21c_pointid.py ├── scannetv2_test.txt ├── scannetv2_train.txt ├── scannetv2_val.txt └── util.py ├── pretrained ├── model.cls.1024.t7 ├── model.cls.2048.t7 ├── model.partseg.airplane.t7 ├── model.partseg.car.t7 ├── model.partseg.t7 ├── semseg_s3dis │ ├── model_1.t7 │ ├── model_2.t7 │ ├── model_3.t7 │ ├── model_4.t7 │ ├── model_5.t7 │ └── model_6.t7 └── semseg_scannet │ └── models │ └── model_200.pth ├── sync_bn ├── __init__.py ├── batchnorm.py ├── comm.py ├── replicate.py └── unittest.py └── util.py /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | */.DS_Store 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 An Tao 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DGCNN.pytorch 2 | [[中文版]](README_zh.md) 3 | 4 | This repo is a PyTorch implementation for **Dynamic Graph CNN for Learning on Point Clouds (DGCNN)**(https://arxiv.org/pdf/1801.07829). Our code skeleton is borrowed from [WangYueFt/dgcnn](https://github.com/WangYueFt/dgcnn/tree/master/pytorch). 5 | 6 | **Updates:** 7 | 8 | - [2022/10/22] Add semantic segmentation on the ScanNet dataset by [Ziyi Wu](https://wuziyi616.github.io/) (dazitu616@gmail.com) and [Yingqi Wang](https://github.com/HilbertWang2002) (yingqi-w19@mails.tsinghua.edu.cn). 9 | - [2021/07/20] Add visualization code for part segmentation and semantic segmentation by [Pengliang Ji](https://github.com/Ji-Pengliang) (jpl1723@buaa.edu.cn). 10 | 11 | The network structure (Fig. 3) for classification in DGCNN paper is not consistent with the corresponding description in section 4.1 of the paper. The author of DGCNN adopts the setting of classification network in section 4.1, not Fig. 3. We fixed this mistake in Fig. 3 using PS and present the revised figure below. 12 | 13 |   14 |

15 | 16 |

17 | 18 |   19 | 20 | **Tip:** The result of point cloud experiment usually faces greater randomness than 2D image. We suggest you run your experiment more than one time and select the best result. 21 | 22 |   23 | ## Requirements 24 | - Python >= 3.7 25 | - PyTorch >= 1.2 26 | - CUDA >= 10.0 27 | - Package: glob, h5py, sklearn, plyfile, torch_scatter 28 | 29 |   30 | ## Contents 31 | - [Point Cloud Classification](#point-cloud-classification) 32 | - [Point Cloud Part Segmentation](#point-cloud-part-segmentation) 33 | - [Point Cloud Semantic Segmentation on the S3DIS Dataset](#point-cloud-semantic-segmentation-on-the-s3dis-dataset) 34 | - [Point Cloud Semantic Segmentation on the ScanNet Dataset](#point-cloud-semantic-segmentation-on-the-scannet-dataset) 35 | 36 | **Note:** All following commands default use all GPU cards. To specify the cards to use, add `CUDA_VISIBLE_DEVICES=0,1,2,3` before each command, where the user uses 4 GPU cards with card index `0,1,2,3`. You can change the card number and indexes depending on your own needs. 37 | 38 |   39 | ## Point Cloud Classification 40 | ### Run the training script: 41 | 42 | - 1024 points 43 | 44 | ``` 45 | python main_cls.py --exp_name=cls_1024 --num_points=1024 --k=20 46 | ``` 47 | 48 | - 2048 points 49 | 50 | ``` 51 | python main_cls.py --exp_name=cls_2048 --num_points=2048 --k=40 52 | ``` 53 | 54 | ### Run the evaluation script after training finished: 55 | 56 | - 1024 points 57 | 58 | ``` 59 | python main_cls.py --exp_name=cls_1024_eval --num_points=1024 --k=20 --eval=True --model_path=outputs/cls_1024/models/model.t7 60 | ``` 61 | 62 | - 2048 points 63 | 64 | ``` 65 | python main_cls.py --exp_name=cls_2048_eval --num_points=2048 --k=40 --eval=True --model_path=outputs/cls_2048/models/model.t7 66 | ``` 67 | 68 | ### Run the evaluation script with pretrained models: 69 | 70 | - 1024 points 71 | 72 | ``` 73 | python main_cls.py --exp_name=cls_1024_eval --num_points=1024 --k=20 --eval=True --model_path=pretrained/model.cls.1024.t7 74 | ``` 75 | 76 | - 2048 points 77 | 78 | ``` 79 | python main_cls.py --exp_name=cls_2048_eval --num_points=2048 --k=40 --eval=True --model_path=pretrained/model.cls.2048.t7 80 | ``` 81 | 82 | ### Performance: 83 | ModelNet40 dataset 84 | 85 | | | Mean Class Acc | Overall Acc | 86 | | :---: | :---: | :---: | 87 | | Paper (1024 points) | 90.2 | 92.9 | 88 | | This repo (1024 points) | **90.9** | **93.3** | 89 | | Paper (2048 points) | 90.7 | 93.5 | 90 | | This repo (2048 points) | **91.2** | **93.6** | 91 | 92 |   93 | ## Point Cloud Part Segmentation 94 | **Note:** The training modes **'full dataset'** and **'with class choice'** are different. 95 | 96 | - In **'full dataset'**, the model is trained and evaluated in all 16 classes and outputs mIoU 85.2% in this repo. The prediction of points in each shape can be any part of all 16 classes. 97 | - In **'with class choice'**, the model is trained and evaluated in one class, for example airplane, and outputs mIoU 84.5% for airplane in this repo. The prediction of points in each shape can only be one of the parts in this chosen class. 98 | 99 | ### Run the training script: 100 | 101 | - Full dataset 102 | 103 | ``` 104 | python main_partseg.py --exp_name=partseg 105 | ``` 106 | 107 | - With class choice, for example airplane 108 | 109 | ``` 110 | python main_partseg.py --exp_name=partseg_airplane --class_choice=airplane 111 | ``` 112 | 113 | ### Run the evaluation script after training finished: 114 | 115 | - Full dataset 116 | 117 | ``` 118 | python main_partseg.py --exp_name=partseg_eval --eval=True --model_path=outputs/partseg/models/model.t7 119 | ``` 120 | 121 | - With class choice, for example airplane 122 | 123 | ``` 124 | python main_partseg.py --exp_name=partseg_airplane_eval --class_choice=airplane --eval=True --model_path=outputs/partseg_airplane/models/model.t7 125 | ``` 126 | 127 | ### Run the evaluation script with pretrained models: 128 | 129 | - Full dataset 130 | 131 | ``` 132 | python main_partseg.py --exp_name=partseg_eval --eval=True --model_path=pretrained/model.partseg.t7 133 | ``` 134 | 135 | - With class choice, for example airplane 136 | 137 | ``` 138 | python main_partseg.py --exp_name=partseg_airplane_eval --class_choice=airplane --eval=True --model_path=pretrained/model.partseg.airplane.t7 139 | ``` 140 | 141 | ### Performance: 142 | 143 | ShapeNet part dataset 144 | 145 | | | Mean IoU | Airplane | Bag | Cap | Car | Chair | Earphone | Guitar | Knife | Lamp | Laptop | Motor | Mug | Pistol | Rocket | Skateboard | Table 146 | | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | 147 | | Shapes | | 2690 | 76 | 55 | 898 | 3758 | 69 | 787 | 392 | 1547 | 451 | 202 | 184 | 283 | 66 | 152 | 5271 | 148 | | Paper | **85.2** | 84.0 | **83.4** | **86.7** | 77.8 | 90.6 | 74.7 | 91.2 | **87.5** | 82.8 | **95.7** | 66.3 | **94.9** | 81.1 | **63.5** | 74.5 | 82.6 | 149 | | This repo | **85.2** | **84.5** | 80.3 | 84.7 | **79.8** | **91.1** | **76.8** | **92.0** | 87.3 | **83.8** | **95.7** | **69.6** | 94.3 | **83.7** | 51.5 | **76.1** | **82.8** | 150 | 151 | ### Visualization: 152 | #### Usage: 153 | 154 | Use `--visu` to control visualization file. 155 | 156 | - To visualize a single shape, for example the 0-th airplane (the shape index starts from 0), use `--visu=airplane_0`. 157 | - To visualize all shapes in a class, for example airplane, use `--visu=airplane`. 158 | - To visualize all shapes in all classes, use `--visu=all`. 159 | 160 | Use `--visu_format` to control visualization file format. 161 | 162 | - To output .txt file, use `--visu_format=txt`. 163 | - To output .ply format, use `--visu_format=ply`. 164 | 165 | Both .txt and .ply file can be loaded into [MeshLab](https://www.meshlab.net) for visualization. For the usage of MeshLab on .txt file, see issue [#8](https://github.com/AnTao97/dgcnn.pytorch/issues/8) for details. The .ply file can be directly loaded into MeshLab by dragging. 166 | 167 | The visualization file name follows the format `shapename_pred_miou.FILE_TYPE` for prediction output or `shapename_gt.FILE_TYPE` for ground-truth, where `miou` shows the mIoU prediction for this shape. 168 | 169 | #### Full dataset: 170 | 171 | - Output the visualization file of the 0-th airplane with .ply format 172 | 173 | ``` 174 | # Use trained model 175 | python main_partseg.py --exp_name=partseg_eval --eval=True --model_path=outputs/partseg/models/model.t7 --visu=airplane_0 --visu_format=ply 176 | 177 | # Use pretrained model 178 | python main_partseg.py --exp_name=partseg_eval --eval=True --model_path=pretrained/model.partseg.t7 --visu=airplane_0 --visu_format=ply 179 | ``` 180 | 181 | - Output the visualization files of all shapes in airplane class with .ply format 182 | 183 | ``` 184 | # Use trained model 185 | python main_partseg.py --exp_name=partseg_eval --eval=True --model_path=outputs/partseg/models/model.t7 --visu=airplane --visu_format=ply 186 | 187 | # Use pretrained model 188 | python main_partseg.py --exp_name=partseg_eval --eval=True --model_path=pretrained/model.partseg.t7 --visu=airplane --visu_format=ply 189 | ``` 190 | 191 | - Output the visualization files of all shapes in all classes with .ply format 192 | 193 | ``` 194 | # Use trained model 195 | python main_partseg.py --exp_name=partseg_eval --eval=True --model_path=outputs/partseg/models/model.t7 --visu=all --visu_format=ply 196 | 197 | # Use pretrained model 198 | python main_partseg.py --exp_name=partseg_eval --eval=True --model_path=pretrained/model.partseg.t7 --visu=all --visu_format=ply 199 | ``` 200 | 201 | #### With class choice, for example airplane: 202 | 203 | - Output the visualization file of the 0-th airplane with .ply format 204 | 205 | ``` 206 | # Use trained model 207 | python main_partseg.py --exp_name=partseg_airplane_eval --class_choice=airplane --eval=True --model_path=outputs/partseg_airplane/models/model.t7 --visu=airplane_0 --visu_format=ply 208 | 209 | # Use pretrained model 210 | python main_partseg.py --exp_name=partseg_airplane_eval --class_choice=airplane --eval=True --model_path=pretrained/model.partseg.airplane.t7 --visu=airplane_0 --visu_format=ply 211 | ``` 212 | 213 | - Output the visualization files of all shapes in airplane class with .ply format 214 | 215 | ``` 216 | # Use trained model 217 | python main_partseg.py --exp_name=partseg_airplane_eval --class_choice=airplane --eval=True --model_path=outputs/partseg_airplane/models/model.t7 --visu=airplane --visu_format=ply 218 | 219 | # Use pretrained model 220 | python main_partseg.py --exp_name=partseg_airplane_eval --class_choice=airplane --eval=True --model_path=pretrained/model.partseg.airplane.t7 --visu=airplane --visu_format=ply 221 | ``` 222 | 223 | #### Results: 224 | The visualization result of the airplane 0: 225 | 226 |

227 | 228 |

229 | 230 | Color map: 231 |

232 | 233 |

234 | 235 |   236 | ## Point Cloud Semantic Segmentation on the S3DIS Dataset 237 | 238 | The network structure for this task is slightly different with part segmentation, without spatial transform and categorical vector. The MLP in the end is changed into (512, 256, 13) and only one dropout is used after 256. 239 | 240 | You have to download `Stanford3dDataset_v1.2_Aligned_Version.zip` manually from https://goo.gl/forms/4SoGp4KtH1jfRqEj2 and place it under `data/` 241 | 242 | ### Run the training script: 243 | 244 | This task uses 6-fold training, such that 6 models are trained leaving 1 of 6 areas as the testing area for each model. 245 | 246 | - Train in area 1-5 247 | 248 | ``` 249 | python main_semseg_s3dis.py --exp_name=semseg_s3dis_6 --test_area=6 250 | ``` 251 | 252 | ### Run the evaluation script after training finished: 253 | 254 | - Evaluate in area 6 after the model is trained in area 1-5 255 | 256 | ``` 257 | python main_semseg_s3dis.py --exp_name=semseg_s3dis_eval_6 --test_area=6 --eval=True --model_root=outputs/semseg_s3dis/models/ 258 | ``` 259 | 260 | - Evaluate in all areas after 6 models are trained 261 | 262 | ``` 263 | python main_semseg_s3dis.py --exp_name=semseg_s3dis_eval --test_area=all --eval=True --model_root=outputs/semseg_s3dis/models/ 264 | ``` 265 | 266 | ### Run the evaluation script with pretrained models: 267 | 268 | - Evaluate in area 6 269 | 270 | ``` 271 | python main_semseg_s3dis.py --exp_name=semseg_s3dis_eval_6 --test_area=6 --eval=True --model_root=pretrained/semseg_s3dis/ 272 | ``` 273 | 274 | - Evaluate in all areas 275 | 276 | ``` 277 | python main_semseg_s3dis.py --exp_name=semseg_s3dis_eval --test_area=all --eval=True --model_root=pretrained/semseg_s3dis/ 278 | ``` 279 | 280 | ### Performance: 281 | 282 | Stanford Large-Scale 3D Indoor Spaces Dataset (S3DIS) dataset 283 | 284 | | | Mean IoU | Overall Acc | 285 | | :---: | :---: | :---: | 286 | | Paper | 56.1 | 84.1 | 287 | | This repo | **59.2** | **85.0** | 288 | 289 | ### Visualization: 290 | #### Usage: 291 | 292 | Use `--visu` to control visualization file. 293 | 294 | - To visualize a single room, for example the office room 1 in area 6 (the room index starts from 1), use `--visu=area_6_office_1`. 295 | - To visualize all rooms in an area, for example area 6, use `--visu=area_6`. 296 | - To visualize all rooms in all areas, use `--visu=all`. 297 | 298 | Use `--visu_format` to control visualization file format. 299 | 300 | - To output .txt file, use `--visu_format=txt`. 301 | - To output .ply format, use `--visu_format=ply`. 302 | 303 | Both .txt and .ply file can be loaded into [MeshLab](https://www.meshlab.net) for visualization. For the usage of MeshLab on .txt file, see issue [#8](https://github.com/AnTao97/dgcnn.pytorch/issues/8) for details. The .ply file can be directly loaded into MeshLab by dragging. 304 | 305 | The visualization file name follows the format `roomname_pred_.FILE_TYPE` for prediction output or `roomname_gt.FILE_TYPE` for ground-truth, where `` shows the mIoU prediction for this room. 306 | 307 | **Note:** In semantic segmentation, you need to first run a training or evaluation command without visualization in the above sections to preprocess dataset. With the dataset well prepared, you can run a command with visualization in the following sections. 308 | 309 | #### Evaluate in area 6 after the model is trained in area 1-5: 310 | 311 | - Output the visualization file of office room 1 in area 6 with .ply format 312 | 313 | ``` 314 | # Use trained model 315 | python main_semseg_s3dis.py --exp_name=semseg_s3dis_eval_6 --test_area=6 --eval=True --model_root=outputs/semseg_s3dis/models/ --visu=area_6_office_1 --visu_format=ply 316 | 317 | # Use pretrained model 318 | python main_semseg_s3dis.py --exp_name=semseg_s3dis_eval_6 --test_area=6 --eval=True --model_root=pretrained/semseg_s3dis/ --visu=area_6_office_1 --visu_format=ply 319 | ``` 320 | 321 | - Output the visualization files of all rooms in area 6 with .ply format 322 | 323 | ``` 324 | # Use trained model 325 | python main_semseg_s3dis.py --exp_name=semseg_s3dis_eval_6 --test_area=6 --eval=True --model_root=outputs/semseg_s3dis/models/ --visu=area_6 --visu_format=ply 326 | 327 | # Use pretrained model 328 | python main_semseg_s3dis.py --exp_name=semseg_s3dis_eval_6 --test_area=6 --eval=True --model_root=pretrained/semseg_s3dis/ --visu=area_6 --visu_format=ply 329 | ``` 330 | 331 | #### Evaluate in all areas after 6 models are trained: 332 | 333 | - Output the visualization file of office room 1 in area 6 with .ply format 334 | 335 | 336 | ``` 337 | # Use trained model 338 | python main_semseg_s3dis.py --exp_name=semseg_s3dis_eval --test_area=all --eval=True --model_root=outputs/semseg_s3dis/models/ --visu=area_6_office_1 --visu_format=ply 339 | 340 | # Use pretrained model 341 | python main_semseg_s3dis.py --exp_name=semseg_s3dis_eval --test_area=all --eval=True --model_root=pretrained/semseg_s3dis/ --visu=area_6_office_1 --visu_format=ply 342 | ``` 343 | 344 | - Output the visualization files of all rooms in area 6 with .ply format 345 | 346 | ``` 347 | # Use trained model 348 | python main_semseg_s3dis.py --exp_name=semseg_s3dis_eval --test_area=all --eval=True --model_root=outputs/semseg_s3dis/models/ --visu=area_6 --visu_format=ply 349 | 350 | # Use pretrained model 351 | python main_semseg_s3dis.py --exp_name=semseg_s3dis_eval --test_area=all --eval=True --model_root=pretrained/semseg_s3dis/ --visu=area_6 --visu_format=ply 352 | ``` 353 | 354 | - Output the visualization files of all rooms in all areas with .ply format 355 | 356 | ``` 357 | # Use trained model 358 | python main_semseg_s3dis.py --exp_name=semseg_s3dis_eval --test_area=all --eval=True --model_root=outputs/semseg_s3dis/models/ --visu=all --visu_format=ply 359 | 360 | # Use pretrained model 361 | python main_semseg_s3dis.py --exp_name=semseg_s3dis_eval --test_area=all --eval=True --model_root=pretrained/semseg_s3dis/ --visu=all --visu_format=ply 362 | ``` 363 | 364 | #### Results: 365 | The visualization result of the office room 1 in area 6: 366 | 367 |

368 | 369 |

370 | 371 | Color map: 372 |

373 | 374 |

375 | 376 |   377 | ## Point Cloud Semantic Segmentation on the ScanNet Dataset 378 | 379 | The DGCNN authors do not test on the ScanNet dataset. We try our best to implement the DGCNN model on the dataset. 380 | 381 | ### Prepare dataset: 382 | 383 | You need to change the directory to the `prepare_data/` folder. 384 | 385 | ``` 386 | cd prepare_data/ 387 | ``` 388 | 389 | Please download original dataset from [website](http://www.scan-net.org/). You need to place the dataset under `data/ScanNet/`. The path `data/ScanNet` includes `data/ScanNet/scans/` and `data/ScanNet/scans_test/` folder. 390 | 391 | To prepare the Scannet dataset for training and evaluation, run 392 | 393 | ``` 394 | python scannetv2_seg_dataset_rgb21c_pointid.py 395 | ``` 396 | 397 | This will generate four pickle files: `scannet_train_rgb21c_pointid.pickle`, `scannet_val_rgb21c_pointid.pickle`, `scannet_val_rgb21c_pointid_keep_unanno.pickle`, and `scannet_test_rgb21c_pointid_keep_unanno.pickle`. 398 | 399 | Return to the root directory: 400 | 401 | ``` 402 | cd .. 403 | ``` 404 | 405 | ### Run the training script: 406 | 407 | ``` 408 | python main_semseg_scannet.py --exp_name=semseg_scannet 409 | ``` 410 | 411 | To train with both the training split and the validation split, use `--train_val=True`. 412 | 413 | You can use [TensorBoard](https://tensorflow.google.cn/tensorboard) to view the training log under `outputs/semseg_scannet/logs/`. 414 | 415 | ### Run the evaluation script after training finished: 416 | 417 | - Evaluate on the validation set 418 | 419 | ``` 420 | python main_semseg_scannet.py --eval=True --model_path=outputs/semseg_scannet/models/model_200.pth --exp_name=semseg_scannet_val --split=val 421 | ``` 422 | 423 | - Evaluate on the testing set 424 | 425 | ``` 426 | python main_semseg_scannet.py --eval=True --model_path=outputs/semseg_scannet/models/model_200.pth --exp_name=semseg_scannet_test --split=test 427 | ``` 428 | 429 | ### Run the evaluation script with pretrained models: 430 | 431 | - Evaluate on the validation set 432 | 433 | ``` 434 | python main_semseg_scannet.py --eval=True --model_path=pretrained/semseg_scannet/models/model_200.pth --exp_name=semseg_scannet_val --split=val 435 | ``` 436 | 437 | - Evaluate on the testing set 438 | 439 | ``` 440 | python main_semseg_scannet.py --eval=True --model_path=pretrained/semseg_scannet/models/model_200.pth --exp_name=semseg_scannet_test --split=test 441 | ``` 442 | 443 | Since there are no ground-truth labels on the testing set, this script will directly save prediction result. You need to upload your prediction results to the [website](https://kaldir.vc.in.tum.de/scannet_benchmark/semantic_label_3d) for evaluation. 444 | 445 | ### Performance: 446 | 447 | The validation set of the ScanNet Dataset 448 | 449 | | | Mean IoU | wall | floor | cabinet | bed | chair | sofa | table | door | window | bookshelf | picture | counter | desk | curtain | refrigerator | shower curtain | toilet | sink | bathtub | otherfurniture | 450 | | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | 451 | | This repo | 49.6 | 73.2 | 93.6 | 44.9 | 64.7 | 70.0 | 50.5 | 55.7 | 35.7 | 47.7 | 69.1 | 14.6 | 41.8 | 45.3 | 33.8 | 29.2 | 35.7 | 55.9 | 40.2 | 56.5 | 32.9 | 452 | 453 | The testing set of the ScanNet Dataset 454 | 455 | | | Mean IoU | wall | floor | cabinet | bed | chair | sofa | table | door | window | bookshelf | picture | counter | desk | curtain | refrigerator | shower curtain | toilet | sink | bathtub | other furniture | 456 | | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | 457 | | This repo | 44.6 | 72.3 | 93.7 | 36.6 | 62.3 | 65.1 | 57.7 | 44.5 | 33.0 | 39.4 | 46.3 | 12.6 | 31.0 | 34.9 | 38.9 | 28.5 | 22.4 | 62.5 | 35.0 | 47.4 | 27.1 | 458 | 459 | These is no official results of DGCNN on the ScanNet dataset. You can find our results on the [website](https://kaldir.vc.in.tum.de/scannet_benchmark/semantic_label_3d) as `DGCNN_reproduce`. 460 | 461 | ### Visualization: 462 | #### Usage: 463 | 464 | Use `--visu` to control visualization file. 465 | 466 | - To visualization a single scene, for example `scene0568_00`, use `--visu=scene0568_00`. 467 | - To visualization multiple scenes, please input these scenes separated by commas. e.g. `--visu=scene0568_00,scene0568_01`. 468 | - To visualization the scenes of a split, use `--visu=train`, `--visu=val` or `--visu=test`. 469 | - To visualization all the scenes in the dataset, use `--visu=all`. 470 | 471 | Use `--visu_format` to control visualization file format. 472 | 473 | - To output .txt file, use `--visu_format=txt`. 474 | - To output .ply format, use `--visu_format=ply`. 475 | 476 | Both .txt and .ply file can be loaded into [MeshLab](https://www.meshlab.net) for visualization. For the usage of MeshLab on .txt file, see issue [#8](https://github.com/AnTao97/dgcnn.pytorch/issues/8) for details. The .ply file can be directly loaded into MeshLab by dragging. 477 | 478 | For example, if you want to visualize `scene0568_00` on the pretrained model, run: 479 | 480 | ``` 481 | python main_semseg_scannet.py --eval=True --model_path=pretrained/semseg_scannet/models/model_200.pth --exp_name=semseg_scannet_val_visu --visu=scene0568_00 482 | ``` 483 | 484 | #### Results: 485 | The visualization result of `scene0568_00`: 486 | 487 |

488 | 489 |

490 | 491 | Color map: 492 |

493 | 494 |

-------------------------------------------------------------------------------- /README_zh.md: -------------------------------------------------------------------------------- 1 | # DGCNN.pytorch 2 | [[English]](README.md) 3 | 4 | 本仓库提供了一份PyTorch版本的 **Dynamic Graph CNN for Learning on Point Clouds (DGCNN)**( https://arxiv.org/pdf/1801.07829 )代码实现,代码框架来源于[WangYueFt/dgcnn](https://github.com/WangYueFt/dgcnn/tree/master/pytorch)。 5 | 6 | **更新:** 7 | 8 | - [2022/10/22] 增加ScanNet数据集上语义分割代码,作者:[吴紫屹](https://wuziyi616.github.io/) (dazitu616@gmail.com)、[王英琦](https://github.com/HilbertWang2002)(yingqi-w19@mails.tsinghua.edu.cn)。 9 | - [2021/07/20] 增加了可视化代码,作者:[纪鹏亮](https://github.com/Ji-Pengliang) (jpl1723@buaa.edu.cn). 10 | 11 | 在DGCNN文章中网络结构图(图3)中的分类网络和文章中对应的网络结构描述(第4.1节)并不吻合,原作者实际沿用了网络结构描述(第4.1节)中的网络结构,我们使用PS修复了网络结构图(图3)中不吻合的地方,修改后的图如下: 12 | 13 |   14 |

15 | 16 |

17 | 18 |   19 | 20 | **建议:** 3D点云实验结果往往比2D图像实验结果面临更大的随机性,因此我们建议您多跑几次实验,然后选择最佳的结果。 21 | 22 |   23 | ## 运行需求 24 | - Python 3.7 25 | - PyTorch 1.2 26 | - CUDA 10.0 27 | - Python包:glob, h5py, sklearn, plyfile 28 | 29 |   30 | ## 内容目录 31 | - [点云分类](#_3) 32 | - [点云局部分割](#_8) 33 | - [S3DIS数据集上点云场景语义分割](#s3dis) 34 | - [ScanNet数据集上点云场景语义分割](#scannet) 35 | 36 | **注意:** 以下所有命令默认使用所有显卡,如果需要明确使用几张显卡,比如4张显卡索引为`0,1,2,3`,那么在每个命令前需要添加`CUDA_VISIBLE_DEVICES=0,1,2,3`。你可以根据你的需要调整使用显卡的数量和索引。 37 | 38 |   39 | ## 点云分类 40 | ### 运行训练脚本: 41 | 42 | - 1024点 43 | 44 | ``` 45 | python main_cls.py --exp_name=cls_1024 --num_points=1024 --k=20 46 | ``` 47 | 48 | - 2048点 49 | 50 | ``` 51 | python main_cls.py --exp_name=cls_2048 --num_points=2048 --k=40 52 | ``` 53 | 54 | ### 训练结束后运行评估脚本: 55 | 56 | - 1024点 57 | 58 | ``` 59 | python main_cls.py --exp_name=cls_1024_eval --num_points=1024 --k=20 --eval=True --model_path=outputs/cls_1024/models/model.t7 60 | ``` 61 | 62 | - 2048点 63 | 64 | ``` 65 | python main_cls.py --exp_name=cls_2048_eval --num_points=2048 --k=40 --eval=True --model_path=outputs/cls_2048/models/model.t7 66 | ``` 67 | 68 | ### 使用提供的已训练模型运行评估脚本: 69 | 70 | - 1024点 71 | 72 | ``` 73 | python main_cls.py --exp_name=cls_1024_eval --num_points=1024 --k=20 --eval=True --model_path=pretrained/model.cls.1024.t7 74 | ``` 75 | 76 | - 2048点 77 | 78 | ``` 79 | python main_cls.py --exp_name=cls_2048_eval --num_points=2048 --k=40 --eval=True --model_path=pretrained/model.cls.2048.t7 80 | ``` 81 | 82 | ### 模型性能: 83 | ModelNet40数据集 84 | 85 | | | 平均类别Acc | 整体Acc | 86 | | :---: | :---: | :---: | 87 | | 原文章(1024点) | 90.2 | 92.9 | 88 | | 本仓库(1024点) | **90.9** | **93.3** | 89 | | 原文章(2048点) | 90.7 | 93.5 | 90 | | 本仓库(2048点) | **91.2** | **93.6** | 91 | 92 |   93 | ## 点云局部分割 94 | **注意:** 训练模式中 **“全部类别”** 和 **“特定类别”** 有区别。 95 | 96 | - 在 **“全部类别”** 中,模型使用所有16个类别的数据进行训练和评估,并在这份代码中得出平均IoU为85.2%。每个点云形状中点的预测标签可以是16个类别中的任意一个局部类别。 97 | - 在 **“特定类别”** 中,模型仅使用1个类别的数据进行训练和评估,比如飞机类别,并在这份代码中得出飞机类别的IoU为84.5%。每个点云形状中点的预测标签只能是这1个类别中的任意一个局部类别。 98 | 99 | ### 运行训练脚本: 100 | 101 | - 使用数据集内全部类别 102 | 103 | ``` 104 | python main_partseg.py --exp_name=partseg 105 | ``` 106 | 107 | - 选择数据集内特定类别,例如飞机 108 | 109 | ``` 110 | python main_partseg.py --exp_name=partseg_airplane --class_choice=airplane 111 | ``` 112 | 113 | ### 训练结束后运行评估脚本: 114 | 115 | - 使用数据集内全部类别 116 | 117 | ``` 118 | python main_partseg.py --exp_name=partseg_eval --eval=True --model_path=outputs/partseg/models/model.t7 119 | ``` 120 | 121 | - 选择数据集内特定类别,例如飞机 122 | 123 | ``` 124 | python main_partseg.py --exp_name=partseg_airplane_eval --class_choice=airplane --eval=True --model_path=outputs/partseg_airplane/models/model.t7 125 | ``` 126 | 127 | ### 使用提供的已训练模型运行评估脚本: 128 | 129 | - 使用数据集内全部类别 130 | 131 | ``` 132 | python main_partseg.py --exp_name=partseg_eval --eval=True --model_path=pretrained/model.partseg.t7 133 | ``` 134 | 135 | - 选择数据集内特定类别,例如飞机 136 | 137 | ``` 138 | python main_partseg.py --exp_name=partseg_airplane_eval --class_choice=airplane --eval=True --model_path=pretrained/model.partseg.airplane.t7 139 | ``` 140 | 141 | ### 模型性能 142 | ShapeNet part 数据集 143 | 144 | |      | 平均IoU | 飞机 | 包 | 帽子 | 车 | 椅子 | 耳机 | 吉他 | 刀 | 灯 | 笔记本电脑 | 摩托车 | 杯子 | 手枪 | 火箭 | 滑板 | 桌子 | 145 | | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | 146 | |      | mIoU | airplane | bag | cap | car | chair | earphone | guitar | knife | lamp | laptop | motor | mug | pistol | rocket | skateboard | table | 147 | | 形状数量 |      | 2690 | 76 | 55 | 898 | 3758 | 69 | 787 | 392 | 1547 | 451 | 202 | 184 | 283 | 66 | 152 | 5271 | 148 | | 原文章 | **85.2** | 84.0 | **83.4** | **86.7** | 77.8 | 90.6 | 74.7 | 91.2 | **87.5** | 82.8 | **95.7** | 66.3 | **94.9** | 81.1 | **63.5** | 74.5 | 82.6 | 149 | | 本仓库 | **85.2** | **84.5** | 80.3 | 84.7 | **79.8** | **91.1** | **76.8** | **92.0** | 87.3 | **83.8** | **95.7** | **69.6** | 94.3 | **83.7** | 51.5 | **76.1** | **82.8** | 150 | 151 | ### 可视化: 152 | #### 使用说明: 153 | 154 | 使用`--visu`控制需要可视化的文件。 155 | 156 | - 对于可视化一个点云形状,比如类别飞机索引为0的点云形状(点云形状的索引为从0开始),使用`--visu=airplane_0`。 157 | - 对于可视化一个类别中的所有点云形状,比如飞机类别,使用`--visu=airplane`。 158 | - 对于可视化所有类别的所有点云类别,使用`--visu=all`。 159 | 160 | 使用`--visu_format`控制可视化文件的格式。 161 | 162 | - 输出.txt格式,使用`--visu_format=txt`。 163 | - 输出.ply格式,使用`--visu_format=ply`。 164 | 165 | 所有格式均可通过[MeshLab](https://www.meshlab.net)来加载进行可视化。对于使用MeshLab可视化.txt格式,参考问题[#8](https://github.com/AnTao97/dgcnn.pytorch/issues/8)中的介绍,而.ply格式可以直接拖入MeshLab进行可视化。 166 | 167 | 可视化文件名遵循统一的命名格式。对于预测结果,文件名格式为`点云形状名称_pred_miou.格式后缀`;对于真实标签,文件名格式为`点云形状名称_gt.格式后缀`。文件名中`miou`指该点云形状的平均IoU。 168 | 169 | #### 全部类别: 170 | 171 | - 输出飞机类别索引为0的点云形状的.ply格式可视化结果 172 | 173 | ``` 174 | # 使用训练后的模型 175 | python main_partseg.py --exp_name=partseg_eval --eval=True --model_path=outputs/partseg/models/model.t7 --visu=airplane_0 --visu_format=ply 176 | 177 | # 使用提供的已训练模型 178 | python main_partseg.py --exp_name=partseg_eval --eval=True --model_path=pretrained/model.partseg.t7 --visu=airplane_0 --visu_format=ply 179 | ``` 180 | 181 | - 输出飞机类别的所有点云形状的.ply格式可视化结果 182 | 183 | ``` 184 | # 使用训练后的模型 185 | python main_partseg.py --exp_name=partseg_eval --eval=True --model_path=outputs/partseg/models/model.t7 --visu=airplane --visu_format=ply 186 | 187 | # 使用提供的已训练模型 188 | python main_partseg.py --exp_name=partseg_eval --eval=True --model_path=pretrained/model.partseg.t7 --visu=airplane --visu_format=ply 189 | ``` 190 | 191 | - 输出所有类别的所有点云形状的.ply格式可视化结果 192 | 193 | ``` 194 | # 使用训练后的模型 195 | python main_partseg.py --exp_name=partseg_eval --eval=True --model_path=outputs/partseg/models/model.t7 --visu=all --visu_format=ply 196 | 197 | # 使用提供的已训练模型 198 | python main_partseg.py --exp_name=partseg_eval --eval=True --model_path=pretrained/model.partseg.t7 --visu=all --visu_format=ply 199 | ``` 200 | 201 | #### 选择数据集内特定类别,例如飞机: 202 | 203 | - 输出飞机类别索引为0的点云形状的.ply格式可视化结果 204 | 205 | ``` 206 | # 使用训练后的模型 207 | python main_partseg.py --exp_name=partseg_airplane_eval --class_choice=airplane --eval=True --model_path=outputs/partseg_airplane/models/model.t7 --visu=airplane_0 --visu_format=ply 208 | 209 | # 使用提供的已训练模型 210 | python main_partseg.py --exp_name=partseg_airplane_eval --class_choice=airplane --eval=True --model_path=pretrained/model.partseg.airplane.t7 --visu=airplane_0 --visu_format=ply 211 | ``` 212 | 213 | - 输出飞机类别的所有点云形状的.ply格式可视化结果 214 | 215 | ``` 216 | # 使用训练后的模型 217 | python main_partseg.py --exp_name=partseg_airplane_eval --class_choice=airplane --eval=True --model_path=outputs/partseg_airplane/models/model.t7 --visu=airplane --visu_format=ply 218 | 219 | # 使用提供的已训练模型 220 | python main_partseg.py --exp_name=partseg_airplane_eval --class_choice=airplane --eval=True --model_path=pretrained/model.partseg.airplane.t7 --visu=airplane --visu_format=ply 221 | ``` 222 | 223 | #### 可视化结果: 224 | 属于飞机类别索引为0的点云形状的可视化结果: 225 | 226 |

227 | 228 |

229 | 230 | 颜色对应图: 231 |

232 | 233 |

234 | 235 |   236 | ## 在S3DIS数据集上点云场景语义分割 237 | 238 | 在此任务中网络结构和点云局部分割有细微不同,最后的一个MLP尺寸改为(512, 256, 13),而且在256后只使用一个dropout。 239 | 240 | 您必须从 https://goo.gl/forms/4SoGp4KtH1jfRqEj2 手动下载数据集`Stanford3dDataset_v1.2_Aligned_Version.zip`,然后将其放在`data/`目录下。 241 | 242 | ### 运行训练脚本: 243 | 244 | 此任务使用6折训练,因此需要训练6个模型,轮流选择数据集中6个区域中的1个作为这个模型的测试区域。 245 | 246 | - 在区域1-5上训练 247 | 248 | ``` 249 | python main_semseg_s3dis.py --exp_name=semseg_s3dis_6 --test_area=6 250 | ``` 251 | 252 | ### 训练结束后运行评估脚本: 253 | 254 | - 当模型在区域1-5训练完成后,在区域6中评估 255 | 256 | ``` 257 | python main_semseg_s3dis.py --exp_name=semseg_s3dis_eval_6 --test_area=6 --eval=True --model_root=outputs/semseg_s3dis_s3dis/models/ 258 | ``` 259 | 260 | - 当6个模型训练完成后,在所有区域上评估 261 | 262 | ``` 263 | python main_semseg_s3dis.py --exp_name=semseg_s3dis_eval --test_area=all --eval=True --model_root=outputs/semseg_s3dis/models/ 264 | ``` 265 | 266 | ### 使用提供的已训练模型运行评估脚本: 267 | 268 | - 使用提供的在区域1-5上已训练模型,在区域6中评估 269 | 270 | ``` 271 | python main_semseg_s3dis.py --exp_name=semseg_s3dis_eval_6 --test_area=6 --eval=True --model_root=pretrained/semseg_s3dis/ 272 | ``` 273 | 274 | - 使用提供的6个已训练模型,在所有区域上评估 275 | 276 | ``` 277 | python main_semseg_s3dis.py --exp_name=semseg_s3dis_eval --test_area=all --eval=True --model_root=pretrained/semseg_s3dis/ 278 | ``` 279 | 280 | ### 模型性能: 281 | 斯坦福大学大型3D室内空间数据集(S3DIS) 282 | 283 | | | 平均IoU | 整体Acc | 284 | | :---: | :---: | :---: | 285 | | 原文章 | 56.1 | 84.1 | 286 | | 本仓库 | **59.2** | **85.0** | 287 | 288 | ### 可视化: 289 | #### 使用说明: 290 | 291 | 使用`--visu`控制需要可视化的文件。 292 | 293 | - 对于可视化一个房间,比如第区域6的办公室1(房间索引为从1开始),使用`--visu=area_6_office_1`。 294 | - 对于可视化一个区域中所有的房间,比如区域6,使用`--visu=area_6`。 295 | - 对于可视化所有区域的所有房间,使用`--visu=all`。 296 | 297 | 使用`--visu_format`控制可视化文件的格式。 298 | 299 | - 输出.txt格式,使用`--visu_format=txt`。 300 | - 输出.ply格式,使用`--visu_format=ply`。 301 | 302 | 所有格式均可通过[MeshLab](https://www.meshlab.net)来加载进行可视化。对于使用MeshLab可视化.txt格式,参考问题[#8](https://github.com/AnTao97/dgcnn.pytorch/issues/8)中的介绍,而.ply格式可以直接拖入MeshLab进行可视化。 303 | 304 | 可视化文件名遵循统一的命名格式。对于预测结果,文件名格式为`房间名称_pred_.格式后缀`;对于真实标签,文件名格式为`房间名称_gt.格式后缀`。文件名中``指该房间的平均IoU。 305 | 306 | **注意:**对于语义分割,需要首先运行一个不带可视化的命令来预处理数据集,比如可视化部分之前的训练命令和评估命令。在数据集处理完成后,可以运行下面带有可视化的命令。 307 | 308 | #### 当模型在区域1-5训练完成后,在区域6中评估: 309 | 310 | - 输出区域6的办公室1的.ply格式可视化结果 311 | 312 | ``` 313 | # 使用训练后的模型 314 | python main_semseg_s3dis.py --exp_name=semseg_s3dis_eval_6 --test_area=6 --eval=True --model_root=outputs/semseg_s3dis/models/ --visu=area_6_office_1 --visu_format=ply 315 | 316 | # 使用提供的已训练模型 317 | python main_semseg_s3dis.py --exp_name=semseg_s3dis_eval_6 --test_area=6 --eval=True --model_root=pretrained/semseg_s3dis/ --visu=area_6_office_1 --visu_format=ply 318 | ``` 319 | 320 | - 输出区域6的所有房间的.ply格式可视化结果 321 | 322 | ``` 323 | # 使用训练后的模型 324 | python main_semseg_s3dis.py --exp_name=semseg_s3dis_eval_6 --test_area=6 --eval=True --model_root=outputs/semseg_s3dis/models/ --visu=area_6 --visu_format=ply 325 | 326 | # 使用提供的已训练模型 327 | python main_semseg_s3dis.py --exp_name=semseg_s3dis_eval_6 --test_area=6 --eval=True --model_root=pretrained/semseg_s3dis/ --visu=area_6 --visu_format=ply 328 | ``` 329 | 330 | #### 当6个模型训练完成后,在所有区域上评估: 331 | 332 | - 输出区域6的办公室1的.ply格式可视化结果 333 | 334 | 335 | ``` 336 | # 使用训练后的模型 337 | python main_semseg_s3dis.py --exp_name=semseg_s3dis_eval --test_area=all --eval=True --model_root=outputs/semseg/models_s3dis/ --visu=area_6_office_1 --visu_format=ply 338 | 339 | # 使用提供的已训练模型 340 | python main_semseg_s3dis.py --exp_name=semseg_s3dis_eval --test_area=all --eval=True --model_root=pretrained/semseg_s3dis/ --visu=area_6_office_1 --visu_format=ply 341 | ``` 342 | 343 | - 输出区域6的所有房间的.ply格式可视化结果 344 | 345 | ``` 346 | # 使用训练后的模型 347 | python main_semseg_s3dis.py --exp_name=semseg_s3dis_eval --test_area=all --eval=True --model_root=outputs/semseg_s3dis/models/ --visu=area_6 --visu_format=ply 348 | 349 | # 使用提供的已训练模型 350 | python main_semseg_s3dis.py --exp_name=semseg_s3dis_eval --test_area=all --eval=True --model_root=pretrained/semseg_s3dis/ --visu=area_6 --visu_format=ply 351 | ``` 352 | 353 | - 输出所有区域的所有房间的.ply格式可视化结果 354 | 355 | ``` 356 | # 使用训练后的模型 357 | python main_semseg_s3dis.py --exp_name=semseg_s3dis_eval --test_area=all --eval=True --model_root=outputs/semseg_s3dis/models/ --visu=all --visu_format=ply 358 | 359 | # 使用提供的已训练模型 360 | python main_semseg_s3dis.py --exp_name=semseg_s3dis_eval --test_area=all --eval=True --model_root=pretrained/semseg_s3dis/ --visu=all --visu_format=ply 361 | ``` 362 | 363 | #### 可视化结果: 364 | 区域6的办公室1的可视化结果: 365 | 366 |

367 | 368 |

369 | 370 | 颜色对应图: 371 |

372 | 373 |

374 | 375 |   376 | ## 在ScanNet数据集上点云场景语义分割 377 | 378 | DGCNN的作者并没有在ScanNet数据集上测试, 我们尽可能实现了一个DGCNN模型在ScanNet数据集上的实现版本。 379 | 380 | ### 准备数据集: 381 | 382 | 你需要把程序运行目录改变至`prepare_data/`文件夹下。 383 | 384 | ``` 385 | cd prepare_data/ 386 | ``` 387 | 388 | 请从[ScanNet网站](http://www.scan-net.org/)下载原始数据集,并放置其于`data/ScanNet/`,注意路径`data/ScanNet`内需要包括`data/ScanNet/scans/`文件夹和`data/ScanNet/scans_test/`文件夹。 389 | 390 | 使用下面命令准备ScanNet数据集 391 | 392 | ``` 393 | python scannetv2_seg_dataset_rgb21c_pointid.py 394 | ``` 395 | 396 | 本命令会产生四个pickle文件:`scannet_train_rgb21c_pointid.pickle`、`scannet_val_rgb21c_pointid.pickle`、`scannet_val_rgb21c_pointid_keep_unanno.pickle`和`scannet_test_rgb21c_pointid_keep_unanno.pickle`。 397 | 398 | 返回主目录: 399 | 400 | ``` 401 | cd .. 402 | ``` 403 | 404 | ### 运行训练脚本: 405 | 406 | ``` 407 | python main_semseg_scannet.py --exp_name=semseg_scannet 408 | ``` 409 | 410 | 如果训练时同时使用训练集和验证集,使用`--train_val=True`。 411 | 412 | 你可以使用[TensorBoard](https://tensorflow.google.cn/tensorboard)查看路径`outputs/semseg_scannet/logs/`保存的训练记录。 413 | 414 | ### 训练结束后运行评估脚本: 415 | 416 | - 在验证集上评估 417 | 418 | ``` 419 | python main_semseg_scannet.py --eval=True --model_path=outputs/semseg_scannet/models/model_200.pth --exp_name=semseg_scannet_val --split=val 420 | ``` 421 | 422 | - 在测试集上评估 423 | 424 | ``` 425 | python main_semseg_scannet.py --eval=True --model_path=outputs/semseg_scannet/models/model_200.pth --exp_name=semseg_scannet_test --split=test 426 | ``` 427 | 428 | ### 使用提供的已训练模型运行评估脚本: 429 | 430 | - 在验证集上评估 431 | 432 | ``` 433 | python main_semseg_scannet.py --eval=True --model_path=pretrained/semseg_scannet/models/model_200.pth --exp_name=semseg_scannet_val --split=val 434 | ``` 435 | 436 | - 在测试集上评估 437 | 438 | ``` 439 | python main_semseg_scannet.py --eval=True --model_path=pretrained/semseg_scannet/models/model_200.pth --exp_name=semseg_scannet_test --split=test 440 | ``` 441 | 442 | 由于测试集内没有真实标签,所以测试脚本会直接保存预测结果,你需要将预测结果上传至[ScanNet评估网站](https://kaldir.vc.in.tum.de/scannet_benchmark/semantic_label_3d) 获取评估结果。 443 | 444 | ### 模型性能: 445 | 446 | ScanNet数据集验证集 447 | 448 | | | Mean IoU | wall | floor | cabinet | bed | chair | sofa | table | door | window | bookshelf | picture | counter | desk | curtain | refrigerator | shower curtain | toilet | sink | bathtub | otherfurniture | 449 | | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | 450 | | This repo | 49.7 | 72.6 | 93.8 | 42.1 | 61.4 | 70.0 | 48.5 | 54.9 | 36.9 | 47.6 | 67.4 | 13.3 | 42.8 | 45.1 | 39.5 | 27.5 | 38.1 | 60.0 | 41.5 | 61.5 | 28.9 | 451 | 452 | ScanNet数据集测试集 453 | 454 | | | Mean IoU | wall | floor | cabinet | bed | chair | sofa | table | door | window | bookshelf | picture | counter | desk | curtain | refrigerator | shower curtain | toilet | sink | bathtub | other furniture | 455 | | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | 456 | | This repo | 44.6 | 72.3 | 93.7 | 36.6 | 62.3 | 65.1 | 57.7 | 44.5 | 33.0 | 39.4 | 46.3 | 12.6 | 31.0 | 34.9 | 38.9 | 28.5 | 22.4 | 62.5 | 35.0 | 47.4 | 27.1 | 457 | 458 | ScanNet数据集上没有DGCNN的官方结果,你可以在[ScanNet评估网站](https://kaldir.vc.in.tum.de/scannet_benchmark/semantic_label_3d) 上找到我们的上传记录`DGCNN_reproduce`。 459 | 460 | ### 可视化: 461 | #### 使用说明: 462 | 463 | 使用`--visu`控制需要可视化的文件。 464 | 465 | - 对于可视化一个场景,比如`scene0568_00`,使用`--visu=scene0568_00`。 466 | - 对于可视化多个场景,请将场景之间用英文逗号区分,使用`--visu=scene0568_00,scene0568_01`。 467 | - 对于可视化一个划分集合内的场景,使用`--visu=train`或者`--visu=val`或者`--visu=test`。 468 | - 对于可视化数据集内所有场景,使用`--visu=all`。 469 | 470 | 使用`--visu_format`控制可视化文件的格式。 471 | 472 | - 输出.txt格式,使用`--visu_format=txt`。 473 | - 输出.ply格式,使用`--visu_format=ply`。 474 | 475 | 所有格式均可通过[MeshLab](https://www.meshlab.net)来加载进行可视化。对于使用MeshLab可视化.txt格式,参考问题[#8](https://github.com/AnTao97/dgcnn.pytorch/issues/8)中的介绍,而.ply格式可以直接拖入MeshLab进行可视化。 476 | 477 | 例如,如果你想可视化预训练模型在`scene0568_00`的结果,运行: 478 | 479 | ``` 480 | python main_semseg_scannet.py --eval=True --model_path=pretrained/semseg_scannet/models/model_200.pth --exp_name=semseg_scannet_val_visu --visu=scene0568_00 481 | ``` 482 | 483 | #### 可视化结果: 484 | 场景`scene0568_00`的可视化结果: 485 | 486 |

487 | 488 |

489 | 490 | 颜色对应图: 491 |

492 | 493 |

-------------------------------------------------------------------------------- /data.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | """ 4 | @Author: Yue Wang 5 | @Contact: yuewangx@mit.edu 6 | @File: data.py 7 | @Time: 2018/10/13 6:21 PM 8 | 9 | Modified by 10 | @Author: An Tao, Pengliang Ji, Ziyi Wu 11 | @Contact: ta19@mails.tsinghua.edu.cn, jpl1723@buaa.edu.cn, dazitu616@gmail.com 12 | @Time: 2022/7/30 7:49 PM 13 | """ 14 | 15 | 16 | import os 17 | import sys 18 | import glob 19 | import h5py 20 | import numpy as np 21 | import torch 22 | import json 23 | import cv2 24 | import pickle 25 | from torch.utils.data import Dataset 26 | 27 | 28 | def download_modelnet40(): 29 | BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 30 | DATA_DIR = os.path.join(BASE_DIR, 'data') 31 | if not os.path.exists(DATA_DIR): 32 | os.mkdir(DATA_DIR) 33 | if not os.path.exists(os.path.join(DATA_DIR, 'modelnet40_ply_hdf5_2048')): 34 | www = 'https://shapenet.cs.stanford.edu/media/modelnet40_ply_hdf5_2048.zip' 35 | zipfile = os.path.basename(www) 36 | os.system('wget --no-check-certificate %s; unzip %s' % (www, zipfile)) 37 | os.system('mv %s %s' % ('modelnet40_ply_hdf5_2048', DATA_DIR)) 38 | os.system('rm %s' % (zipfile)) 39 | 40 | 41 | def download_shapenetpart(): 42 | BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 43 | DATA_DIR = os.path.join(BASE_DIR, 'data') 44 | if not os.path.exists(DATA_DIR): 45 | os.mkdir(DATA_DIR) 46 | if not os.path.exists(os.path.join(DATA_DIR, 'shapenet_part_seg_hdf5_data')): 47 | www = 'https://shapenet.cs.stanford.edu/media/shapenet_part_seg_hdf5_data.zip' 48 | zipfile = os.path.basename(www) 49 | os.system('wget --no-check-certificate %s; unzip %s' % (www, zipfile)) 50 | os.system('mv %s %s' % ('hdf5_data', os.path.join(DATA_DIR, 'shapenet_part_seg_hdf5_data'))) 51 | os.system('rm %s' % (zipfile)) 52 | 53 | 54 | def download_S3DIS(): 55 | BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 56 | DATA_DIR = os.path.join(BASE_DIR, 'data') 57 | if not os.path.exists(DATA_DIR): 58 | os.mkdir(DATA_DIR) 59 | if not os.path.exists(os.path.join(DATA_DIR, 'indoor3d_sem_seg_hdf5_data')): 60 | www = 'https://shapenet.cs.stanford.edu/media/indoor3d_sem_seg_hdf5_data.zip' 61 | zipfile = os.path.basename(www) 62 | os.system('wget %s --no-check-certificate; unzip %s' % (www, zipfile)) 63 | os.system('mv %s %s' % ('indoor3d_sem_seg_hdf5_data', DATA_DIR)) 64 | os.system('rm %s' % (zipfile)) 65 | if not os.path.exists(os.path.join(DATA_DIR, 'Stanford3dDataset_v1.2_Aligned_Version')): 66 | if not os.path.exists(os.path.join(DATA_DIR, 'Stanford3dDataset_v1.2_Aligned_Version.zip')): 67 | print('Please download Stanford3dDataset_v1.2_Aligned_Version.zip \ 68 | from https://goo.gl/forms/4SoGp4KtH1jfRqEj2 and place it under data/') 69 | sys.exit(0) 70 | else: 71 | zippath = os.path.join(DATA_DIR, 'Stanford3dDataset_v1.2_Aligned_Version.zip') 72 | os.system('unzip %s' % (zippath)) 73 | os.system('mv %s %s' % ('Stanford3dDataset_v1.2_Aligned_Version', DATA_DIR)) 74 | os.system('rm %s' % (zippath)) 75 | 76 | 77 | def load_data_cls(partition): 78 | download_modelnet40() 79 | BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 80 | DATA_DIR = os.path.join(BASE_DIR, 'data') 81 | all_data = [] 82 | all_label = [] 83 | for h5_name in glob.glob(os.path.join(DATA_DIR, 'modelnet40_ply_hdf5_2048', '*%s*.h5'%partition)): 84 | f = h5py.File(h5_name, 'r+') 85 | data = f['data'][:].astype('float32') 86 | label = f['label'][:].astype('int64') 87 | f.close() 88 | all_data.append(data) 89 | all_label.append(label) 90 | all_data = np.concatenate(all_data, axis=0) 91 | all_label = np.concatenate(all_label, axis=0) 92 | return all_data, all_label 93 | 94 | 95 | def load_data_partseg(partition): 96 | download_shapenetpart() 97 | BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 98 | DATA_DIR = os.path.join(BASE_DIR, 'data') 99 | all_data = [] 100 | all_label = [] 101 | all_seg = [] 102 | if partition == 'trainval': 103 | file = glob.glob(os.path.join(DATA_DIR, 'shapenet_part_seg_hdf5_data', '*train*.h5')) \ 104 | + glob.glob(os.path.join(DATA_DIR, 'shapenet_part_seg_hdf5_data', '*val*.h5')) 105 | else: 106 | file = glob.glob(os.path.join(DATA_DIR, 'shapenet_part_seg_hdf5_data', '*%s*.h5'%partition)) 107 | for h5_name in file: 108 | f = h5py.File(h5_name, 'r+') 109 | data = f['data'][:].astype('float32') 110 | label = f['label'][:].astype('int64') 111 | seg = f['pid'][:].astype('int64') 112 | f.close() 113 | all_data.append(data) 114 | all_label.append(label) 115 | all_seg.append(seg) 116 | all_data = np.concatenate(all_data, axis=0) 117 | all_label = np.concatenate(all_label, axis=0) 118 | all_seg = np.concatenate(all_seg, axis=0) 119 | return all_data, all_label, all_seg 120 | 121 | 122 | def prepare_test_data_semseg(): 123 | BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 124 | DATA_DIR = os.path.join(BASE_DIR, 'data') 125 | if not os.path.exists(os.path.join(DATA_DIR, 'stanford_indoor3d')): 126 | os.system('python prepare_data/collect_indoor3d_data.py') 127 | if not os.path.exists(os.path.join(DATA_DIR, 'indoor3d_sem_seg_hdf5_data_test')): 128 | os.system('python prepare_data/gen_indoor3d_h5.py') 129 | 130 | 131 | def load_data_semseg(partition, test_area): 132 | BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 133 | DATA_DIR = os.path.join(BASE_DIR, 'data') 134 | download_S3DIS() 135 | prepare_test_data_semseg() 136 | if partition == 'train': 137 | data_dir = os.path.join(DATA_DIR, 'indoor3d_sem_seg_hdf5_data') 138 | else: 139 | data_dir = os.path.join(DATA_DIR, 'indoor3d_sem_seg_hdf5_data_test') 140 | with open(os.path.join(data_dir, "all_files.txt")) as f: 141 | all_files = [line.rstrip() for line in f] 142 | with open(os.path.join(data_dir, "room_filelist.txt")) as f: 143 | room_filelist = [line.rstrip() for line in f] 144 | data_batchlist, label_batchlist = [], [] 145 | for f in all_files: 146 | file = h5py.File(os.path.join(DATA_DIR, f), 'r+') 147 | data = file["data"][:] 148 | label = file["label"][:] 149 | data_batchlist.append(data) 150 | label_batchlist.append(label) 151 | data_batches = np.concatenate(data_batchlist, 0) 152 | seg_batches = np.concatenate(label_batchlist, 0) 153 | test_area_name = "Area_" + test_area 154 | train_idxs, test_idxs = [], [] 155 | for i, room_name in enumerate(room_filelist): 156 | if test_area_name in room_name: 157 | test_idxs.append(i) 158 | else: 159 | train_idxs.append(i) 160 | if partition == 'train': 161 | all_data = data_batches[train_idxs, ...] 162 | all_seg = seg_batches[train_idxs, ...] 163 | else: 164 | all_data = data_batches[test_idxs, ...] 165 | all_seg = seg_batches[test_idxs, ...] 166 | return all_data, all_seg 167 | 168 | 169 | def load_color_partseg(): 170 | colors = [] 171 | labels = [] 172 | f = open("prepare_data/meta/partseg_colors.txt") 173 | for line in json.load(f): 174 | colors.append(line['color']) 175 | labels.append(line['label']) 176 | partseg_colors = np.array(colors) 177 | partseg_colors = partseg_colors[:, [2, 1, 0]] 178 | partseg_labels = np.array(labels) 179 | font = cv2.FONT_HERSHEY_SIMPLEX 180 | img_size = 1350 181 | img = np.zeros((1350, 1890, 3), dtype="uint8") 182 | cv2.rectangle(img, (0, 0), (1900, 1900), [255, 255, 255], thickness=-1) 183 | column_numbers = [4, 2, 2, 4, 4, 3, 3, 2, 4, 2, 6, 2, 3, 3, 3, 3] 184 | column_gaps = [320, 320, 300, 300, 285, 285] 185 | color_size = 64 186 | color_index = 0 187 | label_index = 0 188 | row_index = 16 189 | for row in range(0, img_size): 190 | column_index = 32 191 | for column in range(0, img_size): 192 | color = partseg_colors[color_index] 193 | label = partseg_labels[label_index] 194 | length = len(str(label)) 195 | cv2.rectangle(img, (column_index, row_index), (column_index + color_size, row_index + color_size), 196 | color=(int(color[0]), int(color[1]), int(color[2])), thickness=-1) 197 | img = cv2.putText(img, label, (column_index + int(color_size * 1.15), row_index + int(color_size / 2)), 198 | font, 199 | 0.76, (0, 0, 0), 2) 200 | column_index = column_index + column_gaps[column] 201 | color_index = color_index + 1 202 | label_index = label_index + 1 203 | if color_index >= 50: 204 | cv2.imwrite("prepare_data/meta/partseg_colors.png", img, [cv2.IMWRITE_PNG_COMPRESSION, 0]) 205 | return np.array(colors) 206 | elif (column + 1 >= column_numbers[row]): 207 | break 208 | row_index = row_index + int(color_size * 1.3) 209 | if (row_index >= img_size): 210 | break 211 | 212 | 213 | def load_color_semseg(): 214 | colors = [] 215 | labels = [] 216 | f = open("prepare_data/meta/semseg_colors.txt") 217 | for line in json.load(f): 218 | colors.append(line['color']) 219 | labels.append(line['label']) 220 | semseg_colors = np.array(colors) 221 | semseg_colors = semseg_colors[:, [2, 1, 0]] 222 | partseg_labels = np.array(labels) 223 | font = cv2.FONT_HERSHEY_SIMPLEX 224 | img_size = 1500 225 | img = np.zeros((500, img_size, 3), dtype="uint8") 226 | cv2.rectangle(img, (0, 0), (img_size, 750), [255, 255, 255], thickness=-1) 227 | color_size = 64 228 | color_index = 0 229 | label_index = 0 230 | row_index = 16 231 | for _ in range(0, img_size): 232 | column_index = 32 233 | for _ in range(0, img_size): 234 | color = semseg_colors[color_index] 235 | label = partseg_labels[label_index] 236 | length = len(str(label)) 237 | cv2.rectangle(img, (column_index, row_index), (column_index + color_size, row_index + color_size), 238 | color=(int(color[0]), int(color[1]), int(color[2])), thickness=-1) 239 | img = cv2.putText(img, label, (column_index + int(color_size * 1.15), row_index + int(color_size / 2)), 240 | font, 241 | 0.7, (0, 0, 0), 2) 242 | column_index = column_index + 200 243 | color_index = color_index + 1 244 | label_index = label_index + 1 245 | if color_index >= 13: 246 | cv2.imwrite("prepare_data/meta/semseg_colors.png", img, [cv2.IMWRITE_PNG_COMPRESSION, 0]) 247 | return np.array(colors) 248 | elif (column_index >= 1280): 249 | break 250 | row_index = row_index + int(color_size * 1.3) 251 | if (row_index >= img_size): 252 | break 253 | 254 | 255 | def translate_pointcloud(pointcloud): 256 | xyz1 = np.random.uniform(low=2./3., high=3./2., size=[3]) 257 | xyz2 = np.random.uniform(low=-0.2, high=0.2, size=[3]) 258 | 259 | translated_pointcloud = np.add(np.multiply(pointcloud, xyz1), xyz2).astype('float32') 260 | return translated_pointcloud 261 | 262 | 263 | def jitter_pointcloud(pointcloud, sigma=0.01, clip=0.02): 264 | N, C = pointcloud.shape 265 | pointcloud += np.clip(sigma * np.random.randn(N, C), -1*clip, clip) 266 | return pointcloud 267 | 268 | 269 | def rotate_pointcloud(pointcloud): 270 | theta = np.pi*2 * np.random.uniform() 271 | rotation_matrix = np.array([[np.cos(theta), -np.sin(theta)],[np.sin(theta), np.cos(theta)]]) 272 | pointcloud[:,[0,2]] = pointcloud[:,[0,2]].dot(rotation_matrix) # random rotation (x,z) 273 | return pointcloud 274 | 275 | 276 | class ModelNet40(Dataset): 277 | def __init__(self, num_points, partition='train'): 278 | self.data, self.label = load_data_cls(partition) 279 | self.num_points = num_points 280 | self.partition = partition 281 | 282 | def __getitem__(self, item): 283 | pointcloud = self.data[item][:self.num_points] 284 | label = self.label[item] 285 | if self.partition == 'train': 286 | pointcloud = translate_pointcloud(pointcloud) 287 | np.random.shuffle(pointcloud) 288 | return pointcloud, label 289 | 290 | def __len__(self): 291 | return self.data.shape[0] 292 | 293 | 294 | class ShapeNetPart(Dataset): 295 | def __init__(self, num_points, partition='train', class_choice=None): 296 | self.data, self.label, self.seg = load_data_partseg(partition) 297 | self.cat2id = {'airplane': 0, 'bag': 1, 'cap': 2, 'car': 3, 'chair': 4, 298 | 'earphone': 5, 'guitar': 6, 'knife': 7, 'lamp': 8, 'laptop': 9, 299 | 'motor': 10, 'mug': 11, 'pistol': 12, 'rocket': 13, 'skateboard': 14, 'table': 15} 300 | self.seg_num = [4, 2, 2, 4, 4, 3, 3, 2, 4, 2, 6, 2, 3, 3, 3, 3] 301 | self.index_start = [0, 4, 6, 8, 12, 16, 19, 22, 24, 28, 30, 36, 38, 41, 44, 47] 302 | self.num_points = num_points 303 | self.partition = partition 304 | self.class_choice = class_choice 305 | self.partseg_colors = load_color_partseg() 306 | 307 | if self.class_choice != None: 308 | id_choice = self.cat2id[self.class_choice] 309 | indices = (self.label == id_choice).squeeze() 310 | self.data = self.data[indices] 311 | self.label = self.label[indices] 312 | self.seg = self.seg[indices] 313 | self.seg_num_all = self.seg_num[id_choice] 314 | self.seg_start_index = self.index_start[id_choice] 315 | else: 316 | self.seg_num_all = 50 317 | self.seg_start_index = 0 318 | 319 | 320 | def __getitem__(self, item): 321 | pointcloud = self.data[item][:self.num_points] 322 | label = self.label[item] 323 | seg = self.seg[item][:self.num_points] 324 | if self.partition == 'trainval': 325 | # pointcloud = translate_pointcloud(pointcloud) 326 | indices = list(range(pointcloud.shape[0])) 327 | np.random.shuffle(indices) 328 | pointcloud = pointcloud[indices] 329 | seg = seg[indices] 330 | return pointcloud, label, seg 331 | 332 | def __len__(self): 333 | return self.data.shape[0] 334 | 335 | 336 | class S3DIS(Dataset): 337 | def __init__(self, num_points=4096, partition='train', test_area='1'): 338 | self.data, self.seg = load_data_semseg(partition, test_area) 339 | self.num_points = num_points 340 | self.partition = partition 341 | self.semseg_colors = load_color_semseg() 342 | 343 | def __getitem__(self, item): 344 | pointcloud = self.data[item][:self.num_points] 345 | seg = self.seg[item][:self.num_points] 346 | if self.partition == 'train': 347 | indices = list(range(pointcloud.shape[0])) 348 | np.random.shuffle(indices) 349 | pointcloud = pointcloud[indices] 350 | seg = seg[indices] 351 | seg = torch.LongTensor(seg) 352 | return pointcloud, seg 353 | 354 | def __len__(self): 355 | return self.data.shape[0] 356 | 357 | 358 | class ScanNet(Dataset): 359 | def __init__(self, num_point=8192, partition='train', 360 | data_root='scannet', classes=20, block_size=1.5, 361 | sample_rate=1.0, transform=None, use_rgb=False): 362 | self.partition = partition 363 | self.num_point = num_point 364 | self.block_size = block_size 365 | self.transform = transform 366 | xyz_all = [] 367 | label_all = [] 368 | if not isinstance(partition, list): 369 | partition = [partition] 370 | for i in partition: 371 | data_file = os.path.join( 372 | data_root, 'scannet_{}_rgb21c_pointid.pickle'.format(i)) 373 | file_pickle = open(data_file, 'rb') 374 | _xyz_all = pickle.load(file_pickle) 375 | _label_all = pickle.load(file_pickle) 376 | file_pickle.close() 377 | xyz_all.append(_xyz_all) 378 | label_all.append(_label_all) 379 | xyz_all = np.hstack(xyz_all) 380 | label_all = np.hstack(label_all) 381 | self.label_all = [] # for change 0-20 to 0-19 + 255 382 | self.room_coord_min, self.room_coord_max = [], [] 383 | num_point_all = [] 384 | for index in range(len(xyz_all)): 385 | xyz, label = xyz_all[index], label_all[index] # xyzrgb, N*6; l, N 386 | coord_min, coord_max = np.amin( 387 | xyz, axis=0)[:3], np.amax(xyz, axis=0)[:3] 388 | self.room_coord_min.append(coord_min) 389 | self.room_coord_max.append(coord_max) 390 | num_point_all.append(label.size) 391 | # we have set all ignore_class to 0 392 | # class 0 is also ignored 393 | # so we set all them as 255 394 | label_new = label - 1 395 | label_new[label == 0] = 255 396 | self.label_all.append(label_new.astype(np.uint8)) 397 | sample_prob = num_point_all / np.sum(num_point_all) 398 | num_iter = int(np.sum(num_point_all) * sample_rate / num_point) 399 | room_idxs = [] 400 | for index in range(len(xyz_all)): 401 | room_idxs.extend( 402 | [index] * int(round(sample_prob[index] * num_iter))) 403 | self.room_idxs = np.array(room_idxs) 404 | self.xyz_all = xyz_all 405 | 406 | # whether load RGB information 407 | self.use_rgb = use_rgb 408 | 409 | print("Totally {} samples in {} set.".format(len(self.room_idxs), partition)) 410 | 411 | def __getitem__(self, idx): 412 | room_idx = self.room_idxs[idx] 413 | points = self.xyz_all[room_idx] # N * 6 414 | if not self.use_rgb: 415 | points = points[:, :3] 416 | labels = self.label_all[room_idx] # N 417 | N_points = points.shape[0] 418 | 419 | for i in range(10): 420 | center = points[np.random.choice(N_points)][:3] 421 | block_min = center - [self.block_size / 422 | 2.0, self.block_size / 2.0, 0] 423 | block_max = center + [self.block_size / 424 | 2.0, self.block_size / 2.0, 0] 425 | block_min[2] = self.room_coord_min[room_idx][2] 426 | block_max[2] = self.room_coord_max[room_idx][2] 427 | point_idxs = np.where((points[:, 0] >= block_min[0]) & 428 | (points[:, 0] <= block_max[0]) & 429 | (points[:, 1] >= block_min[1]) & 430 | (points[:, 1] <= block_max[1]))[0] 431 | if point_idxs.size == 0: 432 | continue 433 | vidx = np.ceil((points[point_idxs, :3] - block_min) / 434 | (block_max - block_min) * [31.0, 31.0, 62.0]) 435 | vidx = np.unique(vidx[:, 0] * 31.0 * 62.0 + 436 | vidx[:, 1] * 62.0 + vidx[:, 2]) 437 | if ((labels[point_idxs] != 255).sum() / point_idxs.size >= 0.7) and (vidx.size/31.0/31.0/62.0 >= 0.02): 438 | break 439 | 440 | if point_idxs.size >= self.num_point: 441 | selected_point_idxs = np.random.choice( 442 | point_idxs, self.num_point, replace=False) 443 | else: 444 | selected_point_idxs = np.random.choice( 445 | point_idxs, self.num_point, replace=True) 446 | # normalize 447 | selected_points = points[selected_point_idxs, :] # num_point * 3/6 448 | num_feats = 9 if self.use_rgb else 6 449 | current_points = np.zeros( 450 | (self.num_point, num_feats)) # num_point * 6/9 451 | current_points[:, -3] = selected_points[:, 0] / \ 452 | self.room_coord_max[room_idx][0] 453 | current_points[:, -2] = selected_points[:, 1] / \ 454 | self.room_coord_max[room_idx][1] 455 | current_points[:, -1] = selected_points[:, 2] / \ 456 | self.room_coord_max[room_idx][2] 457 | selected_points[:, 0] = selected_points[:, 0] - center[0] 458 | selected_points[:, 1] = selected_points[:, 1] - center[1] 459 | current_points[:, 0:3] = selected_points[:, 0:3] 460 | if self.use_rgb: 461 | current_points[:, 3:6] = selected_points[:, 3:6] / 255. 462 | current_labels = labels[selected_point_idxs] 463 | if self.transform is not None: 464 | current_points, current_labels = self.transform( 465 | current_points, current_labels) 466 | return current_points, current_labels 467 | 468 | def __len__(self): 469 | return len(self.room_idxs) 470 | 471 | if __name__ == '__main__': 472 | train = ModelNet40(1024) 473 | test = ModelNet40(1024, 'test') 474 | data, label = train[0] 475 | print(data.shape) 476 | print(label.shape) 477 | 478 | trainval = ShapeNetPart(2048, 'trainval') 479 | test = ShapeNetPart(2048, 'test') 480 | data, label, seg = trainval[0] 481 | print(data.shape) 482 | print(label.shape) 483 | print(seg.shape) 484 | 485 | train = S3DIS(4096) 486 | test = S3DIS(4096, 'test') 487 | data, seg = train[0] 488 | print(data.shape) 489 | print(seg.shape) 490 | 491 | train = ScanNet(8192) 492 | test = ScanNet(8192, 'test') 493 | data, seg = train[0] 494 | print(data.shape) 495 | print(seg.shape) 496 | -------------------------------------------------------------------------------- /image/DGCNN.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antao97/dgcnn.pytorch/07d534c2702905010ec9991619f552d8cacae45b/image/DGCNN.jpg -------------------------------------------------------------------------------- /image/partseg_colors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antao97/dgcnn.pytorch/07d534c2702905010ec9991619f552d8cacae45b/image/partseg_colors.png -------------------------------------------------------------------------------- /image/partseg_visu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antao97/dgcnn.pytorch/07d534c2702905010ec9991619f552d8cacae45b/image/partseg_visu.png -------------------------------------------------------------------------------- /image/semseg_s3dis_colors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antao97/dgcnn.pytorch/07d534c2702905010ec9991619f552d8cacae45b/image/semseg_s3dis_colors.png -------------------------------------------------------------------------------- /image/semseg_s3dis_visu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antao97/dgcnn.pytorch/07d534c2702905010ec9991619f552d8cacae45b/image/semseg_s3dis_visu.png -------------------------------------------------------------------------------- /image/semseg_scannet_colors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antao97/dgcnn.pytorch/07d534c2702905010ec9991619f552d8cacae45b/image/semseg_scannet_colors.png -------------------------------------------------------------------------------- /image/semseg_scannet_visu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antao97/dgcnn.pytorch/07d534c2702905010ec9991619f552d8cacae45b/image/semseg_scannet_visu.png -------------------------------------------------------------------------------- /main_cls.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | """ 4 | @Author: Yue Wang 5 | @Contact: yuewangx@mit.edu 6 | @File: main_cls.py 7 | @Time: 2018/10/13 10:39 PM 8 | 9 | Modified by 10 | @Author: An Tao 11 | @Contact: ta19@mails.tsinghua.edu.cn 12 | @Time: 2019/12/30 9:32 PM 13 | """ 14 | 15 | 16 | from __future__ import print_function 17 | import os 18 | import argparse 19 | import torch 20 | import torch.nn as nn 21 | import torch.nn.functional as F 22 | import torch.optim as optim 23 | from torch.optim.lr_scheduler import CosineAnnealingLR, StepLR 24 | from data import ModelNet40 25 | from model import PointNet, DGCNN_cls 26 | import numpy as np 27 | from torch.utils.data import DataLoader 28 | from util import cal_loss, IOStream 29 | import sklearn.metrics as metrics 30 | 31 | 32 | def _init_(): 33 | if not os.path.exists('outputs'): 34 | os.makedirs('outputs') 35 | if not os.path.exists('outputs/'+args.exp_name): 36 | os.makedirs('outputs/'+args.exp_name) 37 | if not os.path.exists('outputs/'+args.exp_name+'/'+'models'): 38 | os.makedirs('outputs/'+args.exp_name+'/'+'models') 39 | os.system('cp main_cls.py outputs'+'/'+args.exp_name+'/'+'main_cls.py.backup') 40 | os.system('cp model.py outputs' + '/' + args.exp_name + '/' + 'model.py.backup') 41 | os.system('cp util.py outputs' + '/' + args.exp_name + '/' + 'util.py.backup') 42 | os.system('cp data.py outputs' + '/' + args.exp_name + '/' + 'data.py.backup') 43 | 44 | def train(args, io): 45 | train_loader = DataLoader(ModelNet40(partition='train', num_points=args.num_points), num_workers=8, 46 | batch_size=args.batch_size, shuffle=True, drop_last=True) 47 | test_loader = DataLoader(ModelNet40(partition='test', num_points=args.num_points), num_workers=8, 48 | batch_size=args.test_batch_size, shuffle=True, drop_last=False) 49 | 50 | device = torch.device("cuda" if args.cuda else "cpu") 51 | 52 | #Try to load models 53 | if args.model == 'pointnet': 54 | model = PointNet(args).to(device) 55 | elif args.model == 'dgcnn': 56 | model = DGCNN_cls(args).to(device) 57 | else: 58 | raise Exception("Not implemented") 59 | 60 | print(str(model)) 61 | 62 | model = nn.DataParallel(model) 63 | print("Let's use", torch.cuda.device_count(), "GPUs!") 64 | 65 | if args.use_sgd: 66 | print("Use SGD") 67 | opt = optim.SGD(model.parameters(), lr=args.lr*100, momentum=args.momentum, weight_decay=1e-4) 68 | else: 69 | print("Use Adam") 70 | opt = optim.Adam(model.parameters(), lr=args.lr, weight_decay=1e-4) 71 | 72 | if args.scheduler == 'cos': 73 | scheduler = CosineAnnealingLR(opt, args.epochs, eta_min=1e-3) 74 | elif args.scheduler == 'step': 75 | scheduler = StepLR(opt, step_size=20, gamma=0.7) 76 | 77 | criterion = cal_loss 78 | 79 | best_test_acc = 0 80 | for epoch in range(args.epochs): 81 | #################### 82 | # Train 83 | #################### 84 | train_loss = 0.0 85 | count = 0.0 86 | model.train() 87 | train_pred = [] 88 | train_true = [] 89 | for data, label in train_loader: 90 | data, label = data.to(device), label.to(device).squeeze() 91 | data = data.permute(0, 2, 1) 92 | batch_size = data.size()[0] 93 | opt.zero_grad() 94 | logits = model(data) 95 | loss = criterion(logits, label) 96 | loss.backward() 97 | opt.step() 98 | preds = logits.max(dim=1)[1] 99 | count += batch_size 100 | train_loss += loss.item() * batch_size 101 | train_true.append(label.cpu().numpy()) 102 | train_pred.append(preds.detach().cpu().numpy()) 103 | if args.scheduler == 'cos': 104 | scheduler.step() 105 | elif args.scheduler == 'step': 106 | if opt.param_groups[0]['lr'] > 1e-5: 107 | scheduler.step() 108 | if opt.param_groups[0]['lr'] < 1e-5: 109 | for param_group in opt.param_groups: 110 | param_group['lr'] = 1e-5 111 | 112 | train_true = np.concatenate(train_true) 113 | train_pred = np.concatenate(train_pred) 114 | outstr = 'Train %d, loss: %.6f, train acc: %.6f, train avg acc: %.6f' % (epoch, 115 | train_loss*1.0/count, 116 | metrics.accuracy_score( 117 | train_true, train_pred), 118 | metrics.balanced_accuracy_score( 119 | train_true, train_pred)) 120 | io.cprint(outstr) 121 | 122 | #################### 123 | # Test 124 | #################### 125 | test_loss = 0.0 126 | count = 0.0 127 | model.eval() 128 | test_pred = [] 129 | test_true = [] 130 | for data, label in test_loader: 131 | data, label = data.to(device), label.to(device).squeeze() 132 | data = data.permute(0, 2, 1) 133 | batch_size = data.size()[0] 134 | logits = model(data) 135 | loss = criterion(logits, label) 136 | preds = logits.max(dim=1)[1] 137 | count += batch_size 138 | test_loss += loss.item() * batch_size 139 | test_true.append(label.cpu().numpy()) 140 | test_pred.append(preds.detach().cpu().numpy()) 141 | test_true = np.concatenate(test_true) 142 | test_pred = np.concatenate(test_pred) 143 | test_acc = metrics.accuracy_score(test_true, test_pred) 144 | avg_per_class_acc = metrics.balanced_accuracy_score(test_true, test_pred) 145 | outstr = 'Test %d, loss: %.6f, test acc: %.6f, test avg acc: %.6f' % (epoch, 146 | test_loss*1.0/count, 147 | test_acc, 148 | avg_per_class_acc) 149 | io.cprint(outstr) 150 | if test_acc >= best_test_acc: 151 | best_test_acc = test_acc 152 | torch.save(model.state_dict(), 'outputs/%s/models/model.t7' % args.exp_name) 153 | 154 | 155 | def test(args, io): 156 | test_loader = DataLoader(ModelNet40(partition='test', num_points=args.num_points), 157 | batch_size=args.test_batch_size, shuffle=True, drop_last=False) 158 | 159 | device = torch.device("cuda" if args.cuda else "cpu") 160 | 161 | #Try to load models 162 | if args.model == 'pointnet': 163 | model = PointNet(args).to(device) 164 | elif args.model == 'dgcnn': 165 | model = DGCNN_cls(args).to(device) 166 | else: 167 | raise Exception("Not implemented") 168 | 169 | model = nn.DataParallel(model) 170 | model.load_state_dict(torch.load(args.model_path)) 171 | model = model.eval() 172 | test_acc = 0.0 173 | count = 0.0 174 | test_true = [] 175 | test_pred = [] 176 | for data, label in test_loader: 177 | 178 | data, label = data.to(device), label.to(device).squeeze() 179 | data = data.permute(0, 2, 1) 180 | batch_size = data.size()[0] 181 | logits = model(data) 182 | preds = logits.max(dim=1)[1] 183 | test_true.append(label.cpu().numpy()) 184 | test_pred.append(preds.detach().cpu().numpy()) 185 | test_true = np.concatenate(test_true) 186 | test_pred = np.concatenate(test_pred) 187 | test_acc = metrics.accuracy_score(test_true, test_pred) 188 | avg_per_class_acc = metrics.balanced_accuracy_score(test_true, test_pred) 189 | outstr = 'Test :: test acc: %.6f, test avg acc: %.6f'%(test_acc, avg_per_class_acc) 190 | io.cprint(outstr) 191 | 192 | 193 | if __name__ == "__main__": 194 | # Training settings 195 | parser = argparse.ArgumentParser(description='Point Cloud Recognition') 196 | parser.add_argument('--exp_name', type=str, default='exp', metavar='N', 197 | help='Name of the experiment') 198 | parser.add_argument('--model', type=str, default='dgcnn', metavar='N', 199 | choices=['pointnet', 'dgcnn'], 200 | help='Model to use, [pointnet, dgcnn]') 201 | parser.add_argument('--dataset', type=str, default='modelnet40', metavar='N', 202 | choices=['modelnet40']) 203 | parser.add_argument('--batch_size', type=int, default=32, metavar='batch_size', 204 | help='Size of batch)') 205 | parser.add_argument('--test_batch_size', type=int, default=16, metavar='batch_size', 206 | help='Size of batch)') 207 | parser.add_argument('--epochs', type=int, default=250, metavar='N', 208 | help='number of episode to train ') 209 | parser.add_argument('--use_sgd', type=bool, default=True, 210 | help='Use SGD') 211 | parser.add_argument('--lr', type=float, default=0.001, metavar='LR', 212 | help='learning rate (default: 0.001, 0.1 if using sgd)') 213 | parser.add_argument('--momentum', type=float, default=0.9, metavar='M', 214 | help='SGD momentum (default: 0.9)') 215 | parser.add_argument('--scheduler', type=str, default='cos', metavar='N', 216 | choices=['cos', 'step'], 217 | help='Scheduler to use, [cos, step]') 218 | parser.add_argument('--no_cuda', type=bool, default=False, 219 | help='enables CUDA training') 220 | parser.add_argument('--seed', type=int, default=1, metavar='S', 221 | help='random seed (default: 1)') 222 | parser.add_argument('--eval', type=bool, default=False, 223 | help='evaluate the model') 224 | parser.add_argument('--num_points', type=int, default=1024, 225 | help='num of points to use') 226 | parser.add_argument('--dropout', type=float, default=0.5, 227 | help='initial dropout rate') 228 | parser.add_argument('--emb_dims', type=int, default=1024, metavar='N', 229 | help='Dimension of embeddings') 230 | parser.add_argument('--k', type=int, default=20, metavar='N', 231 | help='Num of nearest neighbors to use') 232 | parser.add_argument('--model_path', type=str, default='', metavar='N', 233 | help='Pretrained model path') 234 | args = parser.parse_args() 235 | 236 | _init_() 237 | 238 | io = IOStream('outputs/' + args.exp_name + '/run.log') 239 | io.cprint(str(args)) 240 | 241 | args.cuda = not args.no_cuda and torch.cuda.is_available() 242 | torch.manual_seed(args.seed) 243 | if args.cuda: 244 | io.cprint( 245 | 'Using GPU : ' + str(torch.cuda.current_device()) + ' from ' + str(torch.cuda.device_count()) + ' devices') 246 | torch.cuda.manual_seed(args.seed) 247 | else: 248 | io.cprint('Using CPU') 249 | 250 | if not args.eval: 251 | train(args, io) 252 | else: 253 | test(args, io) 254 | -------------------------------------------------------------------------------- /main_partseg.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | """ 4 | @Author: An Tao, Pengliang Ji 5 | @Contact: ta19@mails.tsinghua.edu.cn, jpl1723@buaa.edu.cn 6 | @File: main_partseg.py 7 | @Time: 2021/7/20 7:49 PM 8 | """ 9 | 10 | 11 | from __future__ import print_function 12 | import os 13 | import argparse 14 | import torch 15 | import torch.nn as nn 16 | import torch.nn.functional as F 17 | import torch.optim as optim 18 | from torch.optim.lr_scheduler import CosineAnnealingLR, StepLR 19 | from data import ShapeNetPart 20 | from model import DGCNN_partseg 21 | import numpy as np 22 | from torch.utils.data import DataLoader 23 | from util import cal_loss, IOStream 24 | import sklearn.metrics as metrics 25 | from plyfile import PlyData, PlyElement 26 | 27 | global class_cnts 28 | class_indexs = np.zeros((16,), dtype=int) 29 | global visual_warning 30 | visual_warning = True 31 | 32 | class_choices = ['airplane', 'bag', 'cap', 'car', 'chair', 'earphone', 'guitar', 'knife', 'lamp', 'laptop', 'motorbike', 'mug', 'pistol', 'rocket', 'skateboard', 'table'] 33 | seg_num = [4, 2, 2, 4, 4, 3, 3, 2, 4, 2, 6, 2, 3, 3, 3, 3] 34 | index_start = [0, 4, 6, 8, 12, 16, 19, 22, 24, 28, 30, 36, 38, 41, 44, 47] 35 | 36 | 37 | def _init_(): 38 | if not os.path.exists('outputs'): 39 | os.makedirs('outputs') 40 | if not os.path.exists('outputs/'+args.exp_name): 41 | os.makedirs('outputs/'+args.exp_name) 42 | if not os.path.exists('outputs/'+args.exp_name+'/'+'models'): 43 | os.makedirs('outputs/'+args.exp_name+'/'+'models') 44 | if not os.path.exists('outputs/'+args.exp_name+'/'+'visualization'): 45 | os.makedirs('outputs/'+args.exp_name+'/'+'visualization') 46 | os.system('cp main_partseg.py outputs'+'/'+args.exp_name+'/'+'main_partseg.py.backup') 47 | os.system('cp model.py outputs' + '/' + args.exp_name + '/' + 'model.py.backup') 48 | os.system('cp util.py outputs' + '/' + args.exp_name + '/' + 'util.py.backup') 49 | os.system('cp data.py outputs' + '/' + args.exp_name + '/' + 'data.py.backup') 50 | 51 | 52 | def calculate_shape_IoU(pred_np, seg_np, label, class_choice, visual=False): 53 | if not visual: 54 | label = label.squeeze() 55 | shape_ious = [] 56 | for shape_idx in range(seg_np.shape[0]): 57 | if not class_choice: 58 | start_index = index_start[label[shape_idx]] 59 | num = seg_num[label[shape_idx]] 60 | parts = range(start_index, start_index + num) 61 | else: 62 | parts = range(seg_num[label[0]]) 63 | part_ious = [] 64 | for part in parts: 65 | I = np.sum(np.logical_and(pred_np[shape_idx] == part, seg_np[shape_idx] == part)) 66 | U = np.sum(np.logical_or(pred_np[shape_idx] == part, seg_np[shape_idx] == part)) 67 | if U == 0: 68 | iou = 1 # If the union of groundtruth and prediction points is empty, then count part IoU as 1 69 | else: 70 | iou = I / float(U) 71 | part_ious.append(iou) 72 | shape_ious.append(np.mean(part_ious)) 73 | return shape_ious 74 | 75 | 76 | def visualization(visu, visu_format, data, pred, seg, label, partseg_colors, class_choice): 77 | global class_indexs 78 | global visual_warning 79 | visu = visu.split('_') 80 | for i in range(0, data.shape[0]): 81 | RGB = [] 82 | RGB_gt = [] 83 | skip = False 84 | classname = class_choices[int(label[i])] 85 | class_index = class_indexs[int(label[i])] 86 | if visu[0] != 'all': 87 | if len(visu) != 1: 88 | if visu[0] != classname or visu[1] != str(class_index): 89 | skip = True 90 | else: 91 | visual_warning = False 92 | elif visu[0] != classname: 93 | skip = True 94 | else: 95 | visual_warning = False 96 | elif class_choice != None: 97 | skip = True 98 | else: 99 | visual_warning = False 100 | if skip: 101 | class_indexs[int(label[i])] = class_indexs[int(label[i])] + 1 102 | else: 103 | if not os.path.exists('outputs/'+args.exp_name+'/'+'visualization'+'/'+classname): 104 | os.makedirs('outputs/'+args.exp_name+'/'+'visualization'+'/'+classname) 105 | for j in range(0, data.shape[2]): 106 | RGB.append(partseg_colors[int(pred[i][j])]) 107 | RGB_gt.append(partseg_colors[int(seg[i][j])]) 108 | pred_np = [] 109 | seg_np = [] 110 | pred_np.append(pred[i].cpu().numpy()) 111 | seg_np.append(seg[i].cpu().numpy()) 112 | xyz_np = data[i].cpu().numpy() 113 | xyzRGB = np.concatenate((xyz_np.transpose(1, 0), np.array(RGB)), axis=1) 114 | xyzRGB_gt = np.concatenate((xyz_np.transpose(1, 0), np.array(RGB_gt)), axis=1) 115 | IoU = calculate_shape_IoU(np.array(pred_np), np.array(seg_np), label[i].cpu().numpy(), class_choice, visual=True) 116 | IoU = str(round(IoU[0], 4)) 117 | filepath = 'outputs/'+args.exp_name+'/'+'visualization'+'/'+classname+'/'+classname+'_'+str(class_index)+'_pred_'+IoU+'.'+visu_format 118 | filepath_gt = 'outputs/'+args.exp_name+'/'+'visualization'+'/'+classname+'/'+classname+'_'+str(class_index)+'_gt.'+visu_format 119 | if visu_format=='txt': 120 | np.savetxt(filepath, xyzRGB, fmt='%s', delimiter=' ') 121 | np.savetxt(filepath_gt, xyzRGB_gt, fmt='%s', delimiter=' ') 122 | print('TXT visualization file saved in', filepath) 123 | print('TXT visualization file saved in', filepath_gt) 124 | elif visu_format=='ply': 125 | xyzRGB = [(xyzRGB[i, 0], xyzRGB[i, 1], xyzRGB[i, 2], xyzRGB[i, 3], xyzRGB[i, 4], xyzRGB[i, 5]) for i in range(xyzRGB.shape[0])] 126 | xyzRGB_gt = [(xyzRGB_gt[i, 0], xyzRGB_gt[i, 1], xyzRGB_gt[i, 2], xyzRGB_gt[i, 3], xyzRGB_gt[i, 4], xyzRGB_gt[i, 5]) for i in range(xyzRGB_gt.shape[0])] 127 | vertex = PlyElement.describe(np.array(xyzRGB, dtype=[('x', 'f4'), ('y', 'f4'), ('z', 'f4'), ('red', 'u1'), ('green', 'u1'), ('blue', 'u1')]), 'vertex') 128 | PlyData([vertex]).write(filepath) 129 | vertex = PlyElement.describe(np.array(xyzRGB_gt, dtype=[('x', 'f4'), ('y', 'f4'), ('z', 'f4'), ('red', 'u1'), ('green', 'u1'), ('blue', 'u1')]), 'vertex') 130 | PlyData([vertex]).write(filepath_gt) 131 | print('PLY visualization file saved in', filepath) 132 | print('PLY visualization file saved in', filepath_gt) 133 | else: 134 | print('ERROR!! Unknown visualization format: %s, please use txt or ply.' % \ 135 | (visu_format)) 136 | exit() 137 | class_indexs[int(label[i])] = class_indexs[int(label[i])] + 1 138 | 139 | 140 | def train(args, io): 141 | train_dataset = ShapeNetPart(partition='trainval', num_points=args.num_points, class_choice=args.class_choice) 142 | if (len(train_dataset) < 100): 143 | drop_last = False 144 | else: 145 | drop_last = True 146 | train_loader = DataLoader(train_dataset, num_workers=8, batch_size=args.batch_size, shuffle=True, drop_last=drop_last) 147 | test_loader = DataLoader(ShapeNetPart(partition='test', num_points=args.num_points, class_choice=args.class_choice), 148 | num_workers=8, batch_size=args.test_batch_size, shuffle=True, drop_last=False) 149 | 150 | device = torch.device("cuda" if args.cuda else "cpu") 151 | 152 | #Try to load models 153 | seg_num_all = train_loader.dataset.seg_num_all 154 | seg_start_index = train_loader.dataset.seg_start_index 155 | if args.model == 'dgcnn': 156 | model = DGCNN_partseg(args, seg_num_all).to(device) 157 | else: 158 | raise Exception("Not implemented") 159 | print(str(model)) 160 | 161 | model = nn.DataParallel(model) 162 | print("Let's use", torch.cuda.device_count(), "GPUs!") 163 | 164 | if args.use_sgd: 165 | print("Use SGD") 166 | opt = optim.SGD(model.parameters(), lr=args.lr*100, momentum=args.momentum, weight_decay=1e-4) 167 | else: 168 | print("Use Adam") 169 | opt = optim.Adam(model.parameters(), lr=args.lr, weight_decay=1e-4) 170 | 171 | if args.scheduler == 'cos': 172 | scheduler = CosineAnnealingLR(opt, args.epochs, eta_min=1e-3) 173 | elif args.scheduler == 'step': 174 | scheduler = StepLR(opt, step_size=20, gamma=0.5) 175 | 176 | criterion = cal_loss 177 | 178 | best_test_iou = 0 179 | for epoch in range(args.epochs): 180 | #################### 181 | # Train 182 | #################### 183 | train_loss = 0.0 184 | count = 0.0 185 | model.train() 186 | train_true_cls = [] 187 | train_pred_cls = [] 188 | train_true_seg = [] 189 | train_pred_seg = [] 190 | train_label_seg = [] 191 | for data, label, seg in train_loader: 192 | seg = seg - seg_start_index 193 | label_one_hot = np.zeros((label.shape[0], 16)) 194 | for idx in range(label.shape[0]): 195 | label_one_hot[idx, label[idx]] = 1 196 | label_one_hot = torch.from_numpy(label_one_hot.astype(np.float32)) 197 | data, label_one_hot, seg = data.to(device), label_one_hot.to(device), seg.to(device) 198 | data = data.permute(0, 2, 1) 199 | batch_size = data.size()[0] 200 | opt.zero_grad() 201 | seg_pred = model(data, label_one_hot) 202 | seg_pred = seg_pred.permute(0, 2, 1).contiguous() 203 | loss = criterion(seg_pred.view(-1, seg_num_all), seg.view(-1,1).squeeze()) 204 | loss.backward() 205 | opt.step() 206 | pred = seg_pred.max(dim=2)[1] # (batch_size, num_points) 207 | count += batch_size 208 | train_loss += loss.item() * batch_size 209 | seg_np = seg.cpu().numpy() # (batch_size, num_points) 210 | pred_np = pred.detach().cpu().numpy() # (batch_size, num_points) 211 | train_true_cls.append(seg_np.reshape(-1)) # (batch_size * num_points) 212 | train_pred_cls.append(pred_np.reshape(-1)) # (batch_size * num_points) 213 | train_true_seg.append(seg_np) 214 | train_pred_seg.append(pred_np) 215 | train_label_seg.append(label.reshape(-1)) 216 | if args.scheduler == 'cos': 217 | scheduler.step() 218 | elif args.scheduler == 'step': 219 | if opt.param_groups[0]['lr'] > 1e-5: 220 | scheduler.step() 221 | if opt.param_groups[0]['lr'] < 1e-5: 222 | for param_group in opt.param_groups: 223 | param_group['lr'] = 1e-5 224 | train_true_cls = np.concatenate(train_true_cls) 225 | train_pred_cls = np.concatenate(train_pred_cls) 226 | train_acc = metrics.accuracy_score(train_true_cls, train_pred_cls) 227 | avg_per_class_acc = metrics.balanced_accuracy_score(train_true_cls, train_pred_cls) 228 | train_true_seg = np.concatenate(train_true_seg, axis=0) 229 | train_pred_seg = np.concatenate(train_pred_seg, axis=0) 230 | train_label_seg = np.concatenate(train_label_seg) 231 | train_ious = calculate_shape_IoU(train_pred_seg, train_true_seg, train_label_seg, args.class_choice) 232 | outstr = 'Train %d, loss: %.6f, train acc: %.6f, train avg acc: %.6f, train iou: %.6f' % (epoch, 233 | train_loss*1.0/count, 234 | train_acc, 235 | avg_per_class_acc, 236 | np.mean(train_ious)) 237 | io.cprint(outstr) 238 | 239 | #################### 240 | # Test 241 | #################### 242 | test_loss = 0.0 243 | count = 0.0 244 | model.eval() 245 | test_true_cls = [] 246 | test_pred_cls = [] 247 | test_true_seg = [] 248 | test_pred_seg = [] 249 | test_label_seg = [] 250 | for data, label, seg in test_loader: 251 | seg = seg - seg_start_index 252 | label_one_hot = np.zeros((label.shape[0], 16)) 253 | for idx in range(label.shape[0]): 254 | label_one_hot[idx, label[idx]] = 1 255 | label_one_hot = torch.from_numpy(label_one_hot.astype(np.float32)) 256 | data, label_one_hot, seg = data.to(device), label_one_hot.to(device), seg.to(device) 257 | data = data.permute(0, 2, 1) 258 | batch_size = data.size()[0] 259 | seg_pred = model(data, label_one_hot) 260 | seg_pred = seg_pred.permute(0, 2, 1).contiguous() 261 | loss = criterion(seg_pred.view(-1, seg_num_all), seg.view(-1,1).squeeze()) 262 | pred = seg_pred.max(dim=2)[1] 263 | count += batch_size 264 | test_loss += loss.item() * batch_size 265 | seg_np = seg.cpu().numpy() 266 | pred_np = pred.detach().cpu().numpy() 267 | test_true_cls.append(seg_np.reshape(-1)) 268 | test_pred_cls.append(pred_np.reshape(-1)) 269 | test_true_seg.append(seg_np) 270 | test_pred_seg.append(pred_np) 271 | test_label_seg.append(label.reshape(-1)) 272 | test_true_cls = np.concatenate(test_true_cls) 273 | test_pred_cls = np.concatenate(test_pred_cls) 274 | test_acc = metrics.accuracy_score(test_true_cls, test_pred_cls) 275 | avg_per_class_acc = metrics.balanced_accuracy_score(test_true_cls, test_pred_cls) 276 | test_true_seg = np.concatenate(test_true_seg, axis=0) 277 | test_pred_seg = np.concatenate(test_pred_seg, axis=0) 278 | test_label_seg = np.concatenate(test_label_seg) 279 | test_ious = calculate_shape_IoU(test_pred_seg, test_true_seg, test_label_seg, args.class_choice) 280 | outstr = 'Test %d, loss: %.6f, test acc: %.6f, test avg acc: %.6f, test iou: %.6f' % (epoch, 281 | test_loss*1.0/count, 282 | test_acc, 283 | avg_per_class_acc, 284 | np.mean(test_ious)) 285 | io.cprint(outstr) 286 | if np.mean(test_ious) >= best_test_iou: 287 | best_test_iou = np.mean(test_ious) 288 | torch.save(model.state_dict(), 'outputs/%s/models/model.t7' % args.exp_name) 289 | 290 | 291 | def test(args, io): 292 | test_loader = DataLoader(ShapeNetPart(partition='test', num_points=args.num_points, class_choice=args.class_choice), 293 | batch_size=args.test_batch_size, shuffle=True, drop_last=False) 294 | device = torch.device("cuda" if args.cuda else "cpu") 295 | 296 | #Try to load models 297 | seg_num_all = test_loader.dataset.seg_num_all 298 | seg_start_index = test_loader.dataset.seg_start_index 299 | partseg_colors = test_loader.dataset.partseg_colors 300 | if args.model == 'dgcnn': 301 | model = DGCNN_partseg(args, seg_num_all).to(device) 302 | else: 303 | raise Exception("Not implemented") 304 | 305 | model = nn.DataParallel(model) 306 | model.load_state_dict(torch.load(args.model_path)) 307 | model = model.eval() 308 | test_acc = 0.0 309 | count = 0.0 310 | test_true_cls = [] 311 | test_pred_cls = [] 312 | test_true_seg = [] 313 | test_pred_seg = [] 314 | test_label_seg = [] 315 | for data, label, seg in test_loader: 316 | seg = seg - seg_start_index 317 | label_one_hot = np.zeros((label.shape[0], 16)) 318 | for idx in range(label.shape[0]): 319 | label_one_hot[idx, label[idx]] = 1 320 | label_one_hot = torch.from_numpy(label_one_hot.astype(np.float32)) 321 | data, label_one_hot, seg = data.to(device), label_one_hot.to(device), seg.to(device) 322 | data = data.permute(0, 2, 1) 323 | batch_size = data.size()[0] 324 | seg_pred = model(data, label_one_hot) 325 | seg_pred = seg_pred.permute(0, 2, 1).contiguous() 326 | pred = seg_pred.max(dim=2)[1] 327 | seg_np = seg.cpu().numpy() 328 | pred_np = pred.detach().cpu().numpy() 329 | test_true_cls.append(seg_np.reshape(-1)) 330 | test_pred_cls.append(pred_np.reshape(-1)) 331 | test_true_seg.append(seg_np) 332 | test_pred_seg.append(pred_np) 333 | test_label_seg.append(label.reshape(-1)) 334 | # visiualization 335 | visualization(args.visu, args.visu_format, data, pred, seg, label, partseg_colors, args.class_choice) 336 | if visual_warning and args.visu != '': 337 | print('Visualization Failed: You can only choose a point cloud shape to visualize within the scope of the test class') 338 | test_true_cls = np.concatenate(test_true_cls) 339 | test_pred_cls = np.concatenate(test_pred_cls) 340 | test_acc = metrics.accuracy_score(test_true_cls, test_pred_cls) 341 | avg_per_class_acc = metrics.balanced_accuracy_score(test_true_cls, test_pred_cls) 342 | test_true_seg = np.concatenate(test_true_seg, axis=0) 343 | test_pred_seg = np.concatenate(test_pred_seg, axis=0) 344 | test_label_seg = np.concatenate(test_label_seg) 345 | test_ious = calculate_shape_IoU(test_pred_seg, test_true_seg, test_label_seg, args.class_choice) 346 | outstr = 'Test :: test acc: %.6f, test avg acc: %.6f, test iou: %.6f' % (test_acc, 347 | avg_per_class_acc, 348 | np.mean(test_ious)) 349 | io.cprint(outstr) 350 | 351 | 352 | if __name__ == "__main__": 353 | # Training settings 354 | parser = argparse.ArgumentParser(description='Point Cloud Part Segmentation') 355 | parser.add_argument('--exp_name', type=str, default='exp', metavar='N', 356 | help='Name of the experiment') 357 | parser.add_argument('--model', type=str, default='dgcnn', metavar='N', 358 | choices=['dgcnn'], 359 | help='Model to use, [dgcnn]') 360 | parser.add_argument('--dataset', type=str, default='shapenetpart', metavar='N', 361 | choices=['shapenetpart']) 362 | parser.add_argument('--class_choice', type=str, default=None, metavar='N', 363 | choices=['airplane', 'bag', 'cap', 'car', 'chair', 364 | 'earphone', 'guitar', 'knife', 'lamp', 'laptop', 365 | 'motor', 'mug', 'pistol', 'rocket', 'skateboard', 'table']) 366 | parser.add_argument('--batch_size', type=int, default=32, metavar='batch_size', 367 | help='Size of batch)') 368 | parser.add_argument('--test_batch_size', type=int, default=16, metavar='batch_size', 369 | help='Size of batch)') 370 | parser.add_argument('--epochs', type=int, default=200, metavar='N', 371 | help='number of episode to train ') 372 | parser.add_argument('--use_sgd', type=bool, default=True, 373 | help='Use SGD') 374 | parser.add_argument('--lr', type=float, default=0.001, metavar='LR', 375 | help='learning rate (default: 0.001, 0.1 if using sgd)') 376 | parser.add_argument('--momentum', type=float, default=0.9, metavar='M', 377 | help='SGD momentum (default: 0.9)') 378 | parser.add_argument('--scheduler', type=str, default='cos', metavar='N', 379 | choices=['cos', 'step'], 380 | help='Scheduler to use, [cos, step]') 381 | parser.add_argument('--no_cuda', type=bool, default=False, 382 | help='enables CUDA training') 383 | parser.add_argument('--seed', type=int, default=1, metavar='S', 384 | help='random seed (default: 1)') 385 | parser.add_argument('--eval', type=bool, default=False, 386 | help='evaluate the model') 387 | parser.add_argument('--num_points', type=int, default=2048, 388 | help='num of points to use') 389 | parser.add_argument('--dropout', type=float, default=0.5, 390 | help='dropout rate') 391 | parser.add_argument('--emb_dims', type=int, default=1024, metavar='N', 392 | help='Dimension of embeddings') 393 | parser.add_argument('--k', type=int, default=40, metavar='N', 394 | help='Num of nearest neighbors to use') 395 | parser.add_argument('--model_path', type=str, default='', metavar='N', 396 | help='Pretrained model path') 397 | parser.add_argument('--visu', type=str, default='', 398 | help='visualize the model') 399 | parser.add_argument('--visu_format', type=str, default='ply', 400 | help='file format of visualization') 401 | args = parser.parse_args() 402 | 403 | _init_() 404 | 405 | io = IOStream('outputs/' + args.exp_name + '/run.log') 406 | io.cprint(str(args)) 407 | 408 | args.cuda = not args.no_cuda and torch.cuda.is_available() 409 | torch.manual_seed(args.seed) 410 | if args.cuda: 411 | io.cprint( 412 | 'Using GPU : ' + str(torch.cuda.current_device()) + ' from ' + str(torch.cuda.device_count()) + ' devices') 413 | torch.cuda.manual_seed(args.seed) 414 | else: 415 | io.cprint('Using CPU') 416 | 417 | if not args.eval: 418 | train(args, io) 419 | else: 420 | test(args, io) 421 | -------------------------------------------------------------------------------- /prepare_data/collect_indoor3d_data.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 4 | ROOT_DIR = os.path.dirname(BASE_DIR) 5 | DATA_PATH = os.path.join(ROOT_DIR, 'data/Stanford3dDataset_v1.2_Aligned_Version') 6 | import indoor3d_util 7 | 8 | anno_paths = [line.rstrip() for line in open(os.path.join(BASE_DIR, 'meta/anno_paths.txt'))] 9 | anno_paths = [os.path.join(DATA_PATH, p) for p in anno_paths] 10 | 11 | output_folder = os.path.join(ROOT_DIR, 'data/stanford_indoor3d') 12 | if not os.path.exists(output_folder): 13 | os.mkdir(output_folder) 14 | 15 | revise_file = os.path.join(DATA_PATH, "Area_5/hallway_6/Annotations/ceiling_1.txt") 16 | with open(revise_file, "r") as f: 17 | data = f.read() 18 | data = data[:5545347] + ' ' + data[5545348:] 19 | f.close() 20 | with open(revise_file, "w") as f: 21 | f.write(data) 22 | f.close() 23 | 24 | for anno_path in anno_paths: 25 | print(anno_path) 26 | elements = anno_path.split('/') 27 | out_filename = elements[-3]+'_'+elements[-2]+'.npy' # Area_1_hallway_1.npy 28 | indoor3d_util.collect_point_label(anno_path, os.path.join(output_folder, out_filename), 'numpy') -------------------------------------------------------------------------------- /prepare_data/data_prep_util.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 4 | sys.path.append(BASE_DIR) 5 | from plyfile import (PlyData, PlyElement, make2d, PlyParseError, PlyProperty) 6 | import numpy as np 7 | import h5py 8 | 9 | SAMPLING_BIN = os.path.join(BASE_DIR, 'third_party/mesh_sampling/build/pcsample') 10 | 11 | SAMPLING_POINT_NUM = 2048 12 | SAMPLING_LEAF_SIZE = 0.005 13 | 14 | MODELNET40_PATH = '../datasets/modelnet40' 15 | def export_ply(pc, filename): 16 | vertex = np.zeros(pc.shape[0], dtype=[('x', 'f4'), ('y', 'f4'), ('z', 'f4')]) 17 | for i in range(pc.shape[0]): 18 | vertex[i] = (pc[i][0], pc[i][1], pc[i][2]) 19 | ply_out = PlyData([PlyElement.describe(vertex, 'vertex', comments=['vertices'])]) 20 | ply_out.write(filename) 21 | 22 | # Sample points on the obj shape 23 | def get_sampling_command(obj_filename, ply_filename): 24 | cmd = SAMPLING_BIN + ' ' + obj_filename 25 | cmd += ' ' + ply_filename 26 | cmd += ' -n_samples %d ' % SAMPLING_POINT_NUM 27 | cmd += ' -leaf_size %f ' % SAMPLING_LEAF_SIZE 28 | return cmd 29 | 30 | # -------------------------------------------------------------- 31 | # Following are the helper functions to load MODELNET40 shapes 32 | # -------------------------------------------------------------- 33 | 34 | # Read in the list of categories in MODELNET40 35 | def get_category_names(): 36 | shape_names_file = os.path.join(MODELNET40_PATH, 'shape_names.txt') 37 | shape_names = [line.rstrip() for line in open(shape_names_file)] 38 | return shape_names 39 | 40 | # Return all the filepaths for the shapes in MODELNET40 41 | def get_obj_filenames(): 42 | obj_filelist_file = os.path.join(MODELNET40_PATH, 'filelist.txt') 43 | obj_filenames = [os.path.join(MODELNET40_PATH, line.rstrip()) for line in open(obj_filelist_file)] 44 | print('Got %d obj files in modelnet40.' % len(obj_filenames)) 45 | return obj_filenames 46 | 47 | # Helper function to create the father folder and all subdir folders if not exist 48 | def batch_mkdir(output_folder, subdir_list): 49 | if not os.path.exists(output_folder): 50 | os.mkdir(output_folder) 51 | for subdir in subdir_list: 52 | if not os.path.exists(os.path.join(output_folder, subdir)): 53 | os.mkdir(os.path.join(output_folder, subdir)) 54 | 55 | # ---------------------------------------------------------------- 56 | # Following are the helper functions to load save/load HDF5 files 57 | # ---------------------------------------------------------------- 58 | 59 | # Write numpy array data and label to h5_filename 60 | def save_h5_data_label_normal(h5_filename, data, label, normal, 61 | data_dtype='float32', label_dtype='uint8', normal_dtype='float32'): 62 | h5_fout = h5py.File(h5_filename) 63 | h5_fout.create_dataset( 64 | 'data', data=data, 65 | compression='gzip', compression_opts=4, 66 | dtype=data_dtype) 67 | h5_fout.create_dataset( 68 | 'normal', data=normal, 69 | compression='gzip', compression_opts=4, 70 | dtype=normal_dtype) 71 | h5_fout.create_dataset( 72 | 'label', data=label, 73 | compression='gzip', compression_opts=1, 74 | dtype=label_dtype) 75 | h5_fout.close() 76 | 77 | 78 | # Write numpy array data and label to h5_filename 79 | def save_h5(h5_filename, data, label, data_dtype='uint8', label_dtype='uint8'): 80 | h5_fout = h5py.File(h5_filename, "w") 81 | h5_fout.create_dataset( 82 | 'data', data=data, 83 | compression='gzip', compression_opts=4, 84 | dtype=data_dtype) 85 | h5_fout.create_dataset( 86 | 'label', data=label, 87 | compression='gzip', compression_opts=1, 88 | dtype=label_dtype) 89 | h5_fout.close() 90 | 91 | # Read numpy array data and label from h5_filename 92 | def load_h5_data_label_normal(h5_filename): 93 | f = h5py.File(h5_filename) 94 | data = f['data'][:] 95 | label = f['label'][:] 96 | normal = f['normal'][:] 97 | return (data, label, normal) 98 | 99 | # Read numpy array data and label from h5_filename 100 | def load_h5_data_label_seg(h5_filename): 101 | f = h5py.File(h5_filename) 102 | data = f['data'][:] 103 | label = f['label'][:] 104 | seg = f['pid'][:] 105 | return (data, label, seg) 106 | 107 | # Read numpy array data and label from h5_filename 108 | def load_h5(h5_filename): 109 | f = h5py.File(h5_filename) 110 | data = f['data'][:] 111 | label = f['label'][:] 112 | return (data, label) 113 | 114 | # ---------------------------------------------------------------- 115 | # Following are the helper functions to load save/load PLY files 116 | # ---------------------------------------------------------------- 117 | 118 | # Load PLY file 119 | def load_ply_data(filename, point_num): 120 | plydata = PlyData.read(filename) 121 | pc = plydata['vertex'].data[:point_num] 122 | pc_array = np.array([[x, y, z] for x,y,z in pc]) 123 | return pc_array 124 | 125 | # Load PLY file 126 | def load_ply_normal(filename, point_num): 127 | plydata = PlyData.read(filename) 128 | pc = plydata['normal'].data[:point_num] 129 | pc_array = np.array([[x, y, z] for x,y,z in pc]) 130 | return pc_array 131 | 132 | # Make up rows for Nxk array 133 | # Input Pad is 'edge' or 'constant' 134 | def pad_arr_rows(arr, row, pad='edge'): 135 | assert(len(arr.shape) == 2) 136 | assert(arr.shape[0] <= row) 137 | assert(pad == 'edge' or pad == 'constant') 138 | if arr.shape[0] == row: 139 | return arr 140 | if pad == 'edge': 141 | return np.lib.pad(arr, ((0, row-arr.shape[0]), (0, 0)), 'edge') 142 | if pad == 'constant': 143 | return np.lib.pad(arr, ((0, row-arr.shape[0]), (0, 0)), 'constant', (0, 0)) 144 | 145 | 146 | -------------------------------------------------------------------------------- /prepare_data/gen_indoor3d_h5.py: -------------------------------------------------------------------------------- 1 | import os 2 | import numpy as np 3 | import sys 4 | import json 5 | BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 6 | ROOT_DIR = os.path.dirname(BASE_DIR) 7 | sys.path.append(BASE_DIR) 8 | import data_prep_util 9 | import indoor3d_util 10 | 11 | # Constants 12 | data_dir = os.path.join(ROOT_DIR, 'data') 13 | indoor3d_data_dir = os.path.join(data_dir, 'stanford_indoor3d') 14 | NUM_POINT = 4096 15 | H5_BATCH_SIZE = 1000 16 | data_dim = [NUM_POINT, 9] 17 | label_dim = [NUM_POINT] 18 | data_dtype = 'float32' 19 | label_dtype = 'uint8' 20 | 21 | # Set paths 22 | filelist = os.path.join(BASE_DIR, 'meta/all_data_label.txt') 23 | data_label_files = [os.path.join(indoor3d_data_dir, line.rstrip()) for line in open(filelist)] 24 | output_dir = os.path.join(data_dir, 'indoor3d_sem_seg_hdf5_data_test') 25 | if not os.path.exists(output_dir): 26 | os.mkdir(output_dir) 27 | output_filename_prefix = os.path.join(output_dir, 'ply_data_all') 28 | output_room_filelist = os.path.join(output_dir, 'room_filelist.txt') 29 | output_all_file = os.path.join(output_dir, 'all_files.txt') 30 | fout_room = open(output_room_filelist, 'w') 31 | all_file = open(output_all_file, 'w') 32 | 33 | # -------------------------------------- 34 | # ----- BATCH WRITE TO HDF5 ----- 35 | # -------------------------------------- 36 | batch_data_dim = [H5_BATCH_SIZE] + data_dim 37 | batch_label_dim = [H5_BATCH_SIZE] + label_dim 38 | h5_batch_data = np.zeros(batch_data_dim, dtype = np.float32) 39 | h5_batch_label = np.zeros(batch_label_dim, dtype = np.uint8) 40 | buffer_size = 0 # state: record how many samples are currently in buffer 41 | h5_index = 0 # state: the next h5 file to save 42 | 43 | def insert_batch(data, label, last_batch=False): 44 | global h5_batch_data, h5_batch_label 45 | global buffer_size, h5_index 46 | data_size = data.shape[0] 47 | # If there is enough space, just insert 48 | if buffer_size + data_size <= h5_batch_data.shape[0]: 49 | h5_batch_data[buffer_size:buffer_size+data_size, ...] = data 50 | h5_batch_label[buffer_size:buffer_size+data_size] = label 51 | buffer_size += data_size 52 | else: # not enough space 53 | capacity = h5_batch_data.shape[0] - buffer_size 54 | assert(capacity>=0) 55 | if capacity > 0: 56 | h5_batch_data[buffer_size:buffer_size+capacity, ...] = data[0:capacity, ...] 57 | h5_batch_label[buffer_size:buffer_size+capacity, ...] = label[0:capacity, ...] 58 | # Save batch data and label to h5 file, reset buffer_size 59 | h5_filename = output_filename_prefix + '_' + str(h5_index) + '.h5' 60 | data_prep_util.save_h5(h5_filename, h5_batch_data, h5_batch_label, data_dtype, label_dtype) 61 | print('Stored {0} with size {1}'.format(h5_filename, h5_batch_data.shape[0])) 62 | h5_index += 1 63 | buffer_size = 0 64 | # recursive call 65 | insert_batch(data[capacity:, ...], label[capacity:, ...], last_batch) 66 | if last_batch and buffer_size > 0: 67 | h5_filename = output_filename_prefix + '_' + str(h5_index) + '.h5' 68 | data_prep_util.save_h5(h5_filename, h5_batch_data[0:buffer_size, ...], h5_batch_label[0:buffer_size, ...], data_dtype, label_dtype) 69 | print('Stored {0} with size {1}'.format(h5_filename, buffer_size)) 70 | h5_index += 1 71 | buffer_size = 0 72 | return 73 | 74 | 75 | sample_cnt = 0 76 | for i, data_label_filename in enumerate(data_label_files): 77 | print(data_label_filename) 78 | data, label = indoor3d_util.room2blocks_wrapper_normalized(data_label_filename, NUM_POINT, block_size=1.0, stride=1, 79 | random_sample=False, sample_num=None) 80 | print('{0}, {1}'.format(data.shape, label.shape)) 81 | for _ in range(data.shape[0]): 82 | fout_room.write(os.path.basename(data_label_filename)[0:-4]+'\n') 83 | 84 | sample_cnt += data.shape[0] 85 | insert_batch(data, label, i == len(data_label_files)-1) 86 | 87 | fout_room.close() 88 | print("Total samples: {0}".format(sample_cnt)) 89 | 90 | for i in range(h5_index): 91 | all_file.write(os.path.join('indoor3d_sem_seg_hdf5_data_test', 'ply_data_all_') + str(i) +'.h5\n') 92 | all_file.close() 93 | -------------------------------------------------------------------------------- /prepare_data/meta/all_data_label.txt: -------------------------------------------------------------------------------- 1 | Area_1_conferenceRoom_1.npy 2 | Area_1_conferenceRoom_2.npy 3 | Area_1_copyRoom_1.npy 4 | Area_1_hallway_1.npy 5 | Area_1_hallway_2.npy 6 | Area_1_hallway_3.npy 7 | Area_1_hallway_4.npy 8 | Area_1_hallway_5.npy 9 | Area_1_hallway_6.npy 10 | Area_1_hallway_7.npy 11 | Area_1_hallway_8.npy 12 | Area_1_office_10.npy 13 | Area_1_office_11.npy 14 | Area_1_office_12.npy 15 | Area_1_office_13.npy 16 | Area_1_office_14.npy 17 | Area_1_office_15.npy 18 | Area_1_office_16.npy 19 | Area_1_office_17.npy 20 | Area_1_office_18.npy 21 | Area_1_office_19.npy 22 | Area_1_office_1.npy 23 | Area_1_office_20.npy 24 | Area_1_office_21.npy 25 | Area_1_office_22.npy 26 | Area_1_office_23.npy 27 | Area_1_office_24.npy 28 | Area_1_office_25.npy 29 | Area_1_office_26.npy 30 | Area_1_office_27.npy 31 | Area_1_office_28.npy 32 | Area_1_office_29.npy 33 | Area_1_office_2.npy 34 | Area_1_office_30.npy 35 | Area_1_office_31.npy 36 | Area_1_office_3.npy 37 | Area_1_office_4.npy 38 | Area_1_office_5.npy 39 | Area_1_office_6.npy 40 | Area_1_office_7.npy 41 | Area_1_office_8.npy 42 | Area_1_office_9.npy 43 | Area_1_pantry_1.npy 44 | Area_1_WC_1.npy 45 | Area_2_auditorium_1.npy 46 | Area_2_auditorium_2.npy 47 | Area_2_conferenceRoom_1.npy 48 | Area_2_hallway_10.npy 49 | Area_2_hallway_11.npy 50 | Area_2_hallway_12.npy 51 | Area_2_hallway_1.npy 52 | Area_2_hallway_2.npy 53 | Area_2_hallway_3.npy 54 | Area_2_hallway_4.npy 55 | Area_2_hallway_5.npy 56 | Area_2_hallway_6.npy 57 | Area_2_hallway_7.npy 58 | Area_2_hallway_8.npy 59 | Area_2_hallway_9.npy 60 | Area_2_office_10.npy 61 | Area_2_office_11.npy 62 | Area_2_office_12.npy 63 | Area_2_office_13.npy 64 | Area_2_office_14.npy 65 | Area_2_office_1.npy 66 | Area_2_office_2.npy 67 | Area_2_office_3.npy 68 | Area_2_office_4.npy 69 | Area_2_office_5.npy 70 | Area_2_office_6.npy 71 | Area_2_office_7.npy 72 | Area_2_office_8.npy 73 | Area_2_office_9.npy 74 | Area_2_storage_1.npy 75 | Area_2_storage_2.npy 76 | Area_2_storage_3.npy 77 | Area_2_storage_4.npy 78 | Area_2_storage_5.npy 79 | Area_2_storage_6.npy 80 | Area_2_storage_7.npy 81 | Area_2_storage_8.npy 82 | Area_2_storage_9.npy 83 | Area_2_WC_1.npy 84 | Area_2_WC_2.npy 85 | Area_3_conferenceRoom_1.npy 86 | Area_3_hallway_1.npy 87 | Area_3_hallway_2.npy 88 | Area_3_hallway_3.npy 89 | Area_3_hallway_4.npy 90 | Area_3_hallway_5.npy 91 | Area_3_hallway_6.npy 92 | Area_3_lounge_1.npy 93 | Area_3_lounge_2.npy 94 | Area_3_office_10.npy 95 | Area_3_office_1.npy 96 | Area_3_office_2.npy 97 | Area_3_office_3.npy 98 | Area_3_office_4.npy 99 | Area_3_office_5.npy 100 | Area_3_office_6.npy 101 | Area_3_office_7.npy 102 | Area_3_office_8.npy 103 | Area_3_office_9.npy 104 | Area_3_storage_1.npy 105 | Area_3_storage_2.npy 106 | Area_3_WC_1.npy 107 | Area_3_WC_2.npy 108 | Area_4_conferenceRoom_1.npy 109 | Area_4_conferenceRoom_2.npy 110 | Area_4_conferenceRoom_3.npy 111 | Area_4_hallway_10.npy 112 | Area_4_hallway_11.npy 113 | Area_4_hallway_12.npy 114 | Area_4_hallway_13.npy 115 | Area_4_hallway_14.npy 116 | Area_4_hallway_1.npy 117 | Area_4_hallway_2.npy 118 | Area_4_hallway_3.npy 119 | Area_4_hallway_4.npy 120 | Area_4_hallway_5.npy 121 | Area_4_hallway_6.npy 122 | Area_4_hallway_7.npy 123 | Area_4_hallway_8.npy 124 | Area_4_hallway_9.npy 125 | Area_4_lobby_1.npy 126 | Area_4_lobby_2.npy 127 | Area_4_office_10.npy 128 | Area_4_office_11.npy 129 | Area_4_office_12.npy 130 | Area_4_office_13.npy 131 | Area_4_office_14.npy 132 | Area_4_office_15.npy 133 | Area_4_office_16.npy 134 | Area_4_office_17.npy 135 | Area_4_office_18.npy 136 | Area_4_office_19.npy 137 | Area_4_office_1.npy 138 | Area_4_office_20.npy 139 | Area_4_office_21.npy 140 | Area_4_office_22.npy 141 | Area_4_office_2.npy 142 | Area_4_office_3.npy 143 | Area_4_office_4.npy 144 | Area_4_office_5.npy 145 | Area_4_office_6.npy 146 | Area_4_office_7.npy 147 | Area_4_office_8.npy 148 | Area_4_office_9.npy 149 | Area_4_storage_1.npy 150 | Area_4_storage_2.npy 151 | Area_4_storage_3.npy 152 | Area_4_storage_4.npy 153 | Area_4_WC_1.npy 154 | Area_4_WC_2.npy 155 | Area_4_WC_3.npy 156 | Area_4_WC_4.npy 157 | Area_5_conferenceRoom_1.npy 158 | Area_5_conferenceRoom_2.npy 159 | Area_5_conferenceRoom_3.npy 160 | Area_5_hallway_10.npy 161 | Area_5_hallway_11.npy 162 | Area_5_hallway_12.npy 163 | Area_5_hallway_13.npy 164 | Area_5_hallway_14.npy 165 | Area_5_hallway_15.npy 166 | Area_5_hallway_1.npy 167 | Area_5_hallway_2.npy 168 | Area_5_hallway_3.npy 169 | Area_5_hallway_4.npy 170 | Area_5_hallway_5.npy 171 | Area_5_hallway_6.npy 172 | Area_5_hallway_7.npy 173 | Area_5_hallway_8.npy 174 | Area_5_hallway_9.npy 175 | Area_5_lobby_1.npy 176 | Area_5_office_10.npy 177 | Area_5_office_11.npy 178 | Area_5_office_12.npy 179 | Area_5_office_13.npy 180 | Area_5_office_14.npy 181 | Area_5_office_15.npy 182 | Area_5_office_16.npy 183 | Area_5_office_17.npy 184 | Area_5_office_18.npy 185 | Area_5_office_19.npy 186 | Area_5_office_1.npy 187 | Area_5_office_20.npy 188 | Area_5_office_21.npy 189 | Area_5_office_22.npy 190 | Area_5_office_23.npy 191 | Area_5_office_24.npy 192 | Area_5_office_25.npy 193 | Area_5_office_26.npy 194 | Area_5_office_27.npy 195 | Area_5_office_28.npy 196 | Area_5_office_29.npy 197 | Area_5_office_2.npy 198 | Area_5_office_30.npy 199 | Area_5_office_31.npy 200 | Area_5_office_32.npy 201 | Area_5_office_33.npy 202 | Area_5_office_34.npy 203 | Area_5_office_35.npy 204 | Area_5_office_36.npy 205 | Area_5_office_37.npy 206 | Area_5_office_38.npy 207 | Area_5_office_39.npy 208 | Area_5_office_3.npy 209 | Area_5_office_40.npy 210 | Area_5_office_41.npy 211 | Area_5_office_42.npy 212 | Area_5_office_4.npy 213 | Area_5_office_5.npy 214 | Area_5_office_6.npy 215 | Area_5_office_7.npy 216 | Area_5_office_8.npy 217 | Area_5_office_9.npy 218 | Area_5_pantry_1.npy 219 | Area_5_storage_1.npy 220 | Area_5_storage_2.npy 221 | Area_5_storage_3.npy 222 | Area_5_storage_4.npy 223 | Area_5_WC_1.npy 224 | Area_5_WC_2.npy 225 | Area_6_conferenceRoom_1.npy 226 | Area_6_copyRoom_1.npy 227 | Area_6_hallway_1.npy 228 | Area_6_hallway_2.npy 229 | Area_6_hallway_3.npy 230 | Area_6_hallway_4.npy 231 | Area_6_hallway_5.npy 232 | Area_6_hallway_6.npy 233 | Area_6_lounge_1.npy 234 | Area_6_office_10.npy 235 | Area_6_office_11.npy 236 | Area_6_office_12.npy 237 | Area_6_office_13.npy 238 | Area_6_office_14.npy 239 | Area_6_office_15.npy 240 | Area_6_office_16.npy 241 | Area_6_office_17.npy 242 | Area_6_office_18.npy 243 | Area_6_office_19.npy 244 | Area_6_office_1.npy 245 | Area_6_office_20.npy 246 | Area_6_office_21.npy 247 | Area_6_office_22.npy 248 | Area_6_office_23.npy 249 | Area_6_office_24.npy 250 | Area_6_office_25.npy 251 | Area_6_office_26.npy 252 | Area_6_office_27.npy 253 | Area_6_office_28.npy 254 | Area_6_office_29.npy 255 | Area_6_office_2.npy 256 | Area_6_office_30.npy 257 | Area_6_office_31.npy 258 | Area_6_office_32.npy 259 | Area_6_office_33.npy 260 | Area_6_office_34.npy 261 | Area_6_office_35.npy 262 | Area_6_office_36.npy 263 | Area_6_office_37.npy 264 | Area_6_office_3.npy 265 | Area_6_office_4.npy 266 | Area_6_office_5.npy 267 | Area_6_office_6.npy 268 | Area_6_office_7.npy 269 | Area_6_office_8.npy 270 | Area_6_office_9.npy 271 | Area_6_openspace_1.npy 272 | Area_6_pantry_1.npy 273 | -------------------------------------------------------------------------------- /prepare_data/meta/anno_paths.txt: -------------------------------------------------------------------------------- 1 | Area_1/conferenceRoom_1/Annotations 2 | Area_1/conferenceRoom_2/Annotations 3 | Area_1/copyRoom_1/Annotations 4 | Area_1/hallway_1/Annotations 5 | Area_1/hallway_2/Annotations 6 | Area_1/hallway_3/Annotations 7 | Area_1/hallway_4/Annotations 8 | Area_1/hallway_5/Annotations 9 | Area_1/hallway_6/Annotations 10 | Area_1/hallway_7/Annotations 11 | Area_1/hallway_8/Annotations 12 | Area_1/office_10/Annotations 13 | Area_1/office_11/Annotations 14 | Area_1/office_12/Annotations 15 | Area_1/office_13/Annotations 16 | Area_1/office_14/Annotations 17 | Area_1/office_15/Annotations 18 | Area_1/office_16/Annotations 19 | Area_1/office_17/Annotations 20 | Area_1/office_18/Annotations 21 | Area_1/office_19/Annotations 22 | Area_1/office_1/Annotations 23 | Area_1/office_20/Annotations 24 | Area_1/office_21/Annotations 25 | Area_1/office_22/Annotations 26 | Area_1/office_23/Annotations 27 | Area_1/office_24/Annotations 28 | Area_1/office_25/Annotations 29 | Area_1/office_26/Annotations 30 | Area_1/office_27/Annotations 31 | Area_1/office_28/Annotations 32 | Area_1/office_29/Annotations 33 | Area_1/office_2/Annotations 34 | Area_1/office_30/Annotations 35 | Area_1/office_31/Annotations 36 | Area_1/office_3/Annotations 37 | Area_1/office_4/Annotations 38 | Area_1/office_5/Annotations 39 | Area_1/office_6/Annotations 40 | Area_1/office_7/Annotations 41 | Area_1/office_8/Annotations 42 | Area_1/office_9/Annotations 43 | Area_1/pantry_1/Annotations 44 | Area_1/WC_1/Annotations 45 | Area_2/auditorium_1/Annotations 46 | Area_2/auditorium_2/Annotations 47 | Area_2/conferenceRoom_1/Annotations 48 | Area_2/hallway_10/Annotations 49 | Area_2/hallway_11/Annotations 50 | Area_2/hallway_12/Annotations 51 | Area_2/hallway_1/Annotations 52 | Area_2/hallway_2/Annotations 53 | Area_2/hallway_3/Annotations 54 | Area_2/hallway_4/Annotations 55 | Area_2/hallway_5/Annotations 56 | Area_2/hallway_6/Annotations 57 | Area_2/hallway_7/Annotations 58 | Area_2/hallway_8/Annotations 59 | Area_2/hallway_9/Annotations 60 | Area_2/office_10/Annotations 61 | Area_2/office_11/Annotations 62 | Area_2/office_12/Annotations 63 | Area_2/office_13/Annotations 64 | Area_2/office_14/Annotations 65 | Area_2/office_1/Annotations 66 | Area_2/office_2/Annotations 67 | Area_2/office_3/Annotations 68 | Area_2/office_4/Annotations 69 | Area_2/office_5/Annotations 70 | Area_2/office_6/Annotations 71 | Area_2/office_7/Annotations 72 | Area_2/office_8/Annotations 73 | Area_2/office_9/Annotations 74 | Area_2/storage_1/Annotations 75 | Area_2/storage_2/Annotations 76 | Area_2/storage_3/Annotations 77 | Area_2/storage_4/Annotations 78 | Area_2/storage_5/Annotations 79 | Area_2/storage_6/Annotations 80 | Area_2/storage_7/Annotations 81 | Area_2/storage_8/Annotations 82 | Area_2/storage_9/Annotations 83 | Area_2/WC_1/Annotations 84 | Area_2/WC_2/Annotations 85 | Area_3/conferenceRoom_1/Annotations 86 | Area_3/hallway_1/Annotations 87 | Area_3/hallway_2/Annotations 88 | Area_3/hallway_3/Annotations 89 | Area_3/hallway_4/Annotations 90 | Area_3/hallway_5/Annotations 91 | Area_3/hallway_6/Annotations 92 | Area_3/lounge_1/Annotations 93 | Area_3/lounge_2/Annotations 94 | Area_3/office_10/Annotations 95 | Area_3/office_1/Annotations 96 | Area_3/office_2/Annotations 97 | Area_3/office_3/Annotations 98 | Area_3/office_4/Annotations 99 | Area_3/office_5/Annotations 100 | Area_3/office_6/Annotations 101 | Area_3/office_7/Annotations 102 | Area_3/office_8/Annotations 103 | Area_3/office_9/Annotations 104 | Area_3/storage_1/Annotations 105 | Area_3/storage_2/Annotations 106 | Area_3/WC_1/Annotations 107 | Area_3/WC_2/Annotations 108 | Area_4/conferenceRoom_1/Annotations 109 | Area_4/conferenceRoom_2/Annotations 110 | Area_4/conferenceRoom_3/Annotations 111 | Area_4/hallway_10/Annotations 112 | Area_4/hallway_11/Annotations 113 | Area_4/hallway_12/Annotations 114 | Area_4/hallway_13/Annotations 115 | Area_4/hallway_14/Annotations 116 | Area_4/hallway_1/Annotations 117 | Area_4/hallway_2/Annotations 118 | Area_4/hallway_3/Annotations 119 | Area_4/hallway_4/Annotations 120 | Area_4/hallway_5/Annotations 121 | Area_4/hallway_6/Annotations 122 | Area_4/hallway_7/Annotations 123 | Area_4/hallway_8/Annotations 124 | Area_4/hallway_9/Annotations 125 | Area_4/lobby_1/Annotations 126 | Area_4/lobby_2/Annotations 127 | Area_4/office_10/Annotations 128 | Area_4/office_11/Annotations 129 | Area_4/office_12/Annotations 130 | Area_4/office_13/Annotations 131 | Area_4/office_14/Annotations 132 | Area_4/office_15/Annotations 133 | Area_4/office_16/Annotations 134 | Area_4/office_17/Annotations 135 | Area_4/office_18/Annotations 136 | Area_4/office_19/Annotations 137 | Area_4/office_1/Annotations 138 | Area_4/office_20/Annotations 139 | Area_4/office_21/Annotations 140 | Area_4/office_22/Annotations 141 | Area_4/office_2/Annotations 142 | Area_4/office_3/Annotations 143 | Area_4/office_4/Annotations 144 | Area_4/office_5/Annotations 145 | Area_4/office_6/Annotations 146 | Area_4/office_7/Annotations 147 | Area_4/office_8/Annotations 148 | Area_4/office_9/Annotations 149 | Area_4/storage_1/Annotations 150 | Area_4/storage_2/Annotations 151 | Area_4/storage_3/Annotations 152 | Area_4/storage_4/Annotations 153 | Area_4/WC_1/Annotations 154 | Area_4/WC_2/Annotations 155 | Area_4/WC_3/Annotations 156 | Area_4/WC_4/Annotations 157 | Area_5/conferenceRoom_1/Annotations 158 | Area_5/conferenceRoom_2/Annotations 159 | Area_5/conferenceRoom_3/Annotations 160 | Area_5/hallway_10/Annotations 161 | Area_5/hallway_11/Annotations 162 | Area_5/hallway_12/Annotations 163 | Area_5/hallway_13/Annotations 164 | Area_5/hallway_14/Annotations 165 | Area_5/hallway_15/Annotations 166 | Area_5/hallway_1/Annotations 167 | Area_5/hallway_2/Annotations 168 | Area_5/hallway_3/Annotations 169 | Area_5/hallway_4/Annotations 170 | Area_5/hallway_5/Annotations 171 | Area_5/hallway_6/Annotations 172 | Area_5/hallway_7/Annotations 173 | Area_5/hallway_8/Annotations 174 | Area_5/hallway_9/Annotations 175 | Area_5/lobby_1/Annotations 176 | Area_5/office_10/Annotations 177 | Area_5/office_11/Annotations 178 | Area_5/office_12/Annotations 179 | Area_5/office_13/Annotations 180 | Area_5/office_14/Annotations 181 | Area_5/office_15/Annotations 182 | Area_5/office_16/Annotations 183 | Area_5/office_17/Annotations 184 | Area_5/office_18/Annotations 185 | Area_5/office_19/Annotations 186 | Area_5/office_1/Annotations 187 | Area_5/office_20/Annotations 188 | Area_5/office_21/Annotations 189 | Area_5/office_22/Annotations 190 | Area_5/office_23/Annotations 191 | Area_5/office_24/Annotations 192 | Area_5/office_25/Annotations 193 | Area_5/office_26/Annotations 194 | Area_5/office_27/Annotations 195 | Area_5/office_28/Annotations 196 | Area_5/office_29/Annotations 197 | Area_5/office_2/Annotations 198 | Area_5/office_30/Annotations 199 | Area_5/office_31/Annotations 200 | Area_5/office_32/Annotations 201 | Area_5/office_33/Annotations 202 | Area_5/office_34/Annotations 203 | Area_5/office_35/Annotations 204 | Area_5/office_36/Annotations 205 | Area_5/office_37/Annotations 206 | Area_5/office_38/Annotations 207 | Area_5/office_39/Annotations 208 | Area_5/office_3/Annotations 209 | Area_5/office_40/Annotations 210 | Area_5/office_41/Annotations 211 | Area_5/office_42/Annotations 212 | Area_5/office_4/Annotations 213 | Area_5/office_5/Annotations 214 | Area_5/office_6/Annotations 215 | Area_5/office_7/Annotations 216 | Area_5/office_8/Annotations 217 | Area_5/office_9/Annotations 218 | Area_5/pantry_1/Annotations 219 | Area_5/storage_1/Annotations 220 | Area_5/storage_2/Annotations 221 | Area_5/storage_3/Annotations 222 | Area_5/storage_4/Annotations 223 | Area_5/WC_1/Annotations 224 | Area_5/WC_2/Annotations 225 | Area_6/conferenceRoom_1/Annotations 226 | Area_6/copyRoom_1/Annotations 227 | Area_6/hallway_1/Annotations 228 | Area_6/hallway_2/Annotations 229 | Area_6/hallway_3/Annotations 230 | Area_6/hallway_4/Annotations 231 | Area_6/hallway_5/Annotations 232 | Area_6/hallway_6/Annotations 233 | Area_6/lounge_1/Annotations 234 | Area_6/office_10/Annotations 235 | Area_6/office_11/Annotations 236 | Area_6/office_12/Annotations 237 | Area_6/office_13/Annotations 238 | Area_6/office_14/Annotations 239 | Area_6/office_15/Annotations 240 | Area_6/office_16/Annotations 241 | Area_6/office_17/Annotations 242 | Area_6/office_18/Annotations 243 | Area_6/office_19/Annotations 244 | Area_6/office_1/Annotations 245 | Area_6/office_20/Annotations 246 | Area_6/office_21/Annotations 247 | Area_6/office_22/Annotations 248 | Area_6/office_23/Annotations 249 | Area_6/office_24/Annotations 250 | Area_6/office_25/Annotations 251 | Area_6/office_26/Annotations 252 | Area_6/office_27/Annotations 253 | Area_6/office_28/Annotations 254 | Area_6/office_29/Annotations 255 | Area_6/office_2/Annotations 256 | Area_6/office_30/Annotations 257 | Area_6/office_31/Annotations 258 | Area_6/office_32/Annotations 259 | Area_6/office_33/Annotations 260 | Area_6/office_34/Annotations 261 | Area_6/office_35/Annotations 262 | Area_6/office_36/Annotations 263 | Area_6/office_37/Annotations 264 | Area_6/office_3/Annotations 265 | Area_6/office_4/Annotations 266 | Area_6/office_5/Annotations 267 | Area_6/office_6/Annotations 268 | Area_6/office_7/Annotations 269 | Area_6/office_8/Annotations 270 | Area_6/office_9/Annotations 271 | Area_6/openspace_1/Annotations 272 | Area_6/pantry_1/Annotations 273 | -------------------------------------------------------------------------------- /prepare_data/meta/class_names.txt: -------------------------------------------------------------------------------- 1 | ceiling 2 | floor 3 | wall 4 | beam 5 | column 6 | window 7 | door 8 | table 9 | chair 10 | sofa 11 | bookcase 12 | board 13 | clutter 14 | -------------------------------------------------------------------------------- /prepare_data/meta/partseg_colors.txt: -------------------------------------------------------------------------------- 1 | [ 2 | {"label": "airplane_body","id": 0,"color": [152,223,138]}, 3 | {"label": "airplane_wings","id": 1,"color": [174,199,232]}, 4 | {"label": "airplane_tail","id": 2,"color": [255,105,180]}, 5 | {"label": "airplane_engine","id": 3,"color": [31,119,180]}, 6 | {"label": "bag_handle","id": 4,"color": [255,187,120]}, 7 | {"label": "bag_body","id": 5,"color": [188,189,34]}, 8 | {"label": "cap_peak","id": 6,"color": [140,86,75]}, 9 | {"label": "cap_panel","id": 7,"color": [255,152,150]}, 10 | {"label": "car_roof","id": 8,"color": [214,39,40]}, 11 | {"label": "car_hood","id": 9,"color": [197,176,213]}, 12 | {"label": "car_wheels","id": 10,"color": [148,103,189]}, 13 | {"label": "car_body","id": 11,"color": [196,156,148]}, 14 | {"label": "chair_back","id": 12,"color": [23,190,207]}, 15 | {"label": "chair_seat","id": 13,"color": [186,85,211]}, 16 | {"label": "chair_leg","id": 14,"color": [247,182,210]}, 17 | {"label": "chair_arm","id": 15,"color": [66,188,102]}, 18 | {"label": "earphone_earphone","id": 16,"color": [219,219,141]}, 19 | {"label": "earphone_headband","id": 17,"color": [140,57,197]}, 20 | {"label": "earphone_microphone","id": 18,"color": [202,185,52]}, 21 | {"label": "guitar_head","id": 19,"color": [213,92,176]}, 22 | {"label": "guitar_neck","id": 20,"color": [200,54,131]}, 23 | {"label": "guitar_body","id": 21,"color": [92,193,61]}, 24 | {"label": "knife_blade","id": 22,"color": [78,71,183]}, 25 | {"label": "knife_handle","id": 23,"color": [172,114,82]}, 26 | {"label": "lamp_base","id": 24,"color": [255,127,14]}, 27 | {"label": "lamp_shape","id": 25,"color": [91,163,138]}, 28 | {"label": "lamp_ceiling","id": 26,"color": [153,98,156]}, 29 | {"label": "lamp_tube","id": 27,"color": [140,153,101]}, 30 | {"label": "laptop_keyboard","id": 28,"color": [158,218,229]}, 31 | {"label": "laptop_screen","id": 29,"color": [100,125,154]}, 32 | {"label": "motorbike_gastank","id": 30,"color": [178,127,135]}, 33 | {"label": "motorbike_seat","id": 31,"color": [120,185,128]}, 34 | {"label": "motorbike_wheel","id": 32,"color": [146,111,194]}, 35 | {"label": "motorbike_handle","id": 33,"color": [44,160,44]}, 36 | {"label": "motorbike_light","id": 34,"color": [112,128,144]}, 37 | {"label": "motorbike_body","id": 35,"color": [96,207,209]}, 38 | {"label": "mug_handle","id": 36,"color": [227,119,194]}, 39 | {"label": "mug_body","id": 37,"color": [51,176,203]}, 40 | {"label": "pistol_barrel","id": 38,"color": [94,106,211]}, 41 | {"label": "pistol_handle","id": 39,"color": [82,84,163]}, 42 | {"label": "pistol_trigger","id": 40,"color": [100,85,144]}, 43 | {"label": "rocket_body","id": 41,"color": [255,127,80]}, 44 | {"label": "rocket_fin","id": 42,"color": [0,100,0]}, 45 | {"label": "rocket_nose","id": 43,"color": [173,255,47]}, 46 | {"label": "skateboard_wheel","id": 44,"color": [64,224,208]}, 47 | {"label": "skateboard_deck","id": 45,"color": [0,255,255]}, 48 | {"label": "skateboard_bearing","id": 46,"color": [25,25,112]}, 49 | {"label": "table_top","id": 47,"color": [178,76,76]}, 50 | {"label": "table_leg","id": 48,"color": [255,0,255]}, 51 | {"label": "table_drawer","id": 49,"color": [152,223,138]} 52 | ] -------------------------------------------------------------------------------- /prepare_data/meta/semseg_colors.txt: -------------------------------------------------------------------------------- 1 | [ 2 | {"label": "ceiling","id": 1,"color": [152,223,138]}, 3 | {"label": "floor","id": 2,"color": [174,199,232]}, 4 | {"label": "wall","id": 3,"color": [255,127,14]}, 5 | {"label": "beam","id": 4,"color": [91,163,138]}, 6 | {"label": "column","id": 5,"color": [255,187,120]}, 7 | {"label": "window","id": 6,"color": [188,189,34]}, 8 | {"label": "door","id": 7,"color": [140,86,75]}, 9 | {"label": "table","id": 8,"color": [255,152,150]}, 10 | {"label": "chair","id": 9,"color": [214,39,40]}, 11 | {"label": "sofa","id": 10,"color": [197,176,213]}, 12 | {"label": "bookcase","id": 11,"color": [196,156,148]}, 13 | {"label": "board","id": 12,"color": [23,190,207]}, 14 | {"label": "clutter","id": 13,"color": [112,128,144]} 15 | ] -------------------------------------------------------------------------------- /prepare_data/scannetv2_seg_dataset_rgb21c_pointid.py: -------------------------------------------------------------------------------- 1 | """ 2 | ScanNet v2 data preprocessing. 3 | Extract point clouds data from .ply files to genrate .pickle files for training and testing. 4 | Author: Wenxuan Wu 5 | Date: July 2018 6 | """ 7 | 8 | import os 9 | import sys 10 | import numpy as np 11 | import util 12 | import h5py 13 | import pickle 14 | from plyfile import PlyData, PlyElement 15 | 16 | 17 | def remove_unano(scene_data, scene_label, scene_data_id): 18 | keep_idx = np.where((scene_label > 0) & ( 19 | scene_label < 41)) # 0: unanotated 20 | scene_data_clean = scene_data[keep_idx] 21 | scene_label_clean = scene_label[keep_idx] 22 | scene_data_id_clean = scene_data_id[keep_idx] 23 | return scene_data_clean, scene_label_clean, scene_data_id_clean 24 | 25 | 26 | test_class = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 27 | 10, 11, 12, 14, 16, 24, 28, 33, 34, 36, 39] 28 | 29 | 30 | def gen_label_map(): 31 | label_map = np.zeros(41) 32 | for i in range(41): 33 | if i in test_class: 34 | label_map[i] = test_class.index(i) 35 | else: 36 | label_map[i] = 0 37 | print(label_map) 38 | return label_map 39 | 40 | 41 | def gen_pickle(split="val", keep_unanno=False, root="DataSet/Scannet_v2"): 42 | if split == 'test': 43 | root_new = root + "/scans_test" 44 | else: 45 | root_new = root + "/scans" 46 | file_list = "scannetv2_%s.txt" % (split) 47 | with open(file_list) as fl: 48 | scene_id = fl.read().splitlines() 49 | 50 | scene_data = [] 51 | scene_data_labels = [] 52 | scene_data_id = [] 53 | scene_data_num = [] 54 | label_map = gen_label_map() 55 | for i in range(len(scene_id)): # len(scene_id) 56 | print('process...', i) 57 | scene_namergb = os.path.join( 58 | root_new, scene_id[i], scene_id[i]+'_vh_clean_2.ply') 59 | scene_xyzlabelrgb = PlyData.read(scene_namergb) 60 | scene_vertex_rgb = scene_xyzlabelrgb['vertex'] 61 | scene_data_tmp = np.stack((scene_vertex_rgb['x'], scene_vertex_rgb['y'], 62 | scene_vertex_rgb['z'], scene_vertex_rgb['red'], 63 | scene_vertex_rgb['green'], scene_vertex_rgb['blue']), axis=-1).astype(np.float32) 64 | scene_points_num = scene_data_tmp.shape[0] 65 | scene_point_id = np.array([c for c in range(scene_points_num)]) 66 | if not keep_unanno: 67 | scene_name = os.path.join( 68 | root_new, scene_id[i], scene_id[i]+'_vh_clean_2.labels.ply') 69 | scene_xyzlabel = PlyData.read(scene_name) 70 | scene_vertex = scene_xyzlabel['vertex'] 71 | scene_data_label_tmp = scene_vertex['label'] 72 | scene_data_tmp, scene_data_label_tmp, scene_point_id_tmp = remove_unano( 73 | scene_data_tmp, scene_data_label_tmp, scene_point_id) 74 | scene_data_label_tmp = label_map[scene_data_label_tmp] 75 | elif split != 'test': 76 | scene_name = os.path.join( 77 | root_new, scene_id[i], scene_id[i]+'_vh_clean_2.labels.ply') 78 | scene_xyzlabel = PlyData.read(scene_name) 79 | scene_vertex = scene_xyzlabel['vertex'] 80 | scene_point_id_tmp = scene_point_id 81 | scene_data_label_tmp = scene_vertex['label'] 82 | scene_data_label_tmp[np.where(scene_data_label_tmp > 40)] = 0 83 | scene_data_label_tmp = label_map[scene_data_label_tmp] 84 | else: 85 | scene_data_label_tmp = np.zeros( 86 | (scene_data_tmp.shape[0])).astype(np.int32) 87 | scene_point_id_tmp = scene_point_id 88 | scene_data.append(scene_data_tmp) 89 | scene_data_labels.append(scene_data_label_tmp) 90 | scene_data_id.append(scene_point_id_tmp) 91 | scene_data_num.append(scene_points_num) 92 | 93 | if not keep_unanno: 94 | out_path = os.path.join(root, "scannet_%s_rgb21c_pointid.pickle" % (split)) 95 | else: 96 | out_path = os.path.join(root, "scannet_%s_rgb21c_pointid_keep_unanno.pickle" % (split)) 97 | pickle_out = open(out_path, "wb") 98 | pickle.dump(scene_data, pickle_out, protocol=0) 99 | pickle.dump(scene_data_labels, pickle_out, protocol=0) 100 | pickle.dump(scene_data_id, pickle_out, protocol=0) 101 | pickle.dump(scene_data_num, pickle_out, protocol=0) 102 | pickle_out.close() 103 | 104 | 105 | if __name__ == '__main__': 106 | 107 | # modify this path to your Scannet v2 dataset Path 108 | root = "../data/ScanNet" 109 | gen_pickle(split='train', keep_unanno=False, root=root) 110 | gen_pickle(split='val', keep_unanno=False, root=root) 111 | gen_pickle(split='val', keep_unanno=True, root=root) 112 | gen_pickle(split='test', keep_unanno=True, root=root) 113 | 114 | print('Done!!!') 115 | -------------------------------------------------------------------------------- /prepare_data/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 | -------------------------------------------------------------------------------- /prepare_data/scannetv2_train.txt: -------------------------------------------------------------------------------- 1 | scene0191_00 2 | scene0191_01 3 | scene0191_02 4 | scene0119_00 5 | scene0230_00 6 | scene0528_00 7 | scene0528_01 8 | scene0705_00 9 | scene0705_01 10 | scene0705_02 11 | scene0415_00 12 | scene0415_01 13 | scene0415_02 14 | scene0007_00 15 | scene0141_00 16 | scene0141_01 17 | scene0141_02 18 | scene0515_00 19 | scene0515_01 20 | scene0515_02 21 | scene0447_00 22 | scene0447_01 23 | scene0447_02 24 | scene0531_00 25 | scene0503_00 26 | scene0285_00 27 | scene0069_00 28 | scene0584_00 29 | scene0584_01 30 | scene0584_02 31 | scene0581_00 32 | scene0581_01 33 | scene0581_02 34 | scene0620_00 35 | scene0620_01 36 | scene0263_00 37 | scene0263_01 38 | scene0481_00 39 | scene0481_01 40 | scene0020_00 41 | scene0020_01 42 | scene0291_00 43 | scene0291_01 44 | scene0291_02 45 | scene0469_00 46 | scene0469_01 47 | scene0469_02 48 | scene0659_00 49 | scene0659_01 50 | scene0024_00 51 | scene0024_01 52 | scene0024_02 53 | scene0564_00 54 | scene0117_00 55 | scene0027_00 56 | scene0027_01 57 | scene0027_02 58 | scene0028_00 59 | scene0330_00 60 | scene0418_00 61 | scene0418_01 62 | scene0418_02 63 | scene0233_00 64 | scene0233_01 65 | scene0673_00 66 | scene0673_01 67 | scene0673_02 68 | scene0673_03 69 | scene0673_04 70 | scene0673_05 71 | scene0585_00 72 | scene0585_01 73 | scene0362_00 74 | scene0362_01 75 | scene0362_02 76 | scene0362_03 77 | scene0035_00 78 | scene0035_01 79 | scene0358_00 80 | scene0358_01 81 | scene0358_02 82 | scene0037_00 83 | scene0194_00 84 | scene0321_00 85 | scene0293_00 86 | scene0293_01 87 | scene0623_00 88 | scene0623_01 89 | scene0592_00 90 | scene0592_01 91 | scene0569_00 92 | scene0569_01 93 | scene0413_00 94 | scene0313_00 95 | scene0313_01 96 | scene0313_02 97 | scene0480_00 98 | scene0480_01 99 | scene0401_00 100 | scene0517_00 101 | scene0517_01 102 | scene0517_02 103 | scene0032_00 104 | scene0032_01 105 | scene0613_00 106 | scene0613_01 107 | scene0613_02 108 | scene0306_00 109 | scene0306_01 110 | scene0052_00 111 | scene0052_01 112 | scene0052_02 113 | scene0053_00 114 | scene0444_00 115 | scene0444_01 116 | scene0055_00 117 | scene0055_01 118 | scene0055_02 119 | scene0560_00 120 | scene0589_00 121 | scene0589_01 122 | scene0589_02 123 | scene0610_00 124 | scene0610_01 125 | scene0610_02 126 | scene0364_00 127 | scene0364_01 128 | scene0383_00 129 | scene0383_01 130 | scene0383_02 131 | scene0006_00 132 | scene0006_01 133 | scene0006_02 134 | scene0275_00 135 | scene0451_00 136 | scene0451_01 137 | scene0451_02 138 | scene0451_03 139 | scene0451_04 140 | scene0451_05 141 | scene0135_00 142 | scene0065_00 143 | scene0065_01 144 | scene0065_02 145 | scene0104_00 146 | scene0674_00 147 | scene0674_01 148 | scene0448_00 149 | scene0448_01 150 | scene0448_02 151 | scene0502_00 152 | scene0502_01 153 | scene0502_02 154 | scene0440_00 155 | scene0440_01 156 | scene0440_02 157 | scene0071_00 158 | scene0072_00 159 | scene0072_01 160 | scene0072_02 161 | scene0509_00 162 | scene0509_01 163 | scene0509_02 164 | scene0649_00 165 | scene0649_01 166 | scene0602_00 167 | scene0694_00 168 | scene0694_01 169 | scene0101_00 170 | scene0101_01 171 | scene0101_02 172 | scene0101_03 173 | scene0101_04 174 | scene0101_05 175 | scene0218_00 176 | scene0218_01 177 | scene0579_00 178 | scene0579_01 179 | scene0579_02 180 | scene0039_00 181 | scene0039_01 182 | scene0493_00 183 | scene0493_01 184 | scene0242_00 185 | scene0242_01 186 | scene0242_02 187 | scene0083_00 188 | scene0083_01 189 | scene0127_00 190 | scene0127_01 191 | scene0662_00 192 | scene0662_01 193 | scene0662_02 194 | scene0018_00 195 | scene0087_00 196 | scene0087_01 197 | scene0087_02 198 | scene0332_00 199 | scene0332_01 200 | scene0332_02 201 | scene0628_00 202 | scene0628_01 203 | scene0628_02 204 | scene0134_00 205 | scene0134_01 206 | scene0134_02 207 | scene0238_00 208 | scene0238_01 209 | scene0092_00 210 | scene0092_01 211 | scene0092_02 212 | scene0092_03 213 | scene0092_04 214 | scene0022_00 215 | scene0022_01 216 | scene0467_00 217 | scene0392_00 218 | scene0392_01 219 | scene0392_02 220 | scene0424_00 221 | scene0424_01 222 | scene0424_02 223 | scene0646_00 224 | scene0646_01 225 | scene0646_02 226 | scene0098_00 227 | scene0098_01 228 | scene0044_00 229 | scene0044_01 230 | scene0044_02 231 | scene0510_00 232 | scene0510_01 233 | scene0510_02 234 | scene0571_00 235 | scene0571_01 236 | scene0166_00 237 | scene0166_01 238 | scene0166_02 239 | scene0563_00 240 | scene0172_00 241 | scene0172_01 242 | scene0388_00 243 | scene0388_01 244 | scene0215_00 245 | scene0215_01 246 | scene0252_00 247 | scene0287_00 248 | scene0668_00 249 | scene0572_00 250 | scene0572_01 251 | scene0572_02 252 | scene0026_00 253 | scene0224_00 254 | scene0113_00 255 | scene0113_01 256 | scene0551_00 257 | scene0381_00 258 | scene0381_01 259 | scene0381_02 260 | scene0371_00 261 | scene0371_01 262 | scene0460_00 263 | scene0118_00 264 | scene0118_01 265 | scene0118_02 266 | scene0417_00 267 | scene0008_00 268 | scene0634_00 269 | scene0521_00 270 | scene0123_00 271 | scene0123_01 272 | scene0123_02 273 | scene0045_00 274 | scene0045_01 275 | scene0511_00 276 | scene0511_01 277 | scene0114_00 278 | scene0114_01 279 | scene0114_02 280 | scene0070_00 281 | scene0029_00 282 | scene0029_01 283 | scene0029_02 284 | scene0129_00 285 | scene0103_00 286 | scene0103_01 287 | scene0002_00 288 | scene0002_01 289 | scene0132_00 290 | scene0132_01 291 | scene0132_02 292 | scene0124_00 293 | scene0124_01 294 | scene0143_00 295 | scene0143_01 296 | scene0143_02 297 | scene0604_00 298 | scene0604_01 299 | scene0604_02 300 | scene0507_00 301 | scene0105_00 302 | scene0105_01 303 | scene0105_02 304 | scene0428_00 305 | scene0428_01 306 | scene0311_00 307 | scene0140_00 308 | scene0140_01 309 | scene0182_00 310 | scene0182_01 311 | scene0182_02 312 | scene0142_00 313 | scene0142_01 314 | scene0399_00 315 | scene0399_01 316 | scene0012_00 317 | scene0012_01 318 | scene0012_02 319 | scene0060_00 320 | scene0060_01 321 | scene0370_00 322 | scene0370_01 323 | scene0370_02 324 | scene0310_00 325 | scene0310_01 326 | scene0310_02 327 | scene0661_00 328 | scene0650_00 329 | scene0152_00 330 | scene0152_01 331 | scene0152_02 332 | scene0158_00 333 | scene0158_01 334 | scene0158_02 335 | scene0482_00 336 | scene0482_01 337 | scene0600_00 338 | scene0600_01 339 | scene0600_02 340 | scene0393_00 341 | scene0393_01 342 | scene0393_02 343 | scene0562_00 344 | scene0174_00 345 | scene0174_01 346 | scene0157_00 347 | scene0157_01 348 | scene0161_00 349 | scene0161_01 350 | scene0161_02 351 | scene0159_00 352 | scene0254_00 353 | scene0254_01 354 | scene0115_00 355 | scene0115_01 356 | scene0115_02 357 | scene0162_00 358 | scene0163_00 359 | scene0163_01 360 | scene0523_00 361 | scene0523_01 362 | scene0523_02 363 | scene0459_00 364 | scene0459_01 365 | scene0175_00 366 | scene0085_00 367 | scene0085_01 368 | scene0279_00 369 | scene0279_01 370 | scene0279_02 371 | scene0201_00 372 | scene0201_01 373 | scene0201_02 374 | scene0283_00 375 | scene0456_00 376 | scene0456_01 377 | scene0429_00 378 | scene0043_00 379 | scene0043_01 380 | scene0419_00 381 | scene0419_01 382 | scene0419_02 383 | scene0368_00 384 | scene0368_01 385 | scene0348_00 386 | scene0348_01 387 | scene0348_02 388 | scene0442_00 389 | scene0178_00 390 | scene0380_00 391 | scene0380_01 392 | scene0380_02 393 | scene0165_00 394 | scene0165_01 395 | scene0165_02 396 | scene0181_00 397 | scene0181_01 398 | scene0181_02 399 | scene0181_03 400 | scene0333_00 401 | scene0614_00 402 | scene0614_01 403 | scene0614_02 404 | scene0404_00 405 | scene0404_01 406 | scene0404_02 407 | scene0185_00 408 | scene0126_00 409 | scene0126_01 410 | scene0126_02 411 | scene0519_00 412 | scene0236_00 413 | scene0236_01 414 | scene0189_00 415 | scene0075_00 416 | scene0267_00 417 | scene0192_00 418 | scene0192_01 419 | scene0192_02 420 | scene0281_00 421 | scene0420_00 422 | scene0420_01 423 | scene0420_02 424 | scene0195_00 425 | scene0195_01 426 | scene0195_02 427 | scene0597_00 428 | scene0597_01 429 | scene0597_02 430 | scene0041_00 431 | scene0041_01 432 | scene0111_00 433 | scene0111_01 434 | scene0111_02 435 | scene0666_00 436 | scene0666_01 437 | scene0666_02 438 | scene0200_00 439 | scene0200_01 440 | scene0200_02 441 | scene0536_00 442 | scene0536_01 443 | scene0536_02 444 | scene0390_00 445 | scene0280_00 446 | scene0280_01 447 | scene0280_02 448 | scene0344_00 449 | scene0344_01 450 | scene0205_00 451 | scene0205_01 452 | scene0205_02 453 | scene0484_00 454 | scene0484_01 455 | scene0009_00 456 | scene0009_01 457 | scene0009_02 458 | scene0302_00 459 | scene0302_01 460 | scene0209_00 461 | scene0209_01 462 | scene0209_02 463 | scene0210_00 464 | scene0210_01 465 | scene0395_00 466 | scene0395_01 467 | scene0395_02 468 | scene0683_00 469 | scene0601_00 470 | scene0601_01 471 | scene0214_00 472 | scene0214_01 473 | scene0214_02 474 | scene0477_00 475 | scene0477_01 476 | scene0439_00 477 | scene0439_01 478 | scene0468_00 479 | scene0468_01 480 | scene0468_02 481 | scene0546_00 482 | scene0466_00 483 | scene0466_01 484 | scene0220_00 485 | scene0220_01 486 | scene0220_02 487 | scene0122_00 488 | scene0122_01 489 | scene0130_00 490 | scene0110_00 491 | scene0110_01 492 | scene0110_02 493 | scene0327_00 494 | scene0156_00 495 | scene0266_00 496 | scene0266_01 497 | scene0001_00 498 | scene0001_01 499 | scene0228_00 500 | scene0199_00 501 | scene0219_00 502 | scene0464_00 503 | scene0232_00 504 | scene0232_01 505 | scene0232_02 506 | scene0299_00 507 | scene0299_01 508 | scene0530_00 509 | scene0363_00 510 | scene0453_00 511 | scene0453_01 512 | scene0570_00 513 | scene0570_01 514 | scene0570_02 515 | scene0183_00 516 | scene0239_00 517 | scene0239_01 518 | scene0239_02 519 | scene0373_00 520 | scene0373_01 521 | scene0241_00 522 | scene0241_01 523 | scene0241_02 524 | scene0188_00 525 | scene0622_00 526 | scene0622_01 527 | scene0244_00 528 | scene0244_01 529 | scene0691_00 530 | scene0691_01 531 | scene0206_00 532 | scene0206_01 533 | scene0206_02 534 | scene0247_00 535 | scene0247_01 536 | scene0061_00 537 | scene0061_01 538 | scene0082_00 539 | scene0250_00 540 | scene0250_01 541 | scene0250_02 542 | scene0501_00 543 | scene0501_01 544 | scene0501_02 545 | scene0320_00 546 | scene0320_01 547 | scene0320_02 548 | scene0320_03 549 | scene0631_00 550 | scene0631_01 551 | scene0631_02 552 | scene0255_00 553 | scene0255_01 554 | scene0255_02 555 | scene0047_00 556 | scene0265_00 557 | scene0265_01 558 | scene0265_02 559 | scene0004_00 560 | scene0336_00 561 | scene0336_01 562 | scene0058_00 563 | scene0058_01 564 | scene0260_00 565 | scene0260_01 566 | scene0260_02 567 | scene0243_00 568 | scene0603_00 569 | scene0603_01 570 | scene0093_00 571 | scene0093_01 572 | scene0093_02 573 | scene0109_00 574 | scene0109_01 575 | scene0434_00 576 | scene0434_01 577 | scene0434_02 578 | scene0290_00 579 | scene0627_00 580 | scene0627_01 581 | scene0470_00 582 | scene0470_01 583 | scene0137_00 584 | scene0137_01 585 | scene0137_02 586 | scene0270_00 587 | scene0270_01 588 | scene0270_02 589 | scene0271_00 590 | scene0271_01 591 | scene0504_00 592 | scene0274_00 593 | scene0274_01 594 | scene0274_02 595 | scene0036_00 596 | scene0036_01 597 | scene0276_00 598 | scene0276_01 599 | scene0272_00 600 | scene0272_01 601 | scene0499_00 602 | scene0698_00 603 | scene0698_01 604 | scene0051_00 605 | scene0051_01 606 | scene0051_02 607 | scene0051_03 608 | scene0108_00 609 | scene0245_00 610 | scene0369_00 611 | scene0369_01 612 | scene0369_02 613 | scene0284_00 614 | scene0289_00 615 | scene0289_01 616 | scene0286_00 617 | scene0286_01 618 | scene0286_02 619 | scene0286_03 620 | scene0031_00 621 | scene0031_01 622 | scene0031_02 623 | scene0545_00 624 | scene0545_01 625 | scene0545_02 626 | scene0557_00 627 | scene0557_01 628 | scene0557_02 629 | scene0533_00 630 | scene0533_01 631 | scene0116_00 632 | scene0116_01 633 | scene0116_02 634 | scene0611_00 635 | scene0611_01 636 | scene0688_00 637 | scene0294_00 638 | scene0294_01 639 | scene0294_02 640 | scene0295_00 641 | scene0295_01 642 | scene0296_00 643 | scene0296_01 644 | scene0596_00 645 | scene0596_01 646 | scene0596_02 647 | scene0532_00 648 | scene0532_01 649 | scene0637_00 650 | scene0638_00 651 | scene0121_00 652 | scene0121_01 653 | scene0121_02 654 | scene0040_00 655 | scene0040_01 656 | scene0197_00 657 | scene0197_01 658 | scene0197_02 659 | scene0410_00 660 | scene0410_01 661 | scene0305_00 662 | scene0305_01 663 | scene0615_00 664 | scene0615_01 665 | scene0703_00 666 | scene0703_01 667 | scene0555_00 668 | scene0297_00 669 | scene0297_01 670 | scene0297_02 671 | scene0582_00 672 | scene0582_01 673 | scene0582_02 674 | scene0023_00 675 | scene0094_00 676 | scene0013_00 677 | scene0013_01 678 | scene0013_02 679 | scene0136_00 680 | scene0136_01 681 | scene0136_02 682 | scene0407_00 683 | scene0407_01 684 | scene0062_00 685 | scene0062_01 686 | scene0062_02 687 | scene0386_00 688 | scene0318_00 689 | scene0554_00 690 | scene0554_01 691 | scene0497_00 692 | scene0213_00 693 | scene0258_00 694 | scene0323_00 695 | scene0323_01 696 | scene0324_00 697 | scene0324_01 698 | scene0016_00 699 | scene0016_01 700 | scene0016_02 701 | scene0681_00 702 | scene0398_00 703 | scene0398_01 704 | scene0227_00 705 | scene0090_00 706 | scene0066_00 707 | scene0262_00 708 | scene0262_01 709 | scene0155_00 710 | scene0155_01 711 | scene0155_02 712 | scene0352_00 713 | scene0352_01 714 | scene0352_02 715 | scene0038_00 716 | scene0038_01 717 | scene0038_02 718 | scene0335_00 719 | scene0335_01 720 | scene0335_02 721 | scene0261_00 722 | scene0261_01 723 | scene0261_02 724 | scene0261_03 725 | scene0640_00 726 | scene0640_01 727 | scene0640_02 728 | scene0080_00 729 | scene0080_01 730 | scene0080_02 731 | scene0403_00 732 | scene0403_01 733 | scene0282_00 734 | scene0282_01 735 | scene0282_02 736 | scene0682_00 737 | scene0173_00 738 | scene0173_01 739 | scene0173_02 740 | scene0522_00 741 | scene0687_00 742 | scene0345_00 743 | scene0345_01 744 | scene0612_00 745 | scene0612_01 746 | scene0411_00 747 | scene0411_01 748 | scene0411_02 749 | scene0625_00 750 | scene0625_01 751 | scene0211_00 752 | scene0211_01 753 | scene0211_02 754 | scene0211_03 755 | scene0676_00 756 | scene0676_01 757 | scene0179_00 758 | scene0498_00 759 | scene0498_01 760 | scene0498_02 761 | scene0547_00 762 | scene0547_01 763 | scene0547_02 764 | scene0269_00 765 | scene0269_01 766 | scene0269_02 767 | scene0366_00 768 | scene0680_00 769 | scene0680_01 770 | scene0588_00 771 | scene0588_01 772 | scene0588_02 773 | scene0588_03 774 | scene0346_00 775 | scene0346_01 776 | scene0359_00 777 | scene0359_01 778 | scene0014_00 779 | scene0120_00 780 | scene0120_01 781 | scene0212_00 782 | scene0212_01 783 | scene0212_02 784 | scene0176_00 785 | scene0049_00 786 | scene0259_00 787 | scene0259_01 788 | scene0586_00 789 | scene0586_01 790 | scene0586_02 791 | scene0309_00 792 | scene0309_01 793 | scene0125_00 794 | scene0455_00 795 | scene0177_00 796 | scene0177_01 797 | scene0177_02 798 | scene0326_00 799 | scene0372_00 800 | scene0171_00 801 | scene0171_01 802 | scene0374_00 803 | scene0654_00 804 | scene0654_01 805 | scene0445_00 806 | scene0445_01 807 | scene0475_00 808 | scene0475_01 809 | scene0475_02 810 | scene0349_00 811 | scene0349_01 812 | scene0234_00 813 | scene0669_00 814 | scene0669_01 815 | scene0375_00 816 | scene0375_01 817 | scene0375_02 818 | scene0387_00 819 | scene0387_01 820 | scene0387_02 821 | scene0312_00 822 | scene0312_01 823 | scene0312_02 824 | scene0384_00 825 | scene0385_00 826 | scene0385_01 827 | scene0385_02 828 | scene0000_00 829 | scene0000_01 830 | scene0000_02 831 | scene0376_00 832 | scene0376_01 833 | scene0376_02 834 | scene0301_00 835 | scene0301_01 836 | scene0301_02 837 | scene0322_00 838 | scene0542_00 839 | scene0079_00 840 | scene0079_01 841 | scene0099_00 842 | scene0099_01 843 | scene0476_00 844 | scene0476_01 845 | scene0476_02 846 | scene0394_00 847 | scene0394_01 848 | scene0147_00 849 | scene0147_01 850 | scene0067_00 851 | scene0067_01 852 | scene0067_02 853 | scene0397_00 854 | scene0397_01 855 | scene0337_00 856 | scene0337_01 857 | scene0337_02 858 | scene0431_00 859 | scene0223_00 860 | scene0223_01 861 | scene0223_02 862 | scene0010_00 863 | scene0010_01 864 | scene0402_00 865 | scene0268_00 866 | scene0268_01 867 | scene0268_02 868 | scene0679_00 869 | scene0679_01 870 | scene0405_00 871 | scene0128_00 872 | scene0408_00 873 | scene0408_01 874 | scene0190_00 875 | scene0107_00 876 | scene0076_00 877 | scene0167_00 878 | scene0361_00 879 | scene0361_01 880 | scene0361_02 881 | scene0216_00 882 | scene0202_00 883 | scene0303_00 884 | scene0303_01 885 | scene0303_02 886 | scene0446_00 887 | scene0446_01 888 | scene0089_00 889 | scene0089_01 890 | scene0089_02 891 | scene0360_00 892 | scene0150_00 893 | scene0150_01 894 | scene0150_02 895 | scene0421_00 896 | scene0421_01 897 | scene0421_02 898 | scene0454_00 899 | scene0626_00 900 | scene0626_01 901 | scene0626_02 902 | scene0186_00 903 | scene0186_01 904 | scene0538_00 905 | scene0479_00 906 | scene0479_01 907 | scene0479_02 908 | scene0656_00 909 | scene0656_01 910 | scene0656_02 911 | scene0656_03 912 | scene0525_00 913 | scene0525_01 914 | scene0525_02 915 | scene0308_00 916 | scene0396_00 917 | scene0396_01 918 | scene0396_02 919 | scene0624_00 920 | scene0292_00 921 | scene0292_01 922 | scene0632_00 923 | scene0253_00 924 | scene0021_00 925 | scene0325_00 926 | scene0325_01 927 | scene0437_00 928 | scene0437_01 929 | scene0438_00 930 | scene0590_00 931 | scene0590_01 932 | scene0400_00 933 | scene0400_01 934 | scene0541_00 935 | scene0541_01 936 | scene0541_02 937 | scene0677_00 938 | scene0677_01 939 | scene0677_02 940 | scene0443_00 941 | scene0315_00 942 | scene0288_00 943 | scene0288_01 944 | scene0288_02 945 | scene0422_00 946 | scene0672_00 947 | scene0672_01 948 | scene0184_00 949 | scene0449_00 950 | scene0449_01 951 | scene0449_02 952 | scene0048_00 953 | scene0048_01 954 | scene0138_00 955 | scene0452_00 956 | scene0452_01 957 | scene0452_02 958 | scene0667_00 959 | scene0667_01 960 | scene0667_02 961 | scene0463_00 962 | scene0463_01 963 | scene0078_00 964 | scene0078_01 965 | scene0078_02 966 | scene0636_00 967 | scene0457_00 968 | scene0457_01 969 | scene0457_02 970 | scene0465_00 971 | scene0465_01 972 | scene0577_00 973 | scene0151_00 974 | scene0151_01 975 | scene0339_00 976 | scene0573_00 977 | scene0573_01 978 | scene0154_00 979 | scene0096_00 980 | scene0096_01 981 | scene0096_02 982 | scene0235_00 983 | scene0168_00 984 | scene0168_01 985 | scene0168_02 986 | scene0594_00 987 | scene0587_00 988 | scene0587_01 989 | scene0587_02 990 | scene0587_03 991 | scene0229_00 992 | scene0229_01 993 | scene0229_02 994 | scene0512_00 995 | scene0106_00 996 | scene0106_01 997 | scene0106_02 998 | scene0472_00 999 | scene0472_01 1000 | scene0472_02 1001 | scene0489_00 1002 | scene0489_01 1003 | scene0489_02 1004 | scene0425_00 1005 | scene0425_01 1006 | scene0641_00 1007 | scene0526_00 1008 | scene0526_01 1009 | scene0317_00 1010 | scene0317_01 1011 | scene0544_00 1012 | scene0017_00 1013 | scene0017_01 1014 | scene0017_02 1015 | scene0042_00 1016 | scene0042_01 1017 | scene0042_02 1018 | scene0576_00 1019 | scene0576_01 1020 | scene0576_02 1021 | scene0347_00 1022 | scene0347_01 1023 | scene0347_02 1024 | scene0436_00 1025 | scene0226_00 1026 | scene0226_01 1027 | scene0485_00 1028 | scene0486_00 1029 | scene0487_00 1030 | scene0487_01 1031 | scene0619_00 1032 | scene0097_00 1033 | scene0367_00 1034 | scene0367_01 1035 | scene0491_00 1036 | scene0492_00 1037 | scene0492_01 1038 | scene0005_00 1039 | scene0005_01 1040 | scene0543_00 1041 | scene0543_01 1042 | scene0543_02 1043 | scene0657_00 1044 | scene0341_00 1045 | scene0341_01 1046 | scene0534_00 1047 | scene0534_01 1048 | scene0319_00 1049 | scene0273_00 1050 | scene0273_01 1051 | scene0225_00 1052 | scene0198_00 1053 | scene0003_00 1054 | scene0003_01 1055 | scene0003_02 1056 | scene0409_00 1057 | scene0409_01 1058 | scene0331_00 1059 | scene0331_01 1060 | scene0505_00 1061 | scene0505_01 1062 | scene0505_02 1063 | scene0505_03 1064 | scene0505_04 1065 | scene0506_00 1066 | scene0057_00 1067 | scene0057_01 1068 | scene0074_00 1069 | scene0074_01 1070 | scene0074_02 1071 | scene0091_00 1072 | scene0112_00 1073 | scene0112_01 1074 | scene0112_02 1075 | scene0240_00 1076 | scene0102_00 1077 | scene0102_01 1078 | scene0513_00 1079 | scene0514_00 1080 | scene0514_01 1081 | scene0537_00 1082 | scene0516_00 1083 | scene0516_01 1084 | scene0495_00 1085 | scene0617_00 1086 | scene0133_00 1087 | scene0520_00 1088 | scene0520_01 1089 | scene0635_00 1090 | scene0635_01 1091 | scene0054_00 1092 | scene0473_00 1093 | scene0473_01 1094 | scene0524_00 1095 | scene0524_01 1096 | scene0379_00 1097 | scene0471_00 1098 | scene0471_01 1099 | scene0471_02 1100 | scene0566_00 1101 | scene0248_00 1102 | scene0248_01 1103 | scene0248_02 1104 | scene0529_00 1105 | scene0529_01 1106 | scene0529_02 1107 | scene0391_00 1108 | scene0264_00 1109 | scene0264_01 1110 | scene0264_02 1111 | scene0675_00 1112 | scene0675_01 1113 | scene0350_00 1114 | scene0350_01 1115 | scene0350_02 1116 | scene0450_00 1117 | scene0068_00 1118 | scene0068_01 1119 | scene0237_00 1120 | scene0237_01 1121 | scene0365_00 1122 | scene0365_01 1123 | scene0365_02 1124 | scene0605_00 1125 | scene0605_01 1126 | scene0539_00 1127 | scene0539_01 1128 | scene0539_02 1129 | scene0540_00 1130 | scene0540_01 1131 | scene0540_02 1132 | scene0170_00 1133 | scene0170_01 1134 | scene0170_02 1135 | scene0433_00 1136 | scene0340_00 1137 | scene0340_01 1138 | scene0340_02 1139 | scene0160_00 1140 | scene0160_01 1141 | scene0160_02 1142 | scene0160_03 1143 | scene0160_04 1144 | scene0059_00 1145 | scene0059_01 1146 | scene0059_02 1147 | scene0056_00 1148 | scene0056_01 1149 | scene0478_00 1150 | scene0478_01 1151 | scene0548_00 1152 | scene0548_01 1153 | scene0548_02 1154 | scene0204_00 1155 | scene0204_01 1156 | scene0204_02 1157 | scene0033_00 1158 | scene0145_00 1159 | scene0483_00 1160 | scene0508_00 1161 | scene0508_01 1162 | scene0508_02 1163 | scene0180_00 1164 | scene0148_00 1165 | scene0556_00 1166 | scene0556_01 1167 | scene0416_00 1168 | scene0416_01 1169 | scene0416_02 1170 | scene0416_03 1171 | scene0416_04 1172 | scene0073_00 1173 | scene0073_01 1174 | scene0073_02 1175 | scene0073_03 1176 | scene0034_00 1177 | scene0034_01 1178 | scene0034_02 1179 | scene0639_00 1180 | scene0561_00 1181 | scene0561_01 1182 | scene0298_00 1183 | scene0692_00 1184 | scene0692_01 1185 | scene0692_02 1186 | scene0692_03 1187 | scene0692_04 1188 | scene0642_00 1189 | scene0642_01 1190 | scene0642_02 1191 | scene0642_03 1192 | scene0630_00 1193 | scene0630_01 1194 | scene0630_02 1195 | scene0630_03 1196 | scene0630_04 1197 | scene0630_05 1198 | scene0630_06 1199 | scene0706_00 1200 | scene0567_00 1201 | scene0567_01 1202 | -------------------------------------------------------------------------------- /prepare_data/scannetv2_val.txt: -------------------------------------------------------------------------------- 1 | scene0568_00 2 | scene0568_01 3 | scene0568_02 4 | scene0304_00 5 | scene0488_00 6 | scene0488_01 7 | scene0412_00 8 | scene0412_01 9 | scene0217_00 10 | scene0019_00 11 | scene0019_01 12 | scene0414_00 13 | scene0575_00 14 | scene0575_01 15 | scene0575_02 16 | scene0426_00 17 | scene0426_01 18 | scene0426_02 19 | scene0426_03 20 | scene0549_00 21 | scene0549_01 22 | scene0578_00 23 | scene0578_01 24 | scene0578_02 25 | scene0665_00 26 | scene0665_01 27 | scene0050_00 28 | scene0050_01 29 | scene0050_02 30 | scene0257_00 31 | scene0025_00 32 | scene0025_01 33 | scene0025_02 34 | scene0583_00 35 | scene0583_01 36 | scene0583_02 37 | scene0701_00 38 | scene0701_01 39 | scene0701_02 40 | scene0580_00 41 | scene0580_01 42 | scene0565_00 43 | scene0169_00 44 | scene0169_01 45 | scene0655_00 46 | scene0655_01 47 | scene0655_02 48 | scene0063_00 49 | scene0221_00 50 | scene0221_01 51 | scene0591_00 52 | scene0591_01 53 | scene0591_02 54 | scene0678_00 55 | scene0678_01 56 | scene0678_02 57 | scene0462_00 58 | scene0427_00 59 | scene0595_00 60 | scene0193_00 61 | scene0193_01 62 | scene0164_00 63 | scene0164_01 64 | scene0164_02 65 | scene0164_03 66 | scene0598_00 67 | scene0598_01 68 | scene0598_02 69 | scene0599_00 70 | scene0599_01 71 | scene0599_02 72 | scene0328_00 73 | scene0300_00 74 | scene0300_01 75 | scene0354_00 76 | scene0458_00 77 | scene0458_01 78 | scene0423_00 79 | scene0423_01 80 | scene0423_02 81 | scene0307_00 82 | scene0307_01 83 | scene0307_02 84 | scene0606_00 85 | scene0606_01 86 | scene0606_02 87 | scene0432_00 88 | scene0432_01 89 | scene0608_00 90 | scene0608_01 91 | scene0608_02 92 | scene0651_00 93 | scene0651_01 94 | scene0651_02 95 | scene0430_00 96 | scene0430_01 97 | scene0689_00 98 | scene0357_00 99 | scene0357_01 100 | scene0574_00 101 | scene0574_01 102 | scene0574_02 103 | scene0329_00 104 | scene0329_01 105 | scene0329_02 106 | scene0153_00 107 | scene0153_01 108 | scene0616_00 109 | scene0616_01 110 | scene0671_00 111 | scene0671_01 112 | scene0618_00 113 | scene0382_00 114 | scene0382_01 115 | scene0490_00 116 | scene0621_00 117 | scene0607_00 118 | scene0607_01 119 | scene0149_00 120 | scene0695_00 121 | scene0695_01 122 | scene0695_02 123 | scene0695_03 124 | scene0389_00 125 | scene0377_00 126 | scene0377_01 127 | scene0377_02 128 | scene0342_00 129 | scene0139_00 130 | scene0629_00 131 | scene0629_01 132 | scene0629_02 133 | scene0496_00 134 | scene0633_00 135 | scene0633_01 136 | scene0518_00 137 | scene0652_00 138 | scene0406_00 139 | scene0406_01 140 | scene0406_02 141 | scene0144_00 142 | scene0144_01 143 | scene0494_00 144 | scene0278_00 145 | scene0278_01 146 | scene0316_00 147 | scene0609_00 148 | scene0609_01 149 | scene0609_02 150 | scene0609_03 151 | scene0084_00 152 | scene0084_01 153 | scene0084_02 154 | scene0696_00 155 | scene0696_01 156 | scene0696_02 157 | scene0351_00 158 | scene0351_01 159 | scene0643_00 160 | scene0644_00 161 | scene0645_00 162 | scene0645_01 163 | scene0645_02 164 | scene0081_00 165 | scene0081_01 166 | scene0081_02 167 | scene0647_00 168 | scene0647_01 169 | scene0535_00 170 | scene0353_00 171 | scene0353_01 172 | scene0353_02 173 | scene0559_00 174 | scene0559_01 175 | scene0559_02 176 | scene0593_00 177 | scene0593_01 178 | scene0246_00 179 | scene0653_00 180 | scene0653_01 181 | scene0064_00 182 | scene0064_01 183 | scene0356_00 184 | scene0356_01 185 | scene0356_02 186 | scene0030_00 187 | scene0030_01 188 | scene0030_02 189 | scene0222_00 190 | scene0222_01 191 | scene0338_00 192 | scene0338_01 193 | scene0338_02 194 | scene0378_00 195 | scene0378_01 196 | scene0378_02 197 | scene0660_00 198 | scene0553_00 199 | scene0553_01 200 | scene0553_02 201 | scene0527_00 202 | scene0663_00 203 | scene0663_01 204 | scene0663_02 205 | scene0664_00 206 | scene0664_01 207 | scene0664_02 208 | scene0334_00 209 | scene0334_01 210 | scene0334_02 211 | scene0046_00 212 | scene0046_01 213 | scene0046_02 214 | scene0203_00 215 | scene0203_01 216 | scene0203_02 217 | scene0088_00 218 | scene0088_01 219 | scene0088_02 220 | scene0088_03 221 | scene0086_00 222 | scene0086_01 223 | scene0086_02 224 | scene0670_00 225 | scene0670_01 226 | scene0256_00 227 | scene0256_01 228 | scene0256_02 229 | scene0249_00 230 | scene0441_00 231 | scene0658_00 232 | scene0704_00 233 | scene0704_01 234 | scene0187_00 235 | scene0187_01 236 | scene0131_00 237 | scene0131_01 238 | scene0131_02 239 | scene0207_00 240 | scene0207_01 241 | scene0207_02 242 | scene0461_00 243 | scene0011_00 244 | scene0011_01 245 | scene0343_00 246 | scene0251_00 247 | scene0077_00 248 | scene0077_01 249 | scene0684_00 250 | scene0684_01 251 | scene0550_00 252 | scene0686_00 253 | scene0686_01 254 | scene0686_02 255 | scene0208_00 256 | scene0500_00 257 | scene0500_01 258 | scene0552_00 259 | scene0552_01 260 | scene0648_00 261 | scene0648_01 262 | scene0435_00 263 | scene0435_01 264 | scene0435_02 265 | scene0435_03 266 | scene0690_00 267 | scene0690_01 268 | scene0693_00 269 | scene0693_01 270 | scene0693_02 271 | scene0700_00 272 | scene0700_01 273 | scene0700_02 274 | scene0699_00 275 | scene0231_00 276 | scene0231_01 277 | scene0231_02 278 | scene0697_00 279 | scene0697_01 280 | scene0697_02 281 | scene0697_03 282 | scene0474_00 283 | scene0474_01 284 | scene0474_02 285 | scene0474_03 286 | scene0474_04 287 | scene0474_05 288 | scene0355_00 289 | scene0355_01 290 | scene0146_00 291 | scene0146_01 292 | scene0146_02 293 | scene0196_00 294 | scene0702_00 295 | scene0702_01 296 | scene0702_02 297 | scene0314_00 298 | scene0277_00 299 | scene0277_01 300 | scene0277_02 301 | scene0095_00 302 | scene0095_01 303 | scene0015_00 304 | scene0100_00 305 | scene0100_01 306 | scene0100_02 307 | scene0558_00 308 | scene0558_01 309 | scene0558_02 310 | scene0685_00 311 | scene0685_01 312 | scene0685_02 313 | -------------------------------------------------------------------------------- /prepare_data/util.py: -------------------------------------------------------------------------------- 1 | import os, sys 2 | import csv 3 | try: 4 | import numpy as np 5 | except: 6 | print("Failed to import numpy package.") 7 | sys.exit(-1) 8 | try: 9 | import imageio 10 | except: 11 | print("Please install the module 'imageio' for image processing, e.g.") 12 | print("pip install imageio") 13 | sys.exit(-1) 14 | 15 | # print an error message and quit 16 | def print_error(message, user_fault=False): 17 | sys.stderr.write('ERROR: ' + str(message) + '\n') 18 | if user_fault: 19 | sys.exit(2) 20 | sys.exit(-1) 21 | 22 | 23 | # if string s represents an int 24 | def represents_int(s): 25 | try: 26 | int(s) 27 | return True 28 | except ValueError: 29 | return False 30 | 31 | 32 | def read_label_mapping(filename, label_from='raw_category', label_to='nyu40id'): 33 | assert os.path.isfile(filename) 34 | mapping = dict() 35 | with open(filename) as csvfile: 36 | reader = csv.DictReader(csvfile, delimiter='\t') 37 | for row in reader: 38 | mapping[row[label_from]] = int(row[label_to]) 39 | # if ints convert 40 | if represents_int(mapping.keys()[0]): 41 | mapping = {int(k):v for k,v in mapping.items()} 42 | return mapping 43 | 44 | 45 | # input: scene_types.txt or scene_types_all.txt 46 | def read_scene_types_mapping(filename, remove_spaces=True): 47 | assert os.path.isfile(filename) 48 | mapping = dict() 49 | lines = open(filename).read().splitlines() 50 | lines = [line.split('\t') for line in lines] 51 | if remove_spaces: 52 | mapping = { x[1].strip():int(x[0]) for x in lines } 53 | else: 54 | mapping = { x[1]:int(x[0]) for x in lines } 55 | return mapping 56 | 57 | 58 | # color by label 59 | def visualize_label_image(filename, image): 60 | height = image.shape[0] 61 | width = image.shape[1] 62 | vis_image = np.zeros([height, width, 3], dtype=np.uint8) 63 | color_palette = create_color_palette() 64 | for idx, color in enumerate(color_palette): 65 | vis_image[image==idx] = color 66 | imageio.imwrite(filename, vis_image) 67 | 68 | 69 | # color by different instances (mod length of color palette) 70 | def visualize_instance_image(filename, image): 71 | height = image.shape[0] 72 | width = image.shape[1] 73 | vis_image = np.zeros([height, width, 3], dtype=np.uint8) 74 | color_palette = create_color_palette() 75 | instances = np.unique(image) 76 | for idx, inst in enumerate(instances): 77 | vis_image[image==inst] = color_palette[inst%len(color_palette)] 78 | imageio.imwrite(filename, vis_image) 79 | 80 | 81 | # color palette for nyu40 labels 82 | def create_color_palette(): 83 | return [ 84 | (0, 0, 0), 85 | (174, 199, 232), # wall 86 | (152, 223, 138), # floor 87 | (31, 119, 180), # cabinet 88 | (255, 187, 120), # bed 89 | (188, 189, 34), # chair 90 | (140, 86, 75), # sofa 91 | (255, 152, 150), # table 92 | (214, 39, 40), # door 93 | (197, 176, 213), # window 94 | (148, 103, 189), # bookshelf 95 | (196, 156, 148), # picture 96 | (23, 190, 207), # counter 97 | (178, 76, 76), 98 | (247, 182, 210), # desk 99 | (66, 188, 102), 100 | (219, 219, 141), # curtain 101 | (140, 57, 197), 102 | (202, 185, 52), 103 | (51, 176, 203), 104 | (200, 54, 131), 105 | (92, 193, 61), 106 | (78, 71, 183), 107 | (172, 114, 82), 108 | (255, 127, 14), # refrigerator 109 | (91, 163, 138), 110 | (153, 98, 156), 111 | (140, 153, 101), 112 | (158, 218, 229), # shower curtain 113 | (100, 125, 154), 114 | (178, 127, 135), 115 | (120, 185, 128), 116 | (146, 111, 194), 117 | (44, 160, 44), # toilet 118 | (112, 128, 144), # sink 119 | (96, 207, 209), 120 | (227, 119, 194), # bathtub 121 | (213, 92, 176), 122 | (94, 106, 211), 123 | (82, 84, 163), # otherfurn 124 | (100, 85, 144) 125 | ] 126 | -------------------------------------------------------------------------------- /pretrained/model.cls.1024.t7: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antao97/dgcnn.pytorch/07d534c2702905010ec9991619f552d8cacae45b/pretrained/model.cls.1024.t7 -------------------------------------------------------------------------------- /pretrained/model.cls.2048.t7: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antao97/dgcnn.pytorch/07d534c2702905010ec9991619f552d8cacae45b/pretrained/model.cls.2048.t7 -------------------------------------------------------------------------------- /pretrained/model.partseg.airplane.t7: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antao97/dgcnn.pytorch/07d534c2702905010ec9991619f552d8cacae45b/pretrained/model.partseg.airplane.t7 -------------------------------------------------------------------------------- /pretrained/model.partseg.car.t7: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antao97/dgcnn.pytorch/07d534c2702905010ec9991619f552d8cacae45b/pretrained/model.partseg.car.t7 -------------------------------------------------------------------------------- /pretrained/model.partseg.t7: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antao97/dgcnn.pytorch/07d534c2702905010ec9991619f552d8cacae45b/pretrained/model.partseg.t7 -------------------------------------------------------------------------------- /pretrained/semseg_s3dis/model_1.t7: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antao97/dgcnn.pytorch/07d534c2702905010ec9991619f552d8cacae45b/pretrained/semseg_s3dis/model_1.t7 -------------------------------------------------------------------------------- /pretrained/semseg_s3dis/model_2.t7: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antao97/dgcnn.pytorch/07d534c2702905010ec9991619f552d8cacae45b/pretrained/semseg_s3dis/model_2.t7 -------------------------------------------------------------------------------- /pretrained/semseg_s3dis/model_3.t7: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antao97/dgcnn.pytorch/07d534c2702905010ec9991619f552d8cacae45b/pretrained/semseg_s3dis/model_3.t7 -------------------------------------------------------------------------------- /pretrained/semseg_s3dis/model_4.t7: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antao97/dgcnn.pytorch/07d534c2702905010ec9991619f552d8cacae45b/pretrained/semseg_s3dis/model_4.t7 -------------------------------------------------------------------------------- /pretrained/semseg_s3dis/model_5.t7: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antao97/dgcnn.pytorch/07d534c2702905010ec9991619f552d8cacae45b/pretrained/semseg_s3dis/model_5.t7 -------------------------------------------------------------------------------- /pretrained/semseg_s3dis/model_6.t7: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antao97/dgcnn.pytorch/07d534c2702905010ec9991619f552d8cacae45b/pretrained/semseg_s3dis/model_6.t7 -------------------------------------------------------------------------------- /pretrained/semseg_scannet/models/model_200.pth: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antao97/dgcnn.pytorch/07d534c2702905010ec9991619f552d8cacae45b/pretrained/semseg_scannet/models/model_200.pth -------------------------------------------------------------------------------- /sync_bn/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File : __init__.py 3 | # Author : Jiayuan Mao 4 | # Email : maojiayuan@gmail.com 5 | # Date : 27/01/2018 6 | # 7 | # This file is part of Synchronized-BatchNorm-PyTorch. 8 | # https://github.com/vacancy/Synchronized-BatchNorm-PyTorch 9 | # Distributed under MIT License. 10 | 11 | from .batchnorm import SynchronizedBatchNorm1d, SynchronizedBatchNorm2d, SynchronizedBatchNorm3d 12 | from .replicate import DataParallelWithCallback, patch_replication_callback 13 | -------------------------------------------------------------------------------- /sync_bn/batchnorm.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File : batchnorm.py 3 | # Author : Jiayuan Mao 4 | # Email : maojiayuan@gmail.com 5 | # Date : 27/01/2018 6 | # 7 | # This file is part of Synchronized-BatchNorm-PyTorch. 8 | # https://github.com/vacancy/Synchronized-BatchNorm-PyTorch 9 | # Distributed under MIT License. 10 | 11 | import collections 12 | 13 | import torch 14 | import torch.nn.functional as F 15 | 16 | from torch.nn.modules.batchnorm import _BatchNorm 17 | from torch.nn.parallel._functions import ReduceAddCoalesced, Broadcast 18 | 19 | from .comm import SyncMaster 20 | 21 | __all__ = ['SynchronizedBatchNorm1d', 'SynchronizedBatchNorm2d', 'SynchronizedBatchNorm3d'] 22 | 23 | 24 | def _sum_ft(tensor): 25 | """sum over the first and last dimention""" 26 | return tensor.sum(dim=0).sum(dim=-1) 27 | 28 | 29 | def _unsqueeze_ft(tensor): 30 | """add new dementions at the front and the tail""" 31 | return tensor.unsqueeze(0).unsqueeze(-1) 32 | 33 | 34 | _ChildMessage = collections.namedtuple('_ChildMessage', ['sum', 'ssum', 'sum_size']) 35 | _MasterMessage = collections.namedtuple('_MasterMessage', ['sum', 'inv_std']) 36 | 37 | 38 | class _SynchronizedBatchNorm(_BatchNorm): 39 | def __init__(self, num_features, eps=1e-5, momentum=0.1, affine=True): 40 | super(_SynchronizedBatchNorm, self).__init__(num_features, eps=eps, momentum=momentum, affine=affine) 41 | 42 | self._sync_master = SyncMaster(self._data_parallel_master) 43 | 44 | self._is_parallel = False 45 | self._parallel_id = None 46 | self._slave_pipe = None 47 | 48 | def forward(self, input): 49 | # If it is not parallel computation or is in evaluation mode, use PyTorch's implementation. 50 | if not (self._is_parallel and self.training): 51 | return F.batch_norm( 52 | input, self.running_mean, self.running_var, self.weight, self.bias, 53 | self.training, self.momentum, self.eps) 54 | 55 | # Resize the input to (B, C, -1). 56 | input_shape = input.size() 57 | input = input.view(input.size(0), self.num_features, -1) 58 | 59 | # Compute the sum and square-sum. 60 | sum_size = input.size(0) * input.size(2) 61 | input_sum = _sum_ft(input) 62 | input_ssum = _sum_ft(input ** 2) 63 | 64 | # Reduce-and-broadcast the statistics. 65 | if self._parallel_id == 0: 66 | mean, inv_std = self._sync_master.run_master(_ChildMessage(input_sum, input_ssum, sum_size)) 67 | else: 68 | mean, inv_std = self._slave_pipe.run_slave(_ChildMessage(input_sum, input_ssum, sum_size)) 69 | 70 | # Compute the output. 71 | if self.affine: 72 | # MJY:: Fuse the multiplication for speed. 73 | output = (input - _unsqueeze_ft(mean)) * _unsqueeze_ft(inv_std * self.weight) + _unsqueeze_ft(self.bias) 74 | else: 75 | output = (input - _unsqueeze_ft(mean)) * _unsqueeze_ft(inv_std) 76 | 77 | # Reshape it. 78 | return output.view(input_shape) 79 | 80 | def __data_parallel_replicate__(self, ctx, copy_id): 81 | self._is_parallel = True 82 | self._parallel_id = copy_id 83 | 84 | # parallel_id == 0 means master device. 85 | if self._parallel_id == 0: 86 | ctx.sync_master = self._sync_master 87 | else: 88 | self._slave_pipe = ctx.sync_master.register_slave(copy_id) 89 | 90 | def _data_parallel_master(self, intermediates): 91 | """Reduce the sum and square-sum, compute the statistics, and broadcast it.""" 92 | 93 | # Always using same "device order" makes the ReduceAdd operation faster. 94 | # Thanks to:: Tete Xiao (http://tetexiao.com/) 95 | intermediates = sorted(intermediates, key=lambda i: i[1].sum.get_device()) 96 | 97 | to_reduce = [i[1][:2] for i in intermediates] 98 | to_reduce = [j for i in to_reduce for j in i] # flatten 99 | target_gpus = [i[1].sum.get_device() for i in intermediates] 100 | 101 | sum_size = sum([i[1].sum_size for i in intermediates]) 102 | sum_, ssum = ReduceAddCoalesced.apply(target_gpus[0], 2, *to_reduce) 103 | mean, inv_std = self._compute_mean_std(sum_, ssum, sum_size) 104 | 105 | broadcasted = Broadcast.apply(target_gpus, mean, inv_std) 106 | 107 | outputs = [] 108 | for i, rec in enumerate(intermediates): 109 | outputs.append((rec[0], _MasterMessage(*broadcasted[i*2:i*2+2]))) 110 | 111 | return outputs 112 | 113 | def _compute_mean_std(self, sum_, ssum, size): 114 | """Compute the mean and standard-deviation with sum and square-sum. This method 115 | also maintains the moving average on the master device.""" 116 | assert size > 1, 'BatchNorm computes unbiased standard-deviation, which requires size > 1.' 117 | mean = sum_ / size 118 | sumvar = ssum - sum_ * mean 119 | unbias_var = sumvar / (size - 1) 120 | bias_var = sumvar / size 121 | 122 | self.running_mean = (1 - self.momentum) * self.running_mean + self.momentum * mean.data 123 | self.running_var = (1 - self.momentum) * self.running_var + self.momentum * unbias_var.data 124 | 125 | return mean, bias_var.clamp(self.eps) ** -0.5 126 | 127 | 128 | class SynchronizedBatchNorm1d(_SynchronizedBatchNorm): 129 | r"""Applies Synchronized Batch Normalization over a 2d or 3d input that is seen as a 130 | mini-batch. 131 | 132 | .. math:: 133 | 134 | y = \frac{x - mean[x]}{ \sqrt{Var[x] + \epsilon}} * gamma + beta 135 | 136 | This module differs from the built-in PyTorch BatchNorm1d as the mean and 137 | standard-deviation are reduced across all devices during training. 138 | 139 | For example, when one uses `nn.DataParallel` to wrap the network during 140 | training, PyTorch's implementation normalize the tensor on each device using 141 | the statistics only on that device, which accelerated the computation and 142 | is also easy to implement, but the statistics might be inaccurate. 143 | Instead, in this synchronized version, the statistics will be computed 144 | over all training samples distributed on multiple devices. 145 | 146 | Note that, for one-GPU or CPU-only case, this module behaves exactly same 147 | as the built-in PyTorch implementation. 148 | 149 | The mean and standard-deviation are calculated per-dimension over 150 | the mini-batches and gamma and beta are learnable parameter vectors 151 | of size C (where C is the input size). 152 | 153 | During training, this layer keeps a running estimate of its computed mean 154 | and variance. The running sum is kept with a default momentum of 0.1. 155 | 156 | During evaluation, this running mean/variance is used for normalization. 157 | 158 | Because the BatchNorm is done over the `C` dimension, computing statistics 159 | on `(N, L)` slices, it's common terminology to call this Temporal BatchNorm 160 | 161 | Args: 162 | num_features: num_features from an expected input of size 163 | `batch_size x num_features [x width]` 164 | eps: a value added to the denominator for numerical stability. 165 | Default: 1e-5 166 | momentum: the value used for the running_mean and running_var 167 | computation. Default: 0.1 168 | affine: a boolean value that when set to ``True``, gives the layer learnable 169 | affine parameters. Default: ``True`` 170 | 171 | Shape: 172 | - Input: :math:`(N, C)` or :math:`(N, C, L)` 173 | - Output: :math:`(N, C)` or :math:`(N, C, L)` (same shape as input) 174 | 175 | Examples: 176 | >>> # With Learnable Parameters 177 | >>> m = SynchronizedBatchNorm1d(100) 178 | >>> # Without Learnable Parameters 179 | >>> m = SynchronizedBatchNorm1d(100, affine=False) 180 | >>> input = torch.autograd.Variable(torch.randn(20, 100)) 181 | >>> output = m(input) 182 | """ 183 | 184 | def _check_input_dim(self, input): 185 | if input.dim() != 2 and input.dim() != 3: 186 | raise ValueError('expected 2D or 3D input (got {}D input)' 187 | .format(input.dim())) 188 | super(SynchronizedBatchNorm1d, self)._check_input_dim(input) 189 | 190 | 191 | class SynchronizedBatchNorm2d(_SynchronizedBatchNorm): 192 | r"""Applies Batch Normalization over a 4d input that is seen as a mini-batch 193 | of 3d inputs 194 | 195 | .. math:: 196 | 197 | y = \frac{x - mean[x]}{ \sqrt{Var[x] + \epsilon}} * gamma + beta 198 | 199 | This module differs from the built-in PyTorch BatchNorm2d as the mean and 200 | standard-deviation are reduced across all devices during training. 201 | 202 | For example, when one uses `nn.DataParallel` to wrap the network during 203 | training, PyTorch's implementation normalize the tensor on each device using 204 | the statistics only on that device, which accelerated the computation and 205 | is also easy to implement, but the statistics might be inaccurate. 206 | Instead, in this synchronized version, the statistics will be computed 207 | over all training samples distributed on multiple devices. 208 | 209 | Note that, for one-GPU or CPU-only case, this module behaves exactly same 210 | as the built-in PyTorch implementation. 211 | 212 | The mean and standard-deviation are calculated per-dimension over 213 | the mini-batches and gamma and beta are learnable parameter vectors 214 | of size C (where C is the input size). 215 | 216 | During training, this layer keeps a running estimate of its computed mean 217 | and variance. The running sum is kept with a default momentum of 0.1. 218 | 219 | During evaluation, this running mean/variance is used for normalization. 220 | 221 | Because the BatchNorm is done over the `C` dimension, computing statistics 222 | on `(N, H, W)` slices, it's common terminology to call this Spatial BatchNorm 223 | 224 | Args: 225 | num_features: num_features from an expected input of 226 | size batch_size x num_features x height x width 227 | eps: a value added to the denominator for numerical stability. 228 | Default: 1e-5 229 | momentum: the value used for the running_mean and running_var 230 | computation. Default: 0.1 231 | affine: a boolean value that when set to ``True``, gives the layer learnable 232 | affine parameters. Default: ``True`` 233 | 234 | Shape: 235 | - Input: :math:`(N, C, H, W)` 236 | - Output: :math:`(N, C, H, W)` (same shape as input) 237 | 238 | Examples: 239 | >>> # With Learnable Parameters 240 | >>> m = SynchronizedBatchNorm2d(100) 241 | >>> # Without Learnable Parameters 242 | >>> m = SynchronizedBatchNorm2d(100, affine=False) 243 | >>> input = torch.autograd.Variable(torch.randn(20, 100, 35, 45)) 244 | >>> output = m(input) 245 | """ 246 | 247 | def _check_input_dim(self, input): 248 | if input.dim() != 4: 249 | raise ValueError('expected 4D input (got {}D input)' 250 | .format(input.dim())) 251 | super(SynchronizedBatchNorm2d, self)._check_input_dim(input) 252 | 253 | 254 | class SynchronizedBatchNorm3d(_SynchronizedBatchNorm): 255 | r"""Applies Batch Normalization over a 5d input that is seen as a mini-batch 256 | of 4d inputs 257 | 258 | .. math:: 259 | 260 | y = \frac{x - mean[x]}{ \sqrt{Var[x] + \epsilon}} * gamma + beta 261 | 262 | This module differs from the built-in PyTorch BatchNorm3d as the mean and 263 | standard-deviation are reduced across all devices during training. 264 | 265 | For example, when one uses `nn.DataParallel` to wrap the network during 266 | training, PyTorch's implementation normalize the tensor on each device using 267 | the statistics only on that device, which accelerated the computation and 268 | is also easy to implement, but the statistics might be inaccurate. 269 | Instead, in this synchronized version, the statistics will be computed 270 | over all training samples distributed on multiple devices. 271 | 272 | Note that, for one-GPU or CPU-only case, this module behaves exactly same 273 | as the built-in PyTorch implementation. 274 | 275 | The mean and standard-deviation are calculated per-dimension over 276 | the mini-batches and gamma and beta are learnable parameter vectors 277 | of size C (where C is the input size). 278 | 279 | During training, this layer keeps a running estimate of its computed mean 280 | and variance. The running sum is kept with a default momentum of 0.1. 281 | 282 | During evaluation, this running mean/variance is used for normalization. 283 | 284 | Because the BatchNorm is done over the `C` dimension, computing statistics 285 | on `(N, D, H, W)` slices, it's common terminology to call this Volumetric BatchNorm 286 | or Spatio-temporal BatchNorm 287 | 288 | Args: 289 | num_features: num_features from an expected input of 290 | size batch_size x num_features x depth x height x width 291 | eps: a value added to the denominator for numerical stability. 292 | Default: 1e-5 293 | momentum: the value used for the running_mean and running_var 294 | computation. Default: 0.1 295 | affine: a boolean value that when set to ``True``, gives the layer learnable 296 | affine parameters. Default: ``True`` 297 | 298 | Shape: 299 | - Input: :math:`(N, C, D, H, W)` 300 | - Output: :math:`(N, C, D, H, W)` (same shape as input) 301 | 302 | Examples: 303 | >>> # With Learnable Parameters 304 | >>> m = SynchronizedBatchNorm3d(100) 305 | >>> # Without Learnable Parameters 306 | >>> m = SynchronizedBatchNorm3d(100, affine=False) 307 | >>> input = torch.autograd.Variable(torch.randn(20, 100, 35, 45, 10)) 308 | >>> output = m(input) 309 | """ 310 | 311 | def _check_input_dim(self, input): 312 | if input.dim() != 5: 313 | raise ValueError('expected 5D input (got {}D input)' 314 | .format(input.dim())) 315 | super(SynchronizedBatchNorm3d, self)._check_input_dim(input) 316 | -------------------------------------------------------------------------------- /sync_bn/comm.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File : comm.py 3 | # Author : Jiayuan Mao 4 | # Email : maojiayuan@gmail.com 5 | # Date : 27/01/2018 6 | # 7 | # This file is part of Synchronized-BatchNorm-PyTorch. 8 | # https://github.com/vacancy/Synchronized-BatchNorm-PyTorch 9 | # Distributed under MIT License. 10 | 11 | import queue 12 | import collections 13 | import threading 14 | 15 | __all__ = ['FutureResult', 'SlavePipe', 'SyncMaster'] 16 | 17 | 18 | class FutureResult(object): 19 | """A thread-safe future implementation. Used only as one-to-one pipe.""" 20 | 21 | def __init__(self): 22 | self._result = None 23 | self._lock = threading.Lock() 24 | self._cond = threading.Condition(self._lock) 25 | 26 | def put(self, result): 27 | with self._lock: 28 | assert self._result is None, 'Previous result has\'t been fetched.' 29 | self._result = result 30 | self._cond.notify() 31 | 32 | def get(self): 33 | with self._lock: 34 | if self._result is None: 35 | self._cond.wait() 36 | 37 | res = self._result 38 | self._result = None 39 | return res 40 | 41 | 42 | _MasterRegistry = collections.namedtuple('MasterRegistry', ['result']) 43 | _SlavePipeBase = collections.namedtuple('_SlavePipeBase', ['identifier', 'queue', 'result']) 44 | 45 | 46 | class SlavePipe(_SlavePipeBase): 47 | """Pipe for master-slave communication.""" 48 | 49 | def run_slave(self, msg): 50 | self.queue.put((self.identifier, msg)) 51 | ret = self.result.get() 52 | self.queue.put(True) 53 | return ret 54 | 55 | 56 | class SyncMaster(object): 57 | """An abstract `SyncMaster` object. 58 | 59 | - During the replication, as the data parallel will trigger an callback of each module, all slave devices should 60 | call `register(id)` and obtain an `SlavePipe` to communicate with the master. 61 | - During the forward pass, master device invokes `run_master`, all messages from slave devices will be collected, 62 | and passed to a registered callback. 63 | - After receiving the messages, the master device should gather the information and determine to message passed 64 | back to each slave devices. 65 | """ 66 | 67 | def __init__(self, master_callback): 68 | """ 69 | 70 | Args: 71 | master_callback: a callback to be invoked after having collected messages from slave devices. 72 | """ 73 | self._master_callback = master_callback 74 | self._queue = queue.Queue() 75 | self._registry = collections.OrderedDict() 76 | self._activated = False 77 | 78 | def __getstate__(self): 79 | return {'master_callback': self._master_callback} 80 | 81 | def __setstate__(self, state): 82 | self.__init__(state['master_callback']) 83 | 84 | def register_slave(self, identifier): 85 | """ 86 | Register an slave device. 87 | 88 | Args: 89 | identifier: an identifier, usually is the device id. 90 | 91 | Returns: a `SlavePipe` object which can be used to communicate with the master device. 92 | 93 | """ 94 | if self._activated: 95 | assert self._queue.empty(), 'Queue is not clean before next initialization.' 96 | self._activated = False 97 | self._registry.clear() 98 | future = FutureResult() 99 | self._registry[identifier] = _MasterRegistry(future) 100 | return SlavePipe(identifier, self._queue, future) 101 | 102 | def run_master(self, master_msg): 103 | """ 104 | Main entry for the master device in each forward pass. 105 | The messages were first collected from each devices (including the master device), and then 106 | an callback will be invoked to compute the message to be sent back to each devices 107 | (including the master device). 108 | 109 | Args: 110 | master_msg: the message that the master want to send to itself. This will be placed as the first 111 | message when calling `master_callback`. For detailed usage, see `_SynchronizedBatchNorm` for an example. 112 | 113 | Returns: the message to be sent back to the master device. 114 | 115 | """ 116 | self._activated = True 117 | 118 | intermediates = [(0, master_msg)] 119 | for i in range(self.nr_slaves): 120 | intermediates.append(self._queue.get()) 121 | 122 | results = self._master_callback(intermediates) 123 | assert results[0][0] == 0, 'The first result should belongs to the master.' 124 | 125 | for i, res in results: 126 | if i == 0: 127 | continue 128 | self._registry[i].result.put(res) 129 | 130 | for i in range(self.nr_slaves): 131 | assert self._queue.get() is True 132 | 133 | return results[0][1] 134 | 135 | @property 136 | def nr_slaves(self): 137 | return len(self._registry) 138 | -------------------------------------------------------------------------------- /sync_bn/replicate.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File : replicate.py 3 | # Author : Jiayuan Mao 4 | # Email : maojiayuan@gmail.com 5 | # Date : 27/01/2018 6 | # 7 | # This file is part of Synchronized-BatchNorm-PyTorch. 8 | # https://github.com/vacancy/Synchronized-BatchNorm-PyTorch 9 | # Distributed under MIT License. 10 | 11 | import functools 12 | 13 | from torch.nn.parallel.data_parallel import DataParallel 14 | 15 | __all__ = [ 16 | 'CallbackContext', 17 | 'execute_replication_callbacks', 18 | 'DataParallelWithCallback', 19 | 'patch_replication_callback' 20 | ] 21 | 22 | 23 | class CallbackContext(object): 24 | pass 25 | 26 | 27 | def execute_replication_callbacks(modules): 28 | """ 29 | Execute an replication callback `__data_parallel_replicate__` on each module created by original replication. 30 | 31 | The callback will be invoked with arguments `__data_parallel_replicate__(ctx, copy_id)` 32 | 33 | Note that, as all modules are isomorphism, we assign each sub-module with a context 34 | (shared among multiple copies of this module on different devices). 35 | Through this context, different copies can share some information. 36 | 37 | We guarantee that the callback on the master copy (the first copy) will be called ahead of calling the callback 38 | of any slave copies. 39 | """ 40 | master_copy = modules[0] 41 | nr_modules = len(list(master_copy.modules())) 42 | ctxs = [CallbackContext() for _ in range(nr_modules)] 43 | 44 | for i, module in enumerate(modules): 45 | for j, m in enumerate(module.modules()): 46 | if hasattr(m, '__data_parallel_replicate__'): 47 | m.__data_parallel_replicate__(ctxs[j], i) 48 | 49 | 50 | class DataParallelWithCallback(DataParallel): 51 | """ 52 | Data Parallel with a replication callback. 53 | 54 | An replication callback `__data_parallel_replicate__` of each module will be invoked after being created by 55 | original `replicate` functions. 56 | The callback will be invoked with arguments `__data_parallel_replicate__(ctx, copy_id)` 57 | 58 | Examples: 59 | > sync_bn = SynchronizedBatchNorm1d(10, eps=1e-5, affine=False) 60 | > sync_bn = DataParallelWithCallback(sync_bn, device_ids=[0, 1]) 61 | # sync_bn.__data_parallel_replicate__ will be invoked. 62 | """ 63 | 64 | def replicate(self, module, device_ids): 65 | modules = super(DataParallelWithCallback, self).replicate(module, device_ids) 66 | execute_replication_callbacks(modules) 67 | return modules 68 | 69 | 70 | def patch_replication_callback(data_parallel): 71 | """ 72 | Monkey-patch an existing `DataParallel` object. Add the replication callback. 73 | Useful when you have customized `DataParallel` implementation. 74 | 75 | Examples: 76 | > sync_bn = SynchronizedBatchNorm1d(10, eps=1e-5, affine=False) 77 | > sync_bn = DataParallel(sync_bn, device_ids=[0, 1]) 78 | > patch_replication_callback(sync_bn) 79 | # this is equivalent to 80 | > sync_bn = SynchronizedBatchNorm1d(10, eps=1e-5, affine=False) 81 | > sync_bn = DataParallelWithCallback(sync_bn, device_ids=[0, 1]) 82 | """ 83 | 84 | assert isinstance(data_parallel, DataParallel) 85 | 86 | old_replicate = data_parallel.replicate 87 | 88 | @functools.wraps(old_replicate) 89 | def new_replicate(module, device_ids): 90 | modules = old_replicate(module, device_ids) 91 | execute_replication_callbacks(modules) 92 | return modules 93 | 94 | data_parallel.replicate = new_replicate 95 | -------------------------------------------------------------------------------- /sync_bn/unittest.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # File : unittest.py 3 | # Author : Jiayuan Mao 4 | # Email : maojiayuan@gmail.com 5 | # Date : 27/01/2018 6 | # 7 | # This file is part of Synchronized-BatchNorm-PyTorch. 8 | # https://github.com/vacancy/Synchronized-BatchNorm-PyTorch 9 | # Distributed under MIT License. 10 | 11 | import unittest 12 | 13 | import numpy as np 14 | from torch.autograd import Variable 15 | 16 | 17 | def as_numpy(v): 18 | if isinstance(v, Variable): 19 | v = v.data 20 | return v.cpu().numpy() 21 | 22 | 23 | class TorchTestCase(unittest.TestCase): 24 | def assertTensorClose(self, a, b, atol=1e-3, rtol=1e-3): 25 | npa, npb = as_numpy(a), as_numpy(b) 26 | self.assertTrue( 27 | np.allclose(npa, npb, atol=atol), 28 | 'Tensor close check failed\n{}\n{}\nadiff={}, rdiff={}'.format(a, b, np.abs(npa - npb).max(), np.abs((npa - npb) / np.fmax(npa, 1e-5)).max()) 29 | ) 30 | -------------------------------------------------------------------------------- /util.py: -------------------------------------------------------------------------------- 1 | import os 2 | import glob 3 | import copy 4 | import random 5 | import pickle 6 | import numpy as np 7 | from plyfile import PlyData 8 | 9 | import torch 10 | from torch import nn 11 | from torch.nn.modules.conv import _ConvNd 12 | from torch.nn.modules.batchnorm import _BatchNorm 13 | import torch.nn.init as initer 14 | import torch.nn.functional as F 15 | 16 | 17 | class AverageMeter(object): 18 | """Computes and stores the average and current value""" 19 | 20 | def __init__(self): 21 | self.reset() 22 | 23 | def reset(self): 24 | self.val = 0 25 | self.avg = 0 26 | self.sum = 0 27 | self.count = 0 28 | 29 | def update(self, val, n=1): 30 | self.val = val 31 | self.sum += val * n 32 | self.count += n 33 | self.avg = self.sum / self.count 34 | 35 | 36 | def set_seed(seed=1): 37 | print('Using random seed', seed) 38 | random.seed(seed) 39 | np.random.seed(seed) 40 | torch.manual_seed(seed) 41 | torch.cuda.manual_seed_all(seed) 42 | 43 | 44 | def str2bool(v): 45 | return v.lower() in ("yes", "true", "t", "1") 46 | 47 | 48 | def get_lr(optimizer): 49 | return optimizer.param_groups[0]['lr'] 50 | 51 | 52 | def adjust_lr(optimizer, new_lr): 53 | for param_group in optimizer.param_groups: 54 | param_group['lr'] = new_lr 55 | 56 | 57 | def weights_init(m): 58 | classname = m.__class__.__name__ 59 | if classname.find('Conv2d') != -1: 60 | nn.init.xavier_normal_(m.weight.data) 61 | try: 62 | nn.init.constant_(m.bias.data, 0.0) 63 | except AttributeError: 64 | pass 65 | elif classname.find('Linear') != -1: 66 | nn.init.xavier_normal_(m.weight.data) 67 | try: 68 | nn.init.constant_(m.bias.data, 0.0) 69 | except AttributeError: 70 | pass 71 | 72 | 73 | def bn_momentum_adjust(m, momentum): 74 | if isinstance(m, nn.BatchNorm2d) or \ 75 | isinstance(m, nn.BatchNorm1d): 76 | m.momentum = momentum 77 | 78 | 79 | def intersectionAndUnion(output, target, K, ignore_index=255): 80 | # 'K' classes, output and target sizes are N or N * L or N * H * W, each value in range 0 to K - 1. 81 | assert (output.ndim in [1, 2, 3]) 82 | assert output.shape == target.shape 83 | output = output.reshape(output.size).copy() 84 | target = target.reshape(target.size) 85 | output[np.where(target == ignore_index)[0]] = 255 86 | target[np.where(target == ignore_index)[0]] = 255 87 | intersection = output[np.where(output == target)[0]] 88 | area_intersection, _ = np.histogram(intersection, bins=np.arange(K+1)) 89 | area_output, _ = np.histogram(output, bins=np.arange(K+1)) 90 | area_target, _ = np.histogram(target, bins=np.arange(K+1)) 91 | area_union = area_output + area_target - area_intersection 92 | return area_intersection, area_union, area_target 93 | 94 | 95 | def calc_victim_value(class_value, label, victim_class): 96 | values = [] 97 | for lbl in victim_class: 98 | if label is None or (label == lbl).any(): 99 | values.append(class_value[lbl]) 100 | return np.mean(values) 101 | 102 | 103 | def check_makedirs(dir_name): 104 | if not os.path.exists(dir_name): 105 | os.makedirs(dir_name) 106 | 107 | 108 | def init_weights(model, conv='kaiming', batchnorm='normal', linear='kaiming', lstm='kaiming'): 109 | """ 110 | :param model: Pytorch Model which is nn.Module 111 | :param conv: 'kaiming' or 'xavier' 112 | :param batchnorm: 'normal' or 'constant' 113 | :param linear: 'kaiming' or 'xavier' 114 | :param lstm: 'kaiming' or 'xavier' 115 | """ 116 | for m in model.modules(): 117 | if isinstance(m, (_ConvNd)): 118 | if conv == 'kaiming': 119 | initer.kaiming_normal_(m.weight) 120 | elif conv == 'xavier': 121 | initer.xavier_normal_(m.weight) 122 | else: 123 | raise ValueError("init type of conv error.\n") 124 | if m.bias is not None: 125 | initer.constant_(m.bias, 0) 126 | 127 | elif isinstance(m, _BatchNorm): 128 | if batchnorm == 'normal': 129 | initer.normal_(m.weight, 1.0, 0.02) 130 | elif batchnorm == 'constant': 131 | initer.constant_(m.weight, 1.0) 132 | else: 133 | raise ValueError("init type of batchnorm error.\n") 134 | initer.constant_(m.bias, 0.0) 135 | 136 | elif isinstance(m, nn.Linear): 137 | if linear == 'kaiming': 138 | initer.kaiming_normal_(m.weight) 139 | elif linear == 'xavier': 140 | initer.xavier_normal_(m.weight) 141 | else: 142 | raise ValueError("init type of linear error.\n") 143 | if m.bias is not None: 144 | initer.constant_(m.bias, 0) 145 | 146 | elif isinstance(m, nn.LSTM): 147 | for name, param in m.named_parameters(): 148 | if 'weight' in name: 149 | if lstm == 'kaiming': 150 | initer.kaiming_normal_(param) 151 | elif lstm == 'xavier': 152 | initer.xavier_normal_(param) 153 | else: 154 | raise ValueError("init type of lstm error.\n") 155 | elif 'bias' in name: 156 | initer.constant_(param, 0) 157 | 158 | 159 | def convert_to_syncbn(model): 160 | def recursive_set(cur_module, name, module): 161 | if len(name.split('.')) > 1: 162 | recursive_set( 163 | getattr(cur_module, name[:name.find('.')]), name[name.find('.')+1:], module) 164 | else: 165 | setattr(cur_module, name, module) 166 | from sync_bn import SynchronizedBatchNorm1d, SynchronizedBatchNorm2d, \ 167 | SynchronizedBatchNorm3d 168 | for name, m in model.named_modules(): 169 | if isinstance(m, nn.BatchNorm1d): 170 | recursive_set(model, name, SynchronizedBatchNorm1d( 171 | m.num_features, m.eps, m.momentum, m.affine)) 172 | elif isinstance(m, nn.BatchNorm2d): 173 | recursive_set(model, name, SynchronizedBatchNorm2d( 174 | m.num_features, m.eps, m.momentum, m.affine)) 175 | elif isinstance(m, nn.BatchNorm3d): 176 | recursive_set(model, name, SynchronizedBatchNorm3d( 177 | m.num_features, m.eps, m.momentum, m.affine)) 178 | 179 | 180 | def lbl2rgb(label, names): 181 | """Convert label to rgb colors. 182 | label: [N] 183 | """ 184 | from config import NAME2COLOR 185 | if len(names) == 13: 186 | colors = NAME2COLOR['S3DIS'] 187 | else: 188 | colors = NAME2COLOR['ScanNet'] 189 | rgb = np.zeros((label.shape[0], 3)) 190 | uni_lbl = np.unique(label).astype(np.uint8) 191 | for lbl in uni_lbl: 192 | mask = (label == lbl) 193 | rgb[mask] = np.tile(np.array( 194 | colors[names[lbl]])[None, :], (mask.sum(), 1)) 195 | return rgb 196 | 197 | 198 | def convert2vis(xyz, label, names): 199 | """Assign color to each point according to label.""" 200 | rgb = lbl2rgb(label, names) * 255. 201 | data = np.concatenate([xyz, rgb], axis=1) 202 | return data 203 | 204 | 205 | def proc_pert(points, gt, pred, folder, 206 | names, part=False, ignore_label=255): 207 | """Process and save files for visulization in perturbation attack.""" 208 | check_makedirs(folder) 209 | lbl2cls = {i: names[i] for i in range(len(names))} 210 | 211 | np.savetxt(os.path.join(folder, 'all_points.txt'), points, delimiter=';') 212 | gt_seg = convert2vis(points[gt != ignore_label, :3], 213 | gt[gt != ignore_label], names) 214 | pred_seg = convert2vis(points[gt != ignore_label, :3], 215 | pred[gt != ignore_label], names) 216 | np.savetxt(os.path.join(folder, 'gt.txt'), 217 | gt_seg, delimiter=';') 218 | np.savetxt(os.path.join(folder, 'pred.txt'), 219 | pred_seg, delimiter=';') 220 | if part: 221 | uni_lbl = np.unique(gt[gt != ignore_label]).astype(np.uint8) 222 | for lbl in uni_lbl: 223 | lbl = int(lbl) 224 | mask = (gt == lbl) 225 | sel_points = points[mask] 226 | mask = (gt[gt != ignore_label] == lbl) 227 | sel_seg = pred_seg[mask] 228 | np.savetxt( 229 | os.path.join(folder, '{}_{}_points.txt'.format( 230 | lbl, lbl2cls[lbl])), 231 | sel_points, delimiter=';') 232 | np.savetxt( 233 | os.path.join(folder, '{}_{}_pred.txt'.format( 234 | lbl, lbl2cls[lbl])), 235 | sel_seg, delimiter=';') 236 | 237 | 238 | def proc_add(points, noise, gt, pred, noise_pred, folder, 239 | names, part=False, ignore_label=255): 240 | """Process and save files for visulization in adding attack.""" 241 | check_makedirs(folder) 242 | lbl2cls = {i: names[i] for i in range(len(names))} 243 | 244 | np.savetxt(os.path.join(folder, 'all_points.txt'), points, delimiter=';') 245 | np.savetxt(os.path.join(folder, 'noise_points.txt'), noise, delimiter=';') 246 | gt_seg = convert2vis(points[gt != ignore_label, :3], 247 | gt[gt != ignore_label], names) 248 | pred_seg = convert2vis(points[gt != ignore_label, :3], 249 | pred[gt != ignore_label], names) 250 | noise_seg = convert2vis(noise[:, :3], noise_pred, names) 251 | np.savetxt(os.path.join(folder, 'gt.txt'), 252 | gt_seg, delimiter=';') 253 | np.savetxt(os.path.join(folder, 'pred.txt'), 254 | pred_seg, delimiter=';') 255 | np.savetxt(os.path.join(folder, 'noise_pred.txt'), 256 | noise_seg, delimiter=';') 257 | if part: 258 | uni_lbl = np.unique(gt[gt != ignore_label]).astype(np.uint8) 259 | for lbl in uni_lbl: 260 | lbl = int(lbl) 261 | mask = (gt == lbl) 262 | sel_points = points[mask] 263 | mask = (gt[gt != ignore_label] == lbl) 264 | sel_seg = pred_seg[mask] 265 | np.savetxt( 266 | os.path.join(folder, '{}_{}_points.txt'.format( 267 | lbl, lbl2cls[lbl])), 268 | sel_points, delimiter=';') 269 | np.savetxt( 270 | os.path.join(folder, '{}_{}_pred.txt'.format( 271 | lbl, lbl2cls[lbl])), 272 | sel_seg, delimiter=';') 273 | 274 | 275 | def save_vis(pred_root, save_root, data_root): 276 | from config import CLASS_NAMES 277 | if 'S3DIS' in data_root: # save Area5 data 278 | names = CLASS_NAMES['S3DIS']['other'] 279 | gt_save = load_pickle( 280 | os.path.join(pred_root, 'gt_5.pickle'))['gt'] 281 | pred_save = load_pickle( 282 | os.path.join(pred_root, 'pred_5.pickle'))['pred'] 283 | assert len(gt_save) == len(pred_save) 284 | all_rooms = sorted(os.listdir(data_root)) 285 | all_rooms = [ 286 | room for room in all_rooms if 'Area_5' in room 287 | ] 288 | assert len(gt_save) == len(all_rooms) 289 | check_makedirs(save_root) 290 | for i, room in enumerate(all_rooms): 291 | points = np.load(os.path.join(data_root, room))[:, :6] 292 | folder = os.path.join(save_root, room[:-4]) 293 | check_makedirs(folder) 294 | proc_pert(points, gt_save[i], pred_save[i], 295 | folder, names, part=True) 296 | elif 'ScanNet' in data_root: # save val set data 297 | names = CLASS_NAMES['ScanNet']['other'] 298 | gt_save = load_pickle( 299 | os.path.join(pred_root, 'gt_val.pickle'))['gt'] 300 | pred_save = load_pickle( 301 | os.path.join(pred_root, 'pred_val.pickle'))['pred'] 302 | assert len(gt_save) == len(pred_save) 303 | data_file = os.path.join( 304 | data_root, 'scannet_val_rgb21c_pointid.pickle') 305 | file_pickle = open(data_file, 'rb') 306 | xyz_all = pickle.load(file_pickle) 307 | file_pickle.close() 308 | assert len(xyz_all) == len(gt_save) 309 | with open(os.path.join( 310 | data_root, 'meta_data/scannetv2_val.txt')) as fl: 311 | scene_id = fl.read().splitlines() 312 | assert len(scene_id) == len(gt_save) 313 | check_makedirs(save_root) 314 | for i in range(len(gt_save)): 315 | points = xyz_all[i][:, :6] 316 | folder = os.path.join(save_root, scene_id[i]) 317 | check_makedirs(folder) 318 | proc_pert(points, gt_save[i], pred_save[i], 319 | folder, names, part=True) 320 | 321 | 322 | def save_vis_mink(pred_root, save_root, data_root): 323 | from config import CLASS_NAMES 324 | 325 | def load_data(file_name): 326 | plydata = PlyData.read(file_name) 327 | data = plydata.elements[0].data 328 | coords = np.array([data['x'], data['y'], data['z']], 329 | dtype=np.float32).T 330 | colors = np.array([data['red'], data['green'], 331 | data['blue']], dtype=np.float32).T 332 | return np.concatenate([coords, colors], axis=1) 333 | 334 | if 'S3DIS' in data_root: # save Area5 data 335 | names = CLASS_NAMES['S3DIS']['mink'] 336 | gt_save = load_pickle( 337 | os.path.join(pred_root, 'gt_5.pickle'))['gt'] 338 | pred_save = load_pickle( 339 | os.path.join(pred_root, 'pred_5.pickle'))['pred'] 340 | assert len(gt_save) == len(pred_save) 341 | data_root = os.path.join(data_root, 'Area_5') 342 | all_rooms = sorted(os.listdir(data_root)) 343 | assert len(all_rooms) == len(gt_save) 344 | check_makedirs(save_root) 345 | 346 | for i, room in enumerate(all_rooms): 347 | data = os.path.join(data_root, room) 348 | points = load_data(data) 349 | folder = os.path.join( 350 | save_root, 'Area_5_{}'.format(room[:-4])) 351 | check_makedirs(folder) 352 | proc_pert(points, gt_save[i], pred_save[i], 353 | folder, names, part=True) 354 | elif 'ScanNet' in data_root: # save val set 355 | names = CLASS_NAMES['ScanNet']['mink'] 356 | gt_save = load_pickle( 357 | os.path.join(pred_root, 'gt_val.pickle'))['gt'] 358 | pred_save = load_pickle( 359 | os.path.join(pred_root, 'pred_val.pickle'))['pred'] 360 | assert len(gt_save) == len(pred_save) 361 | data_root = os.path.join(data_root, 'train') 362 | with open(os.path.join( 363 | data_root, 'scannetv2_val.txt'), 'r') as f: 364 | all_rooms = f.readlines() 365 | all_rooms = [room[:-1] for room in all_rooms] 366 | assert len(all_rooms) == len(gt_save) 367 | check_makedirs(save_root) 368 | 369 | for i, room in enumerate(all_rooms): 370 | data = os.path.join(data_root, room) 371 | points = load_data(data) 372 | folder = os.path.join(save_root, room[:-4]) 373 | check_makedirs(folder) 374 | proc_pert(points, gt_save[i], pred_save[i], 375 | folder, names, part=True) 376 | 377 | 378 | def save_vis_from_pickle(pkl_root, save_root=None, room_idx=52, 379 | room_name='scene0354_00'): 380 | names = [ 381 | 'wall', 'floor', 'cabinet', 'bed', 'chair', 'sofa', 'table', 382 | 'door', 'window', 'bookshelf', 'picture', 'counter', 'desk', 383 | 'curtain', 'refrigerator', 'showercurtain', 'toilet', 'sink', 384 | 'bathtub', 'otherfurniture' 385 | ] 386 | data = load_pickle(pkl_root) 387 | points = data['data'][room_idx] 388 | pred = data['pred'][room_idx] 389 | gt = data['gt'][room_idx] 390 | if save_root is None: 391 | save_root = os.path.dirname(pkl_root) 392 | save_folder = os.path.join(save_root, room_name) 393 | proc_pert(points, gt, pred, save_folder, names, part=True) 394 | 395 | 396 | def save_pickle(filename, dict_data): 397 | with open(filename, 'wb') as handle: 398 | pickle.dump(dict_data, handle, 399 | protocol=pickle.HIGHEST_PROTOCOL) 400 | 401 | 402 | def load_pickle(filename): 403 | with open(filename, 'rb') as f: 404 | data = pickle.load(f) 405 | return data 406 | 407 | 408 | def load_s3dis_instance(folder, name2cls, load_name=['chair']): 409 | """Load S3DIS room in a Inst Seg format. 410 | Get each instance separately. 411 | 412 | If load_name is None or [], return all instances. 413 | Returns a list of [np.array of [N, 6], label] 414 | """ 415 | cls2name = {name2cls[name]: name for name in name2cls.keys()} 416 | anno_path = os.path.join(folder, 'Annotations') 417 | points_list = [] 418 | labels_list = [] 419 | idx = 0 420 | files = glob.glob(os.path.join(anno_path, '*.txt')) 421 | files.sort() 422 | 423 | for f in files: 424 | cls = os.path.basename(f).split('_')[0] 425 | if cls not in name2cls.keys(): 426 | cls = 'clutter' 427 | points = np.loadtxt(f) # [N, 6] 428 | num = points.shape[0] 429 | points_list.append(points) 430 | labels_list.append((idx, idx + num, name2cls[cls])) 431 | idx += num 432 | 433 | # normalize points coords by minus min 434 | data = np.concatenate(points_list, 0) 435 | xyz_min = np.amin(data, axis=0)[0:3] 436 | data[:, 0:3] -= xyz_min 437 | 438 | # rearrange to separate instances 439 | if load_name is None or not load_name: 440 | load_name = list(name2cls.keys()) 441 | instances = [ 442 | [data[pair[0]:pair[1]], pair[2]] for pair in labels_list if 443 | cls2name[pair[2]] in load_name 444 | ] 445 | return instances 446 | 447 | 448 | def cal_loss(pred, gold, smoothing=False, ignore_index=255): 449 | ''' Calculate cross entropy loss, apply label smoothing if needed. ''' 450 | 451 | gold = gold.contiguous().view(-1) 452 | 453 | if smoothing: 454 | eps = 0.2 455 | n_class = pred.size(1) 456 | 457 | one_hot = torch.zeros_like(pred).scatter(1, gold.view(-1, 1), 1) 458 | one_hot = one_hot * (1 - eps) + (1 - one_hot) * eps / (n_class - 1) 459 | log_prb = F.log_softmax(pred, dim=1) 460 | 461 | loss = -(one_hot * log_prb).sum(dim=1).mean() 462 | else: 463 | loss = F.cross_entropy( 464 | pred, gold, reduction='mean', 465 | ignore_index=ignore_index) 466 | 467 | return loss 468 | 469 | 470 | class IOStream(): 471 | def __init__(self, path): 472 | self.f = open(path, 'a') 473 | 474 | def cprint(self, text): 475 | print(text) 476 | self.f.write(text+'\n') 477 | self.f.flush() 478 | 479 | def close(self): 480 | self.f.close() --------------------------------------------------------------------------------