├── LICENSE ├── README.md ├── data ├── generate_input_list.py └── generate_train_test_list.py ├── meta ├── all_data_label.txt ├── anno_paths.txt ├── area1_data_label.txt ├── area2_data_label.txt ├── area3_data_label.txt ├── area4_data_label.txt ├── area5_data_label.txt ├── area6_data_label.txt ├── class_names.txt └── updata_datalabeltxt.py ├── misc └── fig.png ├── models └── JISS │ ├── estimate_mean_ins_size.py │ ├── eval_iou_accuracy.py │ ├── model.py │ ├── test.py │ └── train.py ├── tf_ops ├── 3d_interpolation │ ├── interpolate.cpp │ ├── tf_interpolate.cpp │ ├── tf_interpolate.py │ ├── tf_interpolate_compile.sh │ ├── tf_interpolate_op_test.py │ ├── tf_interpolate_so.so │ └── visu_interpolation.py ├── grouping │ ├── test │ │ ├── compile.sh │ │ ├── query_ball_point.cpp │ │ ├── query_ball_point.cu │ │ ├── query_ball_point_block.cu │ │ ├── query_ball_point_grid.cu │ │ ├── selection_sort.cpp │ │ ├── selection_sort.cu │ │ └── selection_sort_const.cu │ ├── tf_grouping.cpp │ ├── tf_grouping.py │ ├── tf_grouping_compile.sh │ ├── tf_grouping_g.cu │ ├── tf_grouping_op_test.py │ └── tf_grouping_so.so └── sampling │ ├── tf_sampling.cpp │ ├── tf_sampling.py │ ├── tf_sampling_compile.sh │ ├── tf_sampling_g.cu │ └── tf_sampling_so.so └── utils ├── clustering.py ├── data_prep_util.py ├── eulerangles.py ├── indoor3d_util.py ├── log_util.py ├── loss.py ├── part_color_mapping.json ├── pc_util.py ├── plyfile.py ├── pointconv_util.py ├── pointnet_util.py ├── provider.py ├── s3dis_utils ├── collect_indoor3d_data.py ├── dataset_s3dis.py └── s3dis_gen_h5.py ├── test_utils.py └── tf_util.py /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 dlinzhao 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 | # JSNet: Joint Instance and Semantic Segmentation of 3D Point Clouds [[arXiv]](https://arxiv.org/abs/1912.09654) 2 | 3 | ## Overview 4 | ![](misc/fig.png) 5 | 6 | ## Dependencies 7 | 8 | The code has been tested with Python 3.5 on Ubuntu 16.04. 9 | * [TensorFlow 1.4](https://www.tensorflow.org/) 10 | * h5py 11 | 12 | 13 | 14 | ## Data and Model 15 | 16 | * Download 3D indoor parsing dataset (S3DIS Dataset). Version 1.2 of the dataset is used in this work. 17 | 18 | ``` bash 19 | python utils/s3dis_utils/collect_indoor3d_data.py 20 | python utils/s3dis_utils/s3dis_gen_h5.py 21 | cd data && python generate_input_list.py && python generate_train_test_list.py 22 | cd .. 23 | ``` 24 | 25 | * (optional) Prepared HDF5 data for training is available [here](https://drive.google.com/open?id=1PjWweT61nmIX7zc2vJClhzHxyTHGvstQ). 26 | 27 | * (optional) Trained model can be downloaded from [here](https://drive.google.com/open?id=1YioepsaYmpgqK3zqMtScoZEZBMjmSXMA). 28 | 29 | ## Usage 30 | 31 | * Compile TF Operators 32 | 33 | Refer to [PointNet++](https://github.com/charlesq34/pointnet2) 34 | 35 | * Training, Test, and Evaluation 36 | ``` bash 37 | cd models/JISS/ 38 | ln -s ../../data . 39 | 40 | # training 41 | python train.py \ 42 | --gpu 0 \ 43 | --data_root ./ \ 44 | --data_type numpy \ 45 | --max_epoch 100 \ 46 | --log_dir ../../logs/train_5 \ 47 | --input_list data/train_file_list_woArea5.txt 48 | 49 | # estimate_mean_ins_size 50 | python estimate_mean_ins_size.py \ 51 | --data_root ./ \ 52 | --input_list data/train_hdf5_file_list_woArea5.txt \ 53 | --out_dir ../../logs/train_5 54 | 55 | # test 56 | python test.py \ 57 | --gpu 0 \ 58 | --data_root ./ \ 59 | --data_type hdf5 \ 60 | --bandwidth 0.6 \ 61 | --num_point 4096 \ 62 | --log_dir ../../logs/test_5 \ 63 | --model_path ../../logs/train_5/epoch_99.ckpt \ 64 | --input_list data/test_hdf5_file_list_Area5.txt 65 | 66 | # evaluation 67 | python eval_iou_accuracy.py --log_dir ../../logs/test_5 68 | ``` 69 | 70 | Note: We test on Area5 and train on the rest folds in default. 6 fold CV can be conducted in a similar way. 71 | 72 | ## Citation 73 | If our work is useful for your research, please consider citing: 74 | 75 | @inproceedings{zhao2020jsnet, 76 | title={JSNet: Joint Instance and Semantic Segmentation of 3D Point Clouds}, 77 | author={Zhao, Lin and Tao, Wenbing}, 78 | booktitle={Thirty-Fourth AAAI Conference on Artificial Intelligence}, 79 | year={2020} 80 | } 81 | 82 | 83 | ## Acknowledgemets 84 | This code largely benefits from following repositories: 85 | [ASIS](https://github.com/WXinlong/ASIS), 86 | [PointNet++](https://github.com/charlesq34/pointnet2), 87 | [PointConv](https://github.com/DylanWusee/pointconv), 88 | [SGPN](https://github.com/laughtervv/SGPN) and 89 | [DiscLoss-tf](https://github.com/hq-jiang/instance-segmentation-with-discriminative-loss-tensorflow) 90 | -------------------------------------------------------------------------------- /data/generate_input_list.py: -------------------------------------------------------------------------------- 1 | import os 2 | import glob 3 | 4 | h5_dir = "indoor3d_ins_seg_hdf5/" 5 | 6 | for i in range(6): 7 | 8 | filter_id = i + 1 9 | 10 | # For training 11 | train_fold_pattern = "Area_[!{}]*.h5".format(filter_id) 12 | dst_list_path = "train_hdf5_file_list_woArea{}.txt".format(filter_id) 13 | 14 | h5_files = glob.glob(os.path.join(h5_dir + train_fold_pattern)) 15 | 16 | f = open(dst_list_path, 'w') 17 | for h5_file in h5_files: 18 | f.write('data/' + h5_file) 19 | f.write('\n') 20 | f.close() 21 | 22 | # For test 23 | test_fold_pattern = "Area_{}*.h5".format(filter_id) 24 | dst_list_path = "test_hdf5_file_list_Area{}.txt".format(filter_id) 25 | 26 | h5_files = glob.glob(os.path.join(h5_dir + test_fold_pattern)) 27 | 28 | f = open(dst_list_path, 'w') 29 | for h5_file in h5_files: 30 | f.write('data/' + h5_file) 31 | f.write('\n') 32 | f.close() 33 | -------------------------------------------------------------------------------- /data/generate_train_test_list.py: -------------------------------------------------------------------------------- 1 | import os 2 | import glob 3 | 4 | input_dir = "stanford_indoor3d_ins.sem/" 5 | 6 | for i in range(6): 7 | 8 | filter_id = i + 1 9 | 10 | # For training 11 | train_fold_pattern = "Area_[!{}]*.npy".format(filter_id) 12 | dst_list_path = "train_file_list_woArea{}.txt".format(filter_id) 13 | 14 | npy_files = glob.glob(os.path.join(input_dir + train_fold_pattern)) 15 | 16 | f = open(dst_list_path, 'w') 17 | for npy_file in npy_files: 18 | f.write('data/' + npy_file) 19 | f.write('\n') 20 | f.close() 21 | 22 | # For test 23 | test_fold_pattern = "Area_{}*.npy".format(filter_id) 24 | dst_list_path = "test_file_list_Area{}.txt".format(filter_id) 25 | 26 | npy_files = glob.glob(os.path.join(input_dir + test_fold_pattern)) 27 | 28 | f = open(dst_list_path, 'w') 29 | for npy_file in npy_files: 30 | f.write('data/' + npy_file) 31 | f.write('\n') 32 | f.close() 33 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /meta/area1_data_label.txt: -------------------------------------------------------------------------------- 1 | data/stanford_indoor3d_ins.sem/Area_1_conferenceRoom_1.npy 2 | data/stanford_indoor3d_ins.sem/Area_1_conferenceRoom_2.npy 3 | data/stanford_indoor3d_ins.sem/Area_1_copyRoom_1.npy 4 | data/stanford_indoor3d_ins.sem/Area_1_hallway_1.npy 5 | data/stanford_indoor3d_ins.sem/Area_1_hallway_2.npy 6 | data/stanford_indoor3d_ins.sem/Area_1_hallway_3.npy 7 | data/stanford_indoor3d_ins.sem/Area_1_hallway_4.npy 8 | data/stanford_indoor3d_ins.sem/Area_1_hallway_5.npy 9 | data/stanford_indoor3d_ins.sem/Area_1_hallway_6.npy 10 | data/stanford_indoor3d_ins.sem/Area_1_hallway_7.npy 11 | data/stanford_indoor3d_ins.sem/Area_1_hallway_8.npy 12 | data/stanford_indoor3d_ins.sem/Area_1_office_10.npy 13 | data/stanford_indoor3d_ins.sem/Area_1_office_11.npy 14 | data/stanford_indoor3d_ins.sem/Area_1_office_12.npy 15 | data/stanford_indoor3d_ins.sem/Area_1_office_13.npy 16 | data/stanford_indoor3d_ins.sem/Area_1_office_14.npy 17 | data/stanford_indoor3d_ins.sem/Area_1_office_15.npy 18 | data/stanford_indoor3d_ins.sem/Area_1_office_16.npy 19 | data/stanford_indoor3d_ins.sem/Area_1_office_17.npy 20 | data/stanford_indoor3d_ins.sem/Area_1_office_18.npy 21 | data/stanford_indoor3d_ins.sem/Area_1_office_19.npy 22 | data/stanford_indoor3d_ins.sem/Area_1_office_1.npy 23 | data/stanford_indoor3d_ins.sem/Area_1_office_20.npy 24 | data/stanford_indoor3d_ins.sem/Area_1_office_21.npy 25 | data/stanford_indoor3d_ins.sem/Area_1_office_22.npy 26 | data/stanford_indoor3d_ins.sem/Area_1_office_23.npy 27 | data/stanford_indoor3d_ins.sem/Area_1_office_24.npy 28 | data/stanford_indoor3d_ins.sem/Area_1_office_25.npy 29 | data/stanford_indoor3d_ins.sem/Area_1_office_26.npy 30 | data/stanford_indoor3d_ins.sem/Area_1_office_27.npy 31 | data/stanford_indoor3d_ins.sem/Area_1_office_28.npy 32 | data/stanford_indoor3d_ins.sem/Area_1_office_29.npy 33 | data/stanford_indoor3d_ins.sem/Area_1_office_2.npy 34 | data/stanford_indoor3d_ins.sem/Area_1_office_30.npy 35 | data/stanford_indoor3d_ins.sem/Area_1_office_31.npy 36 | data/stanford_indoor3d_ins.sem/Area_1_office_3.npy 37 | data/stanford_indoor3d_ins.sem/Area_1_office_4.npy 38 | data/stanford_indoor3d_ins.sem/Area_1_office_5.npy 39 | data/stanford_indoor3d_ins.sem/Area_1_office_6.npy 40 | data/stanford_indoor3d_ins.sem/Area_1_office_7.npy 41 | data/stanford_indoor3d_ins.sem/Area_1_office_8.npy 42 | data/stanford_indoor3d_ins.sem/Area_1_office_9.npy 43 | data/stanford_indoor3d_ins.sem/Area_1_pantry_1.npy 44 | data/stanford_indoor3d_ins.sem/Area_1_WC_1.npy 45 | -------------------------------------------------------------------------------- /meta/area2_data_label.txt: -------------------------------------------------------------------------------- 1 | data/stanford_indoor3d_ins.sem/Area_2_auditorium_1.npy 2 | data/stanford_indoor3d_ins.sem/Area_2_auditorium_2.npy 3 | data/stanford_indoor3d_ins.sem/Area_2_conferenceRoom_1.npy 4 | data/stanford_indoor3d_ins.sem/Area_2_hallway_10.npy 5 | data/stanford_indoor3d_ins.sem/Area_2_hallway_11.npy 6 | data/stanford_indoor3d_ins.sem/Area_2_hallway_12.npy 7 | data/stanford_indoor3d_ins.sem/Area_2_hallway_1.npy 8 | data/stanford_indoor3d_ins.sem/Area_2_hallway_2.npy 9 | data/stanford_indoor3d_ins.sem/Area_2_hallway_3.npy 10 | data/stanford_indoor3d_ins.sem/Area_2_hallway_4.npy 11 | data/stanford_indoor3d_ins.sem/Area_2_hallway_5.npy 12 | data/stanford_indoor3d_ins.sem/Area_2_hallway_6.npy 13 | data/stanford_indoor3d_ins.sem/Area_2_hallway_7.npy 14 | data/stanford_indoor3d_ins.sem/Area_2_hallway_8.npy 15 | data/stanford_indoor3d_ins.sem/Area_2_hallway_9.npy 16 | data/stanford_indoor3d_ins.sem/Area_2_office_10.npy 17 | data/stanford_indoor3d_ins.sem/Area_2_office_11.npy 18 | data/stanford_indoor3d_ins.sem/Area_2_office_12.npy 19 | data/stanford_indoor3d_ins.sem/Area_2_office_13.npy 20 | data/stanford_indoor3d_ins.sem/Area_2_office_14.npy 21 | data/stanford_indoor3d_ins.sem/Area_2_office_1.npy 22 | data/stanford_indoor3d_ins.sem/Area_2_office_2.npy 23 | data/stanford_indoor3d_ins.sem/Area_2_office_3.npy 24 | data/stanford_indoor3d_ins.sem/Area_2_office_4.npy 25 | data/stanford_indoor3d_ins.sem/Area_2_office_5.npy 26 | data/stanford_indoor3d_ins.sem/Area_2_office_6.npy 27 | data/stanford_indoor3d_ins.sem/Area_2_office_7.npy 28 | data/stanford_indoor3d_ins.sem/Area_2_office_8.npy 29 | data/stanford_indoor3d_ins.sem/Area_2_office_9.npy 30 | data/stanford_indoor3d_ins.sem/Area_2_storage_1.npy 31 | data/stanford_indoor3d_ins.sem/Area_2_storage_2.npy 32 | data/stanford_indoor3d_ins.sem/Area_2_storage_3.npy 33 | data/stanford_indoor3d_ins.sem/Area_2_storage_4.npy 34 | data/stanford_indoor3d_ins.sem/Area_2_storage_5.npy 35 | data/stanford_indoor3d_ins.sem/Area_2_storage_6.npy 36 | data/stanford_indoor3d_ins.sem/Area_2_storage_7.npy 37 | data/stanford_indoor3d_ins.sem/Area_2_storage_8.npy 38 | data/stanford_indoor3d_ins.sem/Area_2_storage_9.npy 39 | data/stanford_indoor3d_ins.sem/Area_2_WC_1.npy 40 | data/stanford_indoor3d_ins.sem/Area_2_WC_2.npy 41 | -------------------------------------------------------------------------------- /meta/area3_data_label.txt: -------------------------------------------------------------------------------- 1 | data/stanford_indoor3d_ins.sem/Area_3_conferenceRoom_1.npy 2 | data/stanford_indoor3d_ins.sem/Area_3_hallway_1.npy 3 | data/stanford_indoor3d_ins.sem/Area_3_hallway_2.npy 4 | data/stanford_indoor3d_ins.sem/Area_3_hallway_3.npy 5 | data/stanford_indoor3d_ins.sem/Area_3_hallway_4.npy 6 | data/stanford_indoor3d_ins.sem/Area_3_hallway_5.npy 7 | data/stanford_indoor3d_ins.sem/Area_3_hallway_6.npy 8 | data/stanford_indoor3d_ins.sem/Area_3_lounge_1.npy 9 | data/stanford_indoor3d_ins.sem/Area_3_lounge_2.npy 10 | data/stanford_indoor3d_ins.sem/Area_3_office_10.npy 11 | data/stanford_indoor3d_ins.sem/Area_3_office_1.npy 12 | data/stanford_indoor3d_ins.sem/Area_3_office_2.npy 13 | data/stanford_indoor3d_ins.sem/Area_3_office_3.npy 14 | data/stanford_indoor3d_ins.sem/Area_3_office_4.npy 15 | data/stanford_indoor3d_ins.sem/Area_3_office_5.npy 16 | data/stanford_indoor3d_ins.sem/Area_3_office_6.npy 17 | data/stanford_indoor3d_ins.sem/Area_3_office_7.npy 18 | data/stanford_indoor3d_ins.sem/Area_3_office_8.npy 19 | data/stanford_indoor3d_ins.sem/Area_3_office_9.npy 20 | data/stanford_indoor3d_ins.sem/Area_3_storage_1.npy 21 | data/stanford_indoor3d_ins.sem/Area_3_storage_2.npy 22 | data/stanford_indoor3d_ins.sem/Area_3_WC_1.npy 23 | data/stanford_indoor3d_ins.sem/Area_3_WC_2.npy 24 | -------------------------------------------------------------------------------- /meta/area4_data_label.txt: -------------------------------------------------------------------------------- 1 | data/stanford_indoor3d_ins.sem/Area_4_conferenceRoom_1.npy 2 | data/stanford_indoor3d_ins.sem/Area_4_conferenceRoom_2.npy 3 | data/stanford_indoor3d_ins.sem/Area_4_conferenceRoom_3.npy 4 | data/stanford_indoor3d_ins.sem/Area_4_hallway_10.npy 5 | data/stanford_indoor3d_ins.sem/Area_4_hallway_11.npy 6 | data/stanford_indoor3d_ins.sem/Area_4_hallway_12.npy 7 | data/stanford_indoor3d_ins.sem/Area_4_hallway_13.npy 8 | data/stanford_indoor3d_ins.sem/Area_4_hallway_14.npy 9 | data/stanford_indoor3d_ins.sem/Area_4_hallway_1.npy 10 | data/stanford_indoor3d_ins.sem/Area_4_hallway_2.npy 11 | data/stanford_indoor3d_ins.sem/Area_4_hallway_3.npy 12 | data/stanford_indoor3d_ins.sem/Area_4_hallway_4.npy 13 | data/stanford_indoor3d_ins.sem/Area_4_hallway_5.npy 14 | data/stanford_indoor3d_ins.sem/Area_4_hallway_6.npy 15 | data/stanford_indoor3d_ins.sem/Area_4_hallway_7.npy 16 | data/stanford_indoor3d_ins.sem/Area_4_hallway_8.npy 17 | data/stanford_indoor3d_ins.sem/Area_4_hallway_9.npy 18 | data/stanford_indoor3d_ins.sem/Area_4_lobby_1.npy 19 | data/stanford_indoor3d_ins.sem/Area_4_lobby_2.npy 20 | data/stanford_indoor3d_ins.sem/Area_4_office_10.npy 21 | data/stanford_indoor3d_ins.sem/Area_4_office_11.npy 22 | data/stanford_indoor3d_ins.sem/Area_4_office_12.npy 23 | data/stanford_indoor3d_ins.sem/Area_4_office_13.npy 24 | data/stanford_indoor3d_ins.sem/Area_4_office_14.npy 25 | data/stanford_indoor3d_ins.sem/Area_4_office_15.npy 26 | data/stanford_indoor3d_ins.sem/Area_4_office_16.npy 27 | data/stanford_indoor3d_ins.sem/Area_4_office_17.npy 28 | data/stanford_indoor3d_ins.sem/Area_4_office_18.npy 29 | data/stanford_indoor3d_ins.sem/Area_4_office_19.npy 30 | data/stanford_indoor3d_ins.sem/Area_4_office_1.npy 31 | data/stanford_indoor3d_ins.sem/Area_4_office_20.npy 32 | data/stanford_indoor3d_ins.sem/Area_4_office_21.npy 33 | data/stanford_indoor3d_ins.sem/Area_4_office_22.npy 34 | data/stanford_indoor3d_ins.sem/Area_4_office_2.npy 35 | data/stanford_indoor3d_ins.sem/Area_4_office_3.npy 36 | data/stanford_indoor3d_ins.sem/Area_4_office_4.npy 37 | data/stanford_indoor3d_ins.sem/Area_4_office_5.npy 38 | data/stanford_indoor3d_ins.sem/Area_4_office_6.npy 39 | data/stanford_indoor3d_ins.sem/Area_4_office_7.npy 40 | data/stanford_indoor3d_ins.sem/Area_4_office_8.npy 41 | data/stanford_indoor3d_ins.sem/Area_4_office_9.npy 42 | data/stanford_indoor3d_ins.sem/Area_4_storage_1.npy 43 | data/stanford_indoor3d_ins.sem/Area_4_storage_2.npy 44 | data/stanford_indoor3d_ins.sem/Area_4_storage_3.npy 45 | data/stanford_indoor3d_ins.sem/Area_4_storage_4.npy 46 | data/stanford_indoor3d_ins.sem/Area_4_WC_1.npy 47 | data/stanford_indoor3d_ins.sem/Area_4_WC_2.npy 48 | data/stanford_indoor3d_ins.sem/Area_4_WC_3.npy 49 | data/stanford_indoor3d_ins.sem/Area_4_WC_4.npy 50 | -------------------------------------------------------------------------------- /meta/area5_data_label.txt: -------------------------------------------------------------------------------- 1 | data/stanford_indoor3d_ins.sem/Area_5_conferenceRoom_1.npy 2 | data/stanford_indoor3d_ins.sem/Area_5_conferenceRoom_2.npy 3 | data/stanford_indoor3d_ins.sem/Area_5_conferenceRoom_3.npy 4 | data/stanford_indoor3d_ins.sem/Area_5_hallway_10.npy 5 | data/stanford_indoor3d_ins.sem/Area_5_hallway_11.npy 6 | data/stanford_indoor3d_ins.sem/Area_5_hallway_12.npy 7 | data/stanford_indoor3d_ins.sem/Area_5_hallway_13.npy 8 | data/stanford_indoor3d_ins.sem/Area_5_hallway_14.npy 9 | data/stanford_indoor3d_ins.sem/Area_5_hallway_15.npy 10 | data/stanford_indoor3d_ins.sem/Area_5_hallway_1.npy 11 | data/stanford_indoor3d_ins.sem/Area_5_hallway_2.npy 12 | data/stanford_indoor3d_ins.sem/Area_5_hallway_3.npy 13 | data/stanford_indoor3d_ins.sem/Area_5_hallway_4.npy 14 | data/stanford_indoor3d_ins.sem/Area_5_hallway_5.npy 15 | data/stanford_indoor3d_ins.sem/Area_5_hallway_6.npy 16 | data/stanford_indoor3d_ins.sem/Area_5_hallway_7.npy 17 | data/stanford_indoor3d_ins.sem/Area_5_hallway_8.npy 18 | data/stanford_indoor3d_ins.sem/Area_5_hallway_9.npy 19 | data/stanford_indoor3d_ins.sem/Area_5_lobby_1.npy 20 | data/stanford_indoor3d_ins.sem/Area_5_office_10.npy 21 | data/stanford_indoor3d_ins.sem/Area_5_office_11.npy 22 | data/stanford_indoor3d_ins.sem/Area_5_office_12.npy 23 | data/stanford_indoor3d_ins.sem/Area_5_office_13.npy 24 | data/stanford_indoor3d_ins.sem/Area_5_office_14.npy 25 | data/stanford_indoor3d_ins.sem/Area_5_office_15.npy 26 | data/stanford_indoor3d_ins.sem/Area_5_office_16.npy 27 | data/stanford_indoor3d_ins.sem/Area_5_office_17.npy 28 | data/stanford_indoor3d_ins.sem/Area_5_office_18.npy 29 | data/stanford_indoor3d_ins.sem/Area_5_office_19.npy 30 | data/stanford_indoor3d_ins.sem/Area_5_office_1.npy 31 | data/stanford_indoor3d_ins.sem/Area_5_office_20.npy 32 | data/stanford_indoor3d_ins.sem/Area_5_office_21.npy 33 | data/stanford_indoor3d_ins.sem/Area_5_office_22.npy 34 | data/stanford_indoor3d_ins.sem/Area_5_office_23.npy 35 | data/stanford_indoor3d_ins.sem/Area_5_office_24.npy 36 | data/stanford_indoor3d_ins.sem/Area_5_office_25.npy 37 | data/stanford_indoor3d_ins.sem/Area_5_office_26.npy 38 | data/stanford_indoor3d_ins.sem/Area_5_office_27.npy 39 | data/stanford_indoor3d_ins.sem/Area_5_office_28.npy 40 | data/stanford_indoor3d_ins.sem/Area_5_office_29.npy 41 | data/stanford_indoor3d_ins.sem/Area_5_office_2.npy 42 | data/stanford_indoor3d_ins.sem/Area_5_office_30.npy 43 | data/stanford_indoor3d_ins.sem/Area_5_office_31.npy 44 | data/stanford_indoor3d_ins.sem/Area_5_office_32.npy 45 | data/stanford_indoor3d_ins.sem/Area_5_office_33.npy 46 | data/stanford_indoor3d_ins.sem/Area_5_office_34.npy 47 | data/stanford_indoor3d_ins.sem/Area_5_office_35.npy 48 | data/stanford_indoor3d_ins.sem/Area_5_office_36.npy 49 | data/stanford_indoor3d_ins.sem/Area_5_office_37.npy 50 | data/stanford_indoor3d_ins.sem/Area_5_office_38.npy 51 | data/stanford_indoor3d_ins.sem/Area_5_office_39.npy 52 | data/stanford_indoor3d_ins.sem/Area_5_office_3.npy 53 | data/stanford_indoor3d_ins.sem/Area_5_office_40.npy 54 | data/stanford_indoor3d_ins.sem/Area_5_office_41.npy 55 | data/stanford_indoor3d_ins.sem/Area_5_office_42.npy 56 | data/stanford_indoor3d_ins.sem/Area_5_office_4.npy 57 | data/stanford_indoor3d_ins.sem/Area_5_office_5.npy 58 | data/stanford_indoor3d_ins.sem/Area_5_office_6.npy 59 | data/stanford_indoor3d_ins.sem/Area_5_office_7.npy 60 | data/stanford_indoor3d_ins.sem/Area_5_office_8.npy 61 | data/stanford_indoor3d_ins.sem/Area_5_office_9.npy 62 | data/stanford_indoor3d_ins.sem/Area_5_pantry_1.npy 63 | data/stanford_indoor3d_ins.sem/Area_5_storage_1.npy 64 | data/stanford_indoor3d_ins.sem/Area_5_storage_2.npy 65 | data/stanford_indoor3d_ins.sem/Area_5_storage_3.npy 66 | data/stanford_indoor3d_ins.sem/Area_5_storage_4.npy 67 | data/stanford_indoor3d_ins.sem/Area_5_WC_1.npy 68 | data/stanford_indoor3d_ins.sem/Area_5_WC_2.npy 69 | -------------------------------------------------------------------------------- /meta/area6_data_label.txt: -------------------------------------------------------------------------------- 1 | data/stanford_indoor3d_ins.sem/Area_6_conferenceRoom_1.npy 2 | data/stanford_indoor3d_ins.sem/Area_6_copyRoom_1.npy 3 | data/stanford_indoor3d_ins.sem/Area_6_hallway_1.npy 4 | data/stanford_indoor3d_ins.sem/Area_6_hallway_2.npy 5 | data/stanford_indoor3d_ins.sem/Area_6_hallway_3.npy 6 | data/stanford_indoor3d_ins.sem/Area_6_hallway_4.npy 7 | data/stanford_indoor3d_ins.sem/Area_6_hallway_5.npy 8 | data/stanford_indoor3d_ins.sem/Area_6_hallway_6.npy 9 | data/stanford_indoor3d_ins.sem/Area_6_lounge_1.npy 10 | data/stanford_indoor3d_ins.sem/Area_6_office_10.npy 11 | data/stanford_indoor3d_ins.sem/Area_6_office_11.npy 12 | data/stanford_indoor3d_ins.sem/Area_6_office_12.npy 13 | data/stanford_indoor3d_ins.sem/Area_6_office_13.npy 14 | data/stanford_indoor3d_ins.sem/Area_6_office_14.npy 15 | data/stanford_indoor3d_ins.sem/Area_6_office_15.npy 16 | data/stanford_indoor3d_ins.sem/Area_6_office_16.npy 17 | data/stanford_indoor3d_ins.sem/Area_6_office_17.npy 18 | data/stanford_indoor3d_ins.sem/Area_6_office_18.npy 19 | data/stanford_indoor3d_ins.sem/Area_6_office_19.npy 20 | data/stanford_indoor3d_ins.sem/Area_6_office_1.npy 21 | data/stanford_indoor3d_ins.sem/Area_6_office_20.npy 22 | data/stanford_indoor3d_ins.sem/Area_6_office_21.npy 23 | data/stanford_indoor3d_ins.sem/Area_6_office_22.npy 24 | data/stanford_indoor3d_ins.sem/Area_6_office_23.npy 25 | data/stanford_indoor3d_ins.sem/Area_6_office_24.npy 26 | data/stanford_indoor3d_ins.sem/Area_6_office_25.npy 27 | data/stanford_indoor3d_ins.sem/Area_6_office_26.npy 28 | data/stanford_indoor3d_ins.sem/Area_6_office_27.npy 29 | data/stanford_indoor3d_ins.sem/Area_6_office_28.npy 30 | data/stanford_indoor3d_ins.sem/Area_6_office_29.npy 31 | data/stanford_indoor3d_ins.sem/Area_6_office_2.npy 32 | data/stanford_indoor3d_ins.sem/Area_6_office_30.npy 33 | data/stanford_indoor3d_ins.sem/Area_6_office_31.npy 34 | data/stanford_indoor3d_ins.sem/Area_6_office_32.npy 35 | data/stanford_indoor3d_ins.sem/Area_6_office_33.npy 36 | data/stanford_indoor3d_ins.sem/Area_6_office_34.npy 37 | data/stanford_indoor3d_ins.sem/Area_6_office_35.npy 38 | data/stanford_indoor3d_ins.sem/Area_6_office_36.npy 39 | data/stanford_indoor3d_ins.sem/Area_6_office_37.npy 40 | data/stanford_indoor3d_ins.sem/Area_6_office_3.npy 41 | data/stanford_indoor3d_ins.sem/Area_6_office_4.npy 42 | data/stanford_indoor3d_ins.sem/Area_6_office_5.npy 43 | data/stanford_indoor3d_ins.sem/Area_6_office_6.npy 44 | data/stanford_indoor3d_ins.sem/Area_6_office_7.npy 45 | data/stanford_indoor3d_ins.sem/Area_6_office_8.npy 46 | data/stanford_indoor3d_ins.sem/Area_6_office_9.npy 47 | data/stanford_indoor3d_ins.sem/Area_6_openspace_1.npy 48 | data/stanford_indoor3d_ins.sem/Area_6_pantry_1.npy 49 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /meta/updata_datalabeltxt.py: -------------------------------------------------------------------------------- 1 | import glob 2 | 3 | files = glob.glob('area?_data_label.txt') 4 | print(files) 5 | for f in files: 6 | fopen = open(f, 'r') 7 | 8 | new_lines = [] 9 | for line in fopen.readlines(): 10 | new_line = line.replace('stanford_indoor3d', 'stanford_indoor3d_ins.sem') 11 | print(new_line) 12 | new_lines.append(new_line) 13 | 14 | fwrite = open(f, 'w') 15 | 16 | fwrite.writelines(new_lines) 17 | -------------------------------------------------------------------------------- /misc/fig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dlinzhao/JSNet/8fec285e1ccc12a0c5806f4adc60846420ca46a1/misc/fig.png -------------------------------------------------------------------------------- /models/JISS/estimate_mean_ins_size.py: -------------------------------------------------------------------------------- 1 | # To estimate the mean instance size of each class in training set 2 | import os 3 | import sys 4 | import numpy as np 5 | from scipy import stats 6 | import argparse 7 | 8 | BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 9 | ROOT_DIR = os.path.dirname(os.path.dirname(BASE_DIR)) 10 | sys.path.append(BASE_DIR) 11 | sys.path.append(os.path.join(ROOT_DIR, 'utils')) 12 | import provider 13 | 14 | 15 | parser = argparse.ArgumentParser() 16 | parser.add_argument('--data_root', type=str, default='data', help='data dir') 17 | parser.add_argument('--dataset', type=str, default='S3DIS', help='dataset [S3DIS]') 18 | parser.add_argument('--input_list', type=str, default='data/train_hdf5_file_list_woArea5.txt', help='estimate the mean instance size') 19 | parser.add_argument('--num_cls', type=int, default=13, help='estimate the mean instance size') 20 | parser.add_argument('--out_dir', type=str, default='log5', help='log dir to save mean instance size [model path]') 21 | FLAGS = parser.parse_args() 22 | 23 | 24 | def estimate(flags): 25 | num_classes = flags.num_cls 26 | if flags.dataset == 'S3DIS': 27 | train_file_list = [os.path.join(flags.data_root, line.strip()) for line in open(flags.input_list, 'r')] 28 | else: 29 | print("Error: Not support the dataset: ", flags.dataset) 30 | return 31 | 32 | mean_ins_size = np.zeros(num_classes) 33 | ptsnum_in_gt = [[] for itmp in range(num_classes)] 34 | 35 | for h5_filename in train_file_list: 36 | print(h5_filename) 37 | cur_data, cur_group, _, cur_sem = provider.loadDataFile_with_groupseglabel_stanfordindoor(h5_filename) 38 | cur_data = np.reshape(cur_data, [-1, cur_data.shape[-1]]) 39 | cur_group = np.reshape(cur_group, [-1]) 40 | cur_sem = np.reshape(cur_sem, [-1]) 41 | 42 | un = np.unique(cur_group) 43 | for ig, g in enumerate(un): 44 | tmp = (cur_group == g) 45 | sem_seg_g = int(stats.mode(cur_sem[tmp])[0]) 46 | ptsnum_in_gt[sem_seg_g].append(np.sum(tmp)) 47 | 48 | for idx in range(num_classes): 49 | mean_ins_size[idx] = np.mean(ptsnum_in_gt[idx]).astype(np.int) 50 | 51 | print(mean_ins_size) 52 | np.savetxt(os.path.join(flags.out_dir, 'mean_ins_size.txt'), mean_ins_size) 53 | 54 | 55 | if __name__ == "__main__": 56 | estimate(FLAGS) 57 | -------------------------------------------------------------------------------- /models/JISS/eval_iou_accuracy.py: -------------------------------------------------------------------------------- 1 | import os 2 | import numpy as np 3 | import argparse 4 | from scipy import stats 5 | 6 | 7 | parser = argparse.ArgumentParser() 8 | parser.add_argument('--num_cls', type=int, default=13, help='num of classes') 9 | parser.add_argument('--log_dir', type=str, default='logs/test_5', help='test dir') 10 | FLAGS = parser.parse_args() 11 | 12 | LOG_DIR = FLAGS.log_dir 13 | NUM_CLASSES = FLAGS.num_cls 14 | 15 | pred_data_label_filenames = [] 16 | file_name = '{}/output_filelist.txt'.format(LOG_DIR) 17 | pred_data_label_filenames += [line.rstrip() for line in open(file_name)] 18 | 19 | gt_label_filenames = [f.rstrip('_pred\.txt') + '_gt.txt' for f in pred_data_label_filenames] 20 | 21 | num_room = len(gt_label_filenames) 22 | 23 | # Initialize... 24 | # acc and macc 25 | total_true = 0 26 | total_seen = 0 27 | true_positive_classes = np.zeros(NUM_CLASSES) 28 | positive_classes = np.zeros(NUM_CLASSES) 29 | gt_classes = np.zeros(NUM_CLASSES) 30 | # mIoU 31 | ious = np.zeros(NUM_CLASSES) 32 | totalnums = np.zeros(NUM_CLASSES) 33 | # precision & recall 34 | total_gt_ins = np.zeros(NUM_CLASSES) 35 | at = 0.5 36 | tpsins = [[] for itmp in range(NUM_CLASSES)] 37 | fpsins = [[] for itmp in range(NUM_CLASSES)] 38 | # mucov and mwcov 39 | all_mean_cov = [[] for itmp in range(NUM_CLASSES)] 40 | all_mean_weighted_cov = [[] for itmp in range(NUM_CLASSES)] 41 | 42 | for i in range(num_room): 43 | print(i) 44 | data_label = np.loadtxt(pred_data_label_filenames[i]) 45 | pred_ins = data_label[:, -1].reshape(-1).astype(np.int) 46 | pred_sem = data_label[:, -2].reshape(-1).astype(np.int) 47 | gt_label = np.loadtxt(gt_label_filenames[i]) 48 | gt_ins = gt_label[:, -1].reshape(-1).astype(np.int) 49 | gt_sem = gt_label[:, -2].reshape(-1).astype(np.int) 50 | print(gt_label.shape) 51 | 52 | # semantic acc 53 | total_true += np.sum(pred_sem == gt_sem) 54 | total_seen += pred_sem.shape[0] 55 | 56 | # pn semantic mIoU 57 | for j in range(gt_sem.shape[0]): 58 | gt_l = int(gt_sem[j]) 59 | pred_l = int(pred_sem[j]) 60 | gt_classes[gt_l] += 1 61 | positive_classes[pred_l] += 1 62 | true_positive_classes[gt_l] += int(gt_l == pred_l) 63 | 64 | # instance 65 | un = np.unique(pred_ins) 66 | pts_in_pred = [[] for itmp in range(NUM_CLASSES)] 67 | for ig, g in enumerate(un): # each object in prediction 68 | if g == -1: 69 | continue 70 | tmp = (pred_ins == g) 71 | sem_seg_i = int(stats.mode(pred_sem[tmp])[0]) 72 | pts_in_pred[sem_seg_i] += [tmp] 73 | 74 | un = np.unique(gt_ins) 75 | pts_in_gt = [[] for itmp in range(NUM_CLASSES)] 76 | for ig, g in enumerate(un): 77 | tmp = (gt_ins == g) 78 | sem_seg_i = int(stats.mode(gt_sem[tmp])[0]) 79 | pts_in_gt[sem_seg_i] += [tmp] 80 | 81 | # instance mucov & mwcov 82 | for i_sem in range(NUM_CLASSES): 83 | sum_cov = 0 84 | mean_cov = 0 85 | mean_weighted_cov = 0 86 | num_gt_point = 0 87 | for ig, ins_gt in enumerate(pts_in_gt[i_sem]): 88 | ovmax = 0. 89 | num_ins_gt_point = np.sum(ins_gt) 90 | num_gt_point += num_ins_gt_point 91 | for ip, ins_pred in enumerate(pts_in_pred[i_sem]): 92 | union = (ins_pred | ins_gt) 93 | intersect = (ins_pred & ins_gt) 94 | iou = float(np.sum(intersect)) / np.sum(union) 95 | 96 | if iou > ovmax: 97 | ovmax = iou 98 | ipmax = ip 99 | 100 | sum_cov += ovmax 101 | mean_weighted_cov += ovmax * num_ins_gt_point 102 | 103 | if len(pts_in_gt[i_sem]) != 0: 104 | mean_cov = sum_cov / len(pts_in_gt[i_sem]) 105 | all_mean_cov[i_sem].append(mean_cov) 106 | 107 | mean_weighted_cov /= num_gt_point 108 | all_mean_weighted_cov[i_sem].append(mean_weighted_cov) 109 | 110 | # instance precision & recall 111 | for i_sem in range(NUM_CLASSES): 112 | tp = [0.] * len(pts_in_pred[i_sem]) 113 | fp = [0.] * len(pts_in_pred[i_sem]) 114 | gtflag = np.zeros(len(pts_in_gt[i_sem])) 115 | total_gt_ins[i_sem] += len(pts_in_gt[i_sem]) 116 | 117 | for ip, ins_pred in enumerate(pts_in_pred[i_sem]): 118 | ovmax = -1. 119 | 120 | for ig, ins_gt in enumerate(pts_in_gt[i_sem]): 121 | union = (ins_pred | ins_gt) 122 | intersect = (ins_pred & ins_gt) 123 | iou = float(np.sum(intersect)) / np.sum(union) 124 | 125 | if iou > ovmax: 126 | ovmax = iou 127 | igmax = ig 128 | 129 | if ovmax >= at: 130 | tp[ip] = 1 # true 131 | else: 132 | fp[ip] = 1 # false positive 133 | 134 | tpsins[i_sem] += tp 135 | fpsins[i_sem] += fp 136 | 137 | MUCov = np.zeros(NUM_CLASSES) 138 | MWCov = np.zeros(NUM_CLASSES) 139 | for i_sem in range(NUM_CLASSES): 140 | MUCov[i_sem] = np.mean(all_mean_cov[i_sem]) 141 | MWCov[i_sem] = np.mean(all_mean_weighted_cov[i_sem]) 142 | 143 | precision = np.zeros(NUM_CLASSES) 144 | recall = np.zeros(NUM_CLASSES) 145 | for i_sem in range(NUM_CLASSES): 146 | tp = np.asarray(tpsins[i_sem]).astype(np.float) 147 | fp = np.asarray(fpsins[i_sem]).astype(np.float) 148 | tp = np.sum(tp) 149 | fp = np.sum(fp) 150 | rec = tp / total_gt_ins[i_sem] 151 | prec = tp / (tp + fp) 152 | 153 | precision[i_sem] = prec 154 | recall[i_sem] = rec 155 | 156 | LOG_FOUT = open(os.path.join(os.path.dirname(file_name), 'results.txt'), 'w') 157 | 158 | 159 | def log_string(out_str): 160 | LOG_FOUT.write(out_str + '\n') 161 | LOG_FOUT.flush() 162 | print(out_str) 163 | 164 | 165 | # instance results 166 | log_string('Instance Segmentation MUCov: {}'.format(MUCov.tolist())) 167 | log_string('Instance Segmentation mMUCov: {}'.format(np.mean(MUCov))) 168 | log_string('Instance Segmentation MWCov: {}'.format(MWCov.tolist())) 169 | log_string('Instance Segmentation mMWCov: {}'.format(np.mean(MWCov))) 170 | log_string('Instance Segmentation Precision: {}'.format(precision.tolist())) 171 | log_string('Instance Segmentation mPrecision: {}'.format(np.mean(precision))) 172 | log_string('Instance Segmentation Recall: {}'.format(recall.tolist())) 173 | log_string('Instance Segmentation mRecall: {}'.format(np.mean(recall))) 174 | 175 | # semantic results 176 | iou_list = [] 177 | for i in range(NUM_CLASSES): 178 | iou = true_positive_classes[i] / float(gt_classes[i] + positive_classes[i] - true_positive_classes[i]) 179 | # print(iou) 180 | iou_list.append(iou) 181 | 182 | log_string('Semantic Segmentation oAcc: {}'.format(sum(true_positive_classes) / float(sum(positive_classes)))) 183 | # log_string('Semantic Segmentation Acc: {}'.format(true_positive_classes / gt_classes)) 184 | log_string('Semantic Segmentation mAcc: {}'.format(np.mean(true_positive_classes / gt_classes))) 185 | log_string('Semantic Segmentation IoU: {}'.format(iou_list)) 186 | log_string('Semantic Segmentation mIoU: {}'.format(1. * sum(iou_list) / NUM_CLASSES)) 187 | -------------------------------------------------------------------------------- /models/JISS/model.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | BASE_DIR = os.path.dirname(__file__) 5 | sys.path.append(BASE_DIR) 6 | sys.path.append(os.path.join(BASE_DIR, '../utils')) 7 | import tensorflow as tf 8 | import tf_util 9 | from pointnet_util import pointnet_sa_module, pointnet_fp_module, pointnet_upsample 10 | from pointconv_util import pointconv_encoding, pointconv_decoding_depthwise 11 | from loss import * 12 | 13 | 14 | def placeholder_inputs(batch_size, num_point, num_dims=9): 15 | pointclouds_pl = tf.placeholder(tf.float32, shape=(batch_size, num_point, num_dims)) 16 | labels_pl = tf.placeholder(tf.int32, shape=(batch_size, num_point)) 17 | sem_pl = tf.placeholder(tf.int32, shape=(batch_size, num_point)) 18 | return pointclouds_pl, labels_pl, sem_pl 19 | 20 | 21 | def get_model(point_cloud, is_training, num_class, num_embed=5, sigma=0.05, bn_decay=None, is_dist=False): 22 | """ Semantic segmentation PointNet, input is BxNx3, output Bxnum_class """ 23 | batch_size = point_cloud.get_shape()[0].value 24 | num_point = point_cloud.get_shape()[1].value 25 | end_points = {} 26 | l0_xyz = point_cloud[:, :, :3] 27 | l0_points = point_cloud[:, :, 3:] 28 | end_points['l0_xyz'] = l0_xyz 29 | 30 | # shared encoder 31 | l1_xyz, l1_points, l1_indices = pointnet_sa_module(l0_xyz, l0_points, npoint=1024, radius=0.1, nsample=32, mlp=[32, 32, 64], mlp2=None, group_all=False, is_training=is_training, bn_decay=bn_decay, is_dist=is_dist, scope='layer1') 32 | l2_xyz, l2_points = pointconv_encoding(l1_xyz, l1_points, npoint=256, radius=0.2, sigma=2 * sigma, K=32, mlp=[ 64, 64, 128], is_training=is_training, bn_decay=bn_decay, is_dist=is_dist, weight_decay=None, scope='layer2') 33 | l3_xyz, l3_points = pointconv_encoding(l2_xyz, l2_points, npoint=64, radius=0.4, sigma=4 * sigma, K=32, mlp=[128, 128, 256], is_training=is_training, bn_decay=bn_decay, is_dist=is_dist, weight_decay=None, scope='layer3') 34 | l4_xyz, l4_points = pointconv_encoding(l3_xyz, l3_points, npoint=32, radius=0.8, sigma=8 * sigma, K=32, mlp=[256, 256, 512], is_training=is_training, bn_decay=bn_decay, is_dist=is_dist, weight_decay=None, scope='layer4') 35 | 36 | # semantic decoder 37 | l3_points_sem = pointconv_decoding_depthwise(l3_xyz, l4_xyz, l3_points, l4_points, radius=0.8, sigma=8*sigma, K=16, mlp=[512, 512], is_training=is_training, bn_decay=bn_decay, is_dist=is_dist, weight_decay=None, scope='sem_fa_layer1') 38 | l2_points_sem = pointconv_decoding_depthwise(l2_xyz, l3_xyz, l2_points, l3_points_sem, radius=0.4, sigma=4*sigma, K=16, mlp=[256, 256], is_training=is_training, bn_decay=bn_decay, is_dist=is_dist, weight_decay=None, scope='sem_fa_layer2') # 48x256x256 39 | l1_points_sem = pointconv_decoding_depthwise(l1_xyz, l2_xyz, l1_points, l2_points_sem, radius=0.2, sigma=2*sigma, K=16, mlp=[256, 128], is_training=is_training, bn_decay=bn_decay, is_dist=is_dist, weight_decay=None, scope='sem_fa_layer3') # 48x1024x128 40 | l0_points_sem = pointnet_fp_module(l0_xyz, l1_xyz, l0_points, l1_points_sem, [128, 128, 128], is_training, bn_decay, is_dist=is_dist, scope='sem_fa_layer4') # 48x4096x128 41 | 42 | # instance decoder 43 | l3_points_ins = pointconv_decoding_depthwise(l3_xyz, l4_xyz, l3_points, l4_points, radius=0.8, sigma=8*sigma, K=16, mlp=[512, 512], is_training=is_training, bn_decay=bn_decay, is_dist=is_dist, weight_decay=None, scope='ins_fa_layer1') 44 | l2_points_ins = pointconv_decoding_depthwise(l2_xyz, l3_xyz, l2_points, l3_points_ins, radius=0.4, sigma=4*sigma, K=16, mlp=[256, 256], is_training=is_training, bn_decay=bn_decay, is_dist=is_dist, weight_decay=None, scope='ins_fa_layer2') # 48x256x256 45 | l1_points_ins = pointconv_decoding_depthwise(l1_xyz, l2_xyz, l1_points, l2_points_ins, radius=0.2, sigma=2*sigma, K=16, mlp=[256, 128], is_training=is_training, bn_decay=bn_decay, is_dist=is_dist, weight_decay=None, scope='ins_fa_layer3') # 48x1024x128 46 | l0_points_ins = pointnet_fp_module(l0_xyz, l1_xyz, l0_points, l1_points_ins, [128, 128, 128], is_training, bn_decay, is_dist=is_dist, scope='ins_fa_layer4') # 48x4096x128 47 | 48 | # FC layers F_sem 49 | l2_points_sem_up = pointnet_upsample(l0_xyz, l2_xyz, l2_points_sem, scope='sem_up1') 50 | l1_points_sem_up = pointnet_upsample(l0_xyz, l1_xyz, l1_points_sem, scope='sem_up2') 51 | net_sem_0 = tf.add(tf.concat([l0_points_sem, l1_points_sem_up], axis=-1, name='sem_up_concat'), l2_points_sem_up, name='sem_up_add') 52 | net_sem_0 = tf_util.conv1d(net_sem_0, 128, 1, padding='VALID', bn=True, is_training=is_training, is_dist=is_dist, scope='sem_fc1', bn_decay=bn_decay) 53 | 54 | # FC layers F_ins 55 | l2_points_ins_up = pointnet_upsample(l0_xyz, l2_xyz, l2_points_ins, scope='ins_up1') 56 | l1_points_ins_up = pointnet_upsample(l0_xyz, l1_xyz, l1_points_ins, scope='ins_up2') 57 | net_ins_0 = tf.add(tf.concat([l0_points_ins, l1_points_ins_up], axis=-1, name='ins_up_concat'), l2_points_ins_up, name='ins_up_add') 58 | net_ins_0 = tf_util.conv1d(net_ins_0, 128, 1, padding='VALID', bn=True, is_training=is_training, is_dist=is_dist, scope='ins_fc1', bn_decay=bn_decay) 59 | 60 | # Adaptation 61 | net_sem_cache_0 = tf_util.conv1d(net_sem_0, 128, 1, padding='VALID', bn=True, is_training=is_training, is_dist=is_dist, scope='sem_cache_1', bn_decay=bn_decay) 62 | net_ins_1 = net_ins_0 + net_sem_cache_0 63 | 64 | net_ins_2 = tf.concat([net_ins_0, net_ins_1], axis=-1, name='net_ins_2_concat') 65 | net_ins_atten = tf.sigmoid(tf.reduce_mean(net_ins_2, axis=-1, keep_dims=True, name='ins_reduce'), name='ins_atten') 66 | net_ins_3 = net_ins_2 * net_ins_atten 67 | 68 | # Aggregation 69 | net_ins_cache_0 = tf_util.conv1d(net_ins_3, 128, 1, padding='VALID', bn=True, is_training=is_training, is_dist=is_dist, scope='ins_cache_1', bn_decay=bn_decay) 70 | net_ins_cache_1 = tf.reduce_mean(net_ins_cache_0, axis=1, keep_dims=True, name='ins_cache_2') 71 | net_ins_cache_1 = tf.tile(net_ins_cache_1, [1, num_point, 1], name='ins_cache_tile') 72 | net_sem_1 = net_sem_0 + net_ins_cache_1 73 | 74 | net_sem_2 = tf.concat([net_sem_0, net_sem_1], axis=-1, name='net_sem_2_concat') 75 | net_sem_atten = tf.sigmoid(tf.reduce_mean(net_sem_2, axis=-1, keep_dims=True, name='sem_reduce'), name='sem_atten') 76 | net_sem_3 = net_sem_2 * net_sem_atten 77 | 78 | # Output 79 | net_ins_3 = tf_util.conv1d(net_ins_3, 128, 1, padding='VALID', bn=True, is_training=is_training, is_dist=is_dist, scope='ins_fc2', bn_decay=bn_decay) 80 | net_ins_4 = tf_util.dropout(net_ins_3, keep_prob=0.5, is_training=is_training, scope='ins_dp_4') 81 | net_ins_4 = tf_util.conv1d(net_ins_4, num_embed, 1, padding='VALID', activation_fn=None, is_dist=is_dist, scope='ins_fc5') 82 | 83 | net_sem_3 = tf_util.conv1d(net_sem_3, 128, 1, padding='VALID', bn=True, is_training=is_training, is_dist=is_dist, scope='sem_fc2', bn_decay=bn_decay) 84 | net_sem_4 = tf_util.dropout(net_sem_3, keep_prob=0.5, is_training=is_training, scope='sem_dp_4') 85 | net_sem_4 = tf_util.conv1d(net_sem_4, num_class, 1, padding='VALID', activation_fn=None, is_dist=is_dist, scope='sem_fc5') 86 | 87 | return net_sem_4, net_ins_4 88 | 89 | 90 | def get_loss(pred, ins_label, pred_sem_label, pred_sem, sem_label, weights=1.0, disc_weight=1.0): 91 | """ pred: BxNxE, 92 | ins_label: BxN 93 | pred_sem_label: BxN 94 | pred_sem: BxNx13 95 | sem_label: BxN 96 | """ 97 | classify_loss = tf.losses.sparse_softmax_cross_entropy(labels=sem_label, logits=pred_sem, weights=weights) 98 | tf.summary.scalar('classify loss', classify_loss) 99 | 100 | feature_dim = pred.get_shape()[-1] 101 | delta_v = 0.5 102 | delta_d = 1.5 103 | param_var = 1. 104 | param_dist = 1. 105 | 106 | disc_loss, l_var, l_dist = discriminative_loss(pred, ins_label, feature_dim, delta_v, delta_d, param_var, param_dist) 107 | 108 | disc_loss = disc_loss * disc_weight 109 | l_var = l_var * disc_weight 110 | l_dist = l_dist * disc_weight 111 | 112 | loss = classify_loss + disc_loss 113 | 114 | tf.add_to_collection('losses', loss) 115 | return loss, classify_loss, disc_loss, l_var, l_dist 116 | 117 | 118 | if __name__ == '__main__': 119 | with tf.Graph().as_default(): 120 | inputs = tf.zeros((32, 2048, 3)) 121 | net, _ = get_model(inputs, tf.constant(True), 10) 122 | print(net) 123 | -------------------------------------------------------------------------------- /models/JISS/train.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import os 3 | import socket 4 | import sys 5 | 6 | import tensorflow as tf 7 | 8 | CURR_DIR = os.path.dirname(os.path.abspath(__file__)) 9 | BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 10 | ROOT_DIR = os.path.dirname(os.path.dirname(BASE_DIR)) 11 | 12 | sys.path.append(BASE_DIR) 13 | sys.path.append(os.path.join(ROOT_DIR, 'utils')) 14 | 15 | from s3dis_utils.dataset_s3dis import S3DISDataset 16 | from log_util import get_logger 17 | from model import * 18 | 19 | parser = argparse.ArgumentParser() 20 | parser.add_argument('--gpu', type=int, default=0, help='GPU to use [default: GPU 0]') 21 | parser.add_argument('--num_works', type=int, default=8, help='Loading data thread [default: 8]') 22 | parser.add_argument('--data_root', default='data', help='data dir [default: data]') 23 | parser.add_argument('--data_type', default='numpy', help='data type: numpy or hdf5 [default: numpy]') 24 | parser.add_argument('--log_dir', default='logs', help='Log dir [default: logs]') 25 | parser.add_argument('--num_point', type=int, default=4096, help='Point number [default: 4096]') 26 | parser.add_argument('--start_epoch', type=int, default=0, help='Epoch to run [default: 50]') 27 | parser.add_argument('--max_epoch', type=int, default=50, help='Epoch to run [default: 50]') 28 | parser.add_argument('--batch_size', type=int, default=24, help='Batch Size during training [default: 24]') 29 | parser.add_argument('--learning_rate', type=float, default=0.001, help='Initial learning rate [default: 0.001]') 30 | parser.add_argument('--momentum', type=float, default=0.9, help='Initial learning rate [default: 0.9]') 31 | parser.add_argument('--optimizer', default='adam', help='adam or momentum [default: adam]') 32 | parser.add_argument('--decay_step', type=int, default=12500, help='Decay step for lr decay [default: 12500]') 33 | parser.add_argument('--decay_rate', type=float, default=0.5, help='Decay rate for lr decay [default: 0.5]') 34 | parser.add_argument('--input_list', type=str, default='data/train_hdf5_file_list_woArea5.txt', 35 | help='Input data list file') 36 | parser.add_argument('--restore_model', type=str, default='log/', help='Pretrained model') 37 | FLAGS = parser.parse_args() 38 | 39 | BATCH_SIZE = FLAGS.batch_size 40 | NUM_WORKS = FLAGS.num_works 41 | NUM_POINT = FLAGS.num_point 42 | DATA_TYPE = FLAGS.data_type 43 | START_EPOCH = FLAGS.start_epoch 44 | MAX_EPOCH = FLAGS.max_epoch 45 | BASE_LEARNING_RATE = FLAGS.learning_rate 46 | GPU_INDEX = FLAGS.gpu 47 | MOMENTUM = FLAGS.momentum 48 | OPTIMIZER = FLAGS.optimizer 49 | DECAY_STEP = FLAGS.decay_step 50 | DECAY_STEP = int(DECAY_STEP / (BATCH_SIZE / 24)) 51 | DECAY_RATE = FLAGS.decay_rate 52 | 53 | DATA_ROOT = FLAGS.data_root 54 | TRAINING_FILE_LIST = FLAGS.input_list 55 | PRETRAINED_MODEL_PATH = FLAGS.restore_model 56 | 57 | LOG_DIR = FLAGS.log_dir 58 | if not os.path.exists(LOG_DIR): 59 | os.makedirs(LOG_DIR) 60 | 61 | MAX_NUM_POINT = 4096 62 | NUM_CLASSES = 13 63 | 64 | BN_INIT_DECAY = 0.5 65 | BN_DECAY_DECAY_RATE = 0.5 66 | BN_DECAY_DECAY_STEP = float(DECAY_STEP) 67 | BN_DECAY_CLIP = 0.99 68 | 69 | HOSTNAME = socket.gethostname() 70 | 71 | # backup model 72 | os.system('cp model.py {}'.format(LOG_DIR)) 73 | os.system('cp train.py {}'.format(LOG_DIR)) 74 | 75 | logger = get_logger(__file__, LOG_DIR, 'log_train.txt') 76 | logger.info(str(FLAGS) + '\n') 77 | 78 | 79 | def get_learning_rate(batch): 80 | learning_rate = tf.train.exponential_decay( 81 | BASE_LEARNING_RATE, # Base learning rate. 82 | batch, # Current index into the dataset. 83 | DECAY_STEP, # Decay step. 84 | DECAY_RATE, # Decay rate. 85 | staircase=True) 86 | learning_rate = tf.maximum(learning_rate, 0.00001) # CLIP THE LEARNING RATE!! 87 | return learning_rate 88 | 89 | 90 | def get_bn_decay(batch): 91 | bn_momentum = tf.train.exponential_decay( 92 | BN_INIT_DECAY, 93 | batch, 94 | BN_DECAY_DECAY_STEP, 95 | BN_DECAY_DECAY_RATE, 96 | staircase=True) 97 | bn_decay = tf.minimum(BN_DECAY_CLIP, 1 - bn_momentum) 98 | return bn_decay 99 | 100 | 101 | def train(): 102 | # Load data 103 | dataset = S3DISDataset(DATA_ROOT, TRAINING_FILE_LIST, split='train', epoch=MAX_EPOCH - START_EPOCH, 104 | batch_size=BATCH_SIZE, num_works=NUM_WORKS, data_type=DATA_TYPE, block_points=NUM_POINT) 105 | # build network and create session 106 | with tf.Graph().as_default(), tf.device('/gpu:'+str(GPU_INDEX)): 107 | pointclouds_pl, labels_pl, sem_labels_pl = placeholder_inputs(BATCH_SIZE, NUM_POINT) 108 | is_training_pl = tf.placeholder(tf.bool, shape=()) 109 | 110 | # Note the global_step=batch parameter to minimize. 111 | # That tells the optimizer to helpfully increment the 'batch' parameter for you every time it trains. 112 | batch = tf.get_variable('batch', [], initializer=tf.constant_initializer(0), trainable=False) 113 | bn_decay = get_bn_decay(batch) 114 | tf.summary.scalar('bn_decay', bn_decay) 115 | 116 | # Get model and loss 117 | pred_sem, pred_ins = get_model(pointclouds_pl, is_training_pl, NUM_CLASSES, bn_decay=bn_decay) 118 | pred_sem_softmax = tf.nn.softmax(pred_sem) 119 | pred_sem_label = tf.argmax(pred_sem_softmax, axis=2) 120 | 121 | loss, sem_loss, disc_loss, l_var, l_dist = get_loss(pred_ins, labels_pl, pred_sem_label, pred_sem, sem_labels_pl) 122 | 123 | tf.summary.scalar('loss', loss) 124 | tf.summary.scalar('sem_loss', sem_loss) 125 | tf.summary.scalar('disc_loss', disc_loss) 126 | tf.summary.scalar('l_var', l_var) 127 | tf.summary.scalar('l_dist', l_dist) 128 | 129 | # Get training operator 130 | learning_rate = get_learning_rate(batch) 131 | tf.summary.scalar('learning_rate', learning_rate) 132 | if OPTIMIZER == 'momentum': 133 | optimizer = tf.train.MomentumOptimizer(learning_rate, momentum=MOMENTUM) 134 | elif OPTIMIZER == 'adam': 135 | optimizer = tf.train.AdamOptimizer(learning_rate) 136 | 137 | train_op = optimizer.minimize(loss, var_list=tf.trainable_variables(), global_step=batch) 138 | 139 | # Add ops to save and restore all the variables. 140 | saver = tf.train.Saver(max_to_keep=15) 141 | 142 | # Create a session 143 | config = tf.ConfigProto() 144 | config.gpu_options.allow_growth = True 145 | # config.gpu_options.allocator_type = 'BFC' 146 | config.gpu_options.per_process_gpu_memory_fraction = 0.9 147 | config.allow_soft_placement = True 148 | config.log_device_placement = False 149 | sess = tf.Session(config=config) 150 | 151 | # Add summary writers 152 | merged = tf.summary.merge_all() 153 | train_writer = tf.summary.FileWriter(os.path.join(LOG_DIR, 'train'), sess.graph) 154 | 155 | # Init variables 156 | init = tf.global_variables_initializer() 157 | sess.run(init, {is_training_pl: True}) 158 | 159 | ckptstate = tf.train.get_checkpoint_state(PRETRAINED_MODEL_PATH) 160 | if ckptstate is not None: 161 | LOAD_MODEL_FILE = os.path.join(PRETRAINED_MODEL_PATH, os.path.basename(ckptstate.model_checkpoint_path)) 162 | saver.restore(sess, LOAD_MODEL_FILE) 163 | logger.info("Model loaded in file: %s" % LOAD_MODEL_FILE) 164 | 165 | adam_initializers = [var.initializer for var in tf.global_variables() if 'Adam' in var.name] 166 | sess.run(adam_initializers) 167 | 168 | ops = {'pointclouds_pl': pointclouds_pl, 169 | 'labels_pl': labels_pl, 170 | 'sem_labels_pl': sem_labels_pl, 171 | 'is_training_pl': is_training_pl, 172 | 'loss': loss, 173 | 'sem_loss': sem_loss, 174 | 'disc_loss': disc_loss, 175 | 'l_var': l_var, 176 | 'l_dist': l_dist, 177 | 'train_op': train_op, 178 | 'merged': merged, 179 | 'step': batch, 180 | 'learning_rate': learning_rate} 181 | 182 | for epoch in range(START_EPOCH, MAX_EPOCH): 183 | train_one_epoch(sess, ops, train_writer, dataset, epoch) 184 | 185 | # Save the variables to disk. 186 | if epoch % 5 == 0 or epoch == (MAX_EPOCH - 1): 187 | save_path = saver.save(sess, os.path.join(LOG_DIR, 'epoch_' + str(epoch) + '.ckpt')) 188 | logger.info("Model saved in file: %s" % save_path) 189 | 190 | 191 | def train_one_epoch(sess, ops, train_writer, dataset, epoch): 192 | """ ops: dict mapping from string to tf ops """ 193 | is_training = True 194 | file_size = dataset.get_length() 195 | num_batches = file_size // BATCH_SIZE 196 | 197 | loss_sum = 0 198 | 199 | max_epoch_len = len(str(MAX_EPOCH)) 200 | num_batches_len = len(str(num_batches)) 201 | 202 | for batch_idx in range(num_batches): 203 | current_data, current_sem, current_label = dataset.get_batch(False) 204 | feed_dict = {ops['pointclouds_pl']: current_data, 205 | ops['labels_pl']: current_label, 206 | ops['sem_labels_pl']: current_sem, 207 | ops['is_training_pl']: is_training} 208 | 209 | summary, step, lr_rate, _, loss_val, sem_loss_val, disc_loss_val, l_var_val, l_dist_val = sess.run( 210 | [ops['merged'], ops['step'], ops['learning_rate'], ops['train_op'], ops['loss'], ops['sem_loss'], 211 | ops['disc_loss'], ops['l_var'], ops['l_dist']], feed_dict=feed_dict) 212 | train_writer.add_summary(summary, step) 213 | loss_sum += loss_val 214 | 215 | if batch_idx % 50 == 0 and batch_idx: 216 | logger_info = "epoch: {1:0{0}d}/{2}; batch_num: {4:0{3}d}/{5}; lr_rate: {6:.6f}; loss: {7:.2f}; " \ 217 | "sem_loss: {8:.2f}; disc_loss: {9:.2f}; l_var: {10:.2f}; l_dist: {11:.2f};" 218 | 219 | logger.info(logger_info.format(max_epoch_len, epoch, MAX_EPOCH, num_batches_len, batch_idx, num_batches, 220 | lr_rate, loss_val, sem_loss_val, disc_loss_val, l_var_val, l_dist_val)) 221 | 222 | logger.info('mean loss: %f' % (loss_sum / float(num_batches))) 223 | 224 | 225 | if __name__ == "__main__": 226 | train() 227 | -------------------------------------------------------------------------------- /tf_ops/3d_interpolation/interpolate.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include // memset 4 | #include // rand, RAND_MAX 5 | #include // sqrtf 6 | #include 7 | #include 8 | using namespace std; 9 | float randomf(){ 10 | return (rand()+0.5)/(RAND_MAX+1.0); 11 | } 12 | static double get_time(){ 13 | timespec tp; 14 | clock_gettime(CLOCK_MONOTONIC,&tp); 15 | return tp.tv_sec+tp.tv_nsec*1e-9; 16 | } 17 | 18 | // Find three nearest neigbors with square distance 19 | // input: xyz1 (b,n,3), xyz2(b,m,3) 20 | // output: dist (b,n,3), idx (b,n,3) 21 | void threenn_cpu(int b, int n, int m, const float *xyz1, const float *xyz2, float *dist, int *idx) { 22 | for (int i=0;i 2 | #include 3 | #include // memset 4 | #include // rand, RAND_MAX 5 | #include // sqrtf 6 | #include "tensorflow/core/framework/op.h" 7 | #include "tensorflow/core/framework/op_kernel.h" 8 | #include "tensorflow/core/framework/shape_inference.h" 9 | #include "tensorflow/core/framework/common_shape_fns.h" 10 | using namespace tensorflow; 11 | 12 | REGISTER_OP("ThreeNN") 13 | .Input("xyz1: float32") 14 | .Input("xyz2: float32") 15 | .Output("dist: float32") 16 | .Output("idx: int32") 17 | .SetShapeFn([](::tensorflow::shape_inference::InferenceContext* c) { 18 | c->set_output(0, c->input(0)); 19 | c->set_output(1, c->input(0)); 20 | return Status::OK(); 21 | }); 22 | REGISTER_OP("ThreeInterpolate") 23 | .Input("points: float32") 24 | .Input("idx: int32") 25 | .Input("weight: float32") 26 | .Output("out: float32") 27 | .SetShapeFn([](::tensorflow::shape_inference::InferenceContext* c) { 28 | ::tensorflow::shape_inference::ShapeHandle dims1; // (b,m,c) 29 | c->WithRank(c->input(0), 3, &dims1); 30 | ::tensorflow::shape_inference::ShapeHandle dims2; // (b,n,3) 31 | c->WithRank(c->input(1), 3, &dims2); 32 | // (b,n,c) 33 | ::tensorflow::shape_inference::ShapeHandle output = c->MakeShape({c->Dim(dims1, 0), c->Dim(dims2, 1), c->Dim(dims1, 2)}); 34 | c->set_output(0, output); 35 | return Status::OK(); 36 | }); 37 | REGISTER_OP("ThreeInterpolateGrad") 38 | .Input("points: float32") 39 | .Input("idx: int32") 40 | .Input("weight: float32") 41 | .Input("grad_out: float32") 42 | .Output("grad_points: float32") 43 | .SetShapeFn([](::tensorflow::shape_inference::InferenceContext* c) { 44 | c->set_output(0, c->input(0)); 45 | return Status::OK(); 46 | }); 47 | 48 | float randomf(){ 49 | return (rand()+0.5)/(RAND_MAX+1.0); 50 | } 51 | static double get_time(){ 52 | timespec tp; 53 | clock_gettime(CLOCK_MONOTONIC,&tp); 54 | return tp.tv_sec+tp.tv_nsec*1e-9; 55 | } 56 | 57 | // Find three nearest neigbors with square distance 58 | // input: xyz1 (b,n,3), xyz2(b,m,3) 59 | // output: dist (b,n,3), idx (b,n,3) 60 | void threenn_cpu(int b, int n, int m, const float *xyz1, const float *xyz2, float *dist, int *idx) { 61 | for (int i=0;iinput(0); 163 | OP_REQUIRES(context, xyz1_tensor.dims()==3 && xyz1_tensor.shape().dim_size(2)==3, errors::InvalidArgument("ThreeNN expects (b,n,3) xyz1 shape.")); 164 | int b = xyz1_tensor.shape().dim_size(0); 165 | int n = xyz1_tensor.shape().dim_size(1); 166 | 167 | const Tensor& xyz2_tensor = context->input(1); 168 | OP_REQUIRES(context, xyz2_tensor.dims()==3 && xyz2_tensor.shape().dim_size(2)==3, errors::InvalidArgument("ThreeNN expects (b,m,3) xyz2 shape.")); 169 | int m = xyz2_tensor.shape().dim_size(1); 170 | 171 | Tensor *dist_tensor = nullptr; 172 | OP_REQUIRES_OK(context, context->allocate_output(0, TensorShape{b,n,3}, &dist_tensor)); 173 | Tensor *idx_tensor = nullptr; 174 | OP_REQUIRES_OK(context, context->allocate_output(1, TensorShape{b,n,3}, &idx_tensor)); 175 | 176 | auto xyz1_flat = xyz1_tensor.flat(); 177 | const float *xyz1 = &(xyz1_flat(0)); 178 | auto xyz2_flat = xyz2_tensor.flat(); 179 | const float *xyz2 = &(xyz2_flat(0)); 180 | auto dist_flat = dist_tensor->flat(); 181 | float *dist = &(dist_flat(0)); 182 | auto idx_flat = idx_tensor->flat(); 183 | int *idx = &(idx_flat(0)); 184 | threenn_cpu(b,n,m,xyz1,xyz2,dist,idx); 185 | } 186 | }; 187 | REGISTER_KERNEL_BUILDER(Name("ThreeNN").Device(DEVICE_CPU), ThreeNNOp); 188 | 189 | 190 | 191 | class ThreeInterpolateOp: public OpKernel{ 192 | public: 193 | explicit ThreeInterpolateOp(OpKernelConstruction * context):OpKernel(context){} 194 | 195 | void Compute(OpKernelContext * context) override { 196 | const Tensor& points_tensor=context->input(0); 197 | OP_REQUIRES(context, points_tensor.dims()==3, errors::InvalidArgument("ThreeInterpolate expects (b,m,c) points shape")); 198 | int b = points_tensor.shape().dim_size(0); 199 | int m = points_tensor.shape().dim_size(1); 200 | int c = points_tensor.shape().dim_size(2); 201 | 202 | const Tensor& idx_tensor=context->input(1); 203 | OP_REQUIRES(context,idx_tensor.dims()==3 && idx_tensor.shape().dim_size(0)==b && idx_tensor.shape().dim_size(2)==3, errors::InvalidArgument("ThreeInterpolate expects (b,n,3) idx shape")); 204 | int n = idx_tensor.shape().dim_size(1); 205 | const Tensor& weight_tensor=context->input(2); 206 | OP_REQUIRES(context,weight_tensor.dims()==3 && weight_tensor.shape().dim_size(0)==b && weight_tensor.shape().dim_size(1)==n && weight_tensor.shape().dim_size(2)==3, errors::InvalidArgument("ThreeInterpolate expects (b,n,3) weight shape")); 207 | 208 | Tensor * out_tensor = nullptr; 209 | OP_REQUIRES_OK(context, context->allocate_output(0,TensorShape{b,n,c}, &out_tensor)); 210 | 211 | auto points_flat = points_tensor.flat(); 212 | const float *points = &(points_flat(0)); 213 | auto idx_flat = idx_tensor.flat(); 214 | const int *idx = &(idx_flat(0)); 215 | auto weight_flat = weight_tensor.flat(); 216 | const float *weight = &(weight_flat(0)); 217 | auto out_flat = out_tensor->flat(); 218 | float *out = &(out_flat(0)); 219 | threeinterpolate_cpu(b,m,c,n,points,idx,weight,out); 220 | } 221 | }; 222 | REGISTER_KERNEL_BUILDER(Name("ThreeInterpolate").Device(DEVICE_CPU),ThreeInterpolateOp); 223 | 224 | 225 | class ThreeInterpolateGradOp: public OpKernel{ 226 | public: 227 | explicit ThreeInterpolateGradOp(OpKernelConstruction * context):OpKernel(context){} 228 | 229 | void Compute(OpKernelContext * context) override { 230 | const Tensor& points_tensor=context->input(0); 231 | OP_REQUIRES(context, points_tensor.dims()==3, errors::InvalidArgument("ThreeInterpolateGrad expects (b,m,c) points shape")); 232 | int b = points_tensor.shape().dim_size(0); 233 | int m = points_tensor.shape().dim_size(1); 234 | int c = points_tensor.shape().dim_size(2); 235 | 236 | const Tensor& idx_tensor=context->input(1); 237 | OP_REQUIRES(context,idx_tensor.dims()==3 && idx_tensor.shape().dim_size(0)==b, errors::InvalidArgument("ThreeInterpolateGrad expects (b,n,3) idx shape")); 238 | int n = idx_tensor.shape().dim_size(1); 239 | const Tensor& weight_tensor=context->input(2); 240 | OP_REQUIRES(context,weight_tensor.dims()==3 && weight_tensor.shape().dim_size(0)==b && weight_tensor.shape().dim_size(1)==n && weight_tensor.shape().dim_size(2)==3, errors::InvalidArgument("ThreeInterpolateGrad expects (b,n,3) weight shape")); 241 | 242 | const Tensor& grad_out_tensor=context->input(3); 243 | OP_REQUIRES(context,grad_out_tensor.dims()==3 && grad_out_tensor.shape().dim_size(0)==b && grad_out_tensor.shape().dim_size(1)==n && grad_out_tensor.shape().dim_size(2)==c, errors::InvalidArgument("ThreeInterpolateGrad expects (b,n,c) grad_out shape")); 244 | 245 | Tensor * grad_points_tensor = nullptr; 246 | OP_REQUIRES_OK(context, context->allocate_output(0,TensorShape{b,m,c}, &grad_points_tensor)); 247 | 248 | auto points_flat = points_tensor.flat(); 249 | const float *points = &(points_flat(0)); 250 | auto idx_flat = idx_tensor.flat(); 251 | const int *idx = &(idx_flat(0)); 252 | auto weight_flat = weight_tensor.flat(); 253 | const float *weight = &(weight_flat(0)); 254 | auto grad_out_flat = grad_out_tensor.flat(); 255 | const float *grad_out = &(grad_out_flat(0)); 256 | auto grad_points_flat = grad_points_tensor->flat(); 257 | float *grad_points = &(grad_points_flat(0)); 258 | memset(grad_points, 0, sizeof(float)*b*m*c); 259 | threeinterpolate_grad_cpu(b,n,c,m,grad_out,idx,weight,grad_points); 260 | } 261 | }; 262 | REGISTER_KERNEL_BUILDER(Name("ThreeInterpolateGrad").Device(DEVICE_CPU),ThreeInterpolateGradOp); 263 | 264 | 265 | -------------------------------------------------------------------------------- /tf_ops/3d_interpolation/tf_interpolate.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | from tensorflow.python.framework import ops 3 | import sys 4 | import os 5 | 6 | BASE_DIR = os.path.dirname(__file__) 7 | sys.path.append(BASE_DIR) 8 | interpolate_module = tf.load_op_library(os.path.join(BASE_DIR, 'tf_interpolate_so.so')) 9 | 10 | 11 | def three_nn(xyz1, xyz2): 12 | """ 13 | Input: 14 | xyz1: (b,n,3) float32 array, unknown points 15 | xyz2: (b,m,3) float32 array, known points 16 | Output: 17 | dist: (b,n,3) float32 array, distances to known points 18 | idx: (b,n,3) int32 array, indices to known points 19 | """ 20 | return interpolate_module.three_nn(xyz1, xyz2) 21 | 22 | 23 | ops.NoGradient('ThreeNN') 24 | 25 | 26 | def three_interpolate(points, idx, weight): 27 | """ 28 | Input: 29 | points: (b,m,c) float32 array, known points 30 | idx: (b,n,3) int32 array, indices to known points 31 | weight: (b,n,3) float32 array, weights on known points 32 | Output: 33 | out: (b,n,c) float32 array, interpolated point values 34 | """ 35 | return interpolate_module.three_interpolate(points, idx, weight) 36 | 37 | 38 | @tf.RegisterGradient('ThreeInterpolate') 39 | def _three_interpolate_grad(op, grad_out): 40 | points = op.inputs[0] 41 | idx = op.inputs[1] 42 | weight = op.inputs[2] 43 | return [interpolate_module.three_interpolate_grad(points, idx, weight, grad_out), None, None] 44 | 45 | 46 | if __name__ == '__main__': 47 | import numpy as np 48 | import time 49 | 50 | np.random.seed(100) 51 | pts = np.random.random((32, 128, 64)).astype('float32') 52 | tmp1 = np.random.random((32, 512, 3)).astype('float32') 53 | tmp2 = np.random.random((32, 128, 3)).astype('float32') 54 | with tf.device('/cpu:0'): 55 | points = tf.constant(pts) 56 | xyz1 = tf.constant(tmp1) 57 | xyz2 = tf.constant(tmp2) 58 | dist, idx = three_nn(xyz1, xyz2) 59 | weight = tf.ones_like(dist) / 3.0 60 | interpolated_points = three_interpolate(points, idx, weight) 61 | with tf.Session('') as sess: 62 | now = time.time() 63 | for _ in range(100): 64 | ret = sess.run(interpolated_points) 65 | print(time.time() - now) 66 | print(ret.shape, ret.dtype) 67 | # print ret 68 | -------------------------------------------------------------------------------- /tf_ops/3d_interpolation/tf_interpolate_compile.sh: -------------------------------------------------------------------------------- 1 | # TF1.4 Python 3.5 CUDA 8.0 2 | 3 | #g++ -std=c++11 tf_interpolate.cpp -o tf_interpolate_so.so -shared -fPIC -I /usr/local/lib/python2.7/dist-packages/tensorflow/include -I /usr/local/cuda-8.0/include -I /usr/local/lib/python2.7/dist-packages/tensorflow/include/external/nsync/public -lcudart -L /usr/local/cuda-8.0/lib64/ -L/usr/local/lib/python2.7/dist-packages/tensorflow -ltensorflow_framework -O2 -D_GLIBCXX_USE_CXX11_ABI=0 4 | g++ -std=c++11 tf_interpolate.cpp \ 5 | -o tf_interpolate_so.so \ 6 | -shared \ 7 | -fPIC \ 8 | -I$HOME/anaconda3/envs/tensorflow_1.4/lib/python3.5/site-packages/tensorflow/include \ 9 | -I/usr/local/cuda-8.0/include \ 10 | -I$HOME/anaconda3/envs/tensorflow_1.4/lib/python3.5/site-packages/tensorflow/include/external/nsync/public \ 11 | -lcudart \ 12 | -L/usr/local/cuda-8.0/lib64/ \ 13 | -L$HOME/anaconda3/envs/tensorflow_1.4/lib/python3.5/site-packages/tensorflow \ 14 | -ltensorflow_framework -O2 -D_GLIBCXX_USE_CXX11_ABI=0 -------------------------------------------------------------------------------- /tf_ops/3d_interpolation/tf_interpolate_op_test.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | import numpy as np 3 | from tf_interpolate import three_nn, three_interpolate 4 | 5 | 6 | class GroupPointTest(tf.test.TestCase): 7 | def test(self): 8 | pass 9 | 10 | def test_grad(self): 11 | with self.test_session(): 12 | points = tf.constant(np.random.random((1, 8, 16)).astype('float32')) 13 | print(points) 14 | xyz1 = tf.constant(np.random.random((1, 128, 3)).astype('float32')) 15 | xyz2 = tf.constant(np.random.random((1, 8, 3)).astype('float32')) 16 | dist, idx = three_nn(xyz1, xyz2) 17 | weight = tf.ones_like(dist) / 3.0 18 | interpolated_points = three_interpolate(points, idx, weight) 19 | print(interpolated_points) 20 | err = tf.test.compute_gradient_error(points, (1, 8, 16), interpolated_points, (1, 128, 16)) 21 | print(err) 22 | self.assertLess(err, 1e-4) 23 | 24 | 25 | if __name__ == '__main__': 26 | tf.test.main() 27 | -------------------------------------------------------------------------------- /tf_ops/3d_interpolation/tf_interpolate_so.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dlinzhao/JSNet/8fec285e1ccc12a0c5806f4adc60846420ca46a1/tf_ops/3d_interpolation/tf_interpolate_so.so -------------------------------------------------------------------------------- /tf_ops/3d_interpolation/visu_interpolation.py: -------------------------------------------------------------------------------- 1 | """ Visualize part segmentation """ 2 | import os 3 | import sys 4 | 5 | ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 6 | sys.path.append('/home/rqi/Projects/toolkits/visualization') 7 | from show3d_balls import showpoints 8 | import numpy as np 9 | from tf_interpolate import three_nn, three_interpolate 10 | import tensorflow as tf 11 | 12 | pts2 = np.array([[0, 0, 1], [1, 0, 0], [0, 1, 0], [1, 1, 0]]).astype('float32') 13 | xyz1 = np.random.random((100, 3)).astype('float32') 14 | xyz2 = np.array([[0, 0, 0], [1, 0, 0], [0, 1, 0], [1, 1, 1]]).astype('float32') 15 | 16 | 17 | def fun(xyz1, xyz2, pts2): 18 | with tf.device('/cpu:0'): 19 | points = tf.constant(np.expand_dims(pts2, 0)) 20 | xyz1 = tf.constant(np.expand_dims(xyz1, 0)) 21 | xyz2 = tf.constant(np.expand_dims(xyz2, 0)) 22 | dist, idx = three_nn(xyz1, xyz2) 23 | # weight = tf.ones_like(dist)/3.0 24 | dist = tf.maximum(dist, 1e-10) 25 | norm = tf.reduce_sum((1.0 / dist), axis=2, keep_dims=True) 26 | norm = tf.tile(norm, [1, 1, 3]) 27 | print(norm) 28 | weight = (1.0 / dist) / norm 29 | interpolated_points = three_interpolate(points, idx, weight) 30 | with tf.Session('') as sess: 31 | tmp, pts1, d, w = sess.run([xyz1, interpolated_points, dist, weight]) 32 | # print w 33 | pts1 = pts1.squeeze() 34 | return pts1 35 | 36 | 37 | pts1 = fun(xyz1, xyz2, pts2) 38 | all_pts = np.zeros((104, 3)) 39 | all_pts[0:100, :] = pts1 40 | all_pts[100:, :] = pts2 41 | all_xyz = np.zeros((104, 3)) 42 | all_xyz[0:100, :] = xyz1 43 | all_xyz[100:, :] = xyz2 44 | showpoints(xyz2, pts2, ballradius=8) 45 | showpoints(xyz1, pts1, ballradius=8) 46 | showpoints(all_xyz, all_pts, ballradius=8) 47 | -------------------------------------------------------------------------------- /tf_ops/grouping/test/compile.sh: -------------------------------------------------------------------------------- 1 | g++ query_ball_point.cpp -o query_ball_point 2 | nvcc query_ball_point.cu -o query_ball_point_cuda 3 | nvcc query_ball_point_block.cu -o query_ball_point_block 4 | nvcc query_ball_point_grid.cu -o query_ball_point_grid 5 | g++ -Wall selection_sort.cpp -o selection_sort 6 | nvcc selection_sort.cu -o selection_sort_cuda 7 | -------------------------------------------------------------------------------- /tf_ops/grouping/test/query_ball_point.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include // memset 4 | #include // rand, RAND_MAX 5 | #include // sqrtf 6 | #include 7 | #include 8 | using namespace std; 9 | float randomf(){ 10 | return (rand()+0.5)/(RAND_MAX+1.0); 11 | } 12 | static double get_time(){ 13 | timespec tp; 14 | clock_gettime(CLOCK_MONOTONIC,&tp); 15 | return tp.tv_sec+tp.tv_nsec*1e-9; 16 | } 17 | // input: radius (1), nsample (1), xyz1 (b,n,3), xyz2 (b,m,3) 18 | // output: idx (b,m,nsample) 19 | void query_ball_point_cpu(int b, int n, int m, float radius, int nsample, const float *xyz1, const float *xyz2, int *idx) { 20 | for (int i=0;i 2 | #include 3 | #include // memset 4 | #include // rand, RAND_MAX 5 | #include // sqrtf 6 | #include 7 | #include 8 | using namespace std; 9 | float randomf(){ 10 | return (rand()+0.5)/(RAND_MAX+1.0); 11 | } 12 | static double get_time(){ 13 | timespec tp; 14 | clock_gettime(CLOCK_MONOTONIC,&tp); 15 | return tp.tv_sec+tp.tv_nsec*1e-9; 16 | } 17 | // input: radius (1), nsample (1), xyz1 (b,n,3), xyz2 (b,m,3) 18 | // output: idx (b,m,nsample) 19 | __global__ void query_ball_point_gpu(int b, int n, int m, float radius, int nsample, const float *xyz1, const float *xyz2, int *idx) { 20 | for (int i=0;i>>(b,n,m,radius,nsample,xyz1,xyz2,idx); 113 | cudaDeviceSynchronize(); 114 | printf("query_ball_point gpu time %f\n",get_time()-t0); 115 | 116 | t0=get_time(); 117 | group_point_gpu<<<1,1>>>(b,n,c,m,nsample,points,idx,out); 118 | cudaDeviceSynchronize(); 119 | printf("grou_point gpu time %f\n",get_time()-t0); 120 | 121 | t0=get_time(); 122 | group_point_grad_gpu<<<1,1>>>(b,n,c,m,nsample,grad_out,idx,grad_points); 123 | cudaDeviceSynchronize(); 124 | printf("grou_point_grad gpu time %f\n",get_time()-t0); 125 | 126 | cudaFree(xyz1); 127 | cudaFree(xyz2); 128 | cudaFree(points); 129 | cudaFree(idx); 130 | cudaFree(out); 131 | cudaFree(grad_out); 132 | cudaFree(grad_points); 133 | return 0; 134 | } 135 | -------------------------------------------------------------------------------- /tf_ops/grouping/test/query_ball_point_block.cu: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include // memset 4 | #include // rand, RAND_MAX 5 | #include // sqrtf 6 | #include 7 | #include 8 | using namespace std; 9 | float randomf(){ 10 | return (rand()+0.5)/(RAND_MAX+1.0); 11 | } 12 | static double get_time(){ 13 | timespec tp; 14 | clock_gettime(CLOCK_MONOTONIC,&tp); 15 | return tp.tv_sec+tp.tv_nsec*1e-9; 16 | } 17 | // input: radius (1), nsample (1), xyz1 (b,n,3), xyz2 (b,m,3) 18 | // output: idx (b,m,nsample) 19 | __global__ void query_ball_point_gpu(int b, int n, int m, float radius, int nsample, const float *xyz1, const float *xyz2, int *idx) { 20 | int index = threadIdx.x; 21 | xyz1 += n*3*index; 22 | xyz2 += m*3*index; 23 | idx += m*nsample*index; 24 | 25 | for (int j=0;j>>(b,n,m,radius,nsample,xyz1,xyz2,idx); 113 | cudaDeviceSynchronize(); 114 | printf("query_ball_point gpu time %f\n",get_time()-t0); 115 | 116 | t0=get_time(); 117 | group_point_gpu<<<1,b>>>(b,n,c,m,nsample,points,idx,out); 118 | cudaDeviceSynchronize(); 119 | printf("grou_point gpu time %f\n",get_time()-t0); 120 | 121 | t0=get_time(); 122 | group_point_grad_gpu<<<1,b>>>(b,n,c,m,nsample,grad_out,idx,grad_points); 123 | cudaDeviceSynchronize(); 124 | printf("grou_point_grad gpu time %f\n",get_time()-t0); 125 | 126 | cudaFree(xyz1); 127 | cudaFree(xyz2); 128 | cudaFree(points); 129 | cudaFree(idx); 130 | cudaFree(out); 131 | cudaFree(grad_out); 132 | cudaFree(grad_points); 133 | return 0; 134 | } 135 | -------------------------------------------------------------------------------- /tf_ops/grouping/test/query_ball_point_grid.cu: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include // memset 4 | #include // rand, RAND_MAX 5 | #include // sqrtf 6 | #include 7 | #include 8 | using namespace std; 9 | float randomf(){ 10 | return (rand()+0.5)/(RAND_MAX+1.0); 11 | } 12 | static double get_time(){ 13 | timespec tp; 14 | clock_gettime(CLOCK_MONOTONIC,&tp); 15 | return tp.tv_sec+tp.tv_nsec*1e-9; 16 | } 17 | // input: radius (1), nsample (1), xyz1 (b,n,3), xyz2 (b,m,3) 18 | // output: idx (b,m,nsample) 19 | __global__ void query_ball_point_gpu(int b, int n, int m, float radius, int nsample, const float *xyz1, const float *xyz2, int *idx) { 20 | int batch_index = blockIdx.x; 21 | xyz1 += n*3*batch_index; 22 | xyz2 += m*3*batch_index; 23 | idx += m*nsample*batch_index; 24 | 25 | int index = threadIdx.x; 26 | int stride = blockDim.x; 27 | 28 | for (int j=index;j>>(b,n,m,radius,nsample,xyz1,xyz2,idx); 123 | cudaDeviceSynchronize(); 124 | printf("query_ball_point gpu time %f\n",get_time()-t0); 125 | 126 | t0=get_time(); 127 | group_point_gpu<<>>(b,n,c,m,nsample,points,idx,out); 128 | cudaDeviceSynchronize(); 129 | printf("grou_point gpu time %f\n",get_time()-t0); 130 | 131 | t0=get_time(); 132 | group_point_grad_gpu<<>>(b,n,c,m,nsample,grad_out,idx,grad_points); 133 | cudaDeviceSynchronize(); 134 | printf("grou_point_grad gpu time %f\n",get_time()-t0); 135 | 136 | cudaFree(xyz1); 137 | cudaFree(xyz2); 138 | cudaFree(points); 139 | cudaFree(idx); 140 | cudaFree(out); 141 | cudaFree(grad_out); 142 | cudaFree(grad_points); 143 | return 0; 144 | } 145 | -------------------------------------------------------------------------------- /tf_ops/grouping/test/selection_sort.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include // memset 4 | #include // rand, RAND_MAX 5 | #include // sqrtf 6 | #include 7 | #include 8 | using namespace std; 9 | float randomf(){ 10 | return (rand()+0.5)/(RAND_MAX+1.0); 11 | } 12 | static double get_time(){ 13 | timespec tp; 14 | clock_gettime(CLOCK_MONOTONIC,&tp); 15 | return tp.tv_sec+tp.tv_nsec*1e-9; 16 | } 17 | 18 | // input: k (1), distance matrix dist (b,m,n) 19 | // output: idx (b,m,n), val (b,m,n) 20 | void selection_sort_cpu(int b, int n, int m, int k, const float *dist, int *idx, float *val) { 21 | float *p_dist; 22 | float tmp; 23 | int tmpi; 24 | for (int i=0;i 2 | #include 3 | #include // memset 4 | #include // rand, RAND_MAX 5 | #include // sqrtf 6 | #include 7 | #include 8 | using namespace std; 9 | float randomf(){ 10 | return (rand()+0.5)/(RAND_MAX+1.0); 11 | } 12 | static double get_time(){ 13 | timespec tp; 14 | clock_gettime(CLOCK_MONOTONIC,&tp); 15 | return tp.tv_sec+tp.tv_nsec*1e-9; 16 | } 17 | 18 | // input: k (1), distance matrix dist (b,m,n) 19 | // output: idx (b,m,k), val (b,m,k) 20 | __global__ void selection_sort_gpu(int b, int n, int m, int k, float *dist, int *idx, float *val) { 21 | int batch_index = blockIdx.x; 22 | dist+=m*n*batch_index; 23 | idx+=m*k*batch_index; 24 | val+=m*k*batch_index; 25 | 26 | int index = threadIdx.x; 27 | int stride = blockDim.x; 28 | 29 | float *p_dist; 30 | for (int j=index;j>>(b,n,m,k,dist,idx,val); 68 | cudaDeviceSynchronize(); 69 | printf("selection sort cpu time %f\n",get_time()-t0); 70 | 71 | return 0; 72 | } 73 | -------------------------------------------------------------------------------- /tf_ops/grouping/test/selection_sort_const.cu: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include // memset 4 | #include // rand, RAND_MAX 5 | #include // sqrtf 6 | #include 7 | #include 8 | using namespace std; 9 | float randomf(){ 10 | return (rand()+0.5)/(RAND_MAX+1.0); 11 | } 12 | static double get_time(){ 13 | timespec tp; 14 | clock_gettime(CLOCK_MONOTONIC,&tp); 15 | return tp.tv_sec+tp.tv_nsec*1e-9; 16 | } 17 | 18 | // input: k (1), distance matrix dist (b,m,n) 19 | // output: idx (b,m,n), dist_out (b,m,n) 20 | __global__ void selection_sort_gpu(int b, int n, int m, int k, const float *dist, int *outi, float *out) { 21 | int batch_index = blockIdx.x; 22 | dist+=m*n*batch_index; 23 | outi+=m*n*batch_index; 24 | out+=m*n*batch_index; 25 | 26 | int index = threadIdx.x; 27 | int stride = blockDim.x; 28 | 29 | // copy from dist to dist_out 30 | for (int j=index;j>>(b,n,m,k,dist,idx,dist_out); 84 | cudaDeviceSynchronize(); 85 | printf("selection sort cpu time %f\n",get_time()-t0); 86 | 87 | //for (int i=0;i 2 | #include 3 | #include // memset 4 | #include // rand, RAND_MAX 5 | #include // sqrtf 6 | #include "tensorflow/core/framework/op.h" 7 | #include "tensorflow/core/framework/op_kernel.h" 8 | #include "tensorflow/core/framework/shape_inference.h" 9 | #include "tensorflow/core/framework/common_shape_fns.h" 10 | #include 11 | using namespace tensorflow; 12 | 13 | REGISTER_OP("QueryBallPoint") 14 | .Attr("radius: float") 15 | .Attr("nsample: int") 16 | .Input("xyz1: float32") 17 | .Input("xyz2: float32") 18 | .Output("idx: int32") 19 | .Output("pts_cnt: int32") 20 | .SetShapeFn([](::tensorflow::shape_inference::InferenceContext* c) { 21 | ::tensorflow::shape_inference::ShapeHandle dims2; // batch_size * npoint * 3 22 | c->WithRank(c->input(1), 3, &dims2); 23 | int nsample; 24 | TF_RETURN_IF_ERROR(c->GetAttr("nsample", &nsample)); 25 | ::tensorflow::shape_inference::ShapeHandle output1 = c->MakeShape({c->Dim(dims2, 0), c->Dim(dims2, 1), nsample}); 26 | c->set_output(0, output1); 27 | ::tensorflow::shape_inference::ShapeHandle output2 = c->MakeShape({c->Dim(dims2, 0), c->Dim(dims2, 1)}); 28 | c->set_output(1, output2); 29 | return Status::OK(); 30 | }); 31 | REGISTER_OP("SelectionSort") 32 | .Attr("k: int") 33 | .Input("dist: float32") 34 | .Output("outi: int32") 35 | .Output("out: float32") 36 | .SetShapeFn([](::tensorflow::shape_inference::InferenceContext* c) { 37 | c->set_output(0, c->input(0)); 38 | c->set_output(1, c->input(0)); 39 | return Status::OK(); 40 | }); 41 | REGISTER_OP("GroupPoint") 42 | .Input("points: float32") 43 | .Input("idx: int32") 44 | .Output("out: float32") 45 | .SetShapeFn([](::tensorflow::shape_inference::InferenceContext* c) { 46 | ::tensorflow::shape_inference::ShapeHandle dims1; // batch_size * ndataset * channels 47 | c->WithRank(c->input(0), 3, &dims1); 48 | ::tensorflow::shape_inference::ShapeHandle dims2; // batch_size * npoints * nsample 49 | c->WithRank(c->input(1), 3, &dims2); 50 | // batch_size * npoints * nsample * channels 51 | ::tensorflow::shape_inference::ShapeHandle output = c->MakeShape({c->Dim(dims2, 0), c->Dim(dims2, 1), c->Dim(dims2, 2), c->Dim(dims1, 2)}); 52 | c->set_output(0, output); 53 | return Status::OK(); 54 | }); 55 | REGISTER_OP("GroupPointGrad") 56 | .Input("points: float32") 57 | .Input("idx: int32") 58 | .Input("grad_out: float32") 59 | .Output("grad_points: float32") 60 | .SetShapeFn([](::tensorflow::shape_inference::InferenceContext* c) { 61 | c->set_output(0, c->input(0)); 62 | return Status::OK(); 63 | }); 64 | 65 | 66 | void queryBallPointLauncher(int b, int n, int m, float radius, int nsample, const float *xyz1, const float *xyz2, int *idx, int *pts_cnt); 67 | class QueryBallPointGpuOp : public OpKernel { 68 | public: 69 | explicit QueryBallPointGpuOp(OpKernelConstruction* context) : OpKernel(context) { 70 | OP_REQUIRES_OK(context, context->GetAttr("radius", &radius_)); 71 | OP_REQUIRES(context, radius_ > 0, errors::InvalidArgument("QueryBallPoint expects positive radius")); 72 | 73 | OP_REQUIRES_OK(context, context->GetAttr("nsample", &nsample_)); 74 | OP_REQUIRES(context, nsample_ > 0, errors::InvalidArgument("QueryBallPoint expects positive nsample")); 75 | } 76 | 77 | void Compute(OpKernelContext* context) override { 78 | const Tensor& xyz1_tensor = context->input(0); 79 | OP_REQUIRES(context, xyz1_tensor.dims()==3 && xyz1_tensor.shape().dim_size(2)==3, errors::InvalidArgument("QueryBallPoint expects (batch_size, ndataset, 3) xyz1 shape.")); 80 | int b = xyz1_tensor.shape().dim_size(0); 81 | int n = xyz1_tensor.shape().dim_size(1); 82 | 83 | const Tensor& xyz2_tensor = context->input(1); 84 | OP_REQUIRES(context, xyz2_tensor.dims()==3 && xyz2_tensor.shape().dim_size(2)==3, errors::InvalidArgument("QueryBallPoint expects (batch_size, npoint, 3) xyz2 shape.")); 85 | int m = xyz2_tensor.shape().dim_size(1); 86 | 87 | Tensor *idx_tensor = nullptr; 88 | OP_REQUIRES_OK(context, context->allocate_output(0, TensorShape{b,m,nsample_}, &idx_tensor)); 89 | Tensor *pts_cnt_tensor = nullptr; 90 | OP_REQUIRES_OK(context, context->allocate_output(1, TensorShape{b,m}, &pts_cnt_tensor)); 91 | 92 | auto xyz1_flat = xyz1_tensor.flat(); 93 | const float *xyz1 = &(xyz1_flat(0)); 94 | auto xyz2_flat = xyz2_tensor.flat(); 95 | const float *xyz2 = &(xyz2_flat(0)); 96 | auto idx_flat = idx_tensor->flat(); 97 | int *idx = &(idx_flat(0)); 98 | auto pts_cnt_flat = pts_cnt_tensor->flat(); 99 | int *pts_cnt = &(pts_cnt_flat(0)); 100 | queryBallPointLauncher(b,n,m,radius_,nsample_,xyz1,xyz2,idx,pts_cnt); 101 | } 102 | private: 103 | float radius_; 104 | int nsample_; 105 | }; 106 | REGISTER_KERNEL_BUILDER(Name("QueryBallPoint").Device(DEVICE_GPU), QueryBallPointGpuOp); 107 | 108 | void selectionSortLauncher(int b, int n, int m, int k, const float *dist, int *outi, float *out); 109 | class SelectionSortGpuOp : public OpKernel { 110 | public: 111 | explicit SelectionSortGpuOp(OpKernelConstruction* context) : OpKernel(context) { 112 | OP_REQUIRES_OK(context, context->GetAttr("k", &k_)); 113 | OP_REQUIRES(context, k_ > 0, errors::InvalidArgument("SelectionSort expects positive k")); 114 | } 115 | 116 | void Compute(OpKernelContext* context) override { 117 | const Tensor& dist_tensor = context->input(0); 118 | OP_REQUIRES(context, dist_tensor.dims()==3, errors::InvalidArgument("SelectionSort expects (b,m,n) dist shape.")); 119 | int b = dist_tensor.shape().dim_size(0); 120 | int m = dist_tensor.shape().dim_size(1); 121 | int n = dist_tensor.shape().dim_size(2); 122 | 123 | Tensor *outi_tensor = nullptr; 124 | OP_REQUIRES_OK(context, context->allocate_output(0, TensorShape{b,m,n}, &outi_tensor)); 125 | Tensor *out_tensor = nullptr; 126 | OP_REQUIRES_OK(context, context->allocate_output(1, TensorShape{b,m,n}, &out_tensor)); 127 | 128 | auto dist_flat = dist_tensor.flat(); 129 | const float *dist = &(dist_flat(0)); 130 | auto outi_flat = outi_tensor->flat(); 131 | int *outi = &(outi_flat(0)); 132 | auto out_flat = out_tensor->flat(); 133 | float *out = &(out_flat(0)); 134 | selectionSortLauncher(b,n,m,k_,dist,outi,out); 135 | } 136 | private: 137 | int k_; 138 | }; 139 | REGISTER_KERNEL_BUILDER(Name("SelectionSort").Device(DEVICE_GPU), SelectionSortGpuOp); 140 | 141 | 142 | void groupPointLauncher(int b, int n, int c, int m, int nsample, const float *points, const int *idx, float *out); 143 | class GroupPointGpuOp: public OpKernel{ 144 | public: 145 | explicit GroupPointGpuOp(OpKernelConstruction * context):OpKernel(context){} 146 | 147 | void Compute(OpKernelContext * context) override { 148 | const Tensor& points_tensor=context->input(0); 149 | OP_REQUIRES(context, points_tensor.dims()==3, errors::InvalidArgument("GroupPoint expects (batch_size, num_points, channel) points shape")); 150 | int b = points_tensor.shape().dim_size(0); 151 | int n = points_tensor.shape().dim_size(1); 152 | int c = points_tensor.shape().dim_size(2); 153 | 154 | const Tensor& idx_tensor=context->input(1); 155 | OP_REQUIRES(context,idx_tensor.dims()==3 && idx_tensor.shape().dim_size(0)==b, errors::InvalidArgument("GroupPoint expects (batch_size, npoints, nsample) idx shape")); 156 | int m = idx_tensor.shape().dim_size(1); 157 | int nsample = idx_tensor.shape().dim_size(2); 158 | 159 | Tensor * out_tensor = nullptr; 160 | OP_REQUIRES_OK(context, context->allocate_output(0,TensorShape{b,m,nsample,c}, &out_tensor)); 161 | 162 | auto points_flat = points_tensor.flat(); 163 | const float *points = &(points_flat(0)); 164 | auto idx_flat = idx_tensor.flat(); 165 | const int *idx = &(idx_flat(0)); 166 | auto out_flat = out_tensor->flat(); 167 | float *out = &(out_flat(0)); 168 | groupPointLauncher(b,n,c,m,nsample,points,idx,out); 169 | } 170 | }; 171 | REGISTER_KERNEL_BUILDER(Name("GroupPoint").Device(DEVICE_GPU),GroupPointGpuOp); 172 | 173 | void groupPointGradLauncher(int b, int n, int c, int m, int nsample, const float *grad_out, const int *idx, float *grad_points); 174 | class GroupPointGradGpuOp: public OpKernel{ 175 | public: 176 | explicit GroupPointGradGpuOp(OpKernelConstruction * context):OpKernel(context){} 177 | 178 | void Compute(OpKernelContext * context) override { 179 | const Tensor& points_tensor=context->input(0); 180 | OP_REQUIRES(context, points_tensor.dims()==3, errors::InvalidArgument("GroupPointGrad expects (batch_size, num_points, channel) points shape")); 181 | int b = points_tensor.shape().dim_size(0); 182 | int n = points_tensor.shape().dim_size(1); 183 | int c = points_tensor.shape().dim_size(2); 184 | 185 | const Tensor& idx_tensor=context->input(1); 186 | OP_REQUIRES(context,idx_tensor.dims()==3 && idx_tensor.shape().dim_size(0)==b, errors::InvalidArgument("GroupPointGrad expects (batch_size, npoints, nsample) idx shape")); 187 | int m = idx_tensor.shape().dim_size(1); 188 | int nsample = idx_tensor.shape().dim_size(2); 189 | 190 | const Tensor& grad_out_tensor=context->input(2); 191 | OP_REQUIRES(context,grad_out_tensor.dims()==4 && grad_out_tensor.shape().dim_size(0)==b && grad_out_tensor.shape().dim_size(1)==m && grad_out_tensor.shape().dim_size(2)==nsample && grad_out_tensor.shape().dim_size(3)==c, errors::InvalidArgument("GroupPointGrad expects (batch_size, npoints, nsample, channel) grad_out shape")); 192 | 193 | Tensor * grad_points_tensor = nullptr; 194 | OP_REQUIRES_OK(context, context->allocate_output(0,TensorShape{b,n,c}, &grad_points_tensor)); 195 | 196 | auto points_flat = points_tensor.flat(); 197 | const float *points = &(points_flat(0)); 198 | auto idx_flat = idx_tensor.flat(); 199 | const int *idx = &(idx_flat(0)); 200 | auto grad_out_flat = grad_out_tensor.flat(); 201 | const float *grad_out = &(grad_out_flat(0)); 202 | auto grad_points_flat = grad_points_tensor->flat(); 203 | float *grad_points = &(grad_points_flat(0)); 204 | cudaMemset(grad_points, 0, sizeof(float)*b*n*c); 205 | groupPointGradLauncher(b,n,c,m,nsample,grad_out,idx,grad_points); 206 | } 207 | }; 208 | REGISTER_KERNEL_BUILDER(Name("GroupPointGrad").Device(DEVICE_GPU),GroupPointGradGpuOp); 209 | 210 | 211 | -------------------------------------------------------------------------------- /tf_ops/grouping/tf_grouping.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | from tensorflow.python.framework import ops 3 | import sys 4 | import os 5 | 6 | BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 7 | sys.path.append(BASE_DIR) 8 | grouping_module = tf.load_op_library(os.path.join(BASE_DIR, 'tf_grouping_so.so')) 9 | 10 | 11 | def query_ball_point(radius, nsample, xyz1, xyz2): 12 | """ 13 | Input: 14 | radius: float32, ball search radius 15 | nsample: int32, number of points selected in each ball region 16 | xyz1: (batch_size, ndataset, 3) float32 array, input points 17 | xyz2: (batch_size, npoint, 3) float32 array, query points 18 | Output: 19 | idx: (batch_size, npoint, nsample) int32 array, indices to input points 20 | pts_cnt: (batch_size, npoint) int32 array, number of unique points in each local region 21 | """ 22 | # return grouping_module.query_ball_point(radius, nsample, xyz1, xyz2) 23 | return grouping_module.query_ball_point(xyz1, xyz2, radius, nsample) 24 | 25 | 26 | ops.NoGradient('QueryBallPoint') 27 | 28 | 29 | def select_top_k(k, dist): 30 | """ 31 | Input: 32 | k: int32, number of k SMALLEST elements selected 33 | dist: (b,m,n) float32 array, distance matrix, m query points, n dataset points 34 | Output: 35 | idx: (b,m,n) int32 array, first k in n are indices to the top k 36 | dist_out: (b,m,n) float32 array, first k in n are the top k 37 | """ 38 | return grouping_module.selection_sort(dist, k) 39 | 40 | 41 | ops.NoGradient('SelectionSort') 42 | 43 | 44 | def group_point(points, idx): 45 | """ 46 | Input: 47 | points: (batch_size, ndataset, channel) float32 array, points to sample from 48 | idx: (batch_size, npoint, nsample) int32 array, indices to points 49 | Output: 50 | out: (batch_size, npoint, nsample, channel) float32 array, values sampled from points 51 | """ 52 | return grouping_module.group_point(points, idx) 53 | 54 | 55 | @tf.RegisterGradient('GroupPoint') 56 | def _group_point_grad(op, grad_out): 57 | points = op.inputs[0] 58 | idx = op.inputs[1] 59 | return [grouping_module.group_point_grad(points, idx, grad_out), None] 60 | 61 | 62 | def knn_point(k, xyz1, xyz2): 63 | """ 64 | Input: 65 | k: int32, number of k in k-nn search 66 | xyz1: (batch_size, ndataset, c) float32 array, input points 67 | xyz2: (batch_size, npoint, c) float32 array, query points 68 | Output: 69 | val: (batch_size, npoint, k) float32 array, L2 distances 70 | idx: (batch_size, npoint, k) int32 array, indices to input points 71 | """ 72 | b = xyz1.get_shape()[0].value 73 | n = xyz1.get_shape()[1].value 74 | c = xyz1.get_shape()[2].value 75 | m = xyz2.get_shape()[1].value 76 | print(b, n, c, m) 77 | print(xyz1, (b, 1, n, c)) 78 | xyz1 = tf.tile(tf.reshape(xyz1, (b, 1, n, c)), [1, m, 1, 1]) 79 | xyz2 = tf.tile(tf.reshape(xyz2, (b, m, 1, c)), [1, 1, n, 1]) 80 | dist = tf.reduce_sum((xyz1 - xyz2) ** 2, -1) 81 | print(dist, k) 82 | # outi, out = select_top_k(k, dist) 83 | # idx = tf.slice(outi, [0,0,0], [-1,-1,k]) 84 | # val = tf.slice(out, [0,0,0], [-1,-1,k]) 85 | # print idx, val 86 | val, idx = tf.nn.top_k(-dist, k=k) # ONLY SUPPORT CPU 87 | return val, idx 88 | 89 | 90 | if __name__ == '__main__': 91 | knn = True 92 | import numpy as np 93 | import time 94 | 95 | np.random.seed(100) 96 | pts = np.random.random((32, 512, 64)).astype('float32') 97 | tmp1 = np.random.random((32, 512, 3)).astype('float32') 98 | tmp2 = np.random.random((32, 128, 3)).astype('float32') 99 | with tf.device('/gpu:1'): 100 | points = tf.constant(pts) 101 | xyz1 = tf.constant(tmp1) 102 | xyz2 = tf.constant(tmp2) 103 | radius = 0.1 104 | nsample = 64 105 | if knn: 106 | _, idx = knn_point(nsample, xyz1, xyz2) 107 | grouped_points = group_point(points, idx) 108 | else: 109 | idx, _ = query_ball_point(radius, nsample, xyz1, xyz2) 110 | grouped_points = group_point(points, idx) 111 | # grouped_points_grad = tf.ones_like(grouped_points) 112 | # points_grad = tf.gradients(grouped_points, points, grouped_points_grad) 113 | with tf.Session('') as sess: 114 | now = time.time() 115 | for _ in range(100): 116 | ret = sess.run(grouped_points) 117 | print(time.time() - now) 118 | print(ret.shape, ret.dtype) 119 | print(ret) 120 | -------------------------------------------------------------------------------- /tf_ops/grouping/tf_grouping_compile.sh: -------------------------------------------------------------------------------- 1 | #/bin/bash 2 | /usr/local/cuda-8.0/bin/nvcc tf_grouping_g.cu \ 3 | -o tf_grouping_g.cu.o -c -O2 -DGOOGLE_CUDA=1 -x cu -Xcompiler -fPIC 4 | 5 | # TF1.4 Python 3.5 CUDA 8.0 6 | g++ -std=c++11 tf_grouping.cpp \ 7 | tf_grouping_g.cu.o \ 8 | -o tf_grouping_so.so \ 9 | -shared -fPIC \ 10 | -I$HOME/anaconda3/envs/tensorflow_1.4/lib/python3.5/site-packages/tensorflow/include \ 11 | -I/usr/local/cuda-8.0/include \ 12 | -I$HOME/anaconda3/envs/tensorflow_1.4/lib/python3.5/site-packages/tensorflow/include/external/nsync/public \ 13 | -lcudart -L/usr/local/cuda-8.0/lib64/ \ 14 | -L$HOME/anaconda3/envs/tensorflow_1.4/lib/python3.5/site-packages/tensorflow \ 15 | -ltensorflow_framework -O2 -D_GLIBCXX_USE_CXX11_ABI=0 16 | -------------------------------------------------------------------------------- /tf_ops/grouping/tf_grouping_g.cu: -------------------------------------------------------------------------------- 1 | // input: radius (1), nsample (1), xyz1 (b,n,3), xyz2 (b,m,3) 2 | // output: idx (b,m,nsample), pts_cnt (b,m) 3 | __global__ void query_ball_point_gpu(int b, int n, int m, float radius, int nsample, const float *xyz1, const float *xyz2, int *idx, int *pts_cnt) { 4 | int batch_index = blockIdx.x; 5 | xyz1 += n*3*batch_index; 6 | xyz2 += m*3*batch_index; 7 | idx += m*nsample*batch_index; 8 | pts_cnt += m*batch_index; // counting how many unique points selected in local region 9 | 10 | int index = threadIdx.x; 11 | int stride = blockDim.x; 12 | 13 | for (int j=index;j>>(b,n,m,radius,nsample,xyz1,xyz2,idx,pts_cnt); 127 | //cudaDeviceSynchronize(); 128 | } 129 | void selectionSortLauncher(int b, int n, int m, int k, const float *dist, int *outi, float *out) { 130 | selection_sort_gpu<<>>(b,n,m,k,dist,outi,out); 131 | //cudaDeviceSynchronize(); 132 | } 133 | void groupPointLauncher(int b, int n, int c, int m, int nsample, const float *points, const int *idx, float *out){ 134 | group_point_gpu<<>>(b,n,c,m,nsample,points,idx,out); 135 | //cudaDeviceSynchronize(); 136 | } 137 | void groupPointGradLauncher(int b, int n, int c, int m, int nsample, const float *grad_out, const int *idx, float *grad_points){ 138 | group_point_grad_gpu<<>>(b,n,c,m,nsample,grad_out,idx,grad_points); 139 | //group_point_grad_gpu<<<1,1>>>(b,n,c,m,nsample,grad_out,idx,grad_points); 140 | //cudaDeviceSynchronize(); 141 | } 142 | -------------------------------------------------------------------------------- /tf_ops/grouping/tf_grouping_op_test.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | import numpy as np 3 | from tf_grouping import query_ball_point, group_point 4 | 5 | 6 | class GroupPointTest(tf.test.TestCase): 7 | def test(self): 8 | pass 9 | 10 | def test_grad(self): 11 | with tf.device('/gpu:0'): 12 | points = tf.constant(np.random.random((1, 128, 16)).astype('float32')) 13 | print(points) 14 | xyz1 = tf.constant(np.random.random((1, 128, 3)).astype('float32')) 15 | xyz2 = tf.constant(np.random.random((1, 8, 3)).astype('float32')) 16 | radius = 0.3 17 | nsample = 32 18 | idx, pts_cnt = query_ball_point(radius, nsample, xyz1, xyz2) 19 | grouped_points = group_point(points, idx) 20 | print(grouped_points) 21 | 22 | with self.test_session(): 23 | print("---- Going to compute gradient error") 24 | err = tf.test.compute_gradient_error(points, (1, 128, 16), grouped_points, (1, 8, 32, 16)) 25 | print(err) 26 | self.assertLess(err, 1e-4) 27 | 28 | 29 | if __name__ == '__main__': 30 | tf.test.main() 31 | -------------------------------------------------------------------------------- /tf_ops/grouping/tf_grouping_so.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dlinzhao/JSNet/8fec285e1ccc12a0c5806f4adc60846420ca46a1/tf_ops/grouping/tf_grouping_so.so -------------------------------------------------------------------------------- /tf_ops/sampling/tf_sampling.cpp: -------------------------------------------------------------------------------- 1 | /* Furthest point sampling 2 | * Original author: Haoqiang Fan 3 | * Modified by Charles R. Qi 4 | * All Rights Reserved. 2017. 5 | */ 6 | #include "tensorflow/core/framework/op.h" 7 | #include "tensorflow/core/framework/op_kernel.h" 8 | #include "tensorflow/core/framework/shape_inference.h" 9 | #include "tensorflow/core/framework/common_shape_fns.h" 10 | #include 11 | 12 | using namespace tensorflow; 13 | 14 | REGISTER_OP("ProbSample") 15 | .Input("inp: float32") 16 | .Input("inpr: float32") 17 | .Output("out: int32") 18 | .SetShapeFn([](::tensorflow::shape_inference::InferenceContext* c) { 19 | ::tensorflow::shape_inference::ShapeHandle dims1; // batch_size * ncategory 20 | c->WithRank(c->input(0), 2, &dims1); 21 | ::tensorflow::shape_inference::ShapeHandle dims2; // batch_size * npoints 22 | c->WithRank(c->input(1), 2, &dims2); 23 | // batch_size * npoints 24 | ::tensorflow::shape_inference::ShapeHandle output = c->MakeShape({c->Dim(dims2, 0), c->Dim(dims2, 1)}); 25 | c->set_output(0, output); 26 | return Status::OK(); 27 | }); 28 | REGISTER_OP("FarthestPointSample") 29 | .Attr("npoint: int") 30 | .Input("inp: float32") 31 | .Output("out: int32") 32 | .SetShapeFn([](::tensorflow::shape_inference::InferenceContext* c) { 33 | ::tensorflow::shape_inference::ShapeHandle dims1; // batch_size * npoint * 3 34 | c->WithRank(c->input(0), 3, &dims1); 35 | int npoint; 36 | TF_RETURN_IF_ERROR(c->GetAttr("npoint", &npoint)); 37 | ::tensorflow::shape_inference::ShapeHandle output = c->MakeShape({c->Dim(dims1, 0), npoint}); 38 | c->set_output(0, output); 39 | return Status::OK(); 40 | }); 41 | REGISTER_OP("GatherPoint") 42 | .Input("inp: float32") 43 | .Input("idx: int32") 44 | .Output("out: float32") 45 | .SetShapeFn([](::tensorflow::shape_inference::InferenceContext* c) { 46 | ::tensorflow::shape_inference::ShapeHandle dims1; // batch_size * ndataset * 3 47 | c->WithRank(c->input(0), 3, &dims1); 48 | ::tensorflow::shape_inference::ShapeHandle dims2; // batch_size * npoints 49 | c->WithRank(c->input(1), 2, &dims2); 50 | // batch_size * npoints * 3 51 | ::tensorflow::shape_inference::ShapeHandle output = c->MakeShape({c->Dim(dims1, 0), c->Dim(dims2, 1), c->Dim(dims1, 2)}); 52 | c->set_output(0, output); 53 | return Status::OK(); 54 | }); 55 | REGISTER_OP("GatherPointGrad") 56 | .Input("inp: float32") 57 | .Input("idx: int32") 58 | .Input("out_g: float32") 59 | .Output("inp_g: float32") 60 | .SetShapeFn([](::tensorflow::shape_inference::InferenceContext* c) { 61 | c->set_output(0, c->input(0)); 62 | return Status::OK(); 63 | }); 64 | 65 | void probsampleLauncher(int b,int n,int m,const float * inp_p,const float * inp_r,float * temp,int * out); 66 | class ProbSampleGpuOp: public OpKernel{ 67 | public: 68 | explicit ProbSampleGpuOp(OpKernelConstruction* context):OpKernel(context){} 69 | void Compute(OpKernelContext * context)override{ 70 | const Tensor& inp_tensor=context->input(0); 71 | const Tensor& inpr_tensor=context->input(1); 72 | auto inp_flat=inp_tensor.flat(); 73 | auto inpr_flat=inpr_tensor.flat(); 74 | const float * inp=&(inp_flat(0)); 75 | const float * inpr=&(inpr_flat(0)); 76 | OP_REQUIRES(context,inp_tensor.dims()==2,errors::InvalidArgument("ProbSample expects (batch_size,num_choices) inp shape")); 77 | int b=inp_tensor.shape().dim_size(0); 78 | int n=inp_tensor.shape().dim_size(1); 79 | OP_REQUIRES(context,inpr_tensor.dims()==2 && inpr_tensor.shape().dim_size(0)==b,errors::InvalidArgument("ProbSample expects (batch_size,num_points) inpr shape")); 80 | int m=inpr_tensor.shape().dim_size(1); 81 | Tensor * out_tensor=NULL; 82 | OP_REQUIRES_OK(context,context->allocate_output(0,TensorShape{b,m},&out_tensor)); 83 | auto out_flat=out_tensor->flat(); 84 | int * out=&(out_flat(0)); 85 | Tensor temp_tensor; 86 | OP_REQUIRES_OK(context,context->allocate_temp(DataTypeToEnum::value,TensorShape{b,n},&temp_tensor)); 87 | auto temp_flat=temp_tensor.flat(); 88 | float * temp=&(temp_flat(0)); 89 | probsampleLauncher(b,n,m,inp,inpr,temp,out); 90 | } 91 | }; 92 | REGISTER_KERNEL_BUILDER(Name("ProbSample").Device(DEVICE_GPU), ProbSampleGpuOp); 93 | 94 | void farthestpointsamplingLauncher(int b,int n,int m,const float * inp,float * temp,int * out); 95 | class FarthestPointSampleGpuOp: public OpKernel{ 96 | public: 97 | explicit FarthestPointSampleGpuOp(OpKernelConstruction* context):OpKernel(context) { 98 | OP_REQUIRES_OK(context, context->GetAttr("npoint", &npoint_)); 99 | OP_REQUIRES(context, npoint_ > 0, errors::InvalidArgument("FarthestPointSample expects positive npoint")); 100 | } 101 | void Compute(OpKernelContext * context)override{ 102 | int m = npoint_; 103 | 104 | const Tensor& inp_tensor=context->input(0); 105 | OP_REQUIRES(context,inp_tensor.dims()==3 && inp_tensor.shape().dim_size(2)==3,errors::InvalidArgument("FarthestPointSample expects (batch_size,num_points,3) inp shape")); 106 | int b=inp_tensor.shape().dim_size(0); 107 | int n=inp_tensor.shape().dim_size(1); 108 | auto inp_flat=inp_tensor.flat(); 109 | const float * inp=&(inp_flat(0)); 110 | Tensor * out_tensor; 111 | OP_REQUIRES_OK(context,context->allocate_output(0,TensorShape{b,m},&out_tensor)); 112 | auto out_flat=out_tensor->flat(); 113 | int * out=&(out_flat(0)); 114 | Tensor temp_tensor; 115 | OP_REQUIRES_OK(context,context->allocate_temp(DataTypeToEnum::value,TensorShape{32,n},&temp_tensor)); 116 | auto temp_flat=temp_tensor.flat(); 117 | float * temp=&(temp_flat(0)); 118 | farthestpointsamplingLauncher(b,n,m,inp,temp,out); 119 | } 120 | private: 121 | int npoint_; 122 | }; 123 | REGISTER_KERNEL_BUILDER(Name("FarthestPointSample").Device(DEVICE_GPU),FarthestPointSampleGpuOp); 124 | 125 | void gatherpointLauncher(int b,int n,int m,const float * inp,const int * idx,float * out); 126 | class GatherPointGpuOp: public OpKernel{ 127 | public: 128 | explicit GatherPointGpuOp(OpKernelConstruction * context):OpKernel(context){} 129 | void Compute(OpKernelContext * context)override{ 130 | const Tensor& inp_tensor=context->input(0); 131 | OP_REQUIRES(context,inp_tensor.dims()==3 && inp_tensor.shape().dim_size(2)==3,errors::InvalidArgument("GatherPoint expects (batch_size,num_points,3) inp shape")); 132 | int b=inp_tensor.shape().dim_size(0); 133 | int n=inp_tensor.shape().dim_size(1); 134 | const Tensor& idx_tensor=context->input(1); 135 | OP_REQUIRES(context,idx_tensor.dims()==2 && idx_tensor.shape().dim_size(0)==b,errors::InvalidArgument("GatherPoint expects (batch_size,num_result) idx shape")); 136 | int m=idx_tensor.shape().dim_size(1); 137 | auto inp_flat=inp_tensor.flat(); 138 | const float * inp=&(inp_flat(0)); 139 | auto idx_flat=idx_tensor.flat(); 140 | const int * idx=&(idx_flat(0)); 141 | Tensor * out_tensor=NULL; 142 | OP_REQUIRES_OK(context,context->allocate_output(0,TensorShape{b,m,3},&out_tensor)); 143 | auto out_flat=out_tensor->flat(); 144 | float * out=&(out_flat(0)); 145 | gatherpointLauncher(b,n,m,inp,idx,out); 146 | } 147 | }; 148 | REGISTER_KERNEL_BUILDER(Name("GatherPoint").Device(DEVICE_GPU),GatherPointGpuOp); 149 | 150 | void scatteraddpointLauncher(int b,int n,int m,const float * out_g,const int * idx,float * inp_g); 151 | class GatherPointGradGpuOp: public OpKernel{ 152 | public: 153 | explicit GatherPointGradGpuOp(OpKernelConstruction * context):OpKernel(context){} 154 | void Compute(OpKernelContext * context)override{ 155 | const Tensor& inp_tensor=context->input(0); 156 | OP_REQUIRES(context,inp_tensor.dims()==3 && inp_tensor.shape().dim_size(2)==3,errors::InvalidArgument("GatherPointGradGpuOp expects (batch_size,num_points,3) inp")); 157 | int b=inp_tensor.shape().dim_size(0); 158 | int n=inp_tensor.shape().dim_size(1); 159 | const Tensor& idx_tensor=context->input(1); 160 | OP_REQUIRES(context,idx_tensor.dims()==2 && idx_tensor.shape().dim_size(0)==b,errors::InvalidArgument("GatherPointGradGpuOp expects (batch_size,num_result) idx shape")); 161 | int m=idx_tensor.shape().dim_size(1); 162 | auto inp_flat=inp_tensor.flat(); 163 | const float * inp=&(inp_flat(0)); 164 | auto idx_flat=idx_tensor.flat(); 165 | const int * idx=&(idx_flat(0)); 166 | const Tensor& out_g_tensor=context->input(2); 167 | OP_REQUIRES(context,out_g_tensor.dims()==3 && out_g_tensor.shape().dim_size(0)==b && out_g_tensor.shape().dim_size(1)==m && out_g_tensor.shape().dim_size(2)==3,errors::InvalidArgument("GatherPointGradGpuOp expects (batch_size,num_result,3) out_g shape")); 168 | auto out_g_flat=out_g_tensor.flat(); 169 | const float * out_g=&(out_g_flat(0)); 170 | Tensor * inp_g_tensor=NULL; 171 | OP_REQUIRES_OK(context,context->allocate_output(0,TensorShape{b,n,3},&inp_g_tensor)); 172 | auto inp_g_flat=inp_g_tensor->flat(); 173 | float * inp_g=&(inp_g_flat(0)); 174 | cudaMemset(inp_g,0,b*n*3*4); 175 | scatteraddpointLauncher(b,n,m,out_g,idx,inp_g); 176 | } 177 | }; 178 | REGISTER_KERNEL_BUILDER(Name("GatherPointGrad").Device(DEVICE_GPU),GatherPointGradGpuOp); 179 | 180 | -------------------------------------------------------------------------------- /tf_ops/sampling/tf_sampling.py: -------------------------------------------------------------------------------- 1 | ''' Furthest point sampling 2 | Original author: Haoqiang Fan 3 | Modified by Charles R. Qi 4 | All Rights Reserved. 2017. 5 | ''' 6 | import tensorflow as tf 7 | from tensorflow.python.framework import ops 8 | import sys 9 | import os 10 | 11 | BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 12 | sys.path.append(BASE_DIR) 13 | sampling_module = tf.load_op_library(os.path.join(BASE_DIR, 'tf_sampling_so.so')) 14 | 15 | 16 | def prob_sample(inp, inpr): 17 | ''' 18 | input: 19 | batch_size * ncategory float32 20 | batch_size * npoints float32 21 | returns: 22 | batch_size * npoints int32 23 | ''' 24 | return sampling_module.prob_sample(inp, inpr) 25 | 26 | 27 | ops.NoGradient('ProbSample') 28 | 29 | 30 | # TF1.0 API requires set shape in C++ 31 | # @tf.RegisterShape('ProbSample') 32 | # def _prob_sample_shape(op): 33 | # shape1=op.inputs[0].get_shape().with_rank(2) 34 | # shape2=op.inputs[1].get_shape().with_rank(2) 35 | # return [tf.TensorShape([shape2.dims[0],shape2.dims[1]])] 36 | def gather_point(inp, idx): 37 | ''' 38 | input: 39 | batch_size * ndataset * 3 float32 40 | batch_size * npoints int32 41 | returns: 42 | batch_size * npoints * 3 float32 43 | ''' 44 | return sampling_module.gather_point(inp, idx) 45 | 46 | 47 | # @tf.RegisterShape('GatherPoint') 48 | # def _gather_point_shape(op): 49 | # shape1=op.inputs[0].get_shape().with_rank(3) 50 | # shape2=op.inputs[1].get_shape().with_rank(2) 51 | # return [tf.TensorShape([shape1.dims[0],shape2.dims[1],shape1.dims[2]])] 52 | @tf.RegisterGradient('GatherPoint') 53 | def _gather_point_grad(op, out_g): 54 | inp = op.inputs[0] 55 | idx = op.inputs[1] 56 | return [sampling_module.gather_point_grad(inp, idx, out_g), None] 57 | 58 | 59 | def farthest_point_sample(npoint, inp): 60 | ''' 61 | input: 62 | int32 63 | batch_size * ndataset * 3 float32 64 | returns: 65 | batch_size * npoint int32 66 | ''' 67 | return sampling_module.farthest_point_sample(inp, npoint) 68 | 69 | 70 | ops.NoGradient('FarthestPointSample') 71 | 72 | if __name__ == '__main__': 73 | import numpy as np 74 | 75 | np.random.seed(100) 76 | triangles = np.random.rand(1, 5, 3, 3).astype('float32') 77 | with tf.device('/gpu:1'): 78 | inp = tf.constant(triangles) 79 | tria = inp[:, :, 0, :] 80 | trib = inp[:, :, 1, :] 81 | tric = inp[:, :, 2, :] 82 | areas = tf.sqrt(tf.reduce_sum(tf.cross(trib - tria, tric - tria) ** 2, 2) + 1e-9) 83 | randomnumbers = tf.random_uniform((1, 8192)) 84 | triids = prob_sample(areas, randomnumbers) 85 | tria_sample = gather_point(tria, triids) 86 | trib_sample = gather_point(trib, triids) 87 | tric_sample = gather_point(tric, triids) 88 | us = tf.random_uniform((1, 8192)) 89 | vs = tf.random_uniform((1, 8192)) 90 | uplusv = 1 - tf.abs(us + vs - 1) 91 | uminusv = us - vs 92 | us = (uplusv + uminusv) * 0.5 93 | vs = (uplusv - uminusv) * 0.5 94 | pt_sample = tria_sample + (trib_sample - tria_sample) * tf.expand_dims(us, -1) + ( 95 | tric_sample - tria_sample) * tf.expand_dims(vs, -1) 96 | print('pt_sample: ', pt_sample) 97 | reduced_sample = gather_point(pt_sample, farthest_point_sample(1024, pt_sample)) 98 | print(reduced_sample) 99 | with tf.Session('') as sess: 100 | ret = sess.run(reduced_sample) 101 | print(ret.shape, ret.dtype) 102 | import cPickle as pickle 103 | 104 | pickle.dump(ret, open('1.pkl', 'wb'), -1) 105 | -------------------------------------------------------------------------------- /tf_ops/sampling/tf_sampling_compile.sh: -------------------------------------------------------------------------------- 1 | #/bin/bash 2 | /usr/local/cuda-8.0/bin/nvcc tf_sampling_g.cu \ 3 | -o tf_sampling_g.cu.o -c -O2 -DGOOGLE_CUDA=1 -x cu -Xcompiler -fPIC 4 | 5 | 6 | # TF1.4 Python 3.5 CUDA 8.0 7 | g++ -std=c++11 tf_sampling.cpp tf_sampling_g.cu.o \ 8 | -o tf_sampling_so.so \ 9 | -shared -fPIC \ 10 | -I$HOME/anaconda3/envs/tensorflow_1.4/lib/python3.5/site-packages/tensorflow/include \ 11 | -I/usr/local/cuda-8.0/include \ 12 | -I$HOME/anaconda3/envs/tensorflow_1.4/lib/python3.5/site-packages/tensorflow/include/external/nsync/public \ 13 | -lcudart -L/usr/local/cuda-8.0/lib64/ \ 14 | -L$HOME/anaconda3/envs/tensorflow_1.4/lib/python3.5/site-packages/tensorflow \ 15 | -ltensorflow_framework -O2 -D_GLIBCXX_USE_CXX11_ABI=0 16 | -------------------------------------------------------------------------------- /tf_ops/sampling/tf_sampling_g.cu: -------------------------------------------------------------------------------- 1 | /* Furthest point sampling GPU implementation 2 | * Original author: Haoqiang Fan 3 | * Modified by Charles R. Qi 4 | * All Rights Reserved. 2017. 5 | */ 6 | 7 | __global__ void cumsumKernel(int b,int n,const float * __restrict__ inp,float * __restrict__ out){ 8 | const int BlockSize=2048; 9 | const int paddingLevel=5; 10 | __shared__ float buffer4[BlockSize*4]; 11 | __shared__ float buffer[BlockSize+(BlockSize>>paddingLevel)]; 12 | for (int i=blockIdx.x;i>2; 18 | for (int k=threadIdx.x*4;k>2)+(k>>(2+paddingLevel))]=v4; 33 | }else{ 34 | float v=0; 35 | for (int k2=k;k2>2)+(k>>(2+paddingLevel))]=v; 43 | } 44 | } 45 | int u=0; 46 | for (;(2<>(u+1));k+=blockDim.x){ 49 | int i1=(((k<<1)+2)<>paddingLevel; 52 | i2+=i2>>paddingLevel; 53 | buffer[i1]+=buffer[i2]; 54 | } 55 | } 56 | u--; 57 | for (;u>=0;u--){ 58 | __syncthreads(); 59 | for (int k=threadIdx.x;k>(u+1));k+=blockDim.x){ 60 | int i1=(((k<<1)+3)<>paddingLevel; 63 | i2+=i2>>paddingLevel; 64 | buffer[i1]+=buffer[i2]; 65 | } 66 | } 67 | __syncthreads(); 68 | for (int k=threadIdx.x*4;k>2)-1)+(((k>>2)-1)>>paddingLevel); 71 | buffer4[k]+=buffer[k2]; 72 | buffer4[k+1]+=buffer[k2]; 73 | buffer4[k+2]+=buffer[k2]; 74 | buffer4[k+3]+=buffer[k2]; 75 | } 76 | } 77 | __syncthreads(); 78 | for (int k=threadIdx.x;k>paddingLevel)]+runningsum2; 82 | float r2=runningsum+t; 83 | runningsum2=t-(r2-runningsum); 84 | runningsum=r2; 85 | __syncthreads(); 86 | } 87 | } 88 | } 89 | 90 | __global__ void binarysearchKernel(int b,int n,int m,const float * __restrict__ dataset,const float * __restrict__ query, int * __restrict__ result){ 91 | int base=1; 92 | while (base=1;k>>=1) 99 | if (r>=k && dataset[i*n+r-k]>=q) 100 | r-=k; 101 | result[i*m+j]=r; 102 | } 103 | } 104 | } 105 | __global__ void farthestpointsamplingKernel(int b,int n,int m,const float * __restrict__ dataset,float * __restrict__ temp,int * __restrict__ idxs){ 106 | if (m<=0) 107 | return; 108 | const int BlockSize=512; 109 | __shared__ float dists[BlockSize]; 110 | __shared__ int dists_i[BlockSize]; 111 | const int BufferSize=3072; 112 | __shared__ float buf[BufferSize*3]; 113 | for (int i=blockIdx.x;ibest){ 147 | best=d2; 148 | besti=k; 149 | } 150 | } 151 | dists[threadIdx.x]=best; 152 | dists_i[threadIdx.x]=besti; 153 | for (int u=0;(1<>(u+1))){ 156 | int i1=(threadIdx.x*2)<>>(b,n,inp,out); 196 | } 197 | //require b*n working space 198 | void probsampleLauncher(int b,int n,int m,const float * inp_p,const float * inp_r,float * temp,int * out){ 199 | cumsumKernel<<<32,512>>>(b,n,inp_p,temp); 200 | binarysearchKernel<<>>(b,n,m,temp,inp_r,out); 201 | } 202 | //require 32*n working space 203 | void farthestpointsamplingLauncher(int b,int n,int m,const float * inp,float * temp,int * out){ 204 | farthestpointsamplingKernel<<<32,512>>>(b,n,m,inp,temp,out); 205 | } 206 | void gatherpointLauncher(int b,int n,int m,const float * inp,const int * idx,float * out){ 207 | gatherpointKernel<<>>(b,n,m,inp,idx,out); 208 | } 209 | void scatteraddpointLauncher(int b,int n,int m,const float * out_g,const int * idx,float * inp_g){ 210 | scatteraddpointKernel<<>>(b,n,m,out_g,idx,inp_g); 211 | } 212 | 213 | -------------------------------------------------------------------------------- /tf_ops/sampling/tf_sampling_so.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dlinzhao/JSNet/8fec285e1ccc12a0c5806f4adc60846420ca46a1/tf_ops/sampling/tf_sampling_so.so -------------------------------------------------------------------------------- /utils/clustering.py: -------------------------------------------------------------------------------- 1 | import os 2 | import numpy as np 3 | from sklearn.cluster import MeanShift, estimate_bandwidth 4 | import time 5 | import cv2 6 | 7 | COLOR = [np.array([255, 0, 0]), 8 | np.array([0, 255, 0]), 9 | np.array([0, 0, 255]), 10 | np.array([125, 125, 0]), 11 | np.array([0, 125, 125]), 12 | np.array([125, 0, 125]), 13 | np.array([50, 100, 50]), 14 | np.array([100, 50, 100])] 15 | 16 | 17 | def cluster(prediction, bandwidth): 18 | ms = MeanShift(bandwidth, bin_seeding=True) 19 | # print ('Mean shift clustering, might take some time ...') 20 | # tic = time.time() 21 | ms.fit(prediction) 22 | # print ('time for clustering', time.time() - tic) 23 | labels = ms.labels_ 24 | cluster_centers = ms.cluster_centers_ 25 | 26 | num_clusters = cluster_centers.shape[0] 27 | 28 | return num_clusters, labels, cluster_centers 29 | 30 | 31 | def get_instance_masks(prediction, bandwidth): 32 | batch_size, h, w, feature_dim = prediction.shape 33 | 34 | instance_masks = [] 35 | for i in range(batch_size): 36 | num_clusters, labels, cluster_centers = cluster(prediction[i].reshape([h * w, feature_dim]), bandwidth) 37 | print('Number of predicted clusters', num_clusters) 38 | labels = np.array(labels, dtype=np.uint8).reshape([h, w]) 39 | mask = np.zeros([h, w, 3], dtype=np.uint8) 40 | 41 | num_clusters = min([num_clusters, 8]) 42 | for mask_id in range(num_clusters): 43 | ind = np.where(labels == mask_id) 44 | mask[ind] = COLOR[mask_id] 45 | 46 | instance_masks.append(mask) 47 | 48 | return instance_masks 49 | 50 | 51 | def save_instance_masks(prediction, output_dir, bandwidth, count): 52 | batch_size, h, w, feature_dim = prediction.shape 53 | 54 | instance_masks = [] 55 | for i in range(batch_size): 56 | num_clusters, labels, cluster_centers = cluster(prediction[i].reshape([h * w, feature_dim]), bandwidth) 57 | print('Number of predicted clusters', num_clusters) 58 | labels = np.array(labels, dtype=np.uint8).reshape([h, w]) 59 | mask = np.zeros([h, w, 3], dtype=np.uint8) 60 | 61 | num_clusters = min([num_clusters, 8]) 62 | for mask_id in range(num_clusters): 63 | mask = np.zeros([h, w, 3], dtype=np.uint8) 64 | ind = np.where(labels == mask_id) 65 | mask[ind] = np.array([255, 255, 255]) 66 | output_file_name = os.path.join(output_dir, 'cluster_{}_{}.png'.format(str(count).zfill(4), str(mask_id))) 67 | cv2.imwrite(output_file_name, mask) 68 | 69 | instance_masks.append(mask) 70 | 71 | return instance_masks 72 | -------------------------------------------------------------------------------- /utils/data_prep_util.py: -------------------------------------------------------------------------------- 1 | """ 2 | Modified from: https://github.com/charlesq34/pointnet/blob/master/utils/data_prep_util.py 3 | """ 4 | import os 5 | import sys 6 | 7 | BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 8 | sys.path.append(BASE_DIR) 9 | from plyfile import (PlyData, PlyElement, make2d, PlyParseError, PlyProperty) 10 | import numpy as np 11 | import h5py 12 | 13 | SAMPLING_BIN = os.path.join(BASE_DIR, 'third_party/mesh_sampling/build/pcsample') 14 | 15 | SAMPLING_POINT_NUM = 2048 16 | SAMPLING_LEAF_SIZE = 0.005 17 | 18 | MODELNET40_PATH = '../datasets/modelnet40' 19 | 20 | 21 | def export_ply(pc, filename): 22 | vertex = np.zeros(pc.shape[0], dtype=[('x', 'f4'), ('y', 'f4'), ('z', 'f4')]) 23 | for i in range(pc.shape[0]): 24 | vertex[i] = (pc[i][0], pc[i][1], pc[i][2]) 25 | ply_out = PlyData([PlyElement.describe(vertex, 'vertex', comments=['vertices'])]) 26 | ply_out.write(filename) 27 | 28 | 29 | # Sample points on the obj shape 30 | def get_sampling_command(obj_filename, ply_filename): 31 | cmd = SAMPLING_BIN + ' ' + obj_filename 32 | cmd += ' ' + ply_filename 33 | cmd += ' -n_samples %d ' % SAMPLING_POINT_NUM 34 | cmd += ' -leaf_size %f ' % SAMPLING_LEAF_SIZE 35 | return cmd 36 | 37 | 38 | # -------------------------------------------------------------- 39 | # Following are the helper functions to load MODELNET40 shapes 40 | # -------------------------------------------------------------- 41 | 42 | # Read in the list of categories in MODELNET40 43 | def get_category_names(): 44 | shape_names_file = os.path.join(MODELNET40_PATH, 'shape_names.txt') 45 | shape_names = [line.rstrip() for line in open(shape_names_file)] 46 | return shape_names 47 | 48 | 49 | # Return all the filepaths for the shapes in MODELNET40 50 | def get_obj_filenames(): 51 | obj_filelist_file = os.path.join(MODELNET40_PATH, 'filelist.txt') 52 | obj_filenames = [os.path.join(MODELNET40_PATH, line.rstrip()) for line in open(obj_filelist_file)] 53 | print('Got %d obj files in modelnet40.' % len(obj_filenames)) 54 | return obj_filenames 55 | 56 | 57 | # Helper function to create the father folder and all subdir folders if not exist 58 | def batch_mkdir(output_folder, subdir_list): 59 | if not os.path.exists(output_folder): 60 | os.mkdir(output_folder) 61 | for subdir in subdir_list: 62 | if not os.path.exists(os.path.join(output_folder, subdir)): 63 | os.mkdir(os.path.join(output_folder, subdir)) 64 | 65 | 66 | # ---------------------------------------------------------------- 67 | # Following are the helper functions to load save/load HDF5 files 68 | # ---------------------------------------------------------------- 69 | 70 | # Write numpy array data and label to h5_filename 71 | def save_h5_data_label_normal(h5_filename, data, label, normal, 72 | data_dtype='float32', label_dtype='uint8', normal_dtype='float32'): 73 | h5_fout = h5py.File(h5_filename) 74 | h5_fout.create_dataset( 75 | 'data', data=data, 76 | compression='gzip', compression_opts=4, 77 | dtype=data_dtype) 78 | h5_fout.create_dataset( 79 | 'normal', data=normal, 80 | compression='gzip', compression_opts=4, 81 | dtype=normal_dtype) 82 | h5_fout.create_dataset( 83 | 'label', data=label, 84 | compression='gzip', compression_opts=1, 85 | dtype=label_dtype) 86 | h5_fout.close() 87 | 88 | 89 | # Write numpy array data and label to h5_filename 90 | def save_h5(h5_filename, data, label, data_dtype='uint8', label_dtype='uint8'): 91 | h5_fout = h5py.File(h5_filename) 92 | h5_fout.create_dataset( 93 | 'data', data=data, 94 | compression='gzip', compression_opts=4, 95 | dtype=data_dtype) 96 | h5_fout.create_dataset( 97 | 'label', data=label, 98 | compression='gzip', compression_opts=1, 99 | dtype=label_dtype) 100 | h5_fout.close() 101 | 102 | 103 | def save_h5ins(h5_filename, data, label, gid, data_dtype='uint8', label_dtype='uint8'): 104 | h5_fout = h5py.File(h5_filename) 105 | h5_fout.create_dataset( 106 | 'data', data=data, 107 | compression='gzip', compression_opts=4, 108 | dtype=data_dtype) 109 | h5_fout.create_dataset( 110 | 'seglabel', data=label, 111 | compression='gzip', compression_opts=1, 112 | dtype=label_dtype) 113 | h5_fout.create_dataset( 114 | 'pid', data=gid, 115 | compression='gzip', compression_opts=1, 116 | dtype=label_dtype) 117 | # h5_fout.create_dataset( 118 | # 'groupcategory', data=groupcategory, 119 | # compression='gzip', compression_opts=4, 120 | # dtype=label_dtype) 121 | h5_fout.close() 122 | 123 | 124 | # Read numpy array data and label from h5_filename 125 | def load_h5_data_label_normal(h5_filename): 126 | f = h5py.File(h5_filename) 127 | data = f['data'][:] 128 | label = f['label'][:] 129 | normal = f['normal'][:] 130 | return (data, label, normal) 131 | 132 | 133 | # Read numpy array data and label from h5_filename 134 | def load_h5_data_label_seg(h5_filename): 135 | f = h5py.File(h5_filename) 136 | data = f['data'][:] 137 | label = f['label'][:] 138 | seg = f['pid'][:] 139 | return (data, label, seg) 140 | 141 | 142 | # Read numpy array data and label from h5_filename 143 | def load_h5(h5_filename): 144 | f = h5py.File(h5_filename) 145 | data = f['data'][:] 146 | label = f['label'][:] 147 | return (data, label) 148 | 149 | 150 | # ---------------------------------------------------------------- 151 | # Following are the helper functions to load save/load PLY files 152 | # ---------------------------------------------------------------- 153 | 154 | # Load PLY file 155 | def load_ply_data(filename, point_num): 156 | plydata = PlyData.read(filename) 157 | pc = plydata['vertex'].data[:point_num] 158 | pc_array = np.array([[x, y, z] for x, y, z in pc]) 159 | return pc_array 160 | 161 | 162 | # Load PLY file 163 | def load_ply_normal(filename, point_num): 164 | plydata = PlyData.read(filename) 165 | pc = plydata['normal'].data[:point_num] 166 | pc_array = np.array([[x, y, z] for x, y, z in pc]) 167 | return pc_array 168 | 169 | 170 | # Make up rows for Nxk array 171 | # Input Pad is 'edge' or 'constant' 172 | def pad_arr_rows(arr, row, pad='edge'): 173 | assert (len(arr.shape) == 2) 174 | assert (arr.shape[0] <= row) 175 | assert (pad == 'edge' or pad == 'constant') 176 | if arr.shape[0] == row: 177 | return arr 178 | if pad == 'edge': 179 | return np.lib.pad(arr, ((0, row - arr.shape[0]), (0, 0)), 'edge') 180 | if pad == 'constant': 181 | return np.lib.pad(arr, ((0, row - arr.shape[0]), (0, 0)), 'constant', (0, 0)) 182 | -------------------------------------------------------------------------------- /utils/log_util.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import logging 4 | 5 | 6 | def get_logger(name, save_dir=None, filename='log.txt'): 7 | logger = logging.getLogger(name) 8 | logger.setLevel(logging.INFO) 9 | 10 | formatter = logging.Formatter('%(asctime)s %(filename)s %(lineno)d %(levelname)s %(message)s') 11 | shandler = logging.StreamHandler(stream=sys.stdout) 12 | shandler.setLevel(logging.INFO) 13 | shandler.setFormatter(formatter) 14 | 15 | logger.addHandler(shandler) 16 | if save_dir: 17 | fhandler = logging.FileHandler(os.path.join(save_dir, filename)) 18 | fhandler.setLevel(logging.INFO) 19 | fhandler.setFormatter(formatter) 20 | logger.addHandler(fhandler) 21 | 22 | return logger 23 | -------------------------------------------------------------------------------- /utils/loss.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import tensorflow as tf 4 | 5 | BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 6 | sys.path.append(BASE_DIR) 7 | sys.path.append(os.path.join(BASE_DIR, 'utils')) 8 | 9 | 10 | def discriminative_loss_single(prediction, correct_label, feature_dim, delta_v, delta_d, param_var, param_dist): 11 | ''' Discriminative loss for a single prediction/label pair. 12 | :param prediction: inference of network 13 | :param correct_label: instance label 14 | :feature_dim: feature dimension of prediction 15 | :param label_shape: shape of label 16 | :param delta_v: cutoff variance distance 17 | :param delta_d: curoff cluster distance 18 | :param param_var: weight for intra cluster variance 19 | :param param_dist: weight for inter cluster distances 20 | ''' 21 | 22 | ### Reshape so pixels are aligned along a vector 23 | # correct_label = tf.reshape(correct_label, [label_shape[1] * label_shape[0]]) 24 | reshaped_pred = tf.reshape(prediction, [-1, feature_dim]) 25 | 26 | ### Count instances 27 | unique_labels, unique_id, counts = tf.unique_with_counts(correct_label) 28 | 29 | counts = tf.cast(counts, tf.float32) 30 | num_instances = tf.size(unique_labels) 31 | 32 | segmented_sum = tf.unsorted_segment_sum(reshaped_pred, unique_id, num_instances) 33 | 34 | mu = tf.div(segmented_sum, tf.reshape(counts, (-1, 1))) 35 | # partitions = tf.reduce_sum(tf.one_hot(unique_id, tf.shape(mu)[0], dtype=tf.int32), 0) 36 | # mu_expand = tf.dynamic_partition(mu, partitions, 2) 37 | mu_expand = tf.gather(mu, unique_id) 38 | 39 | ### Calculate l_var 40 | # distance = tf.norm(tf.subtract(mu_expand, reshaped_pred), axis=1) 41 | # tmp_distance = tf.subtract(reshaped_pred, mu_expand) 42 | tmp_distance = reshaped_pred - mu_expand 43 | distance = tf.norm(tmp_distance, ord=1, axis=1) 44 | 45 | distance = tf.subtract(distance, delta_v) 46 | distance = tf.clip_by_value(distance, 0., distance) 47 | distance = tf.square(distance) 48 | 49 | l_var = tf.unsorted_segment_sum(distance, unique_id, num_instances) 50 | l_var = tf.div(l_var, counts) 51 | l_var = tf.reduce_sum(l_var) 52 | l_var = tf.divide(l_var, tf.cast(num_instances, tf.float32)) 53 | 54 | ### Calculate l_dist 55 | 56 | # Get distance for each pair of clusters like this: 57 | # mu_1 - mu_1 58 | # mu_2 - mu_1 59 | # mu_3 - mu_1 60 | # mu_1 - mu_2 61 | # mu_2 - mu_2 62 | # mu_3 - mu_2 63 | # mu_1 - mu_3 64 | # mu_2 - mu_3 65 | # mu_3 - mu_3 66 | 67 | mu_interleaved_rep = tf.tile(mu, [num_instances, 1]) 68 | mu_band_rep = tf.tile(mu, [1, num_instances]) 69 | mu_band_rep = tf.reshape(mu_band_rep, (num_instances * num_instances, feature_dim)) 70 | 71 | mu_diff = tf.subtract(mu_band_rep, mu_interleaved_rep) 72 | 73 | # Filter out zeros from same cluster subtraction 74 | eye = tf.eye(num_instances) 75 | zero = tf.zeros(1, dtype=tf.float32) 76 | diff_cluster_mask = tf.equal(eye, zero) 77 | diff_cluster_mask = tf.reshape(diff_cluster_mask, [-1]) 78 | mu_diff_bool = tf.boolean_mask(mu_diff, diff_cluster_mask) 79 | 80 | mu_norm = tf.norm(mu_diff_bool, ord=1, axis=1) 81 | mu_norm = tf.subtract(2. * delta_d, mu_norm) 82 | mu_norm = tf.clip_by_value(mu_norm, 0., mu_norm) 83 | mu_norm = tf.square(mu_norm) 84 | 85 | l_dist = tf.reduce_mean(mu_norm) 86 | 87 | def rt_0(): return 0. 88 | 89 | def rt_l_dist(): return l_dist 90 | 91 | l_dist = tf.cond(tf.equal(1, num_instances), rt_0, rt_l_dist) 92 | 93 | param_scale = 1. 94 | l_var = param_var * l_var 95 | l_dist = param_dist * l_dist 96 | 97 | loss = param_scale * (l_var + l_dist) 98 | 99 | return loss, l_var, l_dist 100 | 101 | 102 | def discriminative_loss(prediction, correct_label, feature_dim, delta_v, delta_d, param_var, param_dist): 103 | ''' Iterate over a batch of prediction/label and cumulate loss 104 | :return: discriminative loss and its two components 105 | ''' 106 | 107 | def cond(label, batch, out_loss, out_var, out_dist, i): 108 | return tf.less(i, tf.shape(batch)[0]) 109 | 110 | def body(label, batch, out_loss, out_var, out_dist, i): 111 | disc_loss, l_var, l_dist = discriminative_loss_single(prediction[i], correct_label[i], feature_dim, delta_v, delta_d, param_var, param_dist) 112 | 113 | out_loss = out_loss.write(i, disc_loss) 114 | out_var = out_var.write(i, l_var) 115 | out_dist = out_dist.write(i, l_dist) 116 | 117 | return label, batch, out_loss, out_var, out_dist, i + 1 118 | 119 | # TensorArray is a data structure that support dynamic writing 120 | output_ta_loss = tf.TensorArray(dtype=tf.float32, size=0, dynamic_size=True) 121 | output_ta_var = tf.TensorArray(dtype=tf.float32, size=0, dynamic_size=True) 122 | output_ta_dist = tf.TensorArray(dtype=tf.float32, size=0, dynamic_size=True) 123 | 124 | _, _, out_loss_op, out_var_op, out_dist_op, _ = tf.while_loop(cond, body, [correct_label, prediction, output_ta_loss, output_ta_var, output_ta_dist, 0]) 125 | out_loss_op = out_loss_op.stack() 126 | out_var_op = out_var_op.stack() 127 | out_dist_op = out_dist_op.stack() 128 | 129 | disc_loss = tf.reduce_mean(out_loss_op) 130 | l_var = tf.reduce_mean(out_var_op) 131 | l_dist = tf.reduce_mean(out_dist_op) 132 | 133 | return disc_loss, l_var, l_dist 134 | -------------------------------------------------------------------------------- /utils/part_color_mapping.json: -------------------------------------------------------------------------------- 1 | [[0.57, 0.15, 0.43], [0.85, 0.57, 0.29], [0.7100000000000001, 0.7100000000000001, 0.01], [0.01, 0.7100000000000001, 0.15], [0.01, 0.57, 0.85], [0.43, 0.01, 0.7100000000000001], [0.43, 0.7100000000000001, 0.57], [0.29, 0.29, 0.29], [0.29, 0.7100000000000001, 0.7100000000000001], [0.57, 0.57, 0.29], [0.57, 0.7100000000000001, 0.7100000000000001], [0.01, 0.01, 0.29], [0.29, 0.57, 0.7100000000000001], [0.29, 0.15, 0.57], [0.29, 0.7100000000000001, 0.43], [0.57, 0.29, 0.01], [0.29, 0.15, 0.15], [0.7100000000000001, 0.29, 0.01], [0.01, 0.85, 0.15], [0.85, 0.01, 0.01], [0.29, 0.15, 0.7100000000000001], [0.7100000000000001, 0.15, 0.43], [0.29, 0.43, 0.7100000000000001], [0.43, 0.43, 0.7100000000000001], [0.29, 0.57, 0.01], [0.57, 0.29, 0.29], [0.57, 0.85, 0.15], [0.15, 0.29, 0.29], [0.15, 0.7100000000000001, 0.15], [0.85, 0.01, 0.29], [0.43, 0.85, 0.29], [0.43, 0.29, 0.85], [0.57, 0.85, 0.85], [0.15, 0.57, 0.01], [0.57, 0.29, 0.15], [0.7100000000000001, 0.85, 0.57], [0.57, 0.01, 0.57], [0.01, 0.85, 0.43], [0.01, 0.01, 0.01], [0.85, 0.01, 0.43], [0.57, 0.43, 0.57], [0.85, 0.01, 0.57], [0.01, 0.43, 0.43], [0.01, 0.29, 0.85], [0.57, 0.57, 0.7100000000000001], [0.7100000000000001, 0.29, 0.57], [0.57, 0.7100000000000001, 0.43], [0.29, 0.15, 0.01], [0.57, 0.15, 0.15], [0.85, 0.57, 0.85], [0.85, 0.29, 0.85], [0.85, 0.15, 0.01], [0.85, 0.7100000000000001, 0.01], [0.01, 0.57, 0.15], [0.43, 0.01, 0.43], [0.57, 0.15, 0.85], [0.01, 0.29, 0.57], [0.29, 0.85, 0.43], [0.57, 0.29, 0.43], [0.43, 0.01, 0.29], [0.15, 0.85, 0.7100000000000001], [0.85, 0.57, 0.43], [0.01, 0.15, 0.57], [0.7100000000000001, 0.7100000000000001, 0.29], [0.7100000000000001, 0.15, 0.57], [0.43, 0.43, 0.29], [0.7100000000000001, 0.43, 0.43], [0.01, 0.43, 0.57], [0.57, 0.01, 0.15], [0.57, 0.57, 0.01], [0.29, 0.01, 0.29], [0.7100000000000001, 0.01, 0.29], [0.85, 0.85, 0.7100000000000001], [0.85, 0.15, 0.29], [0.43, 0.29, 0.57], [0.43, 0.43, 0.85], [0.85, 0.15, 0.85], [0.57, 0.85, 0.29], [0.57, 0.7100000000000001, 0.01], [0.7100000000000001, 0.85, 0.15], [0.85, 0.7100000000000001, 0.43], [0.01, 0.15, 0.01], [0.85, 0.29, 0.43], [0.43, 0.85, 0.15], [0.15, 0.01, 0.15], [0.7100000000000001, 0.7100000000000001, 0.85], [0.43, 0.29, 0.01], [0.15, 0.43, 0.29], [0.7100000000000001, 0.57, 0.15], [0.29, 0.85, 0.29], [0.29, 0.7100000000000001, 0.57], [0.57, 0.85, 0.7100000000000001], [0.15, 0.01, 0.85], [0.43, 0.15, 0.57], [0.57, 0.57, 0.15], [0.01, 0.57, 0.01], [0.15, 0.29, 0.57], [0.29, 0.57, 0.43], [0.15, 0.7100000000000001, 0.01], [0.15, 0.15, 0.15], [0.43, 0.29, 0.15], [0.7100000000000001, 0.29, 0.7100000000000001], [0.7100000000000001, 0.85, 0.43], [0.15, 0.29, 0.7100000000000001], [0.15, 0.43, 0.57], [0.01, 0.7100000000000001, 0.01], [0.85, 0.29, 0.01], [0.15, 0.01, 0.57], [0.29, 0.29, 0.7100000000000001], [0.15, 0.7100000000000001, 0.29], [0.01, 0.15, 0.43], [0.7100000000000001, 0.01, 0.15], [0.57, 0.43, 0.01], [0.85, 0.43, 0.01], [0.43, 0.85, 0.7100000000000001], [0.85, 0.43, 0.43], [0.85, 0.01, 0.15], [0.01, 0.43, 0.85], [0.15, 0.15, 0.7100000000000001], [0.29, 0.57, 0.85], [0.43, 0.15, 0.15], [0.29, 0.85, 0.85], [0.15, 0.57, 0.29], [0.85, 0.85, 0.85], [0.29, 0.43, 0.43], [0.01, 0.43, 0.29], [0.43, 0.15, 0.7100000000000001], [0.7100000000000001, 0.01, 0.57], [0.7100000000000001, 0.43, 0.15], [0.01, 0.85, 0.01], [0.85, 0.01, 0.7100000000000001], [0.57, 0.43, 0.43], [0.57, 0.85, 0.01], [0.01, 0.57, 0.43], [0.15, 0.15, 0.01], [0.85, 0.43, 0.85], [0.57, 0.15, 0.29], [0.7100000000000001, 0.7100000000000001, 0.57], [0.57, 0.01, 0.85], [0.29, 0.43, 0.15], [0.7100000000000001, 0.57, 0.7100000000000001], [0.43, 0.7100000000000001, 0.85], [0.01, 0.15, 0.15], [0.85, 0.85, 0.57], [0.43, 0.85, 0.01], [0.15, 0.15, 0.85], [0.29, 0.29, 0.43], [0.29, 0.43, 0.57], [0.7100000000000001, 0.29, 0.85], [0.15, 0.15, 0.43], [0.85, 0.7100000000000001, 0.85], [0.85, 0.15, 0.43], [0.43, 0.43, 0.15], [0.57, 0.7100000000000001, 0.15], [0.7100000000000001, 0.43, 0.57], [0.7100000000000001, 0.43, 0.01], [0.85, 0.29, 0.29], [0.85, 0.15, 0.15], [0.43, 0.57, 0.85], [0.01, 0.85, 0.29], [0.29, 0.7100000000000001, 0.15], [0.57, 0.85, 0.57], [0.43, 0.43, 0.57], [0.01, 0.7100000000000001, 0.7100000000000001], [0.57, 0.15, 0.57], [0.57, 0.57, 0.43], [0.85, 0.57, 0.57], [0.85, 0.7100000000000001, 0.7100000000000001], [0.57, 0.7100000000000001, 0.85], [0.15, 0.85, 0.85], [0.29, 0.57, 0.57], [0.15, 0.7100000000000001, 0.85], [0.57, 0.01, 0.29], [0.29, 0.7100000000000001, 0.01], [0.7100000000000001, 0.29, 0.15], [0.85, 0.01, 0.85], [0.29, 0.01, 0.57], [0.29, 0.01, 0.7100000000000001], [0.7100000000000001, 0.85, 0.29], [0.85, 0.29, 0.7100000000000001], [0.43, 0.15, 0.43], [0.01, 0.01, 0.15], [0.01, 0.7100000000000001, 0.57], [0.7100000000000001, 0.15, 0.01], [0.7100000000000001, 0.15, 0.15], [0.29, 0.29, 0.85], [0.01, 0.43, 0.7100000000000001], [0.57, 0.15, 0.01], [0.85, 0.15, 0.7100000000000001], [0.43, 0.43, 0.01], [0.29, 0.85, 0.57], [0.01, 0.29, 0.43], [0.57, 0.43, 0.29], [0.43, 0.29, 0.43], [0.85, 0.7100000000000001, 0.29], [0.7100000000000001, 0.85, 0.85], [0.43, 0.43, 0.43], [0.43, 0.29, 0.7100000000000001], [0.7100000000000001, 0.57, 0.57], [0.57, 0.29, 0.85], [0.01, 0.15, 0.7100000000000001], [0.7100000000000001, 0.7100000000000001, 0.15], [0.15, 0.85, 0.01], [0.43, 0.7100000000000001, 0.7100000000000001], [0.43, 0.57, 0.43], [0.7100000000000001, 0.01, 0.85], [0.29, 0.43, 0.29], [0.57, 0.15, 0.7100000000000001], [0.29, 0.57, 0.15], [0.15, 0.29, 0.43], [0.43, 0.85, 0.57], [0.43, 0.57, 0.57], [0.7100000000000001, 0.01, 0.01], [0.85, 0.85, 0.29], [0.15, 0.01, 0.29], [0.85, 0.29, 0.15], [0.15, 0.01, 0.01], [0.01, 0.01, 0.57], [0.15, 0.57, 0.15], [0.15, 0.43, 0.85], [0.01, 0.01, 0.7100000000000001], [0.85, 0.15, 0.57], [0.29, 0.15, 0.29], [0.15, 0.29, 0.15], [0.43, 0.85, 0.43], [0.01, 0.15, 0.29], [0.85, 0.7100000000000001, 0.15], [0.01, 0.01, 0.85], [0.7100000000000001, 0.57, 0.85], [0.01, 0.85, 0.57], [0.29, 0.85, 0.7100000000000001], [0.15, 0.01, 0.7100000000000001], [0.85, 0.57, 0.15], [0.15, 0.57, 0.85], [0.15, 0.43, 0.15], [0.15, 0.85, 0.57], [0.7100000000000001, 0.85, 0.01], [0.7100000000000001, 0.01, 0.7100000000000001], [0.15, 0.43, 0.43], [0.01, 0.57, 0.7100000000000001], [0.43, 0.01, 0.57], [0.01, 0.57, 0.57], [0.57, 0.43, 0.7100000000000001], [0.7100000000000001, 0.29, 0.29], [0.15, 0.43, 0.7100000000000001], [0.57, 0.43, 0.15], [0.01, 0.7100000000000001, 0.29], [0.7100000000000001, 0.57, 0.01], [0.01, 0.29, 0.15], [0.29, 0.85, 0.01], [0.57, 0.29, 0.57], [0.85, 0.43, 0.29], [0.7100000000000001, 0.43, 0.29], [0.29, 0.7100000000000001, 0.85], [0.85, 0.57, 0.01], [0.01, 0.29, 0.7100000000000001], [0.15, 0.57, 0.7100000000000001], [0.85, 0.57, 0.7100000000000001], [0.85, 0.43, 0.7100000000000001], [0.15, 0.57, 0.43], [0.15, 0.85, 0.29], [0.15, 0.29, 0.85], [0.7100000000000001, 0.29, 0.43], [0.15, 0.15, 0.29], [0.43, 0.7100000000000001, 0.01], [0.29, 0.7100000000000001, 0.29], [0.7100000000000001, 0.7100000000000001, 0.43], [0.29, 0.85, 0.15], [0.43, 0.15, 0.29], [0.29, 0.01, 0.15], [0.57, 0.01, 0.01], [0.85, 0.85, 0.15], [0.43, 0.7100000000000001, 0.15], [0.43, 0.7100000000000001, 0.29], [0.7100000000000001, 0.57, 0.43], [0.29, 0.29, 0.57], [0.29, 0.01, 0.43], [0.85, 0.7100000000000001, 0.57], [0.57, 0.29, 0.7100000000000001], [0.43, 0.29, 0.29], [0.7100000000000001, 0.43, 0.7100000000000001], [0.43, 0.57, 0.29], [0.43, 0.01, 0.15], [0.15, 0.7100000000000001, 0.57], [0.01, 0.29, 0.29], [0.29, 0.01, 0.01], [0.7100000000000001, 0.15, 0.85], [0.57, 0.7100000000000001, 0.29], [0.57, 0.7100000000000001, 0.57], [0.15, 0.29, 0.01], [0.29, 0.43, 0.01], [0.7100000000000001, 0.15, 0.7100000000000001], [0.85, 0.85, 0.43], [0.7100000000000001, 0.15, 0.29], [0.7100000000000001, 0.7100000000000001, 0.7100000000000001], [0.29, 0.57, 0.29], [0.85, 0.43, 0.57], [0.01, 0.57, 0.29], [0.57, 0.01, 0.43], [0.01, 0.29, 0.01], [0.29, 0.01, 0.85], [0.43, 0.57, 0.15], [0.85, 0.85, 0.01], [0.29, 0.15, 0.43], [0.29, 0.29, 0.15], [0.01, 0.43, 0.01], [0.43, 0.85, 0.85], [0.15, 0.7100000000000001, 0.7100000000000001], [0.15, 0.85, 0.43], [0.7100000000000001, 0.85, 0.7100000000000001], [0.7100000000000001, 0.01, 0.43], [0.15, 0.43, 0.01], [0.43, 0.57, 0.01], [0.7100000000000001, 0.57, 0.29], [0.01, 0.85, 0.85], [0.01, 0.85, 0.7100000000000001], [0.01, 0.7100000000000001, 0.85], [0.43, 0.7100000000000001, 0.43], [0.43, 0.15, 0.01], [0.57, 0.85, 0.43], [0.85, 0.43, 0.15], [0.01, 0.15, 0.85], [0.85, 0.29, 0.57], [0.01, 0.01, 0.43], [0.57, 0.01, 0.7100000000000001], [0.29, 0.29, 0.01], [0.57, 0.43, 0.85], [0.43, 0.15, 0.85], [0.01, 0.7100000000000001, 0.43], [0.15, 0.57, 0.57], [0.15, 0.15, 0.57], [0.29, 0.43, 0.85], [0.43, 0.01, 0.01], [0.57, 0.57, 0.85], [0.15, 0.7100000000000001, 0.43], [0.43, 0.01, 0.85], [0.15, 0.01, 0.43], [0.43, 0.57, 0.7100000000000001], [0.29, 0.15, 0.85], [0.15, 0.85, 0.15], [0.7100000000000001, 0.43, 0.85], [0.01, 0.43, 0.15], [0.57, 0.57, 0.57]] 2 | -------------------------------------------------------------------------------- /utils/pc_util.py: -------------------------------------------------------------------------------- 1 | """ Utility functions for processing point clouds. 2 | 3 | Author: Charles R. Qi, Hao Su 4 | Date: November 2016 5 | """ 6 | 7 | import os 8 | import sys 9 | 10 | BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 11 | sys.path.append(BASE_DIR) 12 | 13 | # Draw point cloud 14 | from eulerangles import euler2mat 15 | 16 | # Point cloud IO 17 | import numpy as np 18 | from plyfile import PlyData, PlyElement 19 | 20 | 21 | # ---------------------------------------- 22 | # Point Cloud/Volume Conversions 23 | # ---------------------------------------- 24 | 25 | def point_cloud_to_volume_batch(point_clouds, vsize=12, radius=1.0, flatten=True): 26 | """ Input is BxNx3 batch of point cloud 27 | Output is Bx(vsize^3) 28 | """ 29 | vol_list = [] 30 | for b in range(point_clouds.shape[0]): 31 | vol = point_cloud_to_volume(np.squeeze(point_clouds[b, :, :]), vsize, radius) 32 | if flatten: 33 | vol_list.append(vol.flatten()) 34 | else: 35 | vol_list.append(np.expand_dims(np.expand_dims(vol, -1), 0)) 36 | if flatten: 37 | return np.vstack(vol_list) 38 | else: 39 | return np.concatenate(vol_list, 0) 40 | 41 | 42 | def point_cloud_to_volume(points, vsize, radius=1.0): 43 | """ input is Nx3 points. 44 | output is vsize*vsize*vsize 45 | assumes points are in range [-radius, radius] 46 | """ 47 | vol = np.zeros((vsize, vsize, vsize)) 48 | voxel = 2 * radius / float(vsize) 49 | locations = (points + radius) / voxel 50 | locations = locations.astype(int) 51 | vol[locations[:, 0], locations[:, 1], locations[:, 2]] = 1.0 52 | return vol 53 | 54 | 55 | # a = np.zeros((16,1024,3)) 56 | # print point_cloud_to_volume_batch(a, 12, 1.0, False).shape 57 | 58 | def volume_to_point_cloud(vol): 59 | """ vol is occupancy grid (value = 0 or 1) of size vsize*vsize*vsize 60 | return Nx3 numpy array. 61 | """ 62 | vsize = vol.shape[0] 63 | assert (vol.shape[1] == vsize and vol.shape[1] == vsize) 64 | points = [] 65 | for a in range(vsize): 66 | for b in range(vsize): 67 | for c in range(vsize): 68 | if vol[a, b, c] == 1: 69 | points.append(np.array([a, b, c])) 70 | if len(points) == 0: 71 | return np.zeros((0, 3)) 72 | points = np.vstack(points) 73 | return points 74 | 75 | 76 | # ---------------------------------------- 77 | # Point cloud IO 78 | # ---------------------------------------- 79 | 80 | def read_ply(filename): 81 | """ read XYZ point cloud from filename PLY file """ 82 | plydata = PlyData.read(filename) 83 | pc = plydata['vertex'].data 84 | pc_array = np.array([[x, y, z] for x, y, z in pc]) 85 | return pc_array 86 | 87 | 88 | def write_ply(points, filename, text=True): 89 | """ input: Nx3, write points to filename as PLY format. """ 90 | points = [(points[i, 0], points[i, 1], points[i, 2]) for i in range(points.shape[0])] 91 | vertex = np.array(points, dtype=[('x', 'f4'), ('y', 'f4'), ('z', 'f4')]) 92 | el = PlyElement.describe(vertex, 'vertex', comments=['vertices']) 93 | PlyData([el], text=text).write(filename) 94 | 95 | 96 | # ---------------------------------------- 97 | # Simple Point cloud and Volume Renderers 98 | # ---------------------------------------- 99 | 100 | def draw_point_cloud(input_points, canvasSize=500, space=200, diameter=25, 101 | xrot=0, yrot=0, zrot=0, switch_xyz=[0, 1, 2], normalize=True): 102 | """ Render point cloud to image with alpha channel. 103 | Input: 104 | points: Nx3 numpy array (+y is up direction) 105 | Output: 106 | gray image as numpy array of size canvasSizexcanvasSize 107 | """ 108 | image = np.zeros((canvasSize, canvasSize)) 109 | if input_points is None or input_points.shape[0] == 0: 110 | return image 111 | 112 | points = input_points[:, switch_xyz] 113 | M = euler2mat(zrot, yrot, xrot) 114 | points = (np.dot(M, points.transpose())).transpose() 115 | 116 | # Normalize the point cloud 117 | # We normalize scale to fit points in a unit sphere 118 | if normalize: 119 | centroid = np.mean(points, axis=0) 120 | points -= centroid 121 | furthest_distance = np.max(np.sqrt(np.sum(abs(points) ** 2, axis=-1))) 122 | points /= furthest_distance 123 | 124 | # Pre-compute the Gaussian disk 125 | radius = (diameter - 1) / 2.0 126 | disk = np.zeros((diameter, diameter)) 127 | for i in range(diameter): 128 | for j in range(diameter): 129 | if (i - radius) * (i - radius) + (j - radius) * (j - radius) <= radius * radius: 130 | disk[i, j] = np.exp((-(i - radius) ** 2 - (j - radius) ** 2) / (radius ** 2)) 131 | mask = np.argwhere(disk > 0) 132 | dx = mask[:, 0] 133 | dy = mask[:, 1] 134 | dv = disk[disk > 0] 135 | 136 | # Order points by z-buffer 137 | zorder = np.argsort(points[:, 2]) 138 | points = points[zorder, :] 139 | points[:, 2] = (points[:, 2] - np.min(points[:, 2])) / (np.max(points[:, 2] - np.min(points[:, 2]))) 140 | max_depth = np.max(points[:, 2]) 141 | 142 | for i in range(points.shape[0]): 143 | j = points.shape[0] - i - 1 144 | x = points[j, 0] 145 | y = points[j, 1] 146 | xc = canvasSize / 2 + (x * space) 147 | yc = canvasSize / 2 + (y * space) 148 | xc = int(np.round(xc)) 149 | yc = int(np.round(yc)) 150 | 151 | px = dx + xc 152 | py = dy + yc 153 | 154 | image[px, py] = image[px, py] * 0.7 + dv * (max_depth - points[j, 2]) * 0.3 155 | 156 | image = image / np.max(image) 157 | return image 158 | 159 | 160 | def point_cloud_three_views(points): 161 | """ input points Nx3 numpy array (+y is up direction). 162 | return an numpy array gray image of size 500x1500. """ 163 | # +y is up direction 164 | # xrot is azimuth 165 | # yrot is in-plane 166 | # zrot is elevation 167 | img1 = draw_point_cloud(points, zrot=110 / 180.0 * np.pi, xrot=45 / 180.0 * np.pi, yrot=0 / 180.0 * np.pi) 168 | img2 = draw_point_cloud(points, zrot=70 / 180.0 * np.pi, xrot=135 / 180.0 * np.pi, yrot=0 / 180.0 * np.pi) 169 | img3 = draw_point_cloud(points, zrot=180.0 / 180.0 * np.pi, xrot=90 / 180.0 * np.pi, yrot=0 / 180.0 * np.pi) 170 | image_large = np.concatenate([img1, img2, img3], 1) 171 | return image_large 172 | 173 | 174 | from PIL import Image 175 | 176 | 177 | def point_cloud_three_views_demo(): 178 | """ Demo for draw_point_cloud function """ 179 | points = read_ply('../third_party/mesh_sampling/piano.ply') 180 | im_array = point_cloud_three_views(points) 181 | img = Image.fromarray(np.uint8(im_array * 255.0)) 182 | img.save('piano.jpg') 183 | 184 | 185 | if __name__ == "__main__": 186 | point_cloud_three_views_demo() 187 | 188 | import matplotlib.pyplot as plt 189 | 190 | 191 | def pyplot_draw_point_cloud(points, output_filename): 192 | """ points is a Nx3 numpy array """ 193 | fig = plt.figure() 194 | ax = fig.add_subplot(111, projection='3d') 195 | ax.scatter(points[:, 0], points[:, 1], points[:, 2]) 196 | ax.set_xlabel('x') 197 | ax.set_ylabel('y') 198 | ax.set_zlabel('z') 199 | # savefig(output_filename) 200 | 201 | 202 | def pyplot_draw_volume(vol, output_filename): 203 | """ vol is of size vsize*vsize*vsize 204 | output an image to output_filename 205 | """ 206 | points = volume_to_point_cloud(vol) 207 | pyplot_draw_point_cloud(points, output_filename) 208 | -------------------------------------------------------------------------------- /utils/pointnet_util.py: -------------------------------------------------------------------------------- 1 | """ PointNet++ Layers 2 | 3 | Author: Charles R. Qi 4 | Date: November 2017 5 | """ 6 | 7 | import os 8 | import sys 9 | 10 | BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 11 | ROOT_DIR = os.path.dirname(BASE_DIR) 12 | sys.path.append(os.path.join(ROOT_DIR, 'utils')) 13 | sys.path.append(os.path.join(ROOT_DIR, 'tf_ops/sampling')) 14 | sys.path.append(os.path.join(ROOT_DIR, 'tf_ops/grouping')) 15 | sys.path.append(os.path.join(ROOT_DIR, 'tf_ops/3d_interpolation')) 16 | from tf_sampling import farthest_point_sample, gather_point 17 | from tf_grouping import query_ball_point, group_point, knn_point 18 | from tf_interpolate import three_nn, three_interpolate 19 | import tensorflow as tf 20 | import numpy as np 21 | import tf_util 22 | 23 | 24 | def sample_and_group(npoint, radius, nsample, xyz, points, knn=False, use_xyz=True): 25 | ''' 26 | Input: 27 | npoint: int32 28 | radius: float32 29 | nsample: int32 30 | xyz: (batch_size, ndataset, 3) TF tensor 31 | points: (batch_size, ndataset, channel) TF tensor, if None will just use xyz as points 32 | knn: bool, if True use kNN instead of radius search 33 | use_xyz: bool, if True concat XYZ with local point features, otherwise just use point features 34 | Output: 35 | new_xyz: (batch_size, npoint, 3) TF tensor 36 | new_points: (batch_size, npoint, nsample, 3+channel) TF tensor 37 | idx: (batch_size, npoint, nsample) TF tensor, indices of local points as in ndataset points 38 | grouped_xyz: (batch_size, npoint, nsample, 3) TF tensor, normalized point XYZs 39 | (subtracted by seed point XYZ) in local regions 40 | ''' 41 | 42 | new_xyz = gather_point(xyz, farthest_point_sample(npoint, xyz)) # (batch_size, npoint, 3) 43 | if knn: 44 | _, idx = knn_point(nsample, xyz, new_xyz) 45 | else: 46 | idx, pts_cnt = query_ball_point(radius, nsample, xyz, new_xyz) 47 | grouped_xyz = group_point(xyz, idx) # (batch_size, npoint, nsample, 3) 48 | grouped_xyz -= tf.tile(tf.expand_dims(new_xyz, 2), [1, 1, nsample, 1]) # translation normalization 49 | if points is not None: 50 | grouped_points = group_point(points, idx) # (batch_size, npoint, nsample, channel) 51 | if use_xyz: 52 | new_points = tf.concat([grouped_xyz, grouped_points], axis=-1) # (batch_size, npoint, nample, 3+channel) 53 | else: 54 | new_points = grouped_points 55 | else: 56 | new_points = grouped_xyz 57 | 58 | return new_xyz, new_points, idx, grouped_xyz 59 | 60 | 61 | def sample_and_group_all(xyz, points, use_xyz=True): 62 | ''' 63 | Inputs: 64 | xyz: (batch_size, ndataset, 3) TF tensor 65 | points: (batch_size, ndataset, channel) TF tensor, if None will just use xyz as points 66 | use_xyz: bool, if True concat XYZ with local point features, otherwise just use point features 67 | Outputs: 68 | new_xyz: (batch_size, 1, 3) as (0,0,0) 69 | new_points: (batch_size, 1, ndataset, 3+channel) TF tensor 70 | Note: 71 | Equivalent to sample_and_group with npoint=1, radius=inf, use (0,0,0) as the centroid 72 | ''' 73 | batch_size = xyz.get_shape()[0].value 74 | nsample = xyz.get_shape()[1].value 75 | new_xyz = tf.constant(np.tile(np.array([0, 0, 0]).reshape((1, 1, 3)), (batch_size, 1, 1)), 76 | dtype=tf.float32) # (batch_size, 1, 3) 77 | idx = tf.constant(np.tile(np.array(range(nsample)).reshape((1, 1, nsample)), (batch_size, 1, 1))) 78 | grouped_xyz = tf.reshape(xyz, (batch_size, 1, nsample, 3)) # (batch_size, npoint=1, nsample, 3) 79 | if points is not None: 80 | if use_xyz: 81 | new_points = tf.concat([xyz, points], axis=2) # (batch_size, 16, 259) 82 | else: 83 | new_points = points 84 | new_points = tf.expand_dims(new_points, 1) # (batch_size, 1, 16, 259) 85 | else: 86 | new_points = grouped_xyz 87 | return new_xyz, new_points, idx, grouped_xyz 88 | 89 | 90 | def pointnet_sa_module(xyz, points, npoint, radius, nsample, mlp, mlp2, group_all, is_training, bn_decay, scope, 91 | bn=True, pooling='max', knn=False, use_xyz=True, use_nchw=False, is_dist=False): 92 | ''' PointNet Set Abstraction (SA) Module 93 | Input: 94 | xyz: (batch_size, ndataset, 3) TF tensor 95 | points: (batch_size, ndataset, channel) TF tensor 96 | npoint: int32 -- #points sampled in farthest point sampling 97 | radius: float32 -- search radius in local region 98 | nsample: int32 -- how many points in each local region 99 | mlp: list of int32 -- output size for MLP on each point 100 | mlp2: list of int32 -- output size for MLP on each region 101 | group_all: bool -- group all points into one PC if set true, OVERRIDE 102 | npoint, radius and nsample settings 103 | use_xyz: bool, if True concat XYZ with local point features, otherwise just use point features 104 | use_nchw: bool, if True, use NCHW data format for conv2d, which is usually faster than NHWC format 105 | Return: 106 | new_xyz: (batch_size, npoint, 3) TF tensor 107 | new_points: (batch_size, npoint, mlp[-1] or mlp2[-1]) TF tensor 108 | idx: (batch_size, npoint, nsample) int32 -- indices for local regions 109 | ''' 110 | data_format = 'NCHW' if use_nchw else 'NHWC' 111 | with tf.variable_scope(scope) as sc: 112 | # Sample and Grouping 113 | if group_all: 114 | nsample = xyz.get_shape()[1].value 115 | new_xyz, new_points, idx, grouped_xyz = sample_and_group_all(xyz, points, use_xyz) 116 | else: 117 | new_xyz, new_points, idx, grouped_xyz = sample_and_group(npoint, radius, nsample, xyz, points, knn, use_xyz) 118 | 119 | # Point Feature Embedding 120 | if use_nchw: new_points = tf.transpose(new_points, [0, 3, 1, 2]) 121 | for i, num_out_channel in enumerate(mlp): 122 | new_points = tf_util.conv2d(new_points, num_out_channel, [1, 1], 123 | padding='VALID', stride=[1, 1], 124 | bn=bn, is_training=is_training, 125 | scope='conv%d' % (i), bn_decay=bn_decay, 126 | data_format=data_format, is_dist=is_dist) 127 | if use_nchw: new_points = tf.transpose(new_points, [0, 2, 3, 1]) 128 | 129 | # Pooling in Local Regions 130 | if pooling == 'max': 131 | new_points = tf.reduce_max(new_points, axis=[2], keep_dims=True, name='maxpool') 132 | elif pooling == 'avg': 133 | new_points = tf.reduce_mean(new_points, axis=[2], keep_dims=True, name='avgpool') 134 | elif pooling == 'weighted_avg': 135 | with tf.variable_scope('weighted_avg'): 136 | dists = tf.norm(grouped_xyz, axis=-1, ord=2, keep_dims=True) 137 | exp_dists = tf.exp(-dists * 5) 138 | weights = exp_dists / tf.reduce_sum(exp_dists, axis=2, 139 | keep_dims=True) # (batch_size, npoint, nsample, 1) 140 | new_points *= weights # (batch_size, npoint, nsample, mlp[-1]) 141 | new_points = tf.reduce_sum(new_points, axis=2, keep_dims=True) 142 | elif pooling == 'max_and_avg': 143 | max_points = tf.reduce_max(new_points, axis=[2], keep_dims=True, name='maxpool') 144 | avg_points = tf.reduce_mean(new_points, axis=[2], keep_dims=True, name='avgpool') 145 | new_points = tf.concat([avg_points, max_points], axis=-1) 146 | 147 | # [Optional] Further Processing 148 | if mlp2 is not None: 149 | if use_nchw: new_points = tf.transpose(new_points, [0, 3, 1, 2]) 150 | for i, num_out_channel in enumerate(mlp2): 151 | new_points = tf_util.conv2d(new_points, num_out_channel, [1, 1], 152 | padding='VALID', stride=[1, 1], 153 | bn=bn, is_training=is_training, 154 | scope='conv_post_%d' % (i), bn_decay=bn_decay, 155 | data_format=data_format, is_dist=is_dist) 156 | if use_nchw: new_points = tf.transpose(new_points, [0, 2, 3, 1]) 157 | 158 | new_points = tf.squeeze(new_points, [2]) # (batch_size, npoints, mlp2[-1]) 159 | return new_xyz, new_points, idx 160 | 161 | 162 | def pointnet_fp_module(xyz1, xyz2, points1, points2, mlp, is_training, bn_decay, scope, bn=True, is_dist=False): 163 | ''' PointNet Feature Propogation (FP) Module 164 | Input: 165 | xyz1: (batch_size, ndataset1, 3) TF tensor 166 | xyz2: (batch_size, ndataset2, 3) TF tensor, sparser than xyz1 167 | points1: (batch_size, ndataset1, nchannel1) TF tensor 168 | points2: (batch_size, ndataset2, nchannel2) TF tensor 169 | mlp: list of int32 -- output size for MLP on each point 170 | Return: 171 | new_points: (batch_size, ndataset1, mlp[-1]) TF tensor 172 | ''' 173 | with tf.variable_scope(scope) as sc: 174 | dist, idx = three_nn(xyz1, xyz2) 175 | dist = tf.maximum(dist, 1e-10) 176 | norm = tf.reduce_sum((1.0 / dist), axis=2, keep_dims=True) 177 | norm = tf.tile(norm, [1, 1, 3]) 178 | weight = (1.0 / dist) / norm 179 | interpolated_points = three_interpolate(points2, idx, weight) 180 | 181 | if points1 is not None: 182 | new_points1 = tf.concat(axis=2, values=[interpolated_points, points1]) # B,ndataset1,nchannel1+nchannel2 183 | else: 184 | new_points1 = interpolated_points 185 | new_points1 = tf.expand_dims(new_points1, 2) 186 | for i, num_out_channel in enumerate(mlp): 187 | new_points1 = tf_util.conv2d(new_points1, num_out_channel, [1, 1], 188 | padding='VALID', stride=[1, 1], 189 | bn=bn, is_training=is_training, 190 | scope='conv_%d' % (i), bn_decay=bn_decay, 191 | is_dist=is_dist) 192 | new_points1 = tf.squeeze(new_points1, [2]) # B,ndataset1,mlp[-1] 193 | return new_points1 194 | 195 | 196 | def pointnet_upsample(xyz1, xyz2, points2, scope): 197 | """ PointNet Feature Propogation (FP) Module 198 | Input: 199 | xyz1: (batch_size, ndataset1, 3) TF tensor 200 | xyz2: (batch_size, ndataset2, 3) TF tensor, sparser than xyz1 201 | points2: (batch_size, ndataset2, nchannel2) TF tensor 202 | Return: 203 | new_points: (batch_size, ndataset1, nchannel2) TF tensor 204 | """ 205 | with tf.variable_scope(scope) as sc: 206 | dist, idx = three_nn(xyz1, xyz2) 207 | dist = tf.maximum(dist, 1e-10) 208 | norm = tf.reduce_sum((1.0 / dist), axis=2, keep_dims=True) 209 | norm = tf.tile(norm, [1, 1, 3]) 210 | weight = (1.0 / dist) / norm 211 | interpolated_points = three_interpolate(points2, idx, weight) # B x ndataset1 x nchannel2 212 | 213 | return interpolated_points 214 | -------------------------------------------------------------------------------- /utils/provider.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import h5py 3 | 4 | # BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 5 | # sys.path.append(BASE_DIR) 6 | # 7 | # # Download dataset for point cloud classification 8 | # DATA_DIR = os.path.join(BASE_DIR, 'data') 9 | # if not os.path.exists(DATA_DIR): 10 | # os.mkdir(DATA_DIR) 11 | # if not os.path.exists(os.path.join(DATA_DIR, 'modelnet40_ply_hdf5_2048')): 12 | # www = 'https://shapenet.cs.stanford.edu/media/modelnet40_ply_hdf5_2048.zip' 13 | # zipfile = os.path.basename(www) 14 | # os.system('wget %s; unzip %s' % (www, zipfile)) 15 | # os.system('mv %s %s' % (zipfile[:-4], DATA_DIR)) 16 | # os.system('rm %s' % (zipfile)) 17 | 18 | 19 | def shuffle_data(data, labels, ins_label=None): 20 | """ Shuffle data and labels. 21 | Input: 22 | data: B,N,... numpy array 23 | label: B,... numpy array 24 | Return: 25 | shuffled data, label and shuffle indices 26 | """ 27 | idx = np.arange(len(labels)) 28 | np.random.shuffle(idx) 29 | if ins_label is None: 30 | return data[idx, ...], labels[idx], idx 31 | else: 32 | return data[idx, ...], labels[idx], ins_label[idx] 33 | 34 | 35 | def rotate_point_cloud(batch_data): 36 | """ Randomly rotate the point clouds to augument the dataset 37 | rotation is per shape based along up direction 38 | Input: 39 | BxNx3 array, original batch of point clouds 40 | Return: 41 | BxNx3 array, rotated batch of point clouds 42 | """ 43 | rotated_data = np.zeros(batch_data.shape, dtype=np.float32) 44 | for k in range(batch_data.shape[0]): 45 | rotation_angle = np.random.uniform() * 2 * np.pi 46 | cosval = np.cos(rotation_angle) 47 | sinval = np.sin(rotation_angle) 48 | rotation_matrix = np.array([[cosval, 0, sinval], 49 | [0, 1, 0], 50 | [-sinval, 0, cosval]]) 51 | shape_pc = batch_data[k, ...] 52 | rotated_data[k, ...] = np.dot(shape_pc.reshape((-1, 3)), rotation_matrix) 53 | return rotated_data 54 | 55 | 56 | def rotate_point_cloud_3z(batch_data): 57 | """ randomly rotate the point clouds to augument the dataset 58 | rotation is per shape based along z direction 59 | :param batch_data: B x N x 3 array, original batch of point clouds 60 | :return: B x N x 3 array, rotated batch of point clouds 61 | """ 62 | rotated_data = np.zeros(batch_data.shape, dtype=np.float32) 63 | for k in range(batch_data.shape[0]): 64 | rotation_angle = np.random.uniform() * 2 * np.pi 65 | cv = np.cos(rotation_angle) 66 | sv = np.sin(rotation_angle) 67 | rotation_matrix = np.array([[cv, -sv, 0], 68 | [sv, cv, 0], 69 | [ 0, 0, 1]]) 70 | shape_pc = batch_data[k, ...] 71 | rotated_data[k, ...] = np.dot(shape_pc.reshape((-1, 3)), rotation_matrix) 72 | 73 | return rotated_data 74 | 75 | 76 | def rotate_point_cloud_by_angle(batch_data, rotation_angle): 77 | """ Rotate the point cloud along up direction with certain angle. 78 | Input: 79 | BxNx3 array, original batch of point clouds 80 | Return: 81 | BxNx3 array, rotated batch of point clouds 82 | """ 83 | rotated_data = np.zeros(batch_data.shape, dtype=np.float32) 84 | for k in range(batch_data.shape[0]): 85 | # rotation_angle = np.random.uniform() * 2 * np.pi 86 | cosval = np.cos(rotation_angle) 87 | sinval = np.sin(rotation_angle) 88 | rotation_matrix = np.array([[cosval, 0, sinval], 89 | [0, 1, 0], 90 | [-sinval, 0, cosval]]) 91 | shape_pc = batch_data[k, ...] 92 | rotated_data[k, ...] = np.dot(shape_pc.reshape((-1, 3)), rotation_matrix) 93 | return rotated_data 94 | 95 | 96 | def jitter_point_cloud(batch_data, sigma=0.01, clip=0.05): 97 | """ Randomly jitter points. jittering is per point. 98 | Input: 99 | BxNx3 array, original batch of point clouds 100 | Return: 101 | BxNx3 array, jittered batch of point clouds 102 | """ 103 | B, N, C = batch_data.shape 104 | assert (clip > 0) 105 | jittered_data = np.clip(sigma * np.random.randn(B, N, C), -1 * clip, clip) 106 | jittered_data += batch_data 107 | return jittered_data 108 | 109 | 110 | def save_h5_output(h5_filename, seg, segrefine, group, grouppred, label_dtype='uint8'): 111 | print(h5_filename) 112 | h5_fout = h5py.File(h5_filename) 113 | h5_fout.create_dataset( 114 | 'seglabel', data=seg, 115 | compression='gzip', compression_opts=1, 116 | dtype=label_dtype) 117 | h5_fout.create_dataset( 118 | 'segrefine', data=segrefine, 119 | compression='gzip', compression_opts=1, 120 | dtype=label_dtype) 121 | h5_fout.create_dataset( 122 | 'pid', data=group, 123 | compression='gzip', compression_opts=1, 124 | dtype=label_dtype) 125 | h5_fout.create_dataset( 126 | 'predpid', data=grouppred, 127 | compression='gzip', compression_opts=1, 128 | dtype=label_dtype) 129 | h5_fout.close() 130 | 131 | 132 | def getDataFiles(list_filename): 133 | return [line.rstrip() for line in open(list_filename)] 134 | 135 | 136 | def load_h5(h5_filename): 137 | f = h5py.File(h5_filename) 138 | data = f['data'][:] 139 | label = f['label'][:] 140 | return (data, label) 141 | 142 | 143 | def loadDataFile(filename): 144 | return load_h5(filename) 145 | 146 | 147 | def load_h5_data_label_seg(h5_filename): 148 | f = h5py.File(h5_filename) 149 | data = f['data'][:] 150 | label = f['label'][:] 151 | seg = f['pid'][:] 152 | return data, label, seg 153 | 154 | 155 | def loadDataFile_with_seg(filename): 156 | return load_h5_data_label_seg(filename) 157 | 158 | 159 | def loadDataFile_with_grouplabel(filename): 160 | f = h5py.File(filename) 161 | data = f['data'][:] 162 | # label = f['label'][:] 163 | group = f['pid'][:] # Nx1 164 | if 'groupcategory' in f: 165 | cate = f['groupcategory'][:] # Gx1 166 | else: 167 | cate = 0 168 | return data, group, cate 169 | 170 | 171 | def loadDataFile_with_groupseglabel(filename): 172 | f = h5py.File(filename) 173 | data = f['data'][:] 174 | # label = f['label'][:] 175 | group = f['pid'][:] # Nx1 176 | if 'groupcategory' in f: 177 | cate = f['groupcategory'][:] # Gx1 178 | else: 179 | cate = 0 180 | seg = -1 * np.ones_like(group) 181 | for i in range(group.shape[0]): 182 | for j in range(group.shape[1]): 183 | if group[i, j, 0] != -1 and cate[i, group[i, j, 0], 0] != -1: 184 | seg[i, j, 0] = cate[i, group[i, j, 0], 0] 185 | return data, group, cate, seg 186 | 187 | 188 | def loadDataFile_with_groupseglabel_sunrgbd(filename): 189 | f = h5py.File(filename) 190 | data = f['data'][:] 191 | group = f['pid'][:] # NxG 192 | if 'groupcategory' in f: 193 | cate = f['groupcategory'][:] # Gx1 194 | else: 195 | cate = 0 196 | if 'seglabel' in f: 197 | seg = f['seglabel'][:] 198 | else: 199 | seg = f['seglabels'][:] 200 | return (data, group, cate, seg) 201 | 202 | 203 | def loadDataFile_with_groupseglabel_scannet(filename): 204 | f = h5py.File(filename) 205 | data = f['data'][:] 206 | # label = f['label'][:] 207 | group = f['pid'][:] # NxG 208 | if 'groupcategory' in f: 209 | cate = f['groupcategory'][:] # Gx1 210 | else: 211 | cate = 0 212 | if 'seglabel' in f: 213 | seg = f['seglabel'][:] 214 | else: 215 | seg = f['seglabels'][:] 216 | return (data, group, cate, seg) 217 | 218 | 219 | def loadDataFile_with_groupseglabel_nuyv2(filename): 220 | f = h5py.File(filename) 221 | data = f['data'][:] 222 | group = f['pid'][:] # NxG 223 | if 'groupcategory' in f: 224 | cate = f['groupcategory'][:] # Gx1 225 | else: 226 | cate = 0 227 | if 'seglabel' in f: 228 | seg = f['seglabel'][:] 229 | else: 230 | seg = f['seglabels'][:] 231 | boxes = f['bbox'][:] 232 | return data, group, cate, seg, boxes 233 | 234 | 235 | def loadDataFile_with_groupseglabel_stanfordindoor(filename): 236 | f = h5py.File(filename) 237 | data = f['data'][:] 238 | group = f['pid'][:].astype(np.int32) # NxG 239 | if 'label' in f: 240 | label = f['label'][:].astype(np.int32) 241 | else: 242 | label = [] 243 | if 'seglabel' in f: 244 | seg = f['seglabel'][:].astype(np.int32) 245 | else: 246 | seg = f['seglabels'][:].astype(np.int32) 247 | return (data, group, label, seg) 248 | 249 | 250 | def loadDataFile_with_img(filename): 251 | f = h5py.File(filename) 252 | data = f['data'][:] 253 | group = f['pid'][:] # NxG 254 | seg = f['seglabel'][:] 255 | img = f['img'][:].transpose([2, 1, 0]) 256 | return data, group, seg, img 257 | -------------------------------------------------------------------------------- /utils/s3dis_utils/collect_indoor3d_data.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | FILE_DIR = os.path.dirname(os.path.abspath(__file__)) 5 | BASE_DIR = os.path.dirname(os.path.dirname(FILE_DIR)) 6 | 7 | sys.path.append(BASE_DIR) 8 | 9 | from utils import indoor3d_util 10 | 11 | anno_paths = [line.rstrip() for line in open(os.path.join(BASE_DIR, 'meta/anno_paths.txt'))] 12 | anno_paths = [os.path.join(indoor3d_util.DATA_PATH, p) for p in anno_paths] 13 | 14 | output_folder = os.path.join(BASE_DIR, 'data/stanford_indoor3d_ins.sem') 15 | if not os.path.exists(output_folder): 16 | os.mkdir(output_folder) 17 | 18 | # Note: there is an extra character in the v1.2 data in Area_5/hallway_6. It's fixed manually. 19 | for anno_path in anno_paths: 20 | print(anno_path) 21 | try: 22 | elements = anno_path.split('/') 23 | out_filename = elements[-3]+'_'+elements[-2]+'.npy' # Area_1_hallway_1.npy 24 | if os.path.exists(os.path.join(output_folder, out_filename)): 25 | continue 26 | indoor3d_util.collect_point_label(anno_path, os.path.join(output_folder, out_filename), 'numpy') 27 | except: 28 | print(anno_path, 'ERROR!!') 29 | -------------------------------------------------------------------------------- /utils/s3dis_utils/dataset_s3dis.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import time 4 | import gc 5 | import numpy as np 6 | 7 | import multiprocessing 8 | from concurrent import futures 9 | from functools import partial as functools_partial 10 | 11 | BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 12 | ROOT_DIR = os.path.dirname(os.path.dirname(BASE_DIR)) 13 | sys.path.append(BASE_DIR) 14 | sys.path.append(ROOT_DIR) 15 | sys.path.append(os.path.join(ROOT_DIR, 'utils')) 16 | 17 | import provider 18 | import indoor3d_util 19 | 20 | 21 | def data_sample(data_sample_queue, input_list, split, epoch, num_works, block_points=4096, 22 | block_size=1.0, stride=0.5, random_sample=False, sample_num=None, sample_aug=1): 23 | assert (input_list[0].endswith('npy') or input_list[0].endswith('h5')), "data format must be .npy or .h5" 24 | 25 | input_list_length = len(input_list) 26 | num_work = min(min(num_works, multiprocessing.cpu_count()), input_list_length // 4) 27 | 28 | if input_list_length > 4: 29 | num_work = max(num_work, 4) 30 | 31 | chunksize = input_list_length // num_work 32 | print("num input_list: {}, num works: {}, chunksize: {}".format(input_list_length, num_work, chunksize)) 33 | 34 | if input_list[0].endswith('npy'): 35 | data_sample_func = functools_partial( 36 | indoor3d_util.room2blocks_wrapper_normalized, num_point=block_points, block_size=block_size, 37 | stride=stride, random_sample=random_sample, sample_num=sample_num, sample_aug=sample_aug) 38 | elif input_list[0].endswith('h5'): 39 | def load_data_file(input_file): 40 | cur_data, cur_group, _, cur_sem = provider.loadDataFile_with_groupseglabel_stanfordindoor(input_file) 41 | return cur_data, cur_sem, cur_group 42 | data_sample_func = load_data_file 43 | 44 | def data_sample_single(input_file): 45 | datalabel = data_sample_func(input_file) 46 | if split == 'train': 47 | datalabel = provider.shuffle_data(*datalabel) 48 | return datalabel 49 | 50 | for _ in range(epoch): 51 | np.random.shuffle(input_list) 52 | for idx in range(chunksize + 1): 53 | start_idx = min(idx * num_work, input_list_length) 54 | end_idx = min((idx + 1) * num_work, input_list_length) 55 | if start_idx >= input_list_length or end_idx > input_list_length: 56 | continue 57 | 58 | with futures.ThreadPoolExecutor(num_work) as pool: 59 | data_sem_ins = list(pool.map(data_sample_single, input_list[start_idx:end_idx], chunksize=1)) 60 | 61 | for dsi in data_sem_ins: 62 | shuffle_dsi = provider.shuffle_data(*dsi) 63 | data_sample_queue.put(shuffle_dsi) 64 | del dsi 65 | gc.collect() 66 | 67 | pool.shutdown() 68 | gc.collect() 69 | 70 | 71 | def data_prepare(data_sample_queue, data_queue, blocks, epoch, batch_size): 72 | data_list = list() 73 | sem_label_list = list() 74 | ins_label_list = list() 75 | 76 | total_batch = (blocks // batch_size) * epoch 77 | 78 | while total_batch > 0: 79 | data, sem_label, ins_label = data_sample_queue.get() 80 | 81 | data_list.append(data) 82 | sem_label_list.append(sem_label) 83 | ins_label_list.append(ins_label) 84 | 85 | del data 86 | del sem_label 87 | del ins_label 88 | 89 | batch_data = np.concatenate(data_list, axis=0) 90 | batch_sem_label = np.concatenate(sem_label_list, axis=0) 91 | batch_ins_label = np.concatenate(ins_label_list, axis=0) 92 | 93 | batch_data_length = batch_data.shape[0] 94 | num_batch_size = batch_data_length // batch_size 95 | for idx in range(num_batch_size): 96 | total_batch -= 1 97 | start_idx = idx * batch_size 98 | end_idx = (idx + 1) * batch_size 99 | data_queue.put((batch_data[start_idx: end_idx, ...], 100 | batch_sem_label[start_idx: end_idx], 101 | batch_ins_label[start_idx: end_idx])) 102 | 103 | remainder = batch_data_length % batch_size 104 | if remainder: 105 | data_list = [batch_data[-remainder:]] 106 | sem_label_list = [batch_sem_label[-remainder:]] 107 | ins_label_list = [batch_ins_label[-remainder:]] 108 | else: 109 | data_list = list() 110 | sem_label_list = list() 111 | ins_label_list = list() 112 | 113 | del batch_data 114 | del batch_sem_label 115 | del batch_ins_label 116 | 117 | gc.collect() 118 | 119 | 120 | class S3DISDataset(object): 121 | def __init__(self, data_root, input_list_txt, split='train', epoch=1, batch_size=24, num_works=8, 122 | data_type='numpy', block_points=4096, block_size=1.0, stride=0.5, random_sample=False, 123 | sample_num=None, sample_aug=1, with_rgb=True): 124 | self.input_list_txt = input_list_txt 125 | self.split = split 126 | self.data_root = data_root 127 | self.data_type = data_type 128 | self.capacity = 30 129 | self.length = 0 130 | 131 | assert (data_type == 'numpy' or data_type == 'hdf5'), 'data_type must be "numpy" or "hdf5"' 132 | 133 | self.input_list = self.get_input_list() 134 | 135 | self.manager = multiprocessing.Manager() 136 | self.data_sample_queue = self.manager.Queue(3) 137 | self.data_queue = multiprocessing.Manager().Queue(self.capacity) 138 | 139 | self.producer_process = multiprocessing.Process(target=data_sample, args=( 140 | self.data_sample_queue, self.input_list, split, epoch, num_works, 141 | block_points, block_size, stride, random_sample, sample_num, sample_aug)) 142 | 143 | self.consumer_process = multiprocessing.Process(target=data_prepare, args=( 144 | self.data_sample_queue, self.data_queue, self.length, epoch, batch_size)) 145 | 146 | self.producer_process.start() 147 | self.consumer_process.start() 148 | 149 | def __del__(self): 150 | while not self.data_sample_queue.empty() and not self.data_queue.empty(): 151 | self.data_queue.get_nowait() 152 | 153 | if self.producer_process.is_alive(): 154 | self.producer_process.join() 155 | 156 | if self.consumer_process.is_alive(): 157 | self.consumer_process.join() 158 | 159 | def __len__(self): 160 | return self.length 161 | 162 | def get_input_list(self): 163 | input_list = [line.strip() for line in open(self.input_list_txt, 'r')] 164 | temp_list = [item.split('/')[-1].strip('.h5').strip('.npy') for item in input_list] 165 | temp_input_list = [line.strip() for line in 166 | open(os.path.join(self.data_root, 'data/indoor3d_ins_seg_hdf5/room_filelist.txt'), 'r')] 167 | cnt_length = 0 168 | for item in temp_input_list: 169 | if item in temp_list: 170 | cnt_length += 1 171 | 172 | del temp_input_list 173 | self.length = cnt_length 174 | input_list = [os.path.join(self.data_root, item) for item in input_list] 175 | 176 | return input_list 177 | 178 | def get_batch(self, data_aug=False): 179 | data, sem_label, ins_label = self.data_queue.get() 180 | 181 | if data_aug and self.split == 'train': 182 | data[:, :, 0:3] = provider.jitter_point_cloud(data[:, :, 0:3]) 183 | 184 | return data, sem_label, ins_label 185 | 186 | def get_length(self): 187 | return self.__len__() 188 | 189 | 190 | if __name__ == '__main__': 191 | batch_size = 24 192 | data_set = S3DISDataset(ROOT_DIR, 'data/test_file_list_Area1.txt', epoch=2) 193 | num_batch = data_set.get_length() // batch_size 194 | for epoch in range(2): 195 | for idx in range(num_batch): 196 | _, _, _ = data_set.get_batch() 197 | print('epoch/num_epoch: {}/{}; batch/num_batch: {}/{};'.format(epoch, 2, idx, num_batch)) 198 | time.sleep(1) 199 | print('finish') 200 | -------------------------------------------------------------------------------- /utils/s3dis_utils/s3dis_gen_h5.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | FILE_DIR = os.path.dirname(os.path.abspath(__file__)) 5 | BASE_DIR = os.path.dirname(os.path.dirname(FILE_DIR)) 6 | 7 | sys.path.append(BASE_DIR) 8 | sys.path.append(os.path.join(BASE_DIR, 'utils')) 9 | 10 | import data_prep_util 11 | from utils import indoor3d_util 12 | 13 | # Constants 14 | data_dir = os.path.join(BASE_DIR, 'data') 15 | indoor3d_data_dir = os.path.join(data_dir, 'stanford_indoor3d_ins.sem') 16 | NUM_POINT = 4096 17 | data_dtype = 'float32' 18 | label_dtype = 'int32' 19 | 20 | # Set paths 21 | filelist = os.path.join(BASE_DIR, 'meta/all_data_label.txt') 22 | data_label_files = [os.path.join(indoor3d_data_dir, line.rstrip()) for line in open(filelist)] 23 | output_dir = os.path.join(data_dir, 'indoor3d_ins_seg_hdf5') 24 | if not os.path.exists(output_dir): 25 | os.mkdir(output_dir) 26 | output_room_filelist = os.path.join(output_dir, 'room_filelist.txt') 27 | fout_room = open(output_room_filelist, 'w') 28 | 29 | sample_cnt = 0 30 | for i in range(0, len(data_label_files)): 31 | data_label_filename = data_label_files[i] 32 | fname = os.path.basename(data_label_filename).strip('.npy') 33 | if not os.path.exists(data_label_filename): 34 | continue 35 | data, label, inslabel = indoor3d_util.room2blocks_wrapper_normalized(data_label_filename, NUM_POINT, block_size=1.0, stride=0.5, 36 | random_sample=False, sample_num=None) 37 | for _ in range(data.shape[0]): 38 | fout_room.write(os.path.basename(data_label_filename)[0:-4]+'\n') 39 | 40 | sample_cnt += data.shape[0] 41 | h5_filename = os.path.join(output_dir, '%s.h5' % fname) 42 | print('{0}: {1}, {2}, {3}'.format(h5_filename, data.shape, label.shape, inslabel.shape)) 43 | if os.path.exists(h5_filename): 44 | continue 45 | data_prep_util.save_h5ins(h5_filename, 46 | data, 47 | label, 48 | inslabel, 49 | data_dtype, label_dtype) 50 | 51 | fout_room.close() 52 | print("Total samples: {0}".format(sample_cnt)) 53 | --------------------------------------------------------------------------------