├── .gitignore
├── LICENSE
├── README.md
├── batch_generators
├── ReadWriteLock.py
├── __init__.py
├── batch_generator.py
├── center_batch_generator.py
├── multi_scale_batch_generator.py
└── neighboring_grid_batch_generator.py
├── datasets
├── __init__.py
├── color_constants.py
└── general_dataset.py
├── doc
└── exploring_header.png
├── experiments
└── iccvw_paper_2017
│ ├── s3dis_gru
│ ├── s3dis_gru_area_1.yaml
│ ├── s3dis_gru_area_2.yaml
│ ├── s3dis_gru_area_3.yaml
│ ├── s3dis_gru_area_4.yaml
│ ├── s3dis_gru_area_5.yaml
│ └── s3dis_gru_area_6.yaml
│ ├── s3dis_mscu
│ ├── s3dis_mscu_area_1.yaml
│ ├── s3dis_mscu_area_2.yaml
│ ├── s3dis_mscu_area_3.yaml
│ ├── s3dis_mscu_area_4.yaml
│ ├── s3dis_mscu_area_5.yaml
│ └── s3dis_mscu_area_6.yaml
│ └── s3dis_pointnet
│ ├── s3dis_pointnet_area_1.yaml
│ ├── s3dis_pointnet_area_2.yaml
│ ├── s3dis_pointnet_area_3.yaml
│ ├── s3dis_pointnet_area_4.yaml
│ ├── s3dis_pointnet_area_5.yaml
│ └── s3dis_pointnet_area_6.yaml
├── models
├── __init__.py
├── gru_neighbor_model.py
├── multi_block_model.py
├── multi_scale_cu_model.py
└── pointnet.py
├── optimizers
├── __init__.py
└── exponential_decay_adam.py
├── run.py
└── tools
├── __init__.py
├── downsample.py
├── evaluation.py
├── lazy_decorator.py
├── meta
└── class_names.txt
├── prepare_s3dis.py
├── tf_util.py
├── tools.py
└── viz.py
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | *__pycache__*
3 | *.obj
4 | .history*
5 | *.pyc
6 | cache/
7 | log/
8 | log2/
9 | *.npy
10 | logs/
11 | *.so
12 | *.sh
13 | 3d-semantic-segmentation.wiki/
14 |
15 | !experiments/
16 |
17 | experiments/*
18 | !experiments/iccvw_paper_2017/
19 | *.out
20 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Visual Computing Institute
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 | # Exploring Spatial Context for 3D Semantic Segmentation of Point Clouds
2 | Created by Francis Engelmann, Theodora Kontogianni, Alexander Hermans, Jonas Schult and Bastian Leibe
3 | from RWTH Aachen University.
4 |
5 | 
6 |
7 | ### Introduction
8 | This work is based on our paper
9 | [Exploring Spatial Context for 3D Semantic Segmentation of Point Clouds](https://www.vision.rwth-aachen.de/media/papers/PID4967025.pdf),
10 | which appeared at the IEEE International Conference on Computer Vision (ICCV) 2017, 3DRMS Workshop.
11 |
12 | You can also check our [project page](https://www.vision.rwth-aachen.de/page/3dsemseg) for further details.
13 |
14 | Deep learning approaches have made tremendous progress in the field of semantic segmentation over the past few years. However, most current approaches operate in the 2D image space. Direct semantic segmentation of unstructured 3D point clouds is still an open research problem. The recently proposed PointNet architecture presents an interesting step ahead in that it can operate on unstructured point clouds, achieving decent segmentation results. However, it subdivides the input points into a grid of blocks and processes each such block individually. In this paper, we investigate the question how such an architecture can be extended to incorporate larger-scale spatial context. We build upon PointNet and propose two extensions that enlarge the receptive field over the 3D scene. We evaluate the proposed strategies on challenging indoor and outdoor datasets and show improved results in both scenarios.
15 |
16 | In this repository, we release code for training and testing various pointcloud semantic segmentation networks on
17 | arbitrary datasets.
18 |
19 | ### Citation
20 | If you find our work useful in your research, please consider citing:
21 |
22 | @inproceedings{3dsemseg_ICCVW17,
23 | author = {Francis Engelmann and
24 | Theodora Kontogianni and
25 | Alexander Hermans and
26 | Bastian Leibe},
27 | title = {Exploring Spatial Context for 3D Semantic Segmentation of Point Clouds},
28 | booktitle = {{IEEE} International Conference on Computer Vision, 3DRMS Workshop, {ICCV}},
29 | year = {2017}
30 | }
31 |
32 |
33 | ### Installation
34 |
35 | Install TensorFlow.
36 | The code has been tested with Python 3.6 and TensorFlow 1.8.
37 |
38 | ### Usage
39 | In order to get more representative blocks, it is encouraged to uniformly downsample the original point clouds.
40 | This is done via the following script:
41 |
42 | python tools/downsample.py --data_dir path/to/dataset --cell_size 0.03
43 |
44 | This statement will produce pointclouds where each point will be representative for its 3cm x 3cm x 3cm neighborhood.
45 |
46 | To train/test a model for semantic segmentation on pointclouds, you need to run:
47 |
48 | python run.py --config path/to/config/file.yaml
49 |
50 | Detailed instruction of the structure for the yaml config file can be found in the wiki.
51 | Additionally, some example configuration files are given in the folder `experiments`.
52 |
53 | Note that the final evaluation is done on the full sized point clouds using k-nn interpolation.
54 |
55 | ### Reproducing the scores of our paper for stanford indoor 3d
56 |
57 | #### Downloading the data set
58 | First of all, Stanford Large-Scale 3D Indoor Spaces Dataset has to be downloaded.
59 | Follow the instructions [here](https://docs.google.com/forms/d/e/1FAIpQLScDimvNMCGhy_rmBA2gHfDu3naktRm6A8BPwAWWDv-Uhm6Shw/viewform?c=0&w=1).
60 | The aligned version 1.2 is used for our results.
61 |
62 | #### Producing numpy files from the original dataset
63 | Our pipeline cannot handle the original file type of s3dis. So, we need to convert it to npy files.
64 | Note that Area_5/hallway_6 has to be fixed manually due to format inconsistencies.
65 | The following script has to be run from the `tools` directory:
66 |
67 | python prepare_s3dis.py --input_dir path/to/dataset --output_dir path/to/output
68 |
69 | #### Downsampling for training
70 | Before training, we downsampled the pointclouds.
71 |
72 | python tools/downsample.py --data_dir path/to/dataset --cell_size 0.03
73 |
74 | #### Training configuration scripts
75 | Configuration files for all experiments are located in `experiments/iccvw_paper_2017/*`. For example, they can be
76 | launched as follows:
77 |
78 | python run.py --config experiments/iccvw_paper_2017/s3dis_mscu/s3dis_mscu_area_1.yaml
79 |
80 | The above script will run our multi scale consolidation unit network on stanford indoor 3d with test area 1.
81 |
82 | #### Evaluating on full scale point clouds
83 | Reported scores on the dataset are based on the full scale pointclouds.
84 | In order to do so, we need to load the trained model and set the `TEST` flag.
85 |
86 | Replace `modus: TRAIN_VAL` with
87 |
88 | ```yaml
89 | modus: TEST
90 | model_path: 'path/to/trained/model/model_ckpts'
91 | ```
92 | which is located in the log directory specified for training.
93 |
94 |
95 | ### VKitti instructions
96 | * Coming soon...
97 |
98 | ### Trained models for downloading
99 | * Coming soon...
100 |
101 | ### License
102 | Our code is released under MIT License (see LICENSE file for details).
103 |
--------------------------------------------------------------------------------
/batch_generators/ReadWriteLock.py:
--------------------------------------------------------------------------------
1 | """
2 | copyright by:
3 | https://www.safaribooksonline.com/library/view/python-cookbook/0596001673/ch06s04.html
4 | """
5 |
6 | import threading
7 |
8 |
9 | class ReadWriteLock:
10 | """ A lock object that allows many simultaneous "read locks", but
11 | only one "write lock." """
12 |
13 | def __init__(self):
14 | self._read_ready = threading.Condition(threading.Lock())
15 | self._readers = 0
16 |
17 | def acquire_read(self):
18 | """ Acquire a read lock. Blocks only if a thread has
19 | acquired the write lock. """
20 | self._read_ready.acquire()
21 | try:
22 | self._readers += 1
23 | finally:
24 | self._read_ready.release()
25 |
26 | def release_read(self):
27 | """ Release a read lock. """
28 | self._read_ready.acquire()
29 | try:
30 | self._readers -= 1
31 | if not self._readers:
32 | self._read_ready.notifyAll()
33 | finally:
34 | self._read_ready.release()
35 |
36 | def acquire_write(self):
37 | """ Acquire a write lock. Blocks until there are no
38 | acquired read or write locks. """
39 | self._read_ready.acquire()
40 | while self._readers > 0:
41 | self._read_ready.wait()
42 |
43 | def release_write(self):
44 | """ Release a write lock. """
45 | self._read_ready.release()
46 |
--------------------------------------------------------------------------------
/batch_generators/__init__.py:
--------------------------------------------------------------------------------
1 | from batch_generators.batch_generator import *
2 | from batch_generators.center_batch_generator import *
3 | from batch_generators.multi_scale_batch_generator import *
4 | from batch_generators.neighboring_grid_batch_generator import *
5 |
--------------------------------------------------------------------------------
/batch_generators/batch_generator.py:
--------------------------------------------------------------------------------
1 | from abc import *
2 | import numpy as np
3 | import tensorflow as tf
4 | import itertools
5 | from tools.lazy_decorator import *
6 |
7 |
8 | class BatchGenerator(ABC):
9 | """
10 | Abstract base class for batch generators providing the code for parallel creation of batches
11 | """
12 |
13 | def __init__(self, dataset, batch_size, num_points, augmentation):
14 | """
15 | :param dataset: dataset object
16 | :type dataset: Dataset
17 | :param num_points: number of points in a batch
18 | :type num_points: int
19 | """
20 | self.dataset = dataset
21 | self._num_points = num_points
22 | self._batch_size = batch_size
23 | self._augmentation = augmentation
24 |
25 | @lazy_property
26 | def handle_pl(self):
27 | # Handle for datasets
28 | return tf.placeholder(tf.string, shape=[], name='handle_training_test')
29 |
30 | @lazy_property
31 | def next_element(self):
32 | iterator = tf.data.Iterator.from_string_handle(self.handle_pl, self.dataset_train.output_types)
33 | return iterator.get_next()
34 |
35 | @lazy_property
36 | def dataset_train(self):
37 | # Create dataset for training
38 | dataset_train = tf.data.Dataset.from_generator(self._next_train_index, tf.int64, tf.TensorShape([]))
39 | dataset_train = dataset_train.map(self._wrapped_generate_train_blob, num_parallel_calls=8)
40 | dataset_train = dataset_train.batch(self._batch_size)
41 | return dataset_train.prefetch(buffer_size=self._batch_size * 1)
42 |
43 | @lazy_property
44 | def iterator_train(self):
45 | return self.dataset_train.make_one_shot_iterator()
46 |
47 | @lazy_property
48 | def iterator_test(self):
49 | # Create dataset for testing
50 | dataset_test = tf.data.Dataset.from_generator(self._next_test_index, tf.int64, tf.TensorShape([]))
51 | dataset_test = dataset_test.map(self._wrapped_generate_test_blob, num_parallel_calls=8)
52 | dataset_test = dataset_test.batch(self._batch_size)
53 | dataset_test = dataset_test.prefetch(buffer_size=self._batch_size * 1)
54 | return dataset_test.make_one_shot_iterator()
55 |
56 | @property
57 | def batch_size(self):
58 | return self._batch_size
59 |
60 | @property
61 | def num_points(self):
62 | return self._num_points
63 |
64 | @property
65 | def input_shape(self):
66 | return self.pointclouds_pl.get_shape().as_list()
67 |
68 | @lazy_property
69 | @abstractmethod
70 | def pointclouds_pl(self):
71 | raise NotImplementedError('Should be defined in subclass')
72 |
73 | @lazy_property
74 | @abstractmethod
75 | def labels_pl(self):
76 | raise NotImplementedError('Should be defined in subclass')
77 |
78 | @lazy_property
79 | @abstractmethod
80 | def mask_pl(self):
81 | raise NotImplementedError('Should be defined in subclass')
82 |
83 | @lazy_property
84 | @abstractmethod
85 | def cloud_ids_pl(self):
86 | raise NotImplementedError('Should be defined in subclass')
87 |
88 | @lazy_property
89 | @abstractmethod
90 | def point_ids_pl(self):
91 | raise NotImplementedError('Should be defined in subclass')
92 |
93 | def _next_train_index(self):
94 | """
95 | get next index in training sample list (e.g. containing [pointcloud_id, center_x, center_y])
96 | Take care that list is shuffled for each epoch!
97 | :return: next index for training
98 | """
99 | for i in itertools.cycle(range(self.train_sample_idx.shape[0])):
100 | yield (i)
101 |
102 | def _next_test_index(self):
103 | """
104 | get next index in test sample list (e.g. containing [pointcloud_id, center_x, center_y])
105 | Take care that list is shuffled for each epoch!
106 | :return: next index for test
107 | """
108 | for i in itertools.cycle(range(self.test_sample_idx.shape[0])):
109 | yield (i)
110 |
111 | def _wrapped_generate_train_blob(self, index):
112 | return tf.py_func(func=self._generate_blob,
113 | # pos_id, train, aug_trans
114 | inp=[index, True, self._augmentation],
115 | # data labels mask
116 | Tout=(tf.float32, tf.int8, tf.int8, tf.int32, tf.int64),
117 |
118 | name='generate_train_blob')
119 |
120 | def _wrapped_generate_test_blob(self, index):
121 | return tf.py_func(func=self._generate_blob,
122 | # pos_id, train, aug_trans
123 | inp=(index, False, False),
124 | # data labels mask cloud_id point_id
125 | Tout=(tf.float32, tf.int8, tf.int8, tf.int32, tf.int64),
126 | name='generate_test_blob')
127 |
128 | @property
129 | def num_train_batches(self):
130 | return self.train_sample_idx.shape[0] // self._batch_size
131 |
132 | @property
133 | def num_test_batches(self):
134 | return int(np.ceil(self.test_sample_idx.shape[0] // self._batch_size))
135 |
136 | @property
137 | @abstractmethod
138 | def test_sample_idx(self):
139 | """
140 | :rtype: ndarray
141 | :return:
142 | """
143 | raise NotImplementedError('Should be defined in subclass')
144 |
145 | @property
146 | @abstractmethod
147 | def train_sample_idx(self):
148 | """
149 | :rtype: ndarray
150 | :return:
151 | """
152 | raise NotImplementedError('Should be defined in subclass')
153 |
154 | @abstractmethod
155 | def _generate_blob(self, index, train=True, aug_trans=True):
156 | raise NotImplementedError('Should be defined in subclass')
157 |
--------------------------------------------------------------------------------
/batch_generators/center_batch_generator.py:
--------------------------------------------------------------------------------
1 | from tqdm import *
2 | from sklearn.neighbors import BallTree
3 | from .batch_generator import *
4 | from .ReadWriteLock import ReadWriteLock
5 |
6 |
7 | class CenterBatchGenerator(BatchGenerator):
8 | """
9 | creates batches where the blobs are centered around a specific point in the grid cell
10 | """
11 |
12 | def __init__(self, dataset, batch_size, num_points, grid_spacing,
13 | augmentation=False, metric='euclidean'):
14 | self._rw_lock = ReadWriteLock()
15 |
16 | super().__init__(dataset, batch_size, num_points, augmentation)
17 |
18 | self._grid_spacing = grid_spacing
19 |
20 | self._shuffle_train = True
21 | self._shuffle_test = True
22 |
23 | # TODO make min_num_points_per_block variable
24 | self.min_num_points_per_block = 5
25 |
26 | self._ball_trees = self._calc_ball_trees(metric=metric)
27 | self._pc_center_pos = self._generate_center_positions()
28 |
29 | @property
30 | def ball_trees(self):
31 | return self._ball_trees
32 |
33 | @property
34 | def pc_center_pos(self):
35 | return self._pc_center_pos
36 |
37 | @property
38 | def test_sample_idx(self):
39 | # after shuffling we need to recalculate the indices
40 | if self._shuffle_test:
41 | self._test_sample_idx_cache = self.pc_center_pos[np.isin(self.pc_center_pos[:, 0], self.dataset.test_pc_idx)]
42 | self._shuffle_test = False
43 |
44 | return self._test_sample_idx_cache
45 |
46 | @property
47 | def train_sample_idx(self):
48 | # after shuffling we need to recalculate the indices
49 | if self._shuffle_train:
50 | self._train_sample_idx_cache = self.pc_center_pos[np.isin(self.pc_center_pos[:, 0], self.dataset.train_pc_idx)]
51 | self._shuffle_train = False
52 |
53 | return self._train_sample_idx_cache
54 |
55 | @staticmethod
56 | def _sample_data(data, num_sample):
57 | """ data is in N x ...
58 | we want to keep num_samplexC of them.
59 | if N > num_sample, we will randomly keep num_sample of them.
60 | if N < num_sample, we will randomly duplicate samples.
61 | """
62 | N = data.shape[0]
63 | if N == num_sample:
64 | return data, range(N)
65 | elif N > num_sample:
66 | sample = np.random.choice(N, num_sample)
67 | return data[sample, ...], sample
68 | else:
69 | sample = np.random.choice(N, num_sample - N)
70 | dup_data = data[sample, ...]
71 | return np.concatenate([data, dup_data], 0), list(range(N)) + (list(sample))
72 |
73 | def _calc_ball_trees(self, metric='euclidean'):
74 | ball_trees = []
75 | for pointcloud_data in tqdm(self.dataset.data, desc='Ball trees have to be calculated from scratch'):
76 | ball_trees.append(BallTree(pointcloud_data[:, :2], metric=metric))
77 | return ball_trees
78 |
79 | @abstractmethod
80 | def _generate_blob(self, index, train=True, aug_trans=True):
81 | raise NotImplementedError('Should be defined in subclass')
82 |
83 | def shuffle(self):
84 | self._rw_lock.acquire_write()
85 |
86 | try:
87 | self._shuffle_train = True
88 | self._shuffle_test = True
89 |
90 | # Randomly shuffle training data from epoch
91 | np.random.shuffle(self.pc_center_pos)
92 | finally:
93 | self._rw_lock.release_write()
94 |
95 | def _generate_center_positions(self):
96 | """
97 | Generate blob positions while making sure the grid stays inside the pointcloud bounding box
98 | :return: (point_cloud_id, pos_x, pos_y)
99 | """
100 | room_pos_list = []
101 | for room_id, room_data in enumerate(tqdm(self.dataset.data, desc='Calculate center positions')):
102 | room_maxs = np.amax(room_data[:, 0:2], axis=0)
103 | room_mins = np.amin(room_data[:, 0:2], axis=0)
104 | room_size = room_maxs - room_mins
105 | num_blobs = np.ceil(room_size / self._grid_spacing)
106 | num_blobs = num_blobs - np.array([1, 1]) + 1
107 |
108 | if num_blobs[0] <= 0:
109 | num_blobs[0] = 1
110 | if num_blobs[1] <= 0:
111 | num_blobs[1] = 1
112 |
113 | ctrs = [[room_mins[0] + x * self._grid_spacing + self._grid_spacing / 2.0,
114 | room_mins[1] + y * self._grid_spacing + self._grid_spacing / 2.0]
115 | for x in range(int(num_blobs[0]))
116 | for y in range(int(num_blobs[1]))]
117 |
118 | blob_point_ids_all = self.ball_trees[room_id].query_radius(np.reshape(ctrs, [-1, 2]),
119 | r=self._grid_spacing / 2.0)
120 |
121 | blob_point_ids_all = np.reshape(blob_point_ids_all, [-1, 1])
122 |
123 | ctrs = np.reshape(ctrs, [-1, 1, 2])
124 | for i in range(np.shape(ctrs)[0]):
125 | npoints = 0
126 | for j in range(np.shape(ctrs)[1]):
127 | npoints = npoints + np.shape(blob_point_ids_all[i, j])[0]
128 | if npoints >= self.min_num_points_per_block: # TODO CHECK IF MINPOINTS 5 IS GOOD
129 | room_pos_list.append(np.reshape(np.array([room_id, ctrs[i, 0, 0], ctrs[i, 0, 1]]), (1, 3)))
130 |
131 | return np.concatenate(room_pos_list)
132 |
133 |
134 | if __name__ == '__main__':
135 | pass
136 |
--------------------------------------------------------------------------------
/batch_generators/multi_scale_batch_generator.py:
--------------------------------------------------------------------------------
1 | from .center_batch_generator import *
2 | import numpy as np
3 |
4 |
5 | class MultiScaleBatchGenerator(CenterBatchGenerator):
6 | """
7 | Batch Generator for multi scale batches of different radii where the centers are equal
8 | """
9 |
10 | def __init__(self, dataset, params):
11 | super().__init__(dataset=dataset,
12 | num_points=params['num_points'],
13 | batch_size=params['batch_size'],
14 | grid_spacing=params['grid_spacing'],
15 | augmentation=params['augmentation'],
16 | metric=params['metric'])
17 |
18 | self._radii = params['radii']
19 |
20 | @lazy_property
21 | def pointclouds_pl(self):
22 | return tf.reshape(self.next_element[0], (self._batch_size, len(self._radii),
23 | self._num_points, self.dataset.num_features + 3))
24 |
25 | @lazy_property
26 | def labels_pl(self):
27 | return tf.reshape(self.next_element[1], (self._batch_size, 1, self._num_points))
28 |
29 | @lazy_property
30 | def mask_pl(self):
31 | return tf.reshape(self.next_element[2], (self._batch_size, 1))
32 |
33 | @lazy_property
34 | def cloud_ids_pl(self):
35 | return tf.reshape(self.next_element[3], (self._batch_size, 1))
36 |
37 | @lazy_property
38 | def point_ids_pl(self):
39 | return tf.reshape(self.next_element[4], (self._batch_size, 1, self._num_points))
40 |
41 | def _generate_blob(self, index, train=True, aug_trans=True):
42 | b_data = np.zeros((len(self._radii), self._num_points, self.dataset.num_features + 3), dtype=np.float32)
43 | b_label = np.zeros((len(self._radii), self._num_points), dtype=np.int8)
44 | b_mask = np.ones(len(self._radii), dtype=np.int8)
45 | b_cloud_ids = np.zeros(len(self._radii), dtype=np.int32)
46 | b_point_ids = np.zeros((len(self._radii), self._num_points), dtype=np.int64)
47 |
48 | self._rw_lock.acquire_read()
49 |
50 | try:
51 | if train:
52 | [pointcloud_id, grid_center_x, grid_center_y] = np.copy(self.train_sample_idx[index, :])
53 | else:
54 | [pointcloud_id, grid_center_x, grid_center_y] = np.copy(self.test_sample_idx[index, :])
55 | finally:
56 | self._rw_lock.release_read()
57 |
58 | pointcloud_id = int(pointcloud_id)
59 | pointcloud_data = self.dataset.data[pointcloud_id]
60 |
61 | max_x, max_y, max_z = np.amax(pointcloud_data[:, 0:3], axis=0)
62 | min_x, min_y, min_z = np.amin(pointcloud_data[:, 0:3], axis=0)
63 |
64 | diff_x = max_x - min_x
65 | diff_y = max_y - min_y
66 | diff_z = max_z - min_z
67 | noise_x = 0
68 | noise_y = 0
69 |
70 | if aug_trans:
71 | noise_x = np.random.uniform(-self._grid_spacing / 2.0, self._grid_spacing / 2.0)
72 | noise_y = np.random.uniform(-self._grid_spacing / 2.0, self._grid_spacing / 2.0)
73 |
74 | ctr_x = grid_center_x + noise_x
75 | ctr_y = grid_center_y + noise_y
76 | ctr = np.array([ctr_x, ctr_y])
77 |
78 | ctrs = [ctr for _ in range(len(self._radii))]
79 |
80 | blob_point_ids_all = self.ball_trees[pointcloud_id].query_radius(ctrs, r=self._radii)
81 |
82 | for radius_id, radius in enumerate(self._radii):
83 | blob_point_ids = np.array(blob_point_ids_all[radius_id])
84 | blob = pointcloud_data[blob_point_ids, :]
85 |
86 | if blob.shape[0] < self.min_num_points_per_block: # check here if it is empty, set mask to zero
87 | b_mask[radius_id] = 0
88 | else: # blob is not empty
89 | blob, point_ids = self._sample_data(blob, self._num_points)
90 |
91 | # apply normalizations to blob
92 | blob[:, :self.dataset.num_features] /= self.dataset.normalization
93 |
94 | # Normalized coordinates
95 | additional_feats = np.zeros((self._num_points, 3))
96 | blob = np.concatenate((blob, additional_feats), axis=-1)
97 |
98 | blob[:, -1] = blob[:, self.dataset.num_features] # put label to the end
99 | blob[:, self.dataset.num_features] = blob[:, 0] / diff_x
100 | blob[:, self.dataset.num_features+1] = blob[:, 1] / diff_y
101 | blob[:, self.dataset.num_features+2] = blob[:, 2] / diff_z
102 | blob[:, 0:2] -= ctr
103 |
104 | b_label[radius_id, :] = blob[:, -1]
105 | b_data[radius_id, :, :] = blob[:, :-1]
106 | b_point_ids[radius_id, :] = blob_point_ids[point_ids]
107 | b_cloud_ids[radius_id] = pointcloud_id
108 |
109 | # reference radius
110 | b_label = np.reshape(b_label[1, :], (1, b_label.shape[1]))
111 | b_mask = np.reshape(b_mask[1], (1))
112 | b_point_ids = np.reshape(b_point_ids[1, :],
113 | (1, b_point_ids.shape[1]))
114 | b_cloud_ids = np.reshape(b_cloud_ids[1], (1))
115 |
116 | return b_data, b_label, b_mask, b_cloud_ids, b_point_ids
117 |
118 |
119 | if __name__ == '__main__':
120 | pass
121 |
--------------------------------------------------------------------------------
/batch_generators/neighboring_grid_batch_generator.py:
--------------------------------------------------------------------------------
1 | from .center_batch_generator import *
2 |
3 |
4 | class NeighboringGridBatchGenerator(CenterBatchGenerator):
5 | """
6 | neighboring grid batch generator where different blobs are placed next to each other
7 | """
8 |
9 | def __init__(self, dataset, params):
10 | print(params)
11 | super().__init__(dataset=dataset,
12 | num_points=params['num_points'],
13 | batch_size=params['batch_size'],
14 | grid_spacing=params['grid_spacing'],
15 | augmentation=params['augmentation'],
16 | metric=params['metric'])
17 |
18 | self._grid_x = params['grid_x']
19 | self._grid_y = params['grid_y']
20 |
21 | self._radius = params['radius']
22 |
23 | # flattened version of grid
24 | self._num_of_blobs = self._grid_x * self._grid_y
25 |
26 | @property
27 | def num_of_blobs(self):
28 | return self._num_of_blobs
29 |
30 | @lazy_property
31 | def pointclouds_pl(self):
32 | return tf.reshape(self.next_element[0], (self._batch_size, self._num_of_blobs,
33 | self._num_points, self.dataset.num_features + 3))
34 |
35 | @lazy_property
36 | def labels_pl(self):
37 | return tf.reshape(self.next_element[1], (self._batch_size, self._num_of_blobs, self._num_points))
38 |
39 | @lazy_property
40 | def mask_pl(self):
41 | return tf.reshape(self.next_element[2], (self._batch_size, self._num_of_blobs))
42 |
43 | @lazy_property
44 | def cloud_ids_pl(self):
45 | return tf.reshape(self.next_element[3], (self._batch_size, self._num_of_blobs))
46 |
47 | @lazy_property
48 | def point_ids_pl(self):
49 | return tf.reshape(self.next_element[4], (self._batch_size, self._num_of_blobs, self._num_points))
50 |
51 | def _generate_blob(self, index, train=True, aug_trans=True):
52 | b_data = np.zeros((self._grid_x, self._grid_y, self._num_points, self.dataset.num_features + 3), dtype=np.float32)
53 | b_label = np.zeros((self._grid_x, self._grid_y, self._num_points), dtype=np.int8)
54 | b_mask = np.ones((self._grid_x, self._grid_y), dtype=np.int8)
55 | b_cloud_ids = np.zeros((self._grid_x, self._grid_y), dtype=np.int32)
56 | b_point_ids = np.zeros((self._grid_x, self._grid_y, self._num_points), dtype=np.int64)
57 |
58 | self._rw_lock.acquire_read()
59 |
60 | try:
61 | if train:
62 | [pointcloud_id, grid_center_x, grid_center_y] = np.copy(self.train_sample_idx[index, :])
63 | else:
64 | [pointcloud_id, grid_center_x, grid_center_y] = np.copy(self.test_sample_idx[index, :])
65 | finally:
66 | self._rw_lock.release_read()
67 |
68 | pointcloud_id = int(pointcloud_id)
69 | pointcloud_data = self.dataset.data[pointcloud_id]
70 | max_x, max_y, max_z = np.amax(pointcloud_data[:, 0:3], axis=0)
71 | min_x, min_y, min_z = np.amin(pointcloud_data[:, 0:3], axis=0)
72 |
73 | diff_x = max_x - min_x
74 | diff_y = max_y - min_y
75 | diff_z = max_z - min_z
76 |
77 | noise_x = 0
78 | noise_y = 0
79 |
80 | num_points = 0
81 |
82 | while num_points < self.min_num_points_per_block:
83 | if aug_trans:
84 | noise_x = np.random.uniform(-self._grid_spacing / 2.0, self._grid_spacing / 2.0)
85 | noise_y = np.random.uniform(-self._grid_spacing / 2.0, self._grid_spacing / 2.0)
86 |
87 | # Create centers
88 | ctrs = []
89 | for grid_x in range(self._grid_x):
90 | ctr_x = grid_center_x + grid_x * self._grid_spacing + noise_x
91 | for grid_y in range(self._grid_y):
92 | ctr_y = grid_center_y + grid_y * self._grid_spacing + noise_y
93 | ctr = np.array([ctr_x, ctr_y])
94 | ctrs.append(ctr)
95 |
96 | blob_point_ids_all = self.ball_trees[pointcloud_id].query_radius(ctrs, r=self._radius)
97 | num_points = len(blob_point_ids_all[0])
98 |
99 | curr_id = -1
100 | for grid_x in range(self._grid_x):
101 | ctr_x = grid_center_x + grid_x * self._grid_spacing + noise_x
102 | for grid_y in range(self._grid_y):
103 | ctr_y = grid_center_y + grid_y * self._grid_spacing + noise_y
104 | ctr = np.array([ctr_x, ctr_y])
105 |
106 | curr_id += 1
107 |
108 | # New
109 | blob_point_ids = blob_point_ids_all[curr_id]
110 | blob = np.copy(pointcloud_data[blob_point_ids, :])
111 |
112 | if blob.shape[0] < self.min_num_points_per_block: # check here if it is empty, set mask to zero
113 | b_mask[grid_x, grid_y] = 0
114 | else: # blob is not empty
115 | blob, point_ids = self._sample_data(blob, self._num_points)
116 |
117 | # apply normalizations to blob
118 | blob[:, :self.dataset.num_features] /= self.dataset.normalization
119 |
120 | # Normalized coordinates
121 | additional_feats = np.zeros((self._num_points, 3))
122 | blob = np.concatenate((blob, additional_feats), axis=-1)
123 |
124 | blob[:, -1] = blob[:, self.dataset.num_features] # put label to the end
125 | blob[:, self.dataset.num_features] = blob[:, 0] / diff_x
126 | blob[:, self.dataset.num_features + 1] = blob[:, 1] / diff_y
127 | blob[:, self.dataset.num_features + 2] = blob[:, 2] / diff_z
128 | blob[:, 0:2] -= ctr
129 |
130 | b_label[grid_x, grid_y, :] = blob[:, -1]
131 | b_data[grid_x, grid_y, :, :] = blob[:, :-1]
132 | b_point_ids[grid_x, grid_y, :] = blob_point_ids[point_ids]
133 |
134 | b_cloud_ids[grid_x, grid_y] = pointcloud_id
135 |
136 | return b_data, b_label, b_mask, b_cloud_ids, b_point_ids
137 |
138 |
139 | if __name__ == '__main__':
140 | pass
141 |
--------------------------------------------------------------------------------
/datasets/__init__.py:
--------------------------------------------------------------------------------
1 | from datasets.general_dataset import *
2 |
--------------------------------------------------------------------------------
/datasets/color_constants.py:
--------------------------------------------------------------------------------
1 | """
2 | Provide RGB color constants and a colors dictionary with
3 | elements formatted: colors[colorname] = CONSTANT
4 |
5 | adapted from: https://www.webucator.com/blog/2015/03/python-color-constants-module/
6 | """
7 |
8 | from collections import namedtuple, OrderedDict
9 | import numpy as np
10 |
11 | Color = namedtuple('RGB', 'red, green, blue')
12 | colors = {} # dict of colors
13 |
14 |
15 | class RGB(Color):
16 |
17 | def hex_format(self):
18 | """
19 | Returns color in hex format
20 | """
21 | return '#{:02X}{:02X}{:02X}'.format(self.red, self.green, self.blue)
22 |
23 | @property
24 | def npy(self):
25 | """
26 | Returns npy formatted rgb color
27 | :return:
28 | """
29 | return np.array([self.red, self.green, self.blue])
30 |
31 |
32 | # Color Contants
33 | ALICEBLUE = RGB(240, 248, 255)
34 | ANTIQUEWHITE = RGB(250, 235, 215)
35 | ANTIQUEWHITE1 = RGB(255, 239, 219)
36 | ANTIQUEWHITE2 = RGB(238, 223, 204)
37 | ANTIQUEWHITE3 = RGB(205, 192, 176)
38 | ANTIQUEWHITE4 = RGB(139, 131, 120)
39 | AQUA = RGB(0, 255, 255)
40 | AQUAMARINE1 = RGB(127, 255, 212)
41 | AQUAMARINE2 = RGB(118, 238, 198)
42 | AQUAMARINE3 = RGB(102, 205, 170)
43 | AQUAMARINE4 = RGB(69, 139, 116)
44 | AZURE1 = RGB(240, 255, 255)
45 | AZURE2 = RGB(224, 238, 238)
46 | AZURE3 = RGB(193, 205, 205)
47 | AZURE4 = RGB(131, 139, 139)
48 | BANANA = RGB(227, 207, 87)
49 | BEIGE = RGB(245, 245, 220)
50 | BISQUE1 = RGB(255, 228, 196)
51 | BISQUE2 = RGB(238, 213, 183)
52 | BISQUE3 = RGB(205, 183, 158)
53 | BISQUE4 = RGB(139, 125, 107)
54 | BLACK = RGB(0, 0, 0)
55 | BLANCHEDALMOND = RGB(255, 235, 205)
56 | BLUE = RGB(0, 0, 255)
57 | BLUE2 = RGB(0, 0, 238)
58 | BLUE3 = RGB(0, 0, 205)
59 | BLUE4 = RGB(0, 0, 139)
60 | BLUEVIOLET = RGB(138, 43, 226)
61 | BRICK = RGB(156, 102, 31)
62 | BROWN = RGB(165, 42, 42)
63 | BROWN1 = RGB(255, 64, 64)
64 | BROWN2 = RGB(238, 59, 59)
65 | BROWN3 = RGB(205, 51, 51)
66 | BROWN4 = RGB(139, 35, 35)
67 | BURLYWOOD = RGB(222, 184, 135)
68 | BURLYWOOD1 = RGB(255, 211, 155)
69 | BURLYWOOD2 = RGB(238, 197, 145)
70 | BURLYWOOD3 = RGB(205, 170, 125)
71 | BURLYWOOD4 = RGB(139, 115, 85)
72 | BURNTSIENNA = RGB(138, 54, 15)
73 | BURNTUMBER = RGB(138, 51, 36)
74 | CADETBLUE = RGB(95, 158, 160)
75 | CADETBLUE1 = RGB(152, 245, 255)
76 | CADETBLUE2 = RGB(142, 229, 238)
77 | CADETBLUE3 = RGB(122, 197, 205)
78 | CADETBLUE4 = RGB(83, 134, 139)
79 | CADMIUMORANGE = RGB(255, 97, 3)
80 | CADMIUMYELLOW = RGB(255, 153, 18)
81 | CARROT = RGB(237, 145, 33)
82 | CHARTREUSE1 = RGB(127, 255, 0)
83 | CHARTREUSE2 = RGB(118, 238, 0)
84 | CHARTREUSE3 = RGB(102, 205, 0)
85 | CHARTREUSE4 = RGB(69, 139, 0)
86 | CHOCOLATE = RGB(210, 105, 30)
87 | CHOCOLATE1 = RGB(255, 127, 36)
88 | CHOCOLATE2 = RGB(238, 118, 33)
89 | CHOCOLATE3 = RGB(205, 102, 29)
90 | CHOCOLATE4 = RGB(139, 69, 19)
91 | COBALT = RGB(61, 89, 171)
92 | COBALTGREEN = RGB(61, 145, 64)
93 | COLDGREY = RGB(128, 138, 135)
94 | CORAL = RGB(255, 127, 80)
95 | CORAL1 = RGB(255, 114, 86)
96 | CORAL2 = RGB(238, 106, 80)
97 | CORAL3 = RGB(205, 91, 69)
98 | CORAL4 = RGB(139, 62, 47)
99 | CORNFLOWERBLUE = RGB(100, 149, 237)
100 | CORNSILK1 = RGB(255, 248, 220)
101 | CORNSILK2 = RGB(238, 232, 205)
102 | CORNSILK3 = RGB(205, 200, 177)
103 | CORNSILK4 = RGB(139, 136, 120)
104 | CRIMSON = RGB(220, 20, 60)
105 | CYAN2 = RGB(0, 238, 238)
106 | CYAN3 = RGB(0, 205, 205)
107 | CYAN4 = RGB(0, 139, 139)
108 | DARKGOLDENROD = RGB(184, 134, 11)
109 | DARKGOLDENROD1 = RGB(255, 185, 15)
110 | DARKGOLDENROD2 = RGB(238, 173, 14)
111 | DARKGOLDENROD3 = RGB(205, 149, 12)
112 | DARKGOLDENROD4 = RGB(139, 101, 8)
113 | DARKGRAY = RGB(169, 169, 169)
114 | DARKGREEN = RGB(0, 100, 0)
115 | DARKKHAKI = RGB(189, 183, 107)
116 | DARKOLIVEGREEN = RGB(85, 107, 47)
117 | DARKOLIVEGREEN1 = RGB(202, 255, 112)
118 | DARKOLIVEGREEN2 = RGB(188, 238, 104)
119 | DARKOLIVEGREEN3 = RGB(162, 205, 90)
120 | DARKOLIVEGREEN4 = RGB(110, 139, 61)
121 | DARKORANGE = RGB(255, 140, 0)
122 | DARKORANGE1 = RGB(255, 127, 0)
123 | DARKORANGE2 = RGB(238, 118, 0)
124 | DARKORANGE3 = RGB(205, 102, 0)
125 | DARKORANGE4 = RGB(139, 69, 0)
126 | DARKORCHID = RGB(153, 50, 204)
127 | DARKORCHID1 = RGB(191, 62, 255)
128 | DARKORCHID2 = RGB(178, 58, 238)
129 | DARKORCHID3 = RGB(154, 50, 205)
130 | DARKORCHID4 = RGB(104, 34, 139)
131 | DARKSALMON = RGB(233, 150, 122)
132 | DARKSEAGREEN = RGB(143, 188, 143)
133 | DARKSEAGREEN1 = RGB(193, 255, 193)
134 | DARKSEAGREEN2 = RGB(180, 238, 180)
135 | DARKSEAGREEN3 = RGB(155, 205, 155)
136 | DARKSEAGREEN4 = RGB(105, 139, 105)
137 | DARKSLATEBLUE = RGB(72, 61, 139)
138 | DARKSLATEGRAY = RGB(47, 79, 79)
139 | DARKSLATEGRAY1 = RGB(151, 255, 255)
140 | DARKSLATEGRAY2 = RGB(141, 238, 238)
141 | DARKSLATEGRAY3 = RGB(121, 205, 205)
142 | DARKSLATEGRAY4 = RGB(82, 139, 139)
143 | DARKTURQUOISE = RGB(0, 206, 209)
144 | DARKVIOLET = RGB(148, 0, 211)
145 | DEEPPINK1 = RGB(255, 20, 147)
146 | DEEPPINK2 = RGB(238, 18, 137)
147 | DEEPPINK3 = RGB(205, 16, 118)
148 | DEEPPINK4 = RGB(139, 10, 80)
149 | DEEPSKYBLUE1 = RGB(0, 191, 255)
150 | DEEPSKYBLUE2 = RGB(0, 178, 238)
151 | DEEPSKYBLUE3 = RGB(0, 154, 205)
152 | DEEPSKYBLUE4 = RGB(0, 104, 139)
153 | DIMGRAY = RGB(105, 105, 105)
154 | DIMGRAY = RGB(105, 105, 105)
155 | DODGERBLUE1 = RGB(30, 144, 255)
156 | DODGERBLUE2 = RGB(28, 134, 238)
157 | DODGERBLUE3 = RGB(24, 116, 205)
158 | DODGERBLUE4 = RGB(16, 78, 139)
159 | EGGSHELL = RGB(252, 230, 201)
160 | EMERALDGREEN = RGB(0, 201, 87)
161 | FIREBRICK = RGB(178, 34, 34)
162 | FIREBRICK1 = RGB(255, 48, 48)
163 | FIREBRICK2 = RGB(238, 44, 44)
164 | FIREBRICK3 = RGB(205, 38, 38)
165 | FIREBRICK4 = RGB(139, 26, 26)
166 | FLESH = RGB(255, 125, 64)
167 | FLORALWHITE = RGB(255, 250, 240)
168 | FORESTGREEN = RGB(34, 139, 34)
169 | GAINSBORO = RGB(220, 220, 220)
170 | GHOSTWHITE = RGB(248, 248, 255)
171 | GOLD1 = RGB(255, 215, 0)
172 | GOLD2 = RGB(238, 201, 0)
173 | GOLD3 = RGB(205, 173, 0)
174 | GOLD4 = RGB(139, 117, 0)
175 | GOLDENROD = RGB(218, 165, 32)
176 | GOLDENROD1 = RGB(255, 193, 37)
177 | GOLDENROD2 = RGB(238, 180, 34)
178 | GOLDENROD3 = RGB(205, 155, 29)
179 | GOLDENROD4 = RGB(139, 105, 20)
180 | GRAY = RGB(128, 128, 128)
181 | GRAY1 = RGB(3, 3, 3)
182 | GRAY10 = RGB(26, 26, 26)
183 | GRAY11 = RGB(28, 28, 28)
184 | GRAY12 = RGB(31, 31, 31)
185 | GRAY13 = RGB(33, 33, 33)
186 | GRAY14 = RGB(36, 36, 36)
187 | GRAY15 = RGB(38, 38, 38)
188 | GRAY16 = RGB(41, 41, 41)
189 | GRAY17 = RGB(43, 43, 43)
190 | GRAY18 = RGB(46, 46, 46)
191 | GRAY19 = RGB(48, 48, 48)
192 | GRAY2 = RGB(5, 5, 5)
193 | GRAY20 = RGB(51, 51, 51)
194 | GRAY21 = RGB(54, 54, 54)
195 | GRAY22 = RGB(56, 56, 56)
196 | GRAY23 = RGB(59, 59, 59)
197 | GRAY24 = RGB(61, 61, 61)
198 | GRAY25 = RGB(64, 64, 64)
199 | GRAY26 = RGB(66, 66, 66)
200 | GRAY27 = RGB(69, 69, 69)
201 | GRAY28 = RGB(71, 71, 71)
202 | GRAY29 = RGB(74, 74, 74)
203 | GRAY3 = RGB(8, 8, 8)
204 | GRAY30 = RGB(77, 77, 77)
205 | GRAY31 = RGB(79, 79, 79)
206 | GRAY32 = RGB(82, 82, 82)
207 | GRAY33 = RGB(84, 84, 84)
208 | GRAY34 = RGB(87, 87, 87)
209 | GRAY35 = RGB(89, 89, 89)
210 | GRAY36 = RGB(92, 92, 92)
211 | GRAY37 = RGB(94, 94, 94)
212 | GRAY38 = RGB(97, 97, 97)
213 | GRAY39 = RGB(99, 99, 99)
214 | GRAY4 = RGB(10, 10, 10)
215 | GRAY40 = RGB(102, 102, 102)
216 | GRAY42 = RGB(107, 107, 107)
217 | GRAY43 = RGB(110, 110, 110)
218 | GRAY44 = RGB(112, 112, 112)
219 | GRAY45 = RGB(115, 115, 115)
220 | GRAY46 = RGB(117, 117, 117)
221 | GRAY47 = RGB(120, 120, 120)
222 | GRAY48 = RGB(122, 122, 122)
223 | GRAY49 = RGB(125, 125, 125)
224 | GRAY5 = RGB(13, 13, 13)
225 | GRAY50 = RGB(127, 127, 127)
226 | GRAY51 = RGB(130, 130, 130)
227 | GRAY52 = RGB(133, 133, 133)
228 | GRAY53 = RGB(135, 135, 135)
229 | GRAY54 = RGB(138, 138, 138)
230 | GRAY55 = RGB(140, 140, 140)
231 | GRAY56 = RGB(143, 143, 143)
232 | GRAY57 = RGB(145, 145, 145)
233 | GRAY58 = RGB(148, 148, 148)
234 | GRAY59 = RGB(150, 150, 150)
235 | GRAY6 = RGB(15, 15, 15)
236 | GRAY60 = RGB(153, 153, 153)
237 | GRAY61 = RGB(156, 156, 156)
238 | GRAY62 = RGB(158, 158, 158)
239 | GRAY63 = RGB(161, 161, 161)
240 | GRAY64 = RGB(163, 163, 163)
241 | GRAY65 = RGB(166, 166, 166)
242 | GRAY66 = RGB(168, 168, 168)
243 | GRAY67 = RGB(171, 171, 171)
244 | GRAY68 = RGB(173, 173, 173)
245 | GRAY69 = RGB(176, 176, 176)
246 | GRAY7 = RGB(18, 18, 18)
247 | GRAY70 = RGB(179, 179, 179)
248 | GRAY71 = RGB(181, 181, 181)
249 | GRAY72 = RGB(184, 184, 184)
250 | GRAY73 = RGB(186, 186, 186)
251 | GRAY74 = RGB(189, 189, 189)
252 | GRAY75 = RGB(191, 191, 191)
253 | GRAY76 = RGB(194, 194, 194)
254 | GRAY77 = RGB(196, 196, 196)
255 | GRAY78 = RGB(199, 199, 199)
256 | GRAY79 = RGB(201, 201, 201)
257 | GRAY8 = RGB(20, 20, 20)
258 | GRAY80 = RGB(204, 204, 204)
259 | GRAY81 = RGB(207, 207, 207)
260 | GRAY82 = RGB(209, 209, 209)
261 | GRAY83 = RGB(212, 212, 212)
262 | GRAY84 = RGB(214, 214, 214)
263 | GRAY85 = RGB(217, 217, 217)
264 | GRAY86 = RGB(219, 219, 219)
265 | GRAY87 = RGB(222, 222, 222)
266 | GRAY88 = RGB(224, 224, 224)
267 | GRAY89 = RGB(227, 227, 227)
268 | GRAY9 = RGB(23, 23, 23)
269 | GRAY90 = RGB(229, 229, 229)
270 | GRAY91 = RGB(232, 232, 232)
271 | GRAY92 = RGB(235, 235, 235)
272 | GRAY93 = RGB(237, 237, 237)
273 | GRAY94 = RGB(240, 240, 240)
274 | GRAY95 = RGB(242, 242, 242)
275 | GRAY97 = RGB(247, 247, 247)
276 | GRAY98 = RGB(250, 250, 250)
277 | GRAY99 = RGB(252, 252, 252)
278 | GREEN = RGB(0, 128, 0)
279 | GREEN1 = RGB(0, 255, 0)
280 | GREEN2 = RGB(0, 238, 0)
281 | GREEN3 = RGB(0, 205, 0)
282 | GREEN4 = RGB(0, 139, 0)
283 | GREENYELLOW = RGB(173, 255, 47)
284 | HONEYDEW1 = RGB(240, 255, 240)
285 | HONEYDEW2 = RGB(224, 238, 224)
286 | HONEYDEW3 = RGB(193, 205, 193)
287 | HONEYDEW4 = RGB(131, 139, 131)
288 | HOTPINK = RGB(255, 105, 180)
289 | HOTPINK1 = RGB(255, 110, 180)
290 | HOTPINK2 = RGB(238, 106, 167)
291 | HOTPINK3 = RGB(205, 96, 144)
292 | HOTPINK4 = RGB(139, 58, 98)
293 | INDIANRED = RGB(176, 23, 31)
294 | INDIANRED = RGB(205, 92, 92)
295 | INDIANRED1 = RGB(255, 106, 106)
296 | INDIANRED2 = RGB(238, 99, 99)
297 | INDIANRED3 = RGB(205, 85, 85)
298 | INDIANRED4 = RGB(139, 58, 58)
299 | INDIGO = RGB(75, 0, 130)
300 | IVORY1 = RGB(255, 255, 240)
301 | IVORY2 = RGB(238, 238, 224)
302 | IVORY3 = RGB(205, 205, 193)
303 | IVORY4 = RGB(139, 139, 131)
304 | IVORYBLACK = RGB(41, 36, 33)
305 | KHAKI = RGB(240, 230, 140)
306 | KHAKI1 = RGB(255, 246, 143)
307 | KHAKI2 = RGB(238, 230, 133)
308 | KHAKI3 = RGB(205, 198, 115)
309 | KHAKI4 = RGB(139, 134, 78)
310 | LAVENDER = RGB(230, 230, 250)
311 | LAVENDERBLUSH1 = RGB(255, 240, 245)
312 | LAVENDERBLUSH2 = RGB(238, 224, 229)
313 | LAVENDERBLUSH3 = RGB(205, 193, 197)
314 | LAVENDERBLUSH4 = RGB(139, 131, 134)
315 | LAWNGREEN = RGB(124, 252, 0)
316 | LEMONCHIFFON1 = RGB(255, 250, 205)
317 | LEMONCHIFFON2 = RGB(238, 233, 191)
318 | LEMONCHIFFON3 = RGB(205, 201, 165)
319 | LEMONCHIFFON4 = RGB(139, 137, 112)
320 | LIGHTBLUE = RGB(173, 216, 230)
321 | LIGHTBLUE1 = RGB(191, 239, 255)
322 | LIGHTBLUE2 = RGB(178, 223, 238)
323 | LIGHTBLUE3 = RGB(154, 192, 205)
324 | LIGHTBLUE4 = RGB(104, 131, 139)
325 | LIGHTCORAL = RGB(240, 128, 128)
326 | LIGHTCYAN1 = RGB(224, 255, 255)
327 | LIGHTCYAN2 = RGB(209, 238, 238)
328 | LIGHTCYAN3 = RGB(180, 205, 205)
329 | LIGHTCYAN4 = RGB(122, 139, 139)
330 | LIGHTGOLDENROD1 = RGB(255, 236, 139)
331 | LIGHTGOLDENROD2 = RGB(238, 220, 130)
332 | LIGHTGOLDENROD3 = RGB(205, 190, 112)
333 | LIGHTGOLDENROD4 = RGB(139, 129, 76)
334 | LIGHTGOLDENRODYELLOW = RGB(250, 250, 210)
335 | LIGHTGREY = RGB(211, 211, 211)
336 | LIGHTPINK = RGB(255, 182, 193)
337 | LIGHTPINK1 = RGB(255, 174, 185)
338 | LIGHTPINK2 = RGB(238, 162, 173)
339 | LIGHTPINK3 = RGB(205, 140, 149)
340 | LIGHTPINK4 = RGB(139, 95, 101)
341 | LIGHTSALMON1 = RGB(255, 160, 122)
342 | LIGHTSALMON2 = RGB(238, 149, 114)
343 | LIGHTSALMON3 = RGB(205, 129, 98)
344 | LIGHTSALMON4 = RGB(139, 87, 66)
345 | LIGHTSEAGREEN = RGB(32, 178, 170)
346 | LIGHTSKYBLUE = RGB(135, 206, 250)
347 | LIGHTSKYBLUE1 = RGB(176, 226, 255)
348 | LIGHTSKYBLUE2 = RGB(164, 211, 238)
349 | LIGHTSKYBLUE3 = RGB(141, 182, 205)
350 | LIGHTSKYBLUE4 = RGB(96, 123, 139)
351 | LIGHTSLATEBLUE = RGB(132, 112, 255)
352 | LIGHTSLATEGRAY = RGB(119, 136, 153)
353 | LIGHTSTEELBLUE = RGB(176, 196, 222)
354 | LIGHTSTEELBLUE1 = RGB(202, 225, 255)
355 | LIGHTSTEELBLUE2 = RGB(188, 210, 238)
356 | LIGHTSTEELBLUE3 = RGB(162, 181, 205)
357 | LIGHTSTEELBLUE4 = RGB(110, 123, 139)
358 | LIGHTYELLOW1 = RGB(255, 255, 224)
359 | LIGHTYELLOW2 = RGB(238, 238, 209)
360 | LIGHTYELLOW3 = RGB(205, 205, 180)
361 | LIGHTYELLOW4 = RGB(139, 139, 122)
362 | LIMEGREEN = RGB(50, 205, 50)
363 | LINEN = RGB(250, 240, 230)
364 | MAGENTA = RGB(255, 0, 255)
365 | MAGENTA2 = RGB(238, 0, 238)
366 | MAGENTA3 = RGB(205, 0, 205)
367 | MAGENTA4 = RGB(139, 0, 139)
368 | MANGANESEBLUE = RGB(3, 168, 158)
369 | MAROON = RGB(128, 0, 0)
370 | MAROON1 = RGB(255, 52, 179)
371 | MAROON2 = RGB(238, 48, 167)
372 | MAROON3 = RGB(205, 41, 144)
373 | MAROON4 = RGB(139, 28, 98)
374 | MEDIUMORCHID = RGB(186, 85, 211)
375 | MEDIUMORCHID1 = RGB(224, 102, 255)
376 | MEDIUMORCHID2 = RGB(209, 95, 238)
377 | MEDIUMORCHID3 = RGB(180, 82, 205)
378 | MEDIUMORCHID4 = RGB(122, 55, 139)
379 | MEDIUMPURPLE = RGB(147, 112, 219)
380 | MEDIUMPURPLE1 = RGB(171, 130, 255)
381 | MEDIUMPURPLE2 = RGB(159, 121, 238)
382 | MEDIUMPURPLE3 = RGB(137, 104, 205)
383 | MEDIUMPURPLE4 = RGB(93, 71, 139)
384 | MEDIUMSEAGREEN = RGB(60, 179, 113)
385 | MEDIUMSLATEBLUE = RGB(123, 104, 238)
386 | MEDIUMSPRINGGREEN = RGB(0, 250, 154)
387 | MEDIUMTURQUOISE = RGB(72, 209, 204)
388 | MEDIUMVIOLETRED = RGB(199, 21, 133)
389 | MELON = RGB(227, 168, 105)
390 | MIDNIGHTBLUE = RGB(25, 25, 112)
391 | MINT = RGB(189, 252, 201)
392 | MINTCREAM = RGB(245, 255, 250)
393 | MISTYROSE1 = RGB(255, 228, 225)
394 | MISTYROSE2 = RGB(238, 213, 210)
395 | MISTYROSE3 = RGB(205, 183, 181)
396 | MISTYROSE4 = RGB(139, 125, 123)
397 | MOCCASIN = RGB(255, 228, 181)
398 | NAVAJOWHITE1 = RGB(255, 222, 173)
399 | NAVAJOWHITE2 = RGB(238, 207, 161)
400 | NAVAJOWHITE3 = RGB(205, 179, 139)
401 | NAVAJOWHITE4 = RGB(139, 121, 94)
402 | NAVY = RGB(0, 0, 128)
403 | OLDLACE = RGB(253, 245, 230)
404 | OLIVE = RGB(128, 128, 0)
405 | OLIVEDRAB = RGB(107, 142, 35)
406 | OLIVEDRAB1 = RGB(192, 255, 62)
407 | OLIVEDRAB2 = RGB(179, 238, 58)
408 | OLIVEDRAB3 = RGB(154, 205, 50)
409 | OLIVEDRAB4 = RGB(105, 139, 34)
410 | ORANGE = RGB(255, 128, 0)
411 | ORANGE1 = RGB(255, 165, 0)
412 | ORANGE2 = RGB(238, 154, 0)
413 | ORANGE3 = RGB(205, 133, 0)
414 | ORANGE4 = RGB(139, 90, 0)
415 | ORANGERED1 = RGB(255, 69, 0)
416 | ORANGERED2 = RGB(238, 64, 0)
417 | ORANGERED3 = RGB(205, 55, 0)
418 | ORANGERED4 = RGB(139, 37, 0)
419 | ORCHID = RGB(218, 112, 214)
420 | ORCHID1 = RGB(255, 131, 250)
421 | ORCHID2 = RGB(238, 122, 233)
422 | ORCHID3 = RGB(205, 105, 201)
423 | ORCHID4 = RGB(139, 71, 137)
424 | PALEGOLDENROD = RGB(238, 232, 170)
425 | PALEGREEN = RGB(152, 251, 152)
426 | PALEGREEN1 = RGB(154, 255, 154)
427 | PALEGREEN2 = RGB(144, 238, 144)
428 | PALEGREEN3 = RGB(124, 205, 124)
429 | PALEGREEN4 = RGB(84, 139, 84)
430 | PALETURQUOISE1 = RGB(187, 255, 255)
431 | PALETURQUOISE2 = RGB(174, 238, 238)
432 | PALETURQUOISE3 = RGB(150, 205, 205)
433 | PALETURQUOISE4 = RGB(102, 139, 139)
434 | PALEVIOLETRED = RGB(219, 112, 147)
435 | PALEVIOLETRED1 = RGB(255, 130, 171)
436 | PALEVIOLETRED2 = RGB(238, 121, 159)
437 | PALEVIOLETRED3 = RGB(205, 104, 137)
438 | PALEVIOLETRED4 = RGB(139, 71, 93)
439 | PAPAYAWHIP = RGB(255, 239, 213)
440 | PEACHPUFF1 = RGB(255, 218, 185)
441 | PEACHPUFF2 = RGB(238, 203, 173)
442 | PEACHPUFF3 = RGB(205, 175, 149)
443 | PEACHPUFF4 = RGB(139, 119, 101)
444 | PEACOCK = RGB(51, 161, 201)
445 | PINK = RGB(255, 192, 203)
446 | PINK1 = RGB(255, 181, 197)
447 | PINK2 = RGB(238, 169, 184)
448 | PINK3 = RGB(205, 145, 158)
449 | PINK4 = RGB(139, 99, 108)
450 | PLUM = RGB(221, 160, 221)
451 | PLUM1 = RGB(255, 187, 255)
452 | PLUM2 = RGB(238, 174, 238)
453 | PLUM3 = RGB(205, 150, 205)
454 | PLUM4 = RGB(139, 102, 139)
455 | POWDERBLUE = RGB(176, 224, 230)
456 | PURPLE = RGB(128, 0, 128)
457 | PURPLE1 = RGB(155, 48, 255)
458 | PURPLE2 = RGB(145, 44, 238)
459 | PURPLE3 = RGB(125, 38, 205)
460 | PURPLE4 = RGB(85, 26, 139)
461 | RASPBERRY = RGB(135, 38, 87)
462 | RAWSIENNA = RGB(199, 97, 20)
463 | RED1 = RGB(255, 0, 0)
464 | RED2 = RGB(238, 0, 0)
465 | RED3 = RGB(205, 0, 0)
466 | RED4 = RGB(139, 0, 0)
467 | ROSYBROWN = RGB(188, 143, 143)
468 | ROSYBROWN1 = RGB(255, 193, 193)
469 | ROSYBROWN2 = RGB(238, 180, 180)
470 | ROSYBROWN3 = RGB(205, 155, 155)
471 | ROSYBROWN4 = RGB(139, 105, 105)
472 | ROYALBLUE = RGB(65, 105, 225)
473 | ROYALBLUE1 = RGB(72, 118, 255)
474 | ROYALBLUE2 = RGB(67, 110, 238)
475 | ROYALBLUE3 = RGB(58, 95, 205)
476 | ROYALBLUE4 = RGB(39, 64, 139)
477 | SALMON = RGB(250, 128, 114)
478 | SALMON1 = RGB(255, 140, 105)
479 | SALMON2 = RGB(238, 130, 98)
480 | SALMON3 = RGB(205, 112, 84)
481 | SALMON4 = RGB(139, 76, 57)
482 | SANDYBROWN = RGB(244, 164, 96)
483 | SAPGREEN = RGB(48, 128, 20)
484 | SEAGREEN1 = RGB(84, 255, 159)
485 | SEAGREEN2 = RGB(78, 238, 148)
486 | SEAGREEN3 = RGB(67, 205, 128)
487 | SEAGREEN4 = RGB(46, 139, 87)
488 | SEASHELL1 = RGB(255, 245, 238)
489 | SEASHELL2 = RGB(238, 229, 222)
490 | SEASHELL3 = RGB(205, 197, 191)
491 | SEASHELL4 = RGB(139, 134, 130)
492 | SEPIA = RGB(94, 38, 18)
493 | SGIBEET = RGB(142, 56, 142)
494 | SGIBRIGHTGRAY = RGB(197, 193, 170)
495 | SGICHARTREUSE = RGB(113, 198, 113)
496 | SGIDARKGRAY = RGB(85, 85, 85)
497 | SGIGRAY12 = RGB(30, 30, 30)
498 | SGIGRAY16 = RGB(40, 40, 40)
499 | SGIGRAY32 = RGB(81, 81, 81)
500 | SGIGRAY36 = RGB(91, 91, 91)
501 | SGIGRAY52 = RGB(132, 132, 132)
502 | SGIGRAY56 = RGB(142, 142, 142)
503 | SGIGRAY72 = RGB(183, 183, 183)
504 | SGIGRAY76 = RGB(193, 193, 193)
505 | SGIGRAY92 = RGB(234, 234, 234)
506 | SGIGRAY96 = RGB(244, 244, 244)
507 | SGILIGHTBLUE = RGB(125, 158, 192)
508 | SGILIGHTGRAY = RGB(170, 170, 170)
509 | SGIOLIVEDRAB = RGB(142, 142, 56)
510 | SGISALMON = RGB(198, 113, 113)
511 | SGISLATEBLUE = RGB(113, 113, 198)
512 | SGITEAL = RGB(56, 142, 142)
513 | SIENNA = RGB(160, 82, 45)
514 | SIENNA1 = RGB(255, 130, 71)
515 | SIENNA2 = RGB(238, 121, 66)
516 | SIENNA3 = RGB(205, 104, 57)
517 | SIENNA4 = RGB(139, 71, 38)
518 | SILVER = RGB(192, 192, 192)
519 | SKYBLUE = RGB(135, 206, 235)
520 | SKYBLUE1 = RGB(135, 206, 255)
521 | SKYBLUE2 = RGB(126, 192, 238)
522 | SKYBLUE3 = RGB(108, 166, 205)
523 | SKYBLUE4 = RGB(74, 112, 139)
524 | SLATEBLUE = RGB(106, 90, 205)
525 | SLATEBLUE1 = RGB(131, 111, 255)
526 | SLATEBLUE2 = RGB(122, 103, 238)
527 | SLATEBLUE3 = RGB(105, 89, 205)
528 | SLATEBLUE4 = RGB(71, 60, 139)
529 | SLATEGRAY = RGB(112, 128, 144)
530 | SLATEGRAY1 = RGB(198, 226, 255)
531 | SLATEGRAY2 = RGB(185, 211, 238)
532 | SLATEGRAY3 = RGB(159, 182, 205)
533 | SLATEGRAY4 = RGB(108, 123, 139)
534 | SNOW1 = RGB(255, 250, 250)
535 | SNOW2 = RGB(238, 233, 233)
536 | SNOW3 = RGB(205, 201, 201)
537 | SNOW4 = RGB(139, 137, 137)
538 | SPRINGGREEN = RGB(0, 255, 127)
539 | SPRINGGREEN1 = RGB(0, 238, 118)
540 | SPRINGGREEN2 = RGB(0, 205, 102)
541 | SPRINGGREEN3 = RGB(0, 139, 69)
542 | STEELBLUE = RGB(70, 130, 180)
543 | STEELBLUE1 = RGB(99, 184, 255)
544 | STEELBLUE2 = RGB(92, 172, 238)
545 | STEELBLUE3 = RGB(79, 148, 205)
546 | STEELBLUE4 = RGB(54, 100, 139)
547 | TAN = RGB(210, 180, 140)
548 | TAN1 = RGB(255, 165, 79)
549 | TAN2 = RGB(238, 154, 73)
550 | TAN3 = RGB(205, 133, 63)
551 | TAN4 = RGB(139, 90, 43)
552 | TEAL = RGB(0, 128, 128)
553 | THISTLE = RGB(216, 191, 216)
554 | THISTLE1 = RGB(255, 225, 255)
555 | THISTLE2 = RGB(238, 210, 238)
556 | THISTLE3 = RGB(205, 181, 205)
557 | THISTLE4 = RGB(139, 123, 139)
558 | TOMATO1 = RGB(255, 99, 71)
559 | TOMATO2 = RGB(238, 92, 66)
560 | TOMATO3 = RGB(205, 79, 57)
561 | TOMATO4 = RGB(139, 54, 38)
562 | TURQUOISE = RGB(64, 224, 208)
563 | TURQUOISE1 = RGB(0, 245, 255)
564 | TURQUOISE2 = RGB(0, 229, 238)
565 | TURQUOISE3 = RGB(0, 197, 205)
566 | TURQUOISE4 = RGB(0, 134, 139)
567 | TURQUOISEBLUE = RGB(0, 199, 140)
568 | VIOLET = RGB(238, 130, 238)
569 | VIOLETRED = RGB(208, 32, 144)
570 | VIOLETRED1 = RGB(255, 62, 150)
571 | VIOLETRED2 = RGB(238, 58, 140)
572 | VIOLETRED3 = RGB(205, 50, 120)
573 | VIOLETRED4 = RGB(139, 34, 82)
574 | WARMGREY = RGB(128, 128, 105)
575 | WHEAT = RGB(245, 222, 179)
576 | WHEAT1 = RGB(255, 231, 186)
577 | WHEAT2 = RGB(238, 216, 174)
578 | WHEAT3 = RGB(205, 186, 150)
579 | WHEAT4 = RGB(139, 126, 102)
580 | WHITE = RGB(255, 255, 255)
581 | WHITESMOKE = RGB(245, 245, 245)
582 | WHITESMOKE = RGB(245, 245, 245)
583 | YELLOW1 = RGB(255, 255, 0)
584 | YELLOW2 = RGB(238, 238, 0)
585 | YELLOW3 = RGB(205, 205, 0)
586 | YELLOW4 = RGB(139, 139, 0)
587 |
588 | #Add colors to colors dictionary
589 | colors['aliceblue'] = ALICEBLUE
590 | colors['antiquewhite'] = ANTIQUEWHITE
591 | colors['antiquewhite1'] = ANTIQUEWHITE1
592 | colors['antiquewhite2'] = ANTIQUEWHITE2
593 | colors['antiquewhite3'] = ANTIQUEWHITE3
594 | colors['antiquewhite4'] = ANTIQUEWHITE4
595 | colors['aqua'] = AQUA
596 | colors['aquamarine1'] = AQUAMARINE1
597 | colors['aquamarine2'] = AQUAMARINE2
598 | colors['aquamarine3'] = AQUAMARINE3
599 | colors['aquamarine4'] = AQUAMARINE4
600 | colors['azure1'] = AZURE1
601 | colors['azure2'] = AZURE2
602 | colors['azure3'] = AZURE3
603 | colors['azure4'] = AZURE4
604 | colors['banana'] = BANANA
605 | colors['beige'] = BEIGE
606 | colors['bisque1'] = BISQUE1
607 | colors['bisque2'] = BISQUE2
608 | colors['bisque3'] = BISQUE3
609 | colors['bisque4'] = BISQUE4
610 | colors['black'] = BLACK
611 | colors['blanchedalmond'] = BLANCHEDALMOND
612 | colors['blue'] = BLUE
613 | colors['blue2'] = BLUE2
614 | colors['blue3'] = BLUE3
615 | colors['blue4'] = BLUE4
616 | colors['blueviolet'] = BLUEVIOLET
617 | colors['brick'] = BRICK
618 | colors['brown'] = BROWN
619 | colors['brown1'] = BROWN1
620 | colors['brown2'] = BROWN2
621 | colors['brown3'] = BROWN3
622 | colors['brown4'] = BROWN4
623 | colors['burlywood'] = BURLYWOOD
624 | colors['burlywood1'] = BURLYWOOD1
625 | colors['burlywood2'] = BURLYWOOD2
626 | colors['burlywood3'] = BURLYWOOD3
627 | colors['burlywood4'] = BURLYWOOD4
628 | colors['burntsienna'] = BURNTSIENNA
629 | colors['burntumber'] = BURNTUMBER
630 | colors['cadetblue'] = CADETBLUE
631 | colors['cadetblue1'] = CADETBLUE1
632 | colors['cadetblue2'] = CADETBLUE2
633 | colors['cadetblue3'] = CADETBLUE3
634 | colors['cadetblue4'] = CADETBLUE4
635 | colors['cadmiumorange'] = CADMIUMORANGE
636 | colors['cadmiumyellow'] = CADMIUMYELLOW
637 | colors['carrot'] = CARROT
638 | colors['chartreuse1'] = CHARTREUSE1
639 | colors['chartreuse2'] = CHARTREUSE2
640 | colors['chartreuse3'] = CHARTREUSE3
641 | colors['chartreuse4'] = CHARTREUSE4
642 | colors['chocolate'] = CHOCOLATE
643 | colors['chocolate1'] = CHOCOLATE1
644 | colors['chocolate2'] = CHOCOLATE2
645 | colors['chocolate3'] = CHOCOLATE3
646 | colors['chocolate4'] = CHOCOLATE4
647 | colors['cobalt'] = COBALT
648 | colors['cobaltgreen'] = COBALTGREEN
649 | colors['coldgrey'] = COLDGREY
650 | colors['coral'] = CORAL
651 | colors['coral1'] = CORAL1
652 | colors['coral2'] = CORAL2
653 | colors['coral3'] = CORAL3
654 | colors['coral4'] = CORAL4
655 | colors['cornflowerblue'] = CORNFLOWERBLUE
656 | colors['cornsilk1'] = CORNSILK1
657 | colors['cornsilk2'] = CORNSILK2
658 | colors['cornsilk3'] = CORNSILK3
659 | colors['cornsilk4'] = CORNSILK4
660 | colors['crimson'] = CRIMSON
661 | colors['cyan2'] = CYAN2
662 | colors['cyan3'] = CYAN3
663 | colors['cyan4'] = CYAN4
664 | colors['darkgoldenrod'] = DARKGOLDENROD
665 | colors['darkgoldenrod1'] = DARKGOLDENROD1
666 | colors['darkgoldenrod2'] = DARKGOLDENROD2
667 | colors['darkgoldenrod3'] = DARKGOLDENROD3
668 | colors['darkgoldenrod4'] = DARKGOLDENROD4
669 | colors['darkgray'] = DARKGRAY
670 | colors['darkgreen'] = DARKGREEN
671 | colors['darkkhaki'] = DARKKHAKI
672 | colors['darkolivegreen'] = DARKOLIVEGREEN
673 | colors['darkolivegreen1'] = DARKOLIVEGREEN1
674 | colors['darkolivegreen2'] = DARKOLIVEGREEN2
675 | colors['darkolivegreen3'] = DARKOLIVEGREEN3
676 | colors['darkolivegreen4'] = DARKOLIVEGREEN4
677 | colors['darkorange'] = DARKORANGE
678 | colors['darkorange1'] = DARKORANGE1
679 | colors['darkorange2'] = DARKORANGE2
680 | colors['darkorange3'] = DARKORANGE3
681 | colors['darkorange4'] = DARKORANGE4
682 | colors['darkorchid'] = DARKORCHID
683 | colors['darkorchid1'] = DARKORCHID1
684 | colors['darkorchid2'] = DARKORCHID2
685 | colors['darkorchid3'] = DARKORCHID3
686 | colors['darkorchid4'] = DARKORCHID4
687 | colors['darksalmon'] = DARKSALMON
688 | colors['darkseagreen'] = DARKSEAGREEN
689 | colors['darkseagreen1'] = DARKSEAGREEN1
690 | colors['darkseagreen2'] = DARKSEAGREEN2
691 | colors['darkseagreen3'] = DARKSEAGREEN3
692 | colors['darkseagreen4'] = DARKSEAGREEN4
693 | colors['darkslateblue'] = DARKSLATEBLUE
694 | colors['darkslategray'] = DARKSLATEGRAY
695 | colors['darkslategray1'] = DARKSLATEGRAY1
696 | colors['darkslategray2'] = DARKSLATEGRAY2
697 | colors['darkslategray3'] = DARKSLATEGRAY3
698 | colors['darkslategray4'] = DARKSLATEGRAY4
699 | colors['darkturquoise'] = DARKTURQUOISE
700 | colors['darkviolet'] = DARKVIOLET
701 | colors['deeppink1'] = DEEPPINK1
702 | colors['deeppink2'] = DEEPPINK2
703 | colors['deeppink3'] = DEEPPINK3
704 | colors['deeppink4'] = DEEPPINK4
705 | colors['deepskyblue1'] = DEEPSKYBLUE1
706 | colors['deepskyblue2'] = DEEPSKYBLUE2
707 | colors['deepskyblue3'] = DEEPSKYBLUE3
708 | colors['deepskyblue4'] = DEEPSKYBLUE4
709 | colors['dimgray'] = DIMGRAY
710 | colors['dimgray'] = DIMGRAY
711 | colors['dodgerblue1'] = DODGERBLUE1
712 | colors['dodgerblue2'] = DODGERBLUE2
713 | colors['dodgerblue3'] = DODGERBLUE3
714 | colors['dodgerblue4'] = DODGERBLUE4
715 | colors['eggshell'] = EGGSHELL
716 | colors['emeraldgreen'] = EMERALDGREEN
717 | colors['firebrick'] = FIREBRICK
718 | colors['firebrick1'] = FIREBRICK1
719 | colors['firebrick2'] = FIREBRICK2
720 | colors['firebrick3'] = FIREBRICK3
721 | colors['firebrick4'] = FIREBRICK4
722 | colors['flesh'] = FLESH
723 | colors['floralwhite'] = FLORALWHITE
724 | colors['forestgreen'] = FORESTGREEN
725 | colors['gainsboro'] = GAINSBORO
726 | colors['ghostwhite'] = GHOSTWHITE
727 | colors['gold1'] = GOLD1
728 | colors['gold2'] = GOLD2
729 | colors['gold3'] = GOLD3
730 | colors['gold4'] = GOLD4
731 | colors['goldenrod'] = GOLDENROD
732 | colors['goldenrod1'] = GOLDENROD1
733 | colors['goldenrod2'] = GOLDENROD2
734 | colors['goldenrod3'] = GOLDENROD3
735 | colors['goldenrod4'] = GOLDENROD4
736 | colors['gray'] = GRAY
737 | colors['gray1'] = GRAY1
738 | colors['gray10'] = GRAY10
739 | colors['gray11'] = GRAY11
740 | colors['gray12'] = GRAY12
741 | colors['gray13'] = GRAY13
742 | colors['gray14'] = GRAY14
743 | colors['gray15'] = GRAY15
744 | colors['gray16'] = GRAY16
745 | colors['gray17'] = GRAY17
746 | colors['gray18'] = GRAY18
747 | colors['gray19'] = GRAY19
748 | colors['gray2'] = GRAY2
749 | colors['gray20'] = GRAY20
750 | colors['gray21'] = GRAY21
751 | colors['gray22'] = GRAY22
752 | colors['gray23'] = GRAY23
753 | colors['gray24'] = GRAY24
754 | colors['gray25'] = GRAY25
755 | colors['gray26'] = GRAY26
756 | colors['gray27'] = GRAY27
757 | colors['gray28'] = GRAY28
758 | colors['gray29'] = GRAY29
759 | colors['gray3'] = GRAY3
760 | colors['gray30'] = GRAY30
761 | colors['gray31'] = GRAY31
762 | colors['gray32'] = GRAY32
763 | colors['gray33'] = GRAY33
764 | colors['gray34'] = GRAY34
765 | colors['gray35'] = GRAY35
766 | colors['gray36'] = GRAY36
767 | colors['gray37'] = GRAY37
768 | colors['gray38'] = GRAY38
769 | colors['gray39'] = GRAY39
770 | colors['gray4'] = GRAY4
771 | colors['gray40'] = GRAY40
772 | colors['gray42'] = GRAY42
773 | colors['gray43'] = GRAY43
774 | colors['gray44'] = GRAY44
775 | colors['gray45'] = GRAY45
776 | colors['gray46'] = GRAY46
777 | colors['gray47'] = GRAY47
778 | colors['gray48'] = GRAY48
779 | colors['gray49'] = GRAY49
780 | colors['gray5'] = GRAY5
781 | colors['gray50'] = GRAY50
782 | colors['gray51'] = GRAY51
783 | colors['gray52'] = GRAY52
784 | colors['gray53'] = GRAY53
785 | colors['gray54'] = GRAY54
786 | colors['gray55'] = GRAY55
787 | colors['gray56'] = GRAY56
788 | colors['gray57'] = GRAY57
789 | colors['gray58'] = GRAY58
790 | colors['gray59'] = GRAY59
791 | colors['gray6'] = GRAY6
792 | colors['gray60'] = GRAY60
793 | colors['gray61'] = GRAY61
794 | colors['gray62'] = GRAY62
795 | colors['gray63'] = GRAY63
796 | colors['gray64'] = GRAY64
797 | colors['gray65'] = GRAY65
798 | colors['gray66'] = GRAY66
799 | colors['gray67'] = GRAY67
800 | colors['gray68'] = GRAY68
801 | colors['gray69'] = GRAY69
802 | colors['gray7'] = GRAY7
803 | colors['gray70'] = GRAY70
804 | colors['gray71'] = GRAY71
805 | colors['gray72'] = GRAY72
806 | colors['gray73'] = GRAY73
807 | colors['gray74'] = GRAY74
808 | colors['gray75'] = GRAY75
809 | colors['gray76'] = GRAY76
810 | colors['gray77'] = GRAY77
811 | colors['gray78'] = GRAY78
812 | colors['gray79'] = GRAY79
813 | colors['gray8'] = GRAY8
814 | colors['gray80'] = GRAY80
815 | colors['gray81'] = GRAY81
816 | colors['gray82'] = GRAY82
817 | colors['gray83'] = GRAY83
818 | colors['gray84'] = GRAY84
819 | colors['gray85'] = GRAY85
820 | colors['gray86'] = GRAY86
821 | colors['gray87'] = GRAY87
822 | colors['gray88'] = GRAY88
823 | colors['gray89'] = GRAY89
824 | colors['gray9'] = GRAY9
825 | colors['gray90'] = GRAY90
826 | colors['gray91'] = GRAY91
827 | colors['gray92'] = GRAY92
828 | colors['gray93'] = GRAY93
829 | colors['gray94'] = GRAY94
830 | colors['gray95'] = GRAY95
831 | colors['gray97'] = GRAY97
832 | colors['gray98'] = GRAY98
833 | colors['gray99'] = GRAY99
834 | colors['green'] = GREEN
835 | colors['green1'] = GREEN1
836 | colors['green2'] = GREEN2
837 | colors['green3'] = GREEN3
838 | colors['green4'] = GREEN4
839 | colors['greenyellow'] = GREENYELLOW
840 | colors['honeydew1'] = HONEYDEW1
841 | colors['honeydew2'] = HONEYDEW2
842 | colors['honeydew3'] = HONEYDEW3
843 | colors['honeydew4'] = HONEYDEW4
844 | colors['hotpink'] = HOTPINK
845 | colors['hotpink1'] = HOTPINK1
846 | colors['hotpink2'] = HOTPINK2
847 | colors['hotpink3'] = HOTPINK3
848 | colors['hotpink4'] = HOTPINK4
849 | colors['indianred'] = INDIANRED
850 | colors['indianred'] = INDIANRED
851 | colors['indianred1'] = INDIANRED1
852 | colors['indianred2'] = INDIANRED2
853 | colors['indianred3'] = INDIANRED3
854 | colors['indianred4'] = INDIANRED4
855 | colors['indigo'] = INDIGO
856 | colors['ivory1'] = IVORY1
857 | colors['ivory2'] = IVORY2
858 | colors['ivory3'] = IVORY3
859 | colors['ivory4'] = IVORY4
860 | colors['ivoryblack'] = IVORYBLACK
861 | colors['khaki'] = KHAKI
862 | colors['khaki1'] = KHAKI1
863 | colors['khaki2'] = KHAKI2
864 | colors['khaki3'] = KHAKI3
865 | colors['khaki4'] = KHAKI4
866 | colors['lavender'] = LAVENDER
867 | colors['lavenderblush1'] = LAVENDERBLUSH1
868 | colors['lavenderblush2'] = LAVENDERBLUSH2
869 | colors['lavenderblush3'] = LAVENDERBLUSH3
870 | colors['lavenderblush4'] = LAVENDERBLUSH4
871 | colors['lawngreen'] = LAWNGREEN
872 | colors['lemonchiffon1'] = LEMONCHIFFON1
873 | colors['lemonchiffon2'] = LEMONCHIFFON2
874 | colors['lemonchiffon3'] = LEMONCHIFFON3
875 | colors['lemonchiffon4'] = LEMONCHIFFON4
876 | colors['lightblue'] = LIGHTBLUE
877 | colors['lightblue1'] = LIGHTBLUE1
878 | colors['lightblue2'] = LIGHTBLUE2
879 | colors['lightblue3'] = LIGHTBLUE3
880 | colors['lightblue4'] = LIGHTBLUE4
881 | colors['lightcoral'] = LIGHTCORAL
882 | colors['lightcyan1'] = LIGHTCYAN1
883 | colors['lightcyan2'] = LIGHTCYAN2
884 | colors['lightcyan3'] = LIGHTCYAN3
885 | colors['lightcyan4'] = LIGHTCYAN4
886 | colors['lightgoldenrod1'] = LIGHTGOLDENROD1
887 | colors['lightgoldenrod2'] = LIGHTGOLDENROD2
888 | colors['lightgoldenrod3'] = LIGHTGOLDENROD3
889 | colors['lightgoldenrod4'] = LIGHTGOLDENROD4
890 | colors['lightgoldenrodyellow'] = LIGHTGOLDENRODYELLOW
891 | colors['lightgrey'] = LIGHTGREY
892 | colors['lightpink'] = LIGHTPINK
893 | colors['lightpink1'] = LIGHTPINK1
894 | colors['lightpink2'] = LIGHTPINK2
895 | colors['lightpink3'] = LIGHTPINK3
896 | colors['lightpink4'] = LIGHTPINK4
897 | colors['lightsalmon1'] = LIGHTSALMON1
898 | colors['lightsalmon2'] = LIGHTSALMON2
899 | colors['lightsalmon3'] = LIGHTSALMON3
900 | colors['lightsalmon4'] = LIGHTSALMON4
901 | colors['lightseagreen'] = LIGHTSEAGREEN
902 | colors['lightskyblue'] = LIGHTSKYBLUE
903 | colors['lightskyblue1'] = LIGHTSKYBLUE1
904 | colors['lightskyblue2'] = LIGHTSKYBLUE2
905 | colors['lightskyblue3'] = LIGHTSKYBLUE3
906 | colors['lightskyblue4'] = LIGHTSKYBLUE4
907 | colors['lightslateblue'] = LIGHTSLATEBLUE
908 | colors['lightslategray'] = LIGHTSLATEGRAY
909 | colors['lightsteelblue'] = LIGHTSTEELBLUE
910 | colors['lightsteelblue1'] = LIGHTSTEELBLUE1
911 | colors['lightsteelblue2'] = LIGHTSTEELBLUE2
912 | colors['lightsteelblue3'] = LIGHTSTEELBLUE3
913 | colors['lightsteelblue4'] = LIGHTSTEELBLUE4
914 | colors['lightyellow1'] = LIGHTYELLOW1
915 | colors['lightyellow2'] = LIGHTYELLOW2
916 | colors['lightyellow3'] = LIGHTYELLOW3
917 | colors['lightyellow4'] = LIGHTYELLOW4
918 | colors['limegreen'] = LIMEGREEN
919 | colors['linen'] = LINEN
920 | colors['magenta'] = MAGENTA
921 | colors['magenta2'] = MAGENTA2
922 | colors['magenta3'] = MAGENTA3
923 | colors['magenta4'] = MAGENTA4
924 | colors['manganeseblue'] = MANGANESEBLUE
925 | colors['maroon'] = MAROON
926 | colors['maroon1'] = MAROON1
927 | colors['maroon2'] = MAROON2
928 | colors['maroon3'] = MAROON3
929 | colors['maroon4'] = MAROON4
930 | colors['mediumorchid'] = MEDIUMORCHID
931 | colors['mediumorchid1'] = MEDIUMORCHID1
932 | colors['mediumorchid2'] = MEDIUMORCHID2
933 | colors['mediumorchid3'] = MEDIUMORCHID3
934 | colors['mediumorchid4'] = MEDIUMORCHID4
935 | colors['mediumpurple'] = MEDIUMPURPLE
936 | colors['mediumpurple1'] = MEDIUMPURPLE1
937 | colors['mediumpurple2'] = MEDIUMPURPLE2
938 | colors['mediumpurple3'] = MEDIUMPURPLE3
939 | colors['mediumpurple4'] = MEDIUMPURPLE4
940 | colors['mediumseagreen'] = MEDIUMSEAGREEN
941 | colors['mediumslateblue'] = MEDIUMSLATEBLUE
942 | colors['mediumspringgreen'] = MEDIUMSPRINGGREEN
943 | colors['mediumturquoise'] = MEDIUMTURQUOISE
944 | colors['mediumvioletred'] = MEDIUMVIOLETRED
945 | colors['melon'] = MELON
946 | colors['midnightblue'] = MIDNIGHTBLUE
947 | colors['mint'] = MINT
948 | colors['mintcream'] = MINTCREAM
949 | colors['mistyrose1'] = MISTYROSE1
950 | colors['mistyrose2'] = MISTYROSE2
951 | colors['mistyrose3'] = MISTYROSE3
952 | colors['mistyrose4'] = MISTYROSE4
953 | colors['moccasin'] = MOCCASIN
954 | colors['navajowhite1'] = NAVAJOWHITE1
955 | colors['navajowhite2'] = NAVAJOWHITE2
956 | colors['navajowhite3'] = NAVAJOWHITE3
957 | colors['navajowhite4'] = NAVAJOWHITE4
958 | colors['navy'] = NAVY
959 | colors['oldlace'] = OLDLACE
960 | colors['olive'] = OLIVE
961 | colors['olivedrab'] = OLIVEDRAB
962 | colors['olivedrab1'] = OLIVEDRAB1
963 | colors['olivedrab2'] = OLIVEDRAB2
964 | colors['olivedrab3'] = OLIVEDRAB3
965 | colors['olivedrab4'] = OLIVEDRAB4
966 | colors['orange'] = ORANGE
967 | colors['orange1'] = ORANGE1
968 | colors['orange2'] = ORANGE2
969 | colors['orange3'] = ORANGE3
970 | colors['orange4'] = ORANGE4
971 | colors['orangered1'] = ORANGERED1
972 | colors['orangered2'] = ORANGERED2
973 | colors['orangered3'] = ORANGERED3
974 | colors['orangered4'] = ORANGERED4
975 | colors['orchid'] = ORCHID
976 | colors['orchid1'] = ORCHID1
977 | colors['orchid2'] = ORCHID2
978 | colors['orchid3'] = ORCHID3
979 | colors['orchid4'] = ORCHID4
980 | colors['palegoldenrod'] = PALEGOLDENROD
981 | colors['palegreen'] = PALEGREEN
982 | colors['palegreen1'] = PALEGREEN1
983 | colors['palegreen2'] = PALEGREEN2
984 | colors['palegreen3'] = PALEGREEN3
985 | colors['palegreen4'] = PALEGREEN4
986 | colors['paleturquoise1'] = PALETURQUOISE1
987 | colors['paleturquoise2'] = PALETURQUOISE2
988 | colors['paleturquoise3'] = PALETURQUOISE3
989 | colors['paleturquoise4'] = PALETURQUOISE4
990 | colors['palevioletred'] = PALEVIOLETRED
991 | colors['palevioletred1'] = PALEVIOLETRED1
992 | colors['palevioletred2'] = PALEVIOLETRED2
993 | colors['palevioletred3'] = PALEVIOLETRED3
994 | colors['palevioletred4'] = PALEVIOLETRED4
995 | colors['papayawhip'] = PAPAYAWHIP
996 | colors['peachpuff1'] = PEACHPUFF1
997 | colors['peachpuff2'] = PEACHPUFF2
998 | colors['peachpuff3'] = PEACHPUFF3
999 | colors['peachpuff4'] = PEACHPUFF4
1000 | colors['peacock'] = PEACOCK
1001 | colors['pink'] = PINK
1002 | colors['pink1'] = PINK1
1003 | colors['pink2'] = PINK2
1004 | colors['pink3'] = PINK3
1005 | colors['pink4'] = PINK4
1006 | colors['plum'] = PLUM
1007 | colors['plum1'] = PLUM1
1008 | colors['plum2'] = PLUM2
1009 | colors['plum3'] = PLUM3
1010 | colors['plum4'] = PLUM4
1011 | colors['powderblue'] = POWDERBLUE
1012 | colors['purple'] = PURPLE
1013 | colors['purple1'] = PURPLE1
1014 | colors['purple2'] = PURPLE2
1015 | colors['purple3'] = PURPLE3
1016 | colors['purple4'] = PURPLE4
1017 | colors['raspberry'] = RASPBERRY
1018 | colors['rawsienna'] = RAWSIENNA
1019 | colors['red1'] = RED1
1020 | colors['red2'] = RED2
1021 | colors['red3'] = RED3
1022 | colors['red4'] = RED4
1023 | colors['rosybrown'] = ROSYBROWN
1024 | colors['rosybrown1'] = ROSYBROWN1
1025 | colors['rosybrown2'] = ROSYBROWN2
1026 | colors['rosybrown3'] = ROSYBROWN3
1027 | colors['rosybrown4'] = ROSYBROWN4
1028 | colors['royalblue'] = ROYALBLUE
1029 | colors['royalblue1'] = ROYALBLUE1
1030 | colors['royalblue2'] = ROYALBLUE2
1031 | colors['royalblue3'] = ROYALBLUE3
1032 | colors['royalblue4'] = ROYALBLUE4
1033 | colors['salmon'] = SALMON
1034 | colors['salmon1'] = SALMON1
1035 | colors['salmon2'] = SALMON2
1036 | colors['salmon3'] = SALMON3
1037 | colors['salmon4'] = SALMON4
1038 | colors['sandybrown'] = SANDYBROWN
1039 | colors['sapgreen'] = SAPGREEN
1040 | colors['seagreen1'] = SEAGREEN1
1041 | colors['seagreen2'] = SEAGREEN2
1042 | colors['seagreen3'] = SEAGREEN3
1043 | colors['seagreen4'] = SEAGREEN4
1044 | colors['seashell1'] = SEASHELL1
1045 | colors['seashell2'] = SEASHELL2
1046 | colors['seashell3'] = SEASHELL3
1047 | colors['seashell4'] = SEASHELL4
1048 | colors['sepia'] = SEPIA
1049 | colors['sgibeet'] = SGIBEET
1050 | colors['sgibrightgray'] = SGIBRIGHTGRAY
1051 | colors['sgichartreuse'] = SGICHARTREUSE
1052 | colors['sgidarkgray'] = SGIDARKGRAY
1053 | colors['sgigray12'] = SGIGRAY12
1054 | colors['sgigray16'] = SGIGRAY16
1055 | colors['sgigray32'] = SGIGRAY32
1056 | colors['sgigray36'] = SGIGRAY36
1057 | colors['sgigray52'] = SGIGRAY52
1058 | colors['sgigray56'] = SGIGRAY56
1059 | colors['sgigray72'] = SGIGRAY72
1060 | colors['sgigray76'] = SGIGRAY76
1061 | colors['sgigray92'] = SGIGRAY92
1062 | colors['sgigray96'] = SGIGRAY96
1063 | colors['sgilightblue'] = SGILIGHTBLUE
1064 | colors['sgilightgray'] = SGILIGHTGRAY
1065 | colors['sgiolivedrab'] = SGIOLIVEDRAB
1066 | colors['sgisalmon'] = SGISALMON
1067 | colors['sgislateblue'] = SGISLATEBLUE
1068 | colors['sgiteal'] = SGITEAL
1069 | colors['sienna'] = SIENNA
1070 | colors['sienna1'] = SIENNA1
1071 | colors['sienna2'] = SIENNA2
1072 | colors['sienna3'] = SIENNA3
1073 | colors['sienna4'] = SIENNA4
1074 | colors['silver'] = SILVER
1075 | colors['skyblue'] = SKYBLUE
1076 | colors['skyblue1'] = SKYBLUE1
1077 | colors['skyblue2'] = SKYBLUE2
1078 | colors['skyblue3'] = SKYBLUE3
1079 | colors['skyblue4'] = SKYBLUE4
1080 | colors['slateblue'] = SLATEBLUE
1081 | colors['slateblue1'] = SLATEBLUE1
1082 | colors['slateblue2'] = SLATEBLUE2
1083 | colors['slateblue3'] = SLATEBLUE3
1084 | colors['slateblue4'] = SLATEBLUE4
1085 | colors['slategray'] = SLATEGRAY
1086 | colors['slategray1'] = SLATEGRAY1
1087 | colors['slategray2'] = SLATEGRAY2
1088 | colors['slategray3'] = SLATEGRAY3
1089 | colors['slategray4'] = SLATEGRAY4
1090 | colors['snow1'] = SNOW1
1091 | colors['snow2'] = SNOW2
1092 | colors['snow3'] = SNOW3
1093 | colors['snow4'] = SNOW4
1094 | colors['springgreen'] = SPRINGGREEN
1095 | colors['springgreen1'] = SPRINGGREEN1
1096 | colors['springgreen2'] = SPRINGGREEN2
1097 | colors['springgreen3'] = SPRINGGREEN3
1098 | colors['steelblue'] = STEELBLUE
1099 | colors['steelblue1'] = STEELBLUE1
1100 | colors['steelblue2'] = STEELBLUE2
1101 | colors['steelblue3'] = STEELBLUE3
1102 | colors['steelblue4'] = STEELBLUE4
1103 | colors['tan'] = TAN
1104 | colors['tan1'] = TAN1
1105 | colors['tan2'] = TAN2
1106 | colors['tan3'] = TAN3
1107 | colors['tan4'] = TAN4
1108 | colors['teal'] = TEAL
1109 | colors['thistle'] = THISTLE
1110 | colors['thistle1'] = THISTLE1
1111 | colors['thistle2'] = THISTLE2
1112 | colors['thistle3'] = THISTLE3
1113 | colors['thistle4'] = THISTLE4
1114 | colors['tomato1'] = TOMATO1
1115 | colors['tomato2'] = TOMATO2
1116 | colors['tomato3'] = TOMATO3
1117 | colors['tomato4'] = TOMATO4
1118 | colors['turquoise'] = TURQUOISE
1119 | colors['turquoise1'] = TURQUOISE1
1120 | colors['turquoise2'] = TURQUOISE2
1121 | colors['turquoise3'] = TURQUOISE3
1122 | colors['turquoise4'] = TURQUOISE4
1123 | colors['turquoiseblue'] = TURQUOISEBLUE
1124 | colors['violet'] = VIOLET
1125 | colors['violetred'] = VIOLETRED
1126 | colors['violetred1'] = VIOLETRED1
1127 | colors['violetred2'] = VIOLETRED2
1128 | colors['violetred3'] = VIOLETRED3
1129 | colors['violetred4'] = VIOLETRED4
1130 | colors['warmgrey'] = WARMGREY
1131 | colors['wheat'] = WHEAT
1132 | colors['wheat1'] = WHEAT1
1133 | colors['wheat2'] = WHEAT2
1134 | colors['wheat3'] = WHEAT3
1135 | colors['wheat4'] = WHEAT4
1136 | colors['white'] = WHITE
1137 | colors['whitesmoke'] = WHITESMOKE
1138 | colors['whitesmoke'] = WHITESMOKE
1139 | colors['yellow1'] = YELLOW1
1140 | colors['yellow2'] = YELLOW2
1141 | colors['yellow3'] = YELLOW3
1142 | colors['yellow4'] = YELLOW4
1143 |
1144 | colors = OrderedDict(sorted(colors.items(), key=lambda t: t[0]))
1145 |
--------------------------------------------------------------------------------
/datasets/general_dataset.py:
--------------------------------------------------------------------------------
1 | import os
2 | import numpy as np
3 | import datasets.color_constants as cc
4 | from tools.lazy_decorator import *
5 | from typing import Tuple, List, Dict
6 | import logging
7 |
8 |
9 | class GeneralDataset:
10 | """
11 | Class used for reading in datasets for training/testing.
12 | Parameterized in order to handle different kinds of datasets (e.g. k-fold datasets)
13 | """
14 |
15 | @property
16 | def data_path(self) -> str:
17 | return self._data_path
18 |
19 | @property
20 | def data(self) -> List[np.ndarray]:
21 | return self._data
22 |
23 | @property
24 | def full_sized_data(self) -> Dict[str, np.ndarray]:
25 | return self._full_sized_data
26 |
27 | @property
28 | def file_names(self) -> List[str]:
29 | return self._file_names
30 |
31 | @property
32 | def train_pc_idx(self) -> List[int]:
33 | return self._train_pc_idx
34 |
35 | @property
36 | def test_pc_idx(self) -> List[int]:
37 | return self._test_pc_idx
38 |
39 | def __init__(self, data_path: str, is_train: bool, test_sets: list,
40 | downsample_prefix: str, is_colors: bool, is_laser: bool, n_classes=None):
41 | self._test_sets = test_sets
42 | self._downsample_prefix = downsample_prefix
43 | self._is_colors = is_colors
44 | self._is_laser = is_laser
45 |
46 | # it is possible that there is no class information given for test sets
47 | if n_classes is None:
48 | self._is_class = True
49 | else:
50 | self._is_class = False
51 | self._num_classes = n_classes
52 |
53 | self._data_path = data_path
54 | self._data, self._file_names, self._full_sized_data = self._load(is_train)
55 |
56 | # log some dataset properties
57 | logging.debug(f"number of features: {self.num_features}")
58 | logging.debug(f"number of classes: {self.num_classes}")
59 | logging.debug(f"number of training samples: {len(self.train_pc_idx)}")
60 | logging.debug(f"number of test samples: {len(self.test_pc_idx)}")
61 |
62 | @lazy_property
63 | def num_classes(self) -> int:
64 | """
65 | calculate the number of unique class labels if class information is given in npy-file.
66 | Otherwise, just return the number of classes which have been defined in the constructor
67 | :return: number of classes for this dataset
68 | """
69 | if self._is_class:
70 | # assuming that labels are in the last column
71 | # counting unique class labels of all pointclouds
72 | _num_classes = len(np.unique(np.concatenate([np.unique(pointcloud[:, -1])
73 | for pointcloud in self.data])))
74 |
75 | if _num_classes > len(self.label_colors()):
76 | logging.warning(f"There are more classes than label colors for this dataset. "
77 | f"If you want to plot your results, this will not work.")
78 |
79 | return _num_classes
80 | else:
81 | return self._num_classes
82 |
83 | @lazy_property
84 | def normalization(self) -> np.ndarray:
85 | """
86 | before blob is fed into the neural network some normalization takes place in the batch generator
87 | normalization factors specific for each dataset have to be provided
88 | note: this property can be overriden by subclasses if another normalization is needed
89 | :return: np.ndarray with normalization factors
90 | """
91 | _normalizer = np.array([1. for _ in range(self.num_features)])
92 |
93 | if self._is_colors:
94 | _normalizer[3:6] = 255. # normalize colors to [0,1]
95 | if self._is_laser:
96 | _normalizer[6] = 2048. # normalize laser [-1, 1]
97 | elif self._is_laser:
98 | _normalizer[3] = 2048. # normalize laser [-1, 1]
99 |
100 | return _normalizer
101 |
102 | @lazy_property
103 | def num_features(self) -> int:
104 | return 3 + self._is_colors * 3 + self._is_laser
105 |
106 | @staticmethod
107 | def label_colors() -> np.ndarray:
108 | return np.array([cc.colors['brown'].npy,
109 | cc.colors['darkgreen'].npy,
110 | cc.colors['springgreen'].npy,
111 | cc.colors['red1'].npy,
112 | cc.colors['darkgray'].npy,
113 | cc.colors['gray'].npy,
114 | cc.colors['pink'].npy,
115 | cc.colors['yellow1'].npy,
116 | cc.colors['violet'].npy,
117 | cc.colors['hotpink'].npy,
118 | cc.colors['blue'].npy,
119 | cc.colors['lightblue'].npy,
120 | cc.colors['orange'].npy,
121 | cc.colors['black'].npy])
122 |
123 | def _load(self, is_train: bool) -> Tuple[List[np.ndarray], List[str], Dict[str, np.ndarray]]:
124 | """
125 | Note that we assume a folder hierarchy of DATA_PATH/SET_NO/{full_size, sample_X_Y, ...}/POINTCLOUD.npy
126 | :param is_train: true iff training mode
127 | :return: list of pointclouds and list of filenames
128 | """
129 | data_training_test = {}
130 | full_sized_test_data = {}
131 | names = set()
132 |
133 | train_pc_names = set()
134 | test_pc_names = set()
135 |
136 | # pick
137 | pick = [0, 1, 2]
138 |
139 | if self._is_colors:
140 | pick = pick + [3, 4, 5]
141 |
142 | if self._is_laser:
143 | pick = pick + [6]
144 |
145 | if self._is_laser:
146 | pick = pick + [3]
147 |
148 | pick = pick + [-1]
149 |
150 | for dirpath, dirnames, filenames in os.walk(self.data_path):
151 | for filename in [f for f in filenames if f.endswith(".npy")]:
152 | is_test_set = os.path.dirname(dirpath).split('/')[-1] in self._test_sets
153 |
154 | if not is_test_set and not is_train:
155 | # we do not have to load training examples if we only want to evaluate
156 | continue
157 |
158 | name = None
159 | if os.path.basename(dirpath) == self._downsample_prefix:
160 | # dimension of a single npy file: (number of points, number of features + label)
161 | pointcloud_data = np.load(os.path.join(dirpath, filename))
162 | pointcloud_data = pointcloud_data[:, pick]
163 | pointcloud_data = pointcloud_data.astype(np.float32) # just to be sure!
164 |
165 | name = filename.replace('.npy', '')
166 | data_training_test[name] = pointcloud_data
167 | elif os.path.basename(dirpath) == 'full_size':
168 | if not is_train:
169 | # for testing we consider full scale point clouds
170 | if is_test_set:
171 | # dimension of a single npy file: (number of points, number of features + label)
172 | pointcloud_data = np.load(os.path.join(dirpath, filename))
173 | pointcloud_data = pointcloud_data[:, pick]
174 | pointcloud_data = pointcloud_data.astype(np.float32) # just to be sure!
175 |
176 | name = filename.replace('.npy', '')
177 | full_sized_test_data[name] = pointcloud_data
178 |
179 | if name is not None:
180 | names.add(name)
181 |
182 | if is_test_set:
183 | test_pc_names.add(name)
184 | else:
185 | train_pc_names.add(name)
186 |
187 | names = sorted(names)
188 |
189 | data_training_test = [data_training_test[key] for key in names]
190 |
191 | self._train_pc_idx = sorted([names.index(name) for name in train_pc_names])
192 | self._test_pc_idx = sorted([names.index(name) for name in test_pc_names])
193 |
194 | # short sanity check to ensure that data could be read in
195 | if len(data_training_test) == 0 or len(names) == 0:
196 | # error
197 | raise ValueError(f"Dataset could not be found under {self.data_path}")
198 | else:
199 | if (not is_train) and len(full_sized_test_data) == 0:
200 | # error
201 | raise ValueError(f"Dataset could not be found in {self.data_path}")
202 |
203 | return data_training_test, names, full_sized_test_data
204 |
205 |
206 | if __name__ == '__main__':
207 | from tools.tools import setup_logger
208 |
209 | setup_logger()
210 |
211 | dataset = GeneralDataset(data_path='/fastwork/schult/stanford_indoor',
212 | is_train=False,
213 | test_sets=['area_3', 'area_2'],
214 | downsample_prefix='sample_1_1',
215 | is_colors=True,
216 | is_laser=True)
217 |
--------------------------------------------------------------------------------
/doc/exploring_header.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VisualComputingInstitute/3d-semantic-segmentation/1dfc010b370a346902ad29460c9ad969c1892a97/doc/exploring_header.png
--------------------------------------------------------------------------------
/experiments/iccvw_paper_2017/s3dis_gru/s3dis_gru_area_1.yaml:
--------------------------------------------------------------------------------
1 | modus: TRAIN_VAL
2 | dataset:
3 | name: general_dataset
4 | num_classes: 13
5 | data_path: dataset/stanford_indoor/
6 | test_sets: ['Area_1']
7 | downsample_prefix: sample_0.03
8 | colors: True
9 | laser: False
10 | model:
11 | name: gru_neighbor_model
12 | params: None
13 | batch_generator:
14 | name: neighboring_grid_batch_generator
15 | params:
16 | batch_size: 14
17 | num_points: 4096
18 | grid_spacing: 0.5
19 | metric: chebyshev
20 | augmentation: False
21 | grid_x: 2
22 | grid_y: 2
23 | radius: 0.5
24 | optimizer:
25 | name: exponential_decay_adam
26 | params:
27 | initial_lr: 0.001
28 | decay_step: 300000
29 | decay_rate: 0.5
30 | gradient_clipping: 15
31 | train:
32 | epochs: 61
33 |
--------------------------------------------------------------------------------
/experiments/iccvw_paper_2017/s3dis_gru/s3dis_gru_area_2.yaml:
--------------------------------------------------------------------------------
1 | modus: TRAIN_VAL
2 | dataset:
3 | name: general_dataset
4 | num_classes: 13
5 | data_path: dataset/stanford_indoor/
6 | test_sets: ['Area_2']
7 | downsample_prefix: sample_0.03
8 | colors: True
9 | laser: False
10 | model:
11 | name: gru_neighbor_model
12 | params: None
13 | batch_generator:
14 | name: neighboring_grid_batch_generator
15 | params:
16 | batch_size: 14
17 | num_points: 4096
18 | grid_spacing: 0.5
19 | metric: chebyshev
20 | augmentation: False
21 | grid_x: 2
22 | grid_y: 2
23 | radius: 0.5
24 | optimizer:
25 | name: exponential_decay_adam
26 | params:
27 | initial_lr: 0.001
28 | decay_step: 300000
29 | decay_rate: 0.5
30 | gradient_clipping: 15
31 | train:
32 | epochs: 61
33 |
--------------------------------------------------------------------------------
/experiments/iccvw_paper_2017/s3dis_gru/s3dis_gru_area_3.yaml:
--------------------------------------------------------------------------------
1 | modus: TRAIN_VAL
2 | dataset:
3 | name: general_dataset
4 | num_classes: 13
5 | data_path: dataset/stanford_indoor/
6 | test_sets: ['Area_3']
7 | downsample_prefix: sample_0.03
8 | colors: True
9 | laser: False
10 | model:
11 | name: gru_neighbor_model
12 | params: None
13 | batch_generator:
14 | name: neighboring_grid_batch_generator
15 | params:
16 | batch_size: 14
17 | num_points: 4096
18 | grid_spacing: 0.5
19 | metric: chebyshev
20 | augmentation: False
21 | grid_x: 2
22 | grid_y: 2
23 | radius: 0.5
24 | optimizer:
25 | name: exponential_decay_adam
26 | params:
27 | initial_lr: 0.001
28 | decay_step: 300000
29 | decay_rate: 0.5
30 | gradient_clipping: 15
31 | train:
32 | epochs: 61
33 |
--------------------------------------------------------------------------------
/experiments/iccvw_paper_2017/s3dis_gru/s3dis_gru_area_4.yaml:
--------------------------------------------------------------------------------
1 | modus: TRAIN_VAL
2 | dataset:
3 | name: general_dataset
4 | num_classes: 13
5 | data_path: dataset/stanford_indoor/
6 | test_sets: ['Area_4']
7 | downsample_prefix: sample_0.03
8 | colors: True
9 | laser: False
10 | model:
11 | name: gru_neighbor_model
12 | params: None
13 | batch_generator:
14 | name: neighboring_grid_batch_generator
15 | params:
16 | batch_size: 14
17 | num_points: 4096
18 | grid_spacing: 0.5
19 | metric: chebyshev
20 | augmentation: False
21 | grid_x: 2
22 | grid_y: 2
23 | radius: 0.5
24 | optimizer:
25 | name: exponential_decay_adam
26 | params:
27 | initial_lr: 0.001
28 | decay_step: 300000
29 | decay_rate: 0.5
30 | gradient_clipping: 15
31 | train:
32 | epochs: 61
33 |
--------------------------------------------------------------------------------
/experiments/iccvw_paper_2017/s3dis_gru/s3dis_gru_area_5.yaml:
--------------------------------------------------------------------------------
1 | modus: TRAIN_VAL
2 | dataset:
3 | name: general_dataset
4 | num_classes: 13
5 | data_path: dataset/stanford_indoor/
6 | test_sets: ['Area_5']
7 | downsample_prefix: sample_0.03
8 | colors: True
9 | laser: False
10 | model:
11 | name: gru_neighbor_model
12 | params: None
13 | batch_generator:
14 | name: neighboring_grid_batch_generator
15 | params:
16 | batch_size: 14
17 | num_points: 4096
18 | grid_spacing: 0.5
19 | metric: chebyshev
20 | augmentation: False
21 | grid_x: 2
22 | grid_y: 2
23 | radius: 0.5
24 | optimizer:
25 | name: exponential_decay_adam
26 | params:
27 | initial_lr: 0.001
28 | decay_step: 300000
29 | decay_rate: 0.5
30 | gradient_clipping: 15
31 | train:
32 | epochs: 61
33 |
--------------------------------------------------------------------------------
/experiments/iccvw_paper_2017/s3dis_gru/s3dis_gru_area_6.yaml:
--------------------------------------------------------------------------------
1 | modus: TRAIN_VAL
2 | dataset:
3 | name: general_dataset
4 | num_classes: 13
5 | data_path: dataset/stanford_indoor/
6 | test_sets: ['Area_6']
7 | downsample_prefix: sample_0.03
8 | colors: True
9 | laser: False
10 | model:
11 | name: gru_neighbor_model
12 | params: None
13 | batch_generator:
14 | name: neighboring_grid_batch_generator
15 | params:
16 | batch_size: 14
17 | num_points: 4096
18 | grid_spacing: 0.5
19 | metric: chebyshev
20 | augmentation: False
21 | grid_x: 2
22 | grid_y: 2
23 | radius: 0.5
24 | optimizer:
25 | name: exponential_decay_adam
26 | params:
27 | initial_lr: 0.001
28 | decay_step: 300000
29 | decay_rate: 0.5
30 | gradient_clipping: 15
31 | train:
32 | epochs: 61
33 |
--------------------------------------------------------------------------------
/experiments/iccvw_paper_2017/s3dis_mscu/s3dis_mscu_area_1.yaml:
--------------------------------------------------------------------------------
1 | modus: TRAIN_VAL
2 | dataset:
3 | name: general_dataset
4 | num_classes: 13
5 | data_path: dataset/stanford_indoor/
6 | test_sets: ['Area_1']
7 | downsample_prefix: sample_0.03
8 | colors: True
9 | laser: False
10 | model:
11 | name: multi_scale_cu_model
12 | batch_generator:
13 | name: multi_scale_batch_generator
14 | params:
15 | batch_size: 24
16 | num_points: 4096
17 | grid_spacing: 0.5
18 | metric: chebyshev
19 | augmentation: False
20 | radii: [0.25, 0.5, 1.0]
21 | optimizer:
22 | name: exponential_decay_adam
23 | params:
24 | initial_lr: 0.001
25 | decay_step: 300000
26 | decay_rate: 0.5
27 | train:
28 | epochs: 61
--------------------------------------------------------------------------------
/experiments/iccvw_paper_2017/s3dis_mscu/s3dis_mscu_area_2.yaml:
--------------------------------------------------------------------------------
1 | modus: TRAIN_VAL
2 | dataset:
3 | name: general_dataset
4 | num_classes: 13
5 | data_path: dataset/stanford_indoor/
6 | test_sets: ['Area_2']
7 | downsample_prefix: sample_0.03
8 | colors: True
9 | laser: False
10 | model:
11 | name: multi_scale_cu_model
12 | batch_generator:
13 | name: multi_scale_batch_generator
14 | params:
15 | batch_size: 24
16 | num_points: 4096
17 | grid_spacing: 0.5
18 | metric: chebyshev
19 | augmentation: False
20 | radii: [0.25, 0.5, 1.0]
21 | optimizer:
22 | name: exponential_decay_adam
23 | params:
24 | initial_lr: 0.001
25 | decay_step: 300000
26 | decay_rate: 0.5
27 | train:
28 | epochs: 61
--------------------------------------------------------------------------------
/experiments/iccvw_paper_2017/s3dis_mscu/s3dis_mscu_area_3.yaml:
--------------------------------------------------------------------------------
1 | modus: TRAIN_VAL
2 | dataset:
3 | name: general_dataset
4 | num_classes: 13
5 | data_path: dataset/stanford_indoor/
6 | test_sets: ['Area_3']
7 | downsample_prefix: sample_0.03
8 | colors: True
9 | laser: False
10 | model:
11 | name: multi_scale_cu_model
12 | batch_generator:
13 | name: multi_scale_batch_generator
14 | params:
15 | batch_size: 24
16 | num_points: 4096
17 | grid_spacing: 0.5
18 | metric: chebyshev
19 | augmentation: False
20 | radii: [0.25, 0.5, 1.0]
21 | optimizer:
22 | name: exponential_decay_adam
23 | params:
24 | initial_lr: 0.001
25 | decay_step: 300000
26 | decay_rate: 0.5
27 | train:
28 | epochs: 61
--------------------------------------------------------------------------------
/experiments/iccvw_paper_2017/s3dis_mscu/s3dis_mscu_area_4.yaml:
--------------------------------------------------------------------------------
1 | modus: TRAIN_VAL
2 | dataset:
3 | name: general_dataset
4 | num_classes: 13
5 | data_path: dataset/stanford_indoor/
6 | test_sets: ['Area_4']
7 | downsample_prefix: sample_0.03
8 | colors: True
9 | laser: False
10 | model:
11 | name: multi_scale_cu_model
12 | batch_generator:
13 | name: multi_scale_batch_generator
14 | params:
15 | batch_size: 24
16 | num_points: 4096
17 | grid_spacing: 0.5
18 | metric: chebyshev
19 | augmentation: False
20 | radii: [0.25, 0.5, 1.0]
21 | optimizer:
22 | name: exponential_decay_adam
23 | params:
24 | initial_lr: 0.001
25 | decay_step: 300000
26 | decay_rate: 0.5
27 | train:
28 | epochs: 61
--------------------------------------------------------------------------------
/experiments/iccvw_paper_2017/s3dis_mscu/s3dis_mscu_area_5.yaml:
--------------------------------------------------------------------------------
1 | modus: TRAIN_VAL
2 | dataset:
3 | name: general_dataset
4 | num_classes: 13
5 | data_path: dataset/stanford_indoor/
6 | test_sets: ['Area_5']
7 | downsample_prefix: sample_0.03
8 | colors: True
9 | laser: False
10 | model:
11 | name: multi_scale_cu_model
12 | batch_generator:
13 | name: multi_scale_batch_generator
14 | params:
15 | batch_size: 24
16 | num_points: 4096
17 | grid_spacing: 0.5
18 | metric: chebyshev
19 | augmentation: False
20 | radii: [0.25, 0.5, 1.0]
21 | optimizer:
22 | name: exponential_decay_adam
23 | params:
24 | initial_lr: 0.001
25 | decay_step: 300000
26 | decay_rate: 0.5
27 | train:
28 | epochs: 161
--------------------------------------------------------------------------------
/experiments/iccvw_paper_2017/s3dis_mscu/s3dis_mscu_area_6.yaml:
--------------------------------------------------------------------------------
1 | modus: TRAIN_VAL
2 | dataset:
3 | name: general_dataset
4 | num_classes: 13
5 | data_path: dataset/stanford_indoor/
6 | test_sets: ['Area_6']
7 | downsample_prefix: sample_0.03
8 | colors: True
9 | laser: False
10 | model:
11 | name: multi_scale_cu_model
12 | batch_generator:
13 | name: multi_scale_batch_generator
14 | params:
15 | batch_size: 24
16 | num_points: 4096
17 | grid_spacing: 0.5
18 | metric: chebyshev
19 | augmentation: False
20 | radii: [0.25, 0.5, 1.0]
21 | optimizer:
22 | name: exponential_decay_adam
23 | params:
24 | initial_lr: 0.001
25 | decay_step: 300000
26 | decay_rate: 0.5
27 | train:
28 | epochs: 61
--------------------------------------------------------------------------------
/experiments/iccvw_paper_2017/s3dis_pointnet/s3dis_pointnet_area_1.yaml:
--------------------------------------------------------------------------------
1 | modus: TRAIN_VAL
2 | dataset:
3 | name: general_dataset
4 | num_classes: 13
5 | data_path: dataset/stanford_indoor/
6 | test_sets: ['Area_1']
7 | downsample_prefix: sample_0.03
8 | colors: True
9 | laser: False
10 | model:
11 | name: pointnet
12 | batch_generator:
13 | name: neighboring_grid_batch_generator
14 | params:
15 | batch_size: 24
16 | num_points: 4096
17 | grid_spacing: 0.5
18 | metric: chebyshev
19 | augmentation: False
20 | radius: 0.5
21 | grid_x: 1
22 | grid_y: 1
23 | optimizer:
24 | name: exponential_decay_adam
25 | params:
26 | initial_lr: 0.001
27 | decay_step: 300000
28 | decay_rate: 0.5
29 | train:
30 | epochs: 61
--------------------------------------------------------------------------------
/experiments/iccvw_paper_2017/s3dis_pointnet/s3dis_pointnet_area_2.yaml:
--------------------------------------------------------------------------------
1 | modus: TRAIN_VAL
2 | dataset:
3 | name: general_dataset
4 | num_classes: 13
5 | data_path: dataset/stanford_indoor/
6 | test_sets: ['Area_2']
7 | downsample_prefix: sample_0.03
8 | colors: True
9 | laser: False
10 | model:
11 | name: pointnet
12 | batch_generator:
13 | name: neighboring_grid_batch_generator
14 | params:
15 | batch_size: 24
16 | num_points: 4096
17 | grid_spacing: 0.5
18 | metric: chebyshev
19 | augmentation: False
20 | radius: 0.5
21 | grid_x: 1
22 | grid_y: 1
23 | optimizer:
24 | name: exponential_decay_adam
25 | params:
26 | initial_lr: 0.001
27 | decay_step: 300000
28 | decay_rate: 0.5
29 | train:
30 | epochs: 61
--------------------------------------------------------------------------------
/experiments/iccvw_paper_2017/s3dis_pointnet/s3dis_pointnet_area_3.yaml:
--------------------------------------------------------------------------------
1 | modus: TRAIN_VAL
2 | dataset:
3 | name: general_dataset
4 | num_classes: 13
5 | data_path: dataset/stanford_indoor/
6 | test_sets: ['Area_3']
7 | downsample_prefix: sample_0.03
8 | colors: True
9 | laser: False
10 | model:
11 | name: pointnet
12 | batch_generator:
13 | name: neighboring_grid_batch_generator
14 | params:
15 | batch_size: 24
16 | num_points: 4096
17 | grid_spacing: 0.5
18 | metric: chebyshev
19 | augmentation: False
20 | radius: 0.5
21 | grid_x: 1
22 | grid_y: 1
23 | optimizer:
24 | name: exponential_decay_adam
25 | params:
26 | initial_lr: 0.001
27 | decay_step: 300000
28 | decay_rate: 0.5
29 | train:
30 | epochs: 61
--------------------------------------------------------------------------------
/experiments/iccvw_paper_2017/s3dis_pointnet/s3dis_pointnet_area_4.yaml:
--------------------------------------------------------------------------------
1 | modus: TRAIN_VAL
2 | dataset:
3 | name: general_dataset
4 | num_classes: 13
5 | data_path: dataset/stanford_indoor/
6 | test_sets: ['Area_4']
7 | downsample_prefix: sample_0.03
8 | colors: True
9 | laser: False
10 | model:
11 | name: pointnet
12 | batch_generator:
13 | name: neighboring_grid_batch_generator
14 | params:
15 | batch_size: 24
16 | num_points: 4096
17 | grid_spacing: 0.5
18 | metric: chebyshev
19 | augmentation: False
20 | radius: 0.5
21 | grid_x: 1
22 | grid_y: 1
23 | optimizer:
24 | name: exponential_decay_adam
25 | params:
26 | initial_lr: 0.001
27 | decay_step: 300000
28 | decay_rate: 0.5
29 | train:
30 | epochs: 61
--------------------------------------------------------------------------------
/experiments/iccvw_paper_2017/s3dis_pointnet/s3dis_pointnet_area_5.yaml:
--------------------------------------------------------------------------------
1 | modus: TRAIN_VAL
2 | dataset:
3 | name: general_dataset
4 | num_classes: 13
5 | data_path: dataset/stanford_indoor/
6 | test_sets: ['Area_5']
7 | downsample_prefix: sample_0.03
8 | colors: True
9 | laser: False
10 | model:
11 | name: pointnet
12 | batch_generator:
13 | name: neighboring_grid_batch_generator
14 | params:
15 | batch_size: 24
16 | num_points: 4096
17 | grid_spacing: 0.5
18 | metric: chebyshev
19 | augmentation: False
20 | radius: 0.5
21 | grid_x: 1
22 | grid_y: 1
23 | optimizer:
24 | name: exponential_decay_adam
25 | params:
26 | initial_lr: 0.001
27 | decay_step: 300000
28 | decay_rate: 0.5
29 | train:
30 | epochs: 61
--------------------------------------------------------------------------------
/experiments/iccvw_paper_2017/s3dis_pointnet/s3dis_pointnet_area_6.yaml:
--------------------------------------------------------------------------------
1 | modus: TRAIN_VAL
2 | dataset:
3 | name: general_dataset
4 | num_classes: 13
5 | data_path: dataset/stanford_indoor/
6 | test_sets: ['Area_6']
7 | downsample_prefix: sample_0.03
8 | colors: True
9 | laser: False
10 | model:
11 | name: pointnet
12 | batch_generator:
13 | name: neighboring_grid_batch_generator
14 | params:
15 | batch_size: 24
16 | num_points: 4096
17 | grid_spacing: 0.5
18 | metric: chebyshev
19 | augmentation: False
20 | radius: 0.5
21 | grid_x: 1
22 | grid_y: 1
23 | optimizer:
24 | name: exponential_decay_adam
25 | params:
26 | initial_lr: 0.001
27 | decay_step: 300000
28 | decay_rate: 0.5
29 | train:
30 | epochs: 61
--------------------------------------------------------------------------------
/models/__init__.py:
--------------------------------------------------------------------------------
1 | from .multi_block_model import *
2 | from .multi_scale_cu_model import *
3 | from .pointnet import *
4 | from .gru_neighbor_model import *
5 |
--------------------------------------------------------------------------------
/models/gru_neighbor_model.py:
--------------------------------------------------------------------------------
1 | from tools import tf_util
2 | from .multi_block_model import *
3 | from batch_generators import *
4 | from typing import Dict
5 |
6 |
7 | class GruNeighborModel(MultiBlockModel):
8 | """
9 | parameterized version of a neighboring model using GRU units as described in the paper
10 | """
11 |
12 | def __init__(self, batch_generator: BatchGenerator, params: Dict[str, list]):
13 | super().__init__(batch_generator)
14 |
15 | self._bn_decay = 0.9
16 |
17 | @lazy_property
18 | def _prediction_helper(self):
19 | num_point = self.batch_generator.num_points
20 | batch_size = self.batch_generator.batch_size
21 |
22 | dims = self.batch_generator.input_shape
23 |
24 | cumulated_batch_size = dims[0] * dims[1]
25 | time = dims[1]
26 |
27 | input_image = tf.reshape(self.batch_generator.pointclouds_pl, (cumulated_batch_size, dims[2], dims[3]))
28 | input_image = tf.expand_dims(input_image, -1)
29 |
30 | net = input_image
31 |
32 | # CONV
33 | net = tf_util.conv2d(net, 64, [1, self.batch_generator.dataset.num_features + 3], padding='VALID', stride=[1, 1],
34 | bn=True, is_training=self.is_training_pl, scope='conv1', bn_decay=self._bn_decay)
35 | net = tf_util.conv2d(net, 64, [1, 1], padding='VALID', stride=[1, 1],
36 | bn=True, is_training=self.is_training_pl, scope='conv2', bn_decay=self._bn_decay)
37 | net = tf_util.conv2d(net, 64, [1, 1], padding='VALID', stride=[1, 1],
38 | bn=True, is_training=self.is_training_pl, scope='conv3', bn_decay=self._bn_decay)
39 | net = tf_util.conv2d(net, 128, [1, 1], padding='VALID', stride=[1, 1],
40 | bn=True, is_training=self.is_training_pl, scope='conv4', bn_decay=self._bn_decay)
41 | points_feat1 = tf_util.conv2d(net, 1024, [1, 1], padding='VALID', stride=[1, 1],
42 | bn=True, is_training=self.is_training_pl, scope='conv5', bn_decay=self._bn_decay)
43 | # MAX
44 | pc_feat1 = tf_util.max_pool2d(points_feat1, [num_point, 1], padding='VALID', scope='maxpool1')
45 |
46 | # FC
47 | pc_feat2 = tf.reshape(pc_feat1, [cumulated_batch_size, -1])
48 | pc_feat2 = tf_util.fully_connected(pc_feat2, 256, bn=True, is_training=self.is_training_pl, scope='fc1', bn_decay=self._bn_decay)
49 | pc_feat2 = tf_util.fully_connected(pc_feat2, 64, bn=True, is_training=self.is_training_pl, scope='fc2', bn_decay=self._bn_decay)
50 | tf.summary.histogram("pc_feat2_b", pc_feat2[:])
51 | pc_feat2_ = tf.reshape(pc_feat2, (batch_size, time, 64))
52 |
53 | pc_feat2_ = tf_util.gru_seq(pc_feat2_, 64, batch_size, time, False, scope='gru1')
54 |
55 | pc_feat2_ = tf.reshape(pc_feat2_, (cumulated_batch_size, 64))
56 | tf.summary.histogram("pc_feat2_a", pc_feat2_[:])
57 |
58 | pc_feat2 = tf.concat([pc_feat2, pc_feat2_], axis=1)
59 | # CONCAT
60 | pc_feat2_expand = tf.tile(tf.reshape(pc_feat2, [cumulated_batch_size, 1, 1, -1]), [1, num_point, 1, 1])
61 | points_feat2_concat = tf.concat(axis=3, values=[points_feat1, pc_feat2_expand])
62 |
63 | # CONV
64 | net2 = tf_util.conv2d(points_feat2_concat, 512, [1, 1], padding='VALID', stride=[1, 1],
65 | bn=True, is_training=self.is_training_pl, scope='conv6')
66 | net2 = tf_util.conv2d(net2, 256, [1, 1], padding='VALID', stride=[1, 1],
67 | bn=True, is_training=self.is_training_pl, scope='conv7')
68 | net2 = tf_util.dropout(net2, keep_prob=0.7, is_training=self.is_training_pl, scope='dp1')
69 | net2 = tf_util.conv2d(net2, self.batch_generator.dataset.num_classes, [1, 1], padding='VALID', stride=[1, 1],
70 | activation_fn=None, scope='conv8')
71 | net2 = tf.squeeze(net2, [2])
72 |
73 | return tf.reshape(net2, (batch_size, time, num_point, -1))
74 |
--------------------------------------------------------------------------------
/models/multi_block_model.py:
--------------------------------------------------------------------------------
1 | import tensorflow as tf
2 | from abc import *
3 | from tools.lazy_decorator import *
4 |
5 |
6 | class MultiBlockModel(ABC):
7 |
8 | def __init__(self, batch_generator):
9 | self.batch_generator = batch_generator
10 |
11 | self._create_placeholders()
12 |
13 | @lazy_function
14 | def _create_placeholders(self):
15 |
16 | self.eval_per_epoch_pl = tf.placeholder(tf.float32,
17 | name='evaluation_pl',
18 | shape=(3, 1))
19 |
20 | self.block_mask_bool = tf.cast(self.batch_generator.mask_pl, tf.bool) # shape: (BS, 1)
21 | self.labels = tf.boolean_mask(self.batch_generator.labels_pl, self.block_mask_bool) # shape: (BS, N)
22 | self.labels = tf.cast(self.labels, tf.int32)
23 |
24 | num_blocks = tf.reduce_sum(self.batch_generator.mask_pl) # num of blocks per batch
25 | self.num_blocks = tf.cast(num_blocks, tf.float32)
26 |
27 | self.is_training_pl = tf.placeholder(tf.bool,
28 | name='is_training_pl',
29 | shape=())
30 |
31 | @lazy_property
32 | def prediction(self):
33 | pred = self._prediction_helper
34 | # Apply mask to prediction and labels
35 | return tf.boolean_mask(pred, self.block_mask_bool) # shape: (BS, N, K)
36 |
37 | @lazy_property
38 | def prediction_sm(self):
39 | return tf.nn.softmax(self.prediction)
40 |
41 | @lazy_property
42 | @abstractmethod
43 | def _prediction_helper(self):
44 | raise NotImplementedError('Should be defined in subclass')
45 |
46 | @lazy_property
47 | def loss(self):
48 | loss = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=self.prediction,
49 | labels=self.labels)
50 | return tf.reduce_mean(loss)
51 |
52 | @lazy_property
53 | def correct(self):
54 | return tf.equal(tf.argmax(self.prediction, 2), tf.to_int64(self.labels))
55 |
56 | @lazy_property
57 | def accuracy(self):
58 | return tf.reduce_sum(tf.cast(self.correct, tf.float32)) / \
59 | tf.cast(self.batch_generator.num_points * self.num_blocks, tf.float32)
60 |
61 | @lazy_function
62 | def register_summary(self):
63 | tf.summary.scalar('loss', self.loss)
64 | tf.summary.scalar('avg_acc', self.eval_per_epoch_pl[0, 0])
65 | tf.summary.scalar('avg_iou', self.eval_per_epoch_pl[1, 0])
66 | tf.summary.scalar('avg_loss', self.eval_per_epoch_pl[2, 0])
67 | tf.summary.scalar('accuracy', self.accuracy)
68 |
69 | @property
70 | def input_shape(self):
71 | return self.batch_generator.pointclouds_pl.get_shape().as_list()
72 |
73 | @property
74 | def labels_shape(self):
75 | return self.batch_generator.labels_pl.get_shape().as_list()
76 |
--------------------------------------------------------------------------------
/models/multi_scale_cu_model.py:
--------------------------------------------------------------------------------
1 | from tools import tf_util
2 | from .multi_block_model import *
3 | from batch_generators import *
4 | from typing import Dict
5 |
6 |
7 | class MultiScaleCuModel(MultiBlockModel):
8 | """
9 | parameterized version of a multiscale pointnet with consolidation units
10 | """
11 |
12 | def __init__(self, batch_generator: BatchGenerator, params: Dict[str, list]):
13 | """
14 | initialization of multi scale model with consolidation units
15 | :param batch_generator:
16 | :param params: contains parameter concerning
17 | - ilc_sizes (filter sizes for input level context)
18 | - olc_sizes (consolidation units' sizes)
19 | - olc_sizes (filter sizes for output level context)
20 | """
21 | super().__init__(batch_generator)
22 |
23 | if params is None:
24 | # standard parameters from the paper
25 | self._ilc_sizes = [64, 128]
26 | self._cu_sizes = [256, 1024]
27 | self._olc_sizes = [512, 128]
28 | else:
29 | # load custom parameters
30 | self._ilc_sizes = params['ilc_sizes']
31 | self._cu_sizes = params['cu_sizes']
32 | self._olc_sizes = params['olc_sizes']
33 |
34 | self._bn_decay = 0.9
35 |
36 | @lazy_property
37 | def _prediction_helper(self):
38 | # pointcloud placeholder has the following format: BxSxNxF
39 | # Batch B
40 | # Scale S
41 | # Point P
42 | # Feature F
43 |
44 | # allow an arbitrary number of scales
45 | scales = [tf.expand_dims(self.batch_generator.pointclouds_pl[:, i, ...], axis=2)
46 | for i in range(self.input_shape[1])]
47 |
48 | num_points = self.batch_generator.num_points
49 |
50 | # store reference to original scale for later concatenating
51 | scale1 = scales[1]
52 |
53 | ''' INPUT-LEVEL CONTEXT '''
54 | for scale_index in range(len(scales)):
55 | # build global feature extractor for each scale independently
56 | for size_index, ilc_size in enumerate(self._ilc_sizes):
57 | scales[scale_index] = tf_util.conv2d(scales[scale_index], ilc_size, [1, 1], padding='VALID',
58 | stride=[1, 1],
59 | bn=True, is_training=self.is_training_pl,
60 | scope='ilc_conv' + str(size_index) + 'sc' + str(scale_index),
61 | bn_decay=self._bn_decay)
62 | # calculate global features for each scale
63 | scales[scale_index] = tf.reduce_max(scales[scale_index], axis=1,
64 | keep_dims=True, name="gf_sc" + str(scale_index))
65 |
66 | ''' CONCATENATE GLOBAL FEATURES OF ALL SCALES '''
67 | net = tf.concat(values=scales, axis=3)
68 | net = tf.tile(net, [1, num_points, 1, 1], name='repeat')
69 | net = tf.concat(values=[scale1, net], axis=3)
70 |
71 | ''' CONSOLIDATION UNIT SECTION '''
72 | for index, cu_size in enumerate(self._cu_sizes):
73 | net = tf_util.consolidation_unit(net, size=cu_size, scope='cu' + str(index), bn=True,
74 | bn_decay=self._bn_decay, is_training=self.is_training_pl)
75 |
76 | ''' OUTPUT-LEVEL CONTEXT '''
77 | for size_index, olc_size in enumerate(self._olc_sizes):
78 | net = tf_util.conv2d(net, olc_size, [1, 1], padding='VALID', stride=[1, 1], bn=True,
79 | is_training=self.is_training_pl,
80 | scope='olc_conv' + str(size_index), bn_decay=self._bn_decay)
81 |
82 | net = tf_util.conv2d(net, self.batch_generator.dataset.num_classes, [1, 1], padding='VALID', stride=[1, 1],
83 | activation_fn=None, scope='conv_output')
84 |
85 | net = tf.transpose(net, [0, 2, 1, 3])
86 |
87 | return net
88 |
--------------------------------------------------------------------------------
/models/pointnet.py:
--------------------------------------------------------------------------------
1 | from tools import tf_util
2 | from .multi_block_model import *
3 | from batch_generators import *
4 | from typing import Dict
5 |
6 |
7 | class Pointnet(MultiBlockModel):
8 | """
9 | Pointnet network architecture from
10 | PointNet: Deep Learning on Point Sets for 3D Classification and Segmentation
11 | Author: Qi, et al.
12 | """
13 |
14 | def __init__(self, batch_generator: BatchGenerator, params: Dict[str, list]):
15 | super().__init__(batch_generator)
16 | self._bn_decay = 0.9
17 |
18 | @lazy_property
19 | def _prediction_helper(self):
20 | # pointcloud placeholder has the following format: BxSxNxF
21 | # Batch B
22 | # Scale S
23 | # Point P
24 | # Feature F
25 | num_points = self.batch_generator.pointclouds_pl.get_shape().as_list()[2]
26 |
27 | image_pl = tf.transpose(self.batch_generator.pointclouds_pl, [0, 2, 1, 3])
28 |
29 | # CONV
30 | net = tf_util.conv2d(image_pl, 64, [1, 1], padding='VALID', stride=[1, 1],
31 | bn=True, is_training=self.is_training_pl, scope='conv1', bn_decay=self._bn_decay)
32 | net = tf_util.conv2d(net, 64, [1, 1], padding='VALID', stride=[1, 1],
33 | bn=True, is_training=self.is_training_pl, scope='conv2', bn_decay=self._bn_decay)
34 | net = tf_util.conv2d(net, 64, [1, 1], padding='VALID', stride=[1, 1],
35 | bn=True, is_training=self.is_training_pl, scope='conv3', bn_decay=self._bn_decay)
36 | net = tf_util.conv2d(net, 128, [1, 1], padding='VALID', stride=[1, 1],
37 | bn=True, is_training=self.is_training_pl, scope='conv4', bn_decay=self._bn_decay)
38 | points_feat1 = tf_util.conv2d(net, 1024, [1, 1], padding='VALID', stride=[1, 1],
39 | bn=True, is_training=self.is_training_pl, scope='conv5', bn_decay=self._bn_decay)
40 | # MAX
41 | pc_feat1 = tf.reduce_max(points_feat1, axis=1, keep_dims=True, name="global_features")
42 | # FC
43 | pc_feat1 = tf.reshape(pc_feat1, [-1, 1024])
44 | pc_feat1 = tf_util.fully_connected(pc_feat1, 256, bn=True, is_training=self.is_training_pl, scope='fc1',
45 | bn_decay=self._bn_decay)
46 | pc_feat1 = tf_util.fully_connected(pc_feat1, 128, bn=True, is_training=self.is_training_pl, scope='fc2',
47 | bn_decay=self._bn_decay)
48 |
49 | # CONCAT
50 | pc_feat1_expand = tf.tile(tf.reshape(pc_feat1, [-1, 1, 1, 128]), [1, num_points, 1, 1])
51 | points_feat1_concat = tf.concat(axis=3, values=[points_feat1, pc_feat1_expand])
52 |
53 | # CONV
54 | net = tf_util.conv2d(points_feat1_concat, 512, [1, 1], padding='VALID', stride=[1, 1],
55 | bn=True, is_training=self.is_training_pl, scope='conv6')
56 | net = tf_util.conv2d(net, 256, [1, 1], padding='VALID', stride=[1, 1],
57 | bn=True, is_training=self.is_training_pl, scope='conv7')
58 | net = tf_util.dropout(net, keep_prob=0.7, is_training=self.is_training_pl, scope='dp1')
59 | net = tf_util.conv2d(net, self.batch_generator.dataset.num_classes, [1, 1], padding='VALID', stride=[1, 1],
60 | activation_fn=None, scope='conv8')
61 |
62 | net = tf.transpose(net, [0, 2, 1, 3])
63 | return net
64 |
--------------------------------------------------------------------------------
/optimizers/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VisualComputingInstitute/3d-semantic-segmentation/1dfc010b370a346902ad29460c9ad969c1892a97/optimizers/__init__.py
--------------------------------------------------------------------------------
/optimizers/exponential_decay_adam.py:
--------------------------------------------------------------------------------
1 | from models import *
2 | from tools.lazy_decorator import *
3 |
4 |
5 | class ExponentialDecayAdam:
6 | def __init__(self, model, params: dict):
7 | # Variable to increment by one after the variables have been updated by the optimizers
8 | # this helps to keep track of the progress of the training (e.g. adapting learning rate and decay)
9 | self.global_step = tf.Variable(0, name='global_step_counter')
10 | self._model = model
11 | self._base_learning_rate = params['initial_lr']
12 | self._decay_step = params['decay_step']
13 | self._decay_rate = params['decay_rate']
14 |
15 | self._gradient_clipping = params.get('gradient_clipping')
16 |
17 | @lazy_property
18 | def learning_rate(self):
19 | learning_rate = tf.cond(self.global_step * self._model.batch_generator.batch_size < self._decay_step,
20 | lambda: tf.constant(self._base_learning_rate),
21 | lambda: tf.train.exponential_decay(
22 | self._base_learning_rate, # Base learning rate.
23 | self.global_step * self._model.batch_generator.batch_size - self._decay_step,
24 | self._decay_step // 2, # Decay step.
25 | self._decay_rate, # Decay rate.
26 | staircase=False))
27 | return learning_rate
28 |
29 | @lazy_function
30 | def register_summary(self):
31 | tf.summary.scalar('global_step', self.global_step)
32 | tf.summary.scalar('learning_rate', self.learning_rate)
33 |
34 | @lazy_property
35 | def optimize(self):
36 | if self._gradient_clipping is not None:
37 | # Clipping gradients - check if gradients explode
38 | optimizer = tf.train.AdamOptimizer(self.learning_rate)
39 | gradients = tf.gradients(self._model.loss, tf.trainable_variables())
40 | trainables = tf.trainable_variables()
41 | clipped_gradients, _ = tf.clip_by_global_norm(gradients, self._gradient_clipping)
42 |
43 | extra_update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
44 | with tf.control_dependencies(extra_update_ops):
45 | return optimizer.apply_gradients(zip(clipped_gradients, trainables), global_step=self.global_step)
46 | else:
47 | optimizer = tf.train.AdamOptimizer(self.learning_rate)
48 | return optimizer.minimize(self._model.loss, global_step=self.global_step)
49 |
--------------------------------------------------------------------------------
/run.py:
--------------------------------------------------------------------------------
1 | import argparse
2 | from datasets import *
3 | from models import *
4 | import yaml
5 | from tools.tools import *
6 | from pathlib import Path
7 | import tools.evaluation as evaluation
8 | import shutil
9 | import logging
10 |
11 | avg_iou_per_epoch = [0]
12 | avg_class_acc_per_epoch = [0]
13 | avg_loss_per_epoch = [0]
14 |
15 |
16 | def main(config: dict, log_dir: str, isTrain: bool):
17 | with tf.Graph().as_default():
18 | Dataset = import_class('datasets', config['dataset']['name'])
19 | dataset = Dataset(config['dataset']['data_path'],
20 | is_train=isTrain,
21 | test_sets=config['dataset']['test_sets'],
22 | downsample_prefix=config['dataset']['downsample_prefix'],
23 | is_colors=config['dataset']['colors'],
24 | is_laser=config['dataset']['laser'],
25 | n_classes=config['dataset']['num_classes'])
26 |
27 | BatchGenerator = import_class('batch_generators', config['batch_generator']['name'])
28 | batch_generator = BatchGenerator(dataset, config['batch_generator']['params'])
29 |
30 | Model = import_class('models', config['model']['name'])
31 | model = Model(batch_generator, config['model'].get('params'))
32 |
33 | if isTrain:
34 | Optimizer = import_class('optimizers', config['optimizer']['name'])
35 | optimizer = Optimizer(model, config['optimizer']['params'])
36 |
37 | sess, ops, writer, saver, epoch_start = prepare_network(model, log_dir, optimizer, isTrain=isTrain,
38 | model_path=config.get('resume_path'))
39 |
40 | for epoch in range(epoch_start, config['train']['epochs']):
41 | train_one_epoch(sess, ops, writer, model, epoch, config['train']['epochs'])
42 | eval_one_epoch(sess, ops, model, dataset, epoch, config['train']['epochs'])
43 |
44 | # Save the variables to disk.
45 | if epoch % 10 == 0:
46 | path = Path(f"{log_dir}/model_ckpts")
47 | path.mkdir(parents=True, exist_ok=True)
48 | saver.save(sess, os.path.join(f"{log_dir}/model_ckpts",
49 | f"{epoch+1:03d}_model.ckpt"))
50 | else:
51 | sess, ops, writer, saver, _ = prepare_network(model, log_dir,
52 | isTrain=isTrain, model_path=config['model_path'])
53 | predict_on_test_set(sess, ops, model, dataset, log_dir)
54 |
55 |
56 | def prepare_network(model: MultiBlockModel, log_dir: str, optimizer=None, isTrain=True, model_path=None):
57 | # Create a session
58 | config = tf.ConfigProto()
59 | config.gpu_options.allow_growth = True
60 | config.allow_soft_placement = True
61 | config.log_device_placement = False
62 | sess = tf.Session(config=config)
63 |
64 | with tf.device('/gpu:0'):
65 | model.register_summary()
66 | if optimizer is not None:
67 | optimizer.register_summary()
68 |
69 | # Add summary writers
70 | merged = tf.summary.merge_all()
71 | train_writer = tf.summary.FileWriter(os.path.join(log_dir, 'tensorflow'), sess.graph)
72 |
73 | if optimizer is not None:
74 | ops = {'pointclouds_pl': model.batch_generator.pointclouds_pl,
75 | 'labels_pl': model.batch_generator.labels_pl,
76 | 'mask_pl': model.batch_generator.mask_pl,
77 | 'eval_per_epoch_pl': model.eval_per_epoch_pl,
78 | 'is_training_pl': model.is_training_pl,
79 | 'pred': model.prediction,
80 | 'pred_sm': model.prediction_sm,
81 | 'loss': model.loss,
82 | 'train_op': optimizer.optimize,
83 | 'merged': merged,
84 | 'step': optimizer.global_step,
85 | 'correct': model.correct,
86 | 'labels': model.labels,
87 | 'handle_pl': model.batch_generator.handle_pl,
88 | 'iterator_train': model.batch_generator.iterator_train,
89 | 'iterator_test': model.batch_generator.iterator_test,
90 | 'cloud_ids_pl': model.batch_generator.cloud_ids_pl,
91 | 'point_ids_pl': model.batch_generator.point_ids_pl,
92 | 'next_element': model.batch_generator.next_element
93 | }
94 | else:
95 | ops = {'pointclouds_pl': model.batch_generator.pointclouds_pl,
96 | 'labels_pl': model.batch_generator.labels_pl,
97 | 'mask_pl': model.batch_generator.mask_pl,
98 | 'eval_per_epoch_pl': model.eval_per_epoch_pl,
99 | 'is_training_pl': model.is_training_pl,
100 | 'pred': model.prediction,
101 | 'pred_sm': model.prediction_sm,
102 | 'loss': model.loss,
103 | 'correct': model.correct,
104 | 'labels': model.labels,
105 | 'handle_pl': model.batch_generator.handle_pl,
106 | 'iterator_train': model.batch_generator.iterator_train,
107 | 'iterator_test': model.batch_generator.iterator_test,
108 | 'cloud_ids_pl': model.batch_generator.cloud_ids_pl,
109 | 'point_ids_pl': model.batch_generator.point_ids_pl,
110 | 'next_element': model.batch_generator.next_element
111 | }
112 |
113 | # Init variables
114 | init = tf.global_variables_initializer()
115 | sess.run(init, {model.is_training_pl: isTrain})
116 |
117 | # Add ops to save and restore all the variables.
118 | saver = tf.train.Saver()
119 |
120 | epoch_number = 0
121 |
122 | if model_path is not None:
123 | # resume training
124 | latest_checkpoint_path = tf.train.latest_checkpoint(model_path)
125 | # extract latest training epoch number
126 | epoch_number = int(latest_checkpoint_path.split('/')[-1].split('_')[0])
127 |
128 | saver.restore(sess, latest_checkpoint_path)
129 |
130 | return sess, ops, train_writer, saver, epoch_number
131 |
132 |
133 | def train_one_epoch(sess, ops, train_writer, model, epoch, max_epoch):
134 | model.batch_generator.shuffle()
135 |
136 | for _ in tqdm(range(model.batch_generator.num_train_batches),
137 | desc=f"Running training epoch {epoch+1:03d} / {max_epoch:03d}"):
138 |
139 | a = np.reshape(np.array(avg_class_acc_per_epoch[-1]), [1, 1])
140 | b = np.reshape(np.array(avg_iou_per_epoch[-1]), [1, 1])
141 | c = np.reshape(np.array(avg_loss_per_epoch[-1]), [1, 1])
142 | eval_per_epoch = np.concatenate((a, b, c))
143 |
144 | handle_train = sess.run(ops['iterator_train'].string_handle())
145 | feed_dict = {ops['is_training_pl']: True,
146 | ops['eval_per_epoch_pl']: eval_per_epoch,
147 | ops['handle_pl']: handle_train}
148 |
149 | start_time = time.time()
150 |
151 | summary, step, _, loss_val, pc_val, pred_val, labels_val, correct_val = sess.run(
152 | [ops['merged'], ops['step'], ops['train_op'], ops['loss'],
153 | ops['pointclouds_pl'],
154 | ops['pred'], ops['labels'], ops['correct']],
155 | feed_dict=feed_dict)
156 |
157 | elapsed_time = time.time() - start_time
158 | summary2 = tf.Summary()
159 | summary2.value.add(tag='secs_per_iter', simple_value=elapsed_time)
160 | train_writer.add_summary(summary2, step)
161 | train_writer.add_summary(summary, step)
162 |
163 |
164 | def eval_one_epoch(sess, ops, model, dataset, epoch, max_epoch):
165 | total_correct = 0
166 | total_seen = 0
167 | loss_sum = 0
168 |
169 | # Compute avg IoU over classes
170 | total_seen_class = [0 for _ in range(dataset.num_classes)] # true_pos + false_neg i.e. all points from this class
171 | total_correct_class = [0 for _ in range(dataset.num_classes)] # true_pos
172 | total_pred_class = [0 for _ in range(dataset.num_classes)] # true_pos + false_pos i.e. num pred classes
173 |
174 | overall_acc = []
175 |
176 | for _ in tqdm(range(model.batch_generator.num_test_batches),
177 | desc='Running evaluation epoch %04d / %04d' % (epoch+1, max_epoch)):
178 |
179 | a = np.reshape(np.array(avg_class_acc_per_epoch[-1]), [1, 1])
180 | b = np.reshape(np.array(avg_iou_per_epoch[-1]), [1, 1])
181 | c = np.reshape(np.array(avg_loss_per_epoch[-1]), [1, 1])
182 | eval_per_epoch = np.concatenate((a, b, c))
183 |
184 | handle_test = sess.run(ops['iterator_test'].string_handle())
185 | feed_dict = {ops['is_training_pl']: False,
186 | ops['eval_per_epoch_pl']: eval_per_epoch,
187 | ops['handle_pl']: handle_test}
188 |
189 | _, step, loss_val, pred_val, correct_val, labels_val, batch_mask, batch_cloud_ids, batch_point_ids = sess.run(
190 | [ops['merged'], ops['step'], ops['loss'],
191 | ops['pred_sm'], ops['correct'], ops['labels'],
192 | ops['mask_pl'], ops['cloud_ids_pl'], ops['point_ids_pl']], feed_dict=feed_dict)
193 |
194 | total_correct += np.sum(correct_val) # shape: scalar
195 | total_seen += pred_val.shape[0] * pred_val.shape[1]
196 |
197 | overall_acc.append(total_correct / total_seen)
198 |
199 | loss_sum += loss_val
200 |
201 | pred_val = np.argmax(pred_val, 2) # shape: (BS*B' x N)
202 |
203 | for i in range(labels_val.shape[0]): # iterate over blocks
204 | for j in range(labels_val.shape[1]): # iterate over points in block
205 | lbl_gt = int(labels_val[i, j])
206 | lbl_pred = int(pred_val[i, j])
207 | total_seen_class[lbl_gt] += 1
208 | total_correct_class[lbl_gt] += (lbl_pred == lbl_gt)
209 | total_pred_class[lbl_pred] += 1
210 |
211 | iou_per_class = np.zeros(dataset.num_classes)
212 | iou_per_class_mask = np.zeros(dataset.num_classes, dtype=np.int8)
213 | for i in range(dataset.num_classes):
214 | denominator = float(total_seen_class[i] + total_pred_class[i] - total_correct_class[i])
215 |
216 | if denominator != 0:
217 | iou_per_class[i] = total_correct_class[i] / denominator
218 | else:
219 | iou_per_class_mask[i] = 1
220 |
221 | iou_per_class_masked = np.ma.array(iou_per_class, mask=iou_per_class_mask)
222 |
223 | total_seen_class_mask = [1 if seen == 0 else 0 for seen in total_seen_class]
224 |
225 | class_acc = np.array(total_correct_class) / np.array(total_seen_class, dtype=np.float)
226 |
227 | class_acc_masked = np.ma.array(class_acc, mask=total_seen_class_mask)
228 |
229 | avg_iou = iou_per_class_masked.mean()
230 | avg_loss = loss_sum / float(total_seen / model.batch_generator.num_points)
231 | avg_class_acc = class_acc_masked.mean()
232 | avg_class_acc_per_epoch.append(avg_class_acc)
233 | avg_iou_per_epoch.append(avg_iou)
234 | avg_loss_per_epoch.append(avg_loss)
235 |
236 | logging.info(f"[Epoch {epoch+1:03d}] avg class acc: {avg_class_acc}")
237 | logging.info(f"[Epoch {epoch+1:03d}] avg iou: {avg_iou}")
238 | logging.info(f"[Epoch {epoch+1:03d}] avg overall acc: {np.mean(overall_acc)}")
239 |
240 |
241 | def predict_on_test_set(sess, ops, model, dataset: GeneralDataset, log_dir: str):
242 | is_training = False
243 |
244 | cumulated_result = {}
245 |
246 | for _ in tqdm(range(model.batch_generator.num_test_batches)):
247 | handle_test = sess.run(ops['iterator_test'].string_handle())
248 | feed_dict = {ops['is_training_pl']: is_training,
249 | ops['eval_per_epoch_pl']: np.zeros((3,1)),
250 | ops['handle_pl']: handle_test}
251 |
252 | loss_val, pred_val, correct_val, labels_val, batch_mask, batch_cloud_ids, batch_point_ids = sess.run(
253 | [ops['loss'], ops['pred_sm'], ops['correct'], ops['labels'],
254 | ops['mask_pl'], ops['cloud_ids_pl'], ops['point_ids_pl']], feed_dict=feed_dict)
255 |
256 | num_classes = pred_val.shape[2]
257 | num_batches = pred_val.shape[0]
258 |
259 | batch_mask = np.array(batch_mask, dtype=bool) # shape: (BS, B) - convert mask to bool
260 | batch_point_ids = batch_point_ids[batch_mask] # shape: (B, N)
261 | batch_cloud_ids = batch_cloud_ids[batch_mask] # shape: (B)
262 |
263 | for batch_id in range(num_batches):
264 | pc_id = batch_cloud_ids[batch_id]
265 | pc_name = dataset.file_names[pc_id]
266 |
267 | for point_in_batch, point_id in enumerate(batch_point_ids[batch_id, :]):
268 | num_fs_properties = dataset.data[pc_id].shape[1]
269 | if pc_name not in cumulated_result:
270 | # if there is not information about the point cloud so far, initialize it
271 | # label -1 means that there is not label given so far
272 | # cumulate predictions for the same point
273 | cumulated_result[pc_name] = np.zeros((dataset.data[pc_id].shape[0],
274 | num_fs_properties + num_classes + 1))
275 | cumulated_result[pc_name][:, :num_fs_properties] = dataset.data[pc_id]
276 | cumulated_result[pc_name][:, -1] = -1
277 |
278 | cumulated_result[pc_name][point_id, num_fs_properties:-1] += pred_val[batch_id, point_in_batch]
279 | cumulated_result[pc_name][point_id, -1] = np.argmax(cumulated_result[pc_name][point_id,
280 | num_fs_properties:-1])
281 |
282 | for key in tqdm(cumulated_result.keys(), desc='knn interpolation for full sized point cloud'):
283 | cumulated_result[key] = evaluation.knn_interpolation(cumulated_result[key], dataset.full_sized_data[key])
284 |
285 | class_acc, class_iou, overall_acc = evaluation.calculate_scores(cumulated_result, dataset.num_classes)
286 |
287 | logging.info(f" overall accuracy: {overall_acc}")
288 | logging.info(f"mean class accuracy: {np.nanmean(class_acc)}")
289 | logging.info(f" mean iou: {np.nanmean(class_iou)}")
290 |
291 | for i in range(dataset.num_classes):
292 | logging.info(f"accuracy for class {i}: {class_acc[i]}")
293 | logging.info(f" iou for class {i}: {class_iou[i]}")
294 |
295 | evaluation.save_npy_results(cumulated_result, log_dir)
296 | evaluation.save_pc_as_obj(cumulated_result, dataset.label_colors(), log_dir)
297 |
298 |
299 | if __name__ == '__main__':
300 | log_dir = setup_logger()
301 |
302 | parser = argparse.ArgumentParser()
303 | parser.add_argument("--config", help="experiment definition file", metavar="FILE", required=True)
304 | args = parser.parse_args()
305 |
306 | params = parser.parse_args()
307 |
308 | with open(params.config, 'r') as stream:
309 | try:
310 | config = yaml.load(stream)
311 | # backup config file
312 | shutil.copy(params.config, log_dir)
313 |
314 | isTrain = False
315 |
316 | if config['modus'] == 'TRAIN_VAL':
317 | isTrain = True
318 | elif config['modus'] == 'TEST':
319 | isTrain = False
320 |
321 | main(config, log_dir, isTrain)
322 | except yaml.YAMLError as exc:
323 | logging.error('Configuration file could not be read')
324 | exit(1)
325 |
--------------------------------------------------------------------------------
/tools/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VisualComputingInstitute/3d-semantic-segmentation/1dfc010b370a346902ad29460c9ad969c1892a97/tools/__init__.py
--------------------------------------------------------------------------------
/tools/downsample.py:
--------------------------------------------------------------------------------
1 | """
2 | Downsample full sized point clouds in order to speed up batch generation as well as better block representations.
3 | Resulting point clouds will be saved at the appropriate positions in the file system
4 | (For further information consult the wiki)
5 | """
6 |
7 | import numpy as np
8 | import argparse
9 | import tools
10 | from termcolor import colored
11 | import os
12 | from tqdm import tqdm
13 |
14 |
15 | def blockwise_uniform_downsample(data_labels, cell_size):
16 | data_dim = data_labels.shape[1] - 1
17 |
18 | number_classes = int(data_labels[:, -1].max()) + 1 # counting starts with label 0
19 |
20 | d = {}
21 | for i in tqdm(range(data_labels.shape[0]), desc='downsampling of points'):
22 | # create block boundaries
23 | x = int(data_labels[i, 0] / cell_size)
24 | y = int(data_labels[i, 1] / cell_size)
25 | z = int(data_labels[i, 2] / cell_size)
26 |
27 | # add space for one hot encoding (used for easier class occurence counting)
28 | # and counter value at the end of the row
29 | new_tuple = np.zeros([data_labels.shape[1] + number_classes], dtype=float)
30 | new_tuple[0:data_dim] = data_labels[i, 0:-1]
31 | new_tuple[data_dim+int(data_labels[i, -1])] = 1 # set label to one for the corresponding class
32 | new_tuple[-1] = 1 # count elements in the block
33 |
34 | # note: elementwise addition in numpy arrays!!!
35 | try:
36 | d[(x, y, z)] += new_tuple
37 | except:
38 | d[(x, y, z)] = new_tuple
39 |
40 | data = []
41 | labels = []
42 |
43 | for _, v in d.items():
44 | N = v[-1] # number of points in voxel
45 |
46 | # aggregate points in block to one normalized point
47 | data.append([v[i] / N for i in range(data_dim)])
48 |
49 | # find most prominent label (excluding counter and not in one hot encoding anymore)
50 | labels.append(np.argmax(v[data_dim:-1]))
51 |
52 | data = np.stack(data, axis=0)
53 | labels = np.stack(labels, axis=0)
54 |
55 | data_labels_new = np.hstack([data, np.expand_dims(labels, 1)]).astype(np.float32)
56 | return data_labels_new
57 |
58 |
59 | def main(params):
60 | for dirpath, dirnames, filenames in os.walk(params.data_dir):
61 | if os.path.basename(dirpath) == 'full_size':
62 | for filename in [f for f in filenames if f.endswith(".npy")]:
63 | print(f"downsampling {filename} in progress ...")
64 | data_labels = np.load(os.path.join(dirpath, filename))
65 | sampled_data_labels = blockwise_uniform_downsample(data_labels, params.cell_size)
66 |
67 | out_folder = os.path.join(os.path.dirname(dirpath), f"sample_{params.cell_size}")
68 |
69 | if not os.path.exists(out_folder):
70 | os.makedirs(out_folder)
71 |
72 | np.save(os.path.join(out_folder, filename), sampled_data_labels)
73 |
74 |
75 | if __name__ == '__main__':
76 | parser = argparse.ArgumentParser(description='Convert original data set to uniformly downsampled numpy version '
77 | 'in order to speed up batch generation '
78 | 'as well as better block representations')
79 |
80 | parser.add_argument('--data_dir', required=True, help='root directory of original data')
81 | parser.add_argument('--cell_size', type=float, default=0.03, help='width/length of downsampling cell')
82 | params = parser.parse_args()
83 |
84 | tools.pretty_print_arguments(params)
85 |
86 | main(params)
87 |
88 | print(colored('Finished successfully', 'green'))
89 |
--------------------------------------------------------------------------------
/tools/evaluation.py:
--------------------------------------------------------------------------------
1 | """
2 | Contains methods for evaluating and exporting the result of the network
3 | """
4 |
5 | import os
6 | import numpy as np
7 | from typing import Dict
8 | from tqdm import tqdm
9 | from sklearn.neighbors import BallTree
10 |
11 |
12 | def knn_interpolation(cumulated_pc: np.ndarray, full_sized_data: np.ndarray, k=5):
13 | """
14 | Using k-nn interpolation to find labels of points of the full sized pointcloud
15 | :param cumulated_pc: cumulated pointcloud results after running the network
16 | :param full_sized_data: full sized point cloud
17 | :param k: k for k nearest neighbor interpolation
18 | :return: pointcloud with predicted labels in last column and ground truth labels in last but one column
19 | """
20 |
21 | labeled = cumulated_pc[cumulated_pc[:, -1] != -1]
22 | to_be_predicted = full_sized_data.copy()
23 |
24 | ball_tree = BallTree(labeled[:, :3], metric='euclidean')
25 |
26 | knn_classes = labeled[ball_tree.query(to_be_predicted[:, :3], k=k)[1]][:, :, -1].astype(int)
27 |
28 | interpolated = np.zeros(knn_classes.shape[0])
29 |
30 | for i in range(knn_classes.shape[0]):
31 | interpolated[i] = np.bincount(knn_classes[i]).argmax()
32 |
33 | output = np.zeros((to_be_predicted.shape[0], to_be_predicted.shape[1]+1))
34 | output[:, :-1] = to_be_predicted
35 |
36 | output[:, -1] = interpolated
37 |
38 | return output
39 |
40 |
41 | def calculate_scores(cumulated_result: Dict[str, np.ndarray], num_classes: int):
42 | """
43 | calculate evaluation metrics of the prediction
44 | :param cumulated_result: cumulated_result: last column = predicted label; last but one column = ground truth
45 | :param num_classes: number of distinct classes of the dataset
46 | :return: class_acc, class_iou, overall_acc
47 | """
48 | total_seen_from_class = [0 for _ in range(num_classes)]
49 | total_pred_from_class = [0 for _ in range(num_classes)]
50 | total_correct_from_class = [0 for _ in range(num_classes)]
51 |
52 | for key in cumulated_result.keys():
53 | for class_id in range(num_classes):
54 | total_seen_from_class[class_id] += (cumulated_result[key][:, -2] == class_id).sum()
55 | total_pred_from_class[class_id] += (cumulated_result[key][:, -1] == class_id).sum()
56 |
57 | total_correct_from_class[class_id] += \
58 | np.logical_and((cumulated_result[key][:, -2] == class_id),
59 | (cumulated_result[key][:, -1] == class_id)).sum()
60 |
61 | class_acc = [total_correct_from_class[i] / total_seen_from_class[i] for i in range(num_classes)]
62 |
63 | class_iou = [total_correct_from_class[i] /
64 | (total_seen_from_class[i] + total_pred_from_class[i] - total_correct_from_class[i])
65 | for i in range(num_classes)]
66 |
67 | overall_acc = sum(total_correct_from_class) / sum(total_seen_from_class)
68 |
69 | return class_acc, class_iou, overall_acc
70 |
71 |
72 | def save_pc_as_obj(cumulated_result: Dict[str, np.ndarray], label_colors: np.ndarray, save_dir: str):
73 | """
74 | save pointclouds as obj files for later inspection with meshlab
75 | :param cumulated_result: cumulated_result: last column = predicted label; last but one column = ground truth
76 | :param label_colors: npy array containing the color information for each label class
77 | :param save_dir: directory to save obj point clouds
78 | :return: None
79 | """
80 | pointclouds_path = save_dir + '/pointclouds'
81 |
82 | for key in tqdm(cumulated_result.keys(), desc='Save obj point clouds to disk'):
83 | if not os.path.exists(pointclouds_path):
84 | os.makedirs(pointclouds_path)
85 |
86 | # Save predicted point clouds as obj files for later inspection using meshlab
87 | fout = open(f"{pointclouds_path}/{key}_pred.obj", 'w')
88 | pointcloud = cumulated_result[key]
89 | for j in range(pointcloud.shape[0]):
90 | color = label_colors[pointcloud[j, -1].astype(int)]
91 | fout.write(f"v {str(pointcloud[j, 0]).replace('.', ',')}"
92 | f" {str(pointcloud[j, 1]).replace('.', ',')}"
93 | f" {str(pointcloud[j, 2]).replace('.', ',')}"
94 | f" {color[0]} {color[1]} {color[2]}\n")
95 | fout.close()
96 |
97 |
98 | def save_npy_results(cumulated_result: Dict[str, np.ndarray], save_dir: str):
99 | """
100 | save cumulated results to disk
101 | :param cumulated_result: last column = predicted label; last but one column = ground truth
102 | :param save_dir: directory to save npy arrays
103 | :return: None
104 | """
105 | results_npy_path = save_dir + '/results_npy'
106 |
107 | for key in tqdm(cumulated_result.keys(), desc='Save npy results to disk'):
108 | if not os.path.exists(results_npy_path):
109 | os.makedirs(results_npy_path)
110 |
111 | np.save(f"{results_npy_path}/{key}", cumulated_result[key])
112 |
--------------------------------------------------------------------------------
/tools/lazy_decorator.py:
--------------------------------------------------------------------------------
1 | """
2 | Useful tool in order to access cached version of properties and functions which have to be executed just once
3 | (especially useful for building up the tensorflow computation graph)
4 | adapted from: https://stevenloria.com/lazy-properties/ and
5 | https://danijar.com/structuring-your-tensorflow-models/
6 | """
7 | import functools
8 |
9 |
10 | def lazy_property(function):
11 | """
12 | caches the output of the property and just returns the value for next calls
13 | :param function: property to be cached
14 | :return: cached output of property
15 | """
16 | attribute = '_cache_' + function.__name__
17 |
18 | @property
19 | @functools.wraps(function)
20 | def decorator(self):
21 | if not hasattr(self, attribute):
22 | setattr(self, attribute, function(self))
23 | return getattr(self, attribute)
24 |
25 | return decorator
26 |
27 |
28 | def lazy_function(function):
29 | """
30 | caches the output of the function and just returns the value for next calls
31 | :param function: function to be cached
32 | :return: cached output of function
33 | """
34 | attribute = '_cache_' + function.__name__
35 |
36 | @functools.wraps(function)
37 | def decorator(self):
38 | if not hasattr(self, attribute):
39 | setattr(self, attribute, function(self))
40 | return getattr(self, attribute)
41 |
42 | return decorator
43 |
--------------------------------------------------------------------------------
/tools/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 |
--------------------------------------------------------------------------------
/tools/prepare_s3dis.py:
--------------------------------------------------------------------------------
1 | """
2 | adapted from https://github.com/charlesq34/pointnet
3 | """
4 |
5 | import os
6 | import numpy as np
7 | import glob
8 | import argparse
9 | import tools
10 | from pathlib import Path
11 | from tqdm import tqdm
12 |
13 |
14 | def collect_point_label(anno_path, out_filename):
15 | """ Convert original dataset files to data_label file (each line is XYZRGBL).
16 | We aggregated all the points from each instance in the room.
17 |
18 | Args:
19 | anno_path: path to annotations. e.g. Area_1/office_2/Annotations/
20 | out_filename: path to save collected points and labels (each line is XYZRGBL)
21 | Returns:
22 | None
23 | Note:
24 | the points are shifted before save, the most negative point is now at origin.
25 | """
26 |
27 | g_classes = [x.rstrip() for x in open('meta/class_names.txt')]
28 | g_class2label = {cls: i for i, cls in enumerate(g_classes)}
29 |
30 | points_list = []
31 |
32 | for f in glob.glob(os.path.join(anno_path, '*.txt')):
33 | cls = os.path.basename(f).split('_')[0]
34 | if cls not in g_classes: # note: in some room there is 'staris' class..
35 | cls = 'clutter'
36 | points = np.loadtxt(f)
37 | labels = np.ones((points.shape[0], 1)) * g_class2label[cls]
38 | points_list.append(np.concatenate([points, labels], 1)) # Nx7
39 |
40 | data_label = np.concatenate(points_list, 0)
41 | xyz_min = np.amin(data_label, axis=0)[0:3]
42 | data_label[:, 0:3] -= xyz_min
43 | data_label = data_label.astype(dtype=np.float32)
44 |
45 | output_folder = Path(os.path.dirname(out_filename))
46 | output_folder.mkdir(parents=True, exist_ok=True)
47 |
48 | np.save(out_filename, data_label)
49 |
50 |
51 | def main(params):
52 | anno_paths = [x[0] for x in os.walk(params.input_dir) if x[0].endswith('Annotations')]
53 |
54 | # Note: there is an extra character in the v1.2 data in Area_5/hallway_6. It's fixed manually.
55 | for anno_path in tqdm(anno_paths):
56 | elements = anno_path.split('/')
57 | out_filename = elements[-3] + '_' + elements[-2] + '.npy' # Area_1_hallway_1.npy
58 | try:
59 | collect_point_label(anno_path, os.path.join(params.output_dir, elements[-3], 'full_size', out_filename))
60 | except Exception as e:
61 | print(str(e))
62 | print(out_filename)
63 |
64 |
65 | if __name__ == '__main__':
66 | parser = argparse.ArgumentParser(description='Convert original S3DIS dataset to npy based file format used'
67 | 'by our framework')
68 |
69 | parser.add_argument('--input_dir', required=True, help='root directory of original data')
70 | parser.add_argument('--output_dir', required=True, help='root directory of output npys')
71 | params = parser.parse_args()
72 |
73 | tools.pretty_print_arguments(params)
74 |
75 | main(params)
76 |
--------------------------------------------------------------------------------
/tools/tf_util.py:
--------------------------------------------------------------------------------
1 | """ Wrapper functions for TensorFlow layers.
2 |
3 | Author: Charles R. Qi
4 | Date: November 2016
5 | """
6 |
7 | import tensorflow as tf
8 |
9 |
10 | def gru_seq_g(inputs, n_units, dropout, scope):
11 | """
12 |
13 | :param inputs: (BS*N, K, F)
14 | :param n_units: (F) size of GRU cell
15 | :param dropout: BOOL
16 | :param scope:
17 | :return:
18 | """
19 | with tf.variable_scope(scope) as sc:
20 |
21 | # create gru cell
22 | gru = tf.nn.rnn_cell.GRUCell(n_units, reuse=tf.AUTO_REUSE)
23 | if dropout:
24 | gru = tf.nn.rnn_cell.DropoutWrapper(gru, output_keep_prob=0.5)
25 |
26 | input = inputs[:, 0, :] # 0th neighbours is point itself
27 | state = input
28 | output, state = gru(input, state)
29 |
30 | outputs = []
31 | for i in range(0, inputs.shape[1]): # iterate over all neighbours
32 | input = inputs[:, i, :]
33 | output, state = gru(input, state) # shape: (BS*N, F), (BS,*N, F)
34 | outputs.append(output)
35 |
36 | outputs = tf.stack(outputs, axis=1) # shape: (BS*N, K, F)
37 | return output
38 |
39 |
40 | def _variable_on_cpu(name, shape, initializer, use_fp16=False):
41 | """Helper to create a Variable stored on CPU memory.
42 | Args:
43 | name: name of the variable
44 | shape: list of ints
45 | initializer: initializer for Variable
46 | Returns:
47 | Variable Tensor
48 | """
49 | with tf.device('/cpu:0'):
50 | dtype = tf.float16 if use_fp16 else tf.float32
51 | var = tf.get_variable(name, shape, initializer=initializer, dtype=dtype)
52 | return var
53 |
54 |
55 | def _variable_with_weight_decay(name, shape, stddev, wd, use_xavier=True):
56 | """Helper to create an initialized Variable with weight decay.
57 |
58 | Note that the Variable is initialized with a truncated normal distribution.
59 | A weight decay is added only if one is specified.
60 |
61 | Args:
62 | name: name of the variable
63 | shape: list of ints
64 | stddev: standard deviation of a truncated Gaussian
65 | wd: add L2Loss weight decay multiplied by this float. If None, weight
66 | decay is not added for this Variable.
67 | use_xavier: bool, whether to use xavier initializer
68 |
69 | Returns:
70 | Variable Tensor
71 | """
72 | if use_xavier:
73 | initializer = tf.contrib.layers.xavier_initializer()
74 | else:
75 | initializer = tf.truncated_normal_initializer(stddev=stddev)
76 | var = _variable_on_cpu(name, shape, initializer)
77 | if wd is not None:
78 | weight_decay = tf.multiply(tf.nn.l2_loss(var), wd, name='weight_loss')
79 | tf.add_to_collection('losses', weight_decay)
80 | return var
81 |
82 |
83 | def consolidation_unit(inputs,
84 | size,
85 | scope,
86 | bn=False,
87 | bn_decay=None,
88 | is_training=None):
89 | with tf.variable_scope(scope) as sc:
90 | net = conv2d(inputs, size, [1, 1], padding='VALID', stride=[1, 1], bn=bn, is_training=is_training,
91 | scope=scope + '/conv', bn_decay=bn_decay)
92 |
93 | net_pooled = tf.reduce_max(net, axis=1, keep_dims=True, name=scope + '/global_feature')
94 | net_repeated = tf.tile(net_pooled, [1, tf.shape(net)[1], 1, 1], name='repeat')
95 | net = tf.concat(values=[net, net_repeated], axis=3) # put net and global features next to each other
96 |
97 | return net
98 |
99 |
100 | def conv2d(inputs,
101 | num_output_channels,
102 | kernel_size,
103 | scope,
104 | stride=[1, 1],
105 | padding='SAME',
106 | use_xavier=True,
107 | stddev=1e-3,
108 | weight_decay=0.0,
109 | activation_fn=tf.nn.relu,
110 | bn=False,
111 | bn_decay=None,
112 | is_training=None):
113 | """ 2D convolution with non-linear operation.
114 |
115 | Args:
116 | inputs: 4-D tensor variable BxHxWxC
117 | num_output_channels: int
118 | kernel_size: a list of 2 ints
119 | scope: string
120 | stride: a list of 2 ints
121 | padding: 'SAME' or 'VALID'
122 | use_xavier: bool, use xavier_initializer if true
123 | stddev: float, stddev for truncated_normal init
124 | weight_decay: float
125 | activation_fn: function
126 | bn: bool, whether to use batch norm
127 | bn_decay: float or float tensor variable in [0,1]
128 | is_training: bool Tensor variable
129 |
130 | Returns:
131 | Variable tensor
132 | """
133 | with tf.variable_scope(scope) as sc:
134 | kernel_h, kernel_w = kernel_size
135 | num_in_channels = inputs.get_shape()[-1].value
136 | kernel_shape = [kernel_h, kernel_w,
137 | num_in_channels, num_output_channels]
138 | kernel = _variable_with_weight_decay('weights',
139 | shape=kernel_shape,
140 | use_xavier=use_xavier,
141 | stddev=stddev,
142 | wd=weight_decay)
143 | stride_h, stride_w = stride
144 | outputs = tf.nn.conv2d(inputs, kernel,
145 | [1, stride_h, stride_w, 1],
146 | padding=padding)
147 | biases = _variable_on_cpu('biases', [num_output_channels],
148 | tf.constant_initializer(0.0))
149 | outputs = tf.nn.bias_add(outputs, biases)
150 |
151 | if bn:
152 | outputs = batch_norm_for_conv2d(outputs, is_training,
153 | bn_decay=bn_decay, scope='bn')
154 |
155 | if activation_fn is not None:
156 | outputs = activation_fn(outputs)
157 | return outputs
158 |
159 |
160 | def conv2d_transpose(inputs,
161 | num_output_channels,
162 | kernel_size,
163 | scope,
164 | stride=[1, 1],
165 | padding='SAME',
166 | use_xavier=True,
167 | stddev=1e-3,
168 | weight_decay=0.0,
169 | activation_fn=tf.nn.relu,
170 | bn=False,
171 | bn_decay=None,
172 | is_training=None):
173 | """ 2D convolution transpose with non-linear operation.
174 |
175 | Args:
176 | inputs: 4-D tensor variable BxHxWxC
177 | num_output_channels: int
178 | kernel_size: a list of 2 ints
179 | scope: string
180 | stride: a list of 2 ints
181 | padding: 'SAME' or 'VALID'
182 | use_xavier: bool, use xavier_initializer if true
183 | stddev: float, stddev for truncated_normal init
184 | weight_decay: float
185 | activation_fn: function
186 | bn: bool, whether to use batch norm
187 | bn_decay: float or float tensor variable in [0,1]
188 | is_training: bool Tensor variable
189 |
190 | Returns:
191 | Variable tensor
192 |
193 | Note: conv2d(conv2d_transpose(a, num_out, ksize, stride), a.shape[-1], ksize, stride) == a
194 | """
195 | with tf.variable_scope(scope) as sc:
196 | kernel_h, kernel_w = kernel_size
197 | num_in_channels = inputs.get_shape()[-1].value
198 | kernel_shape = [kernel_h, kernel_w,
199 | num_output_channels, num_in_channels] # reversed to conv2d
200 | kernel = _variable_with_weight_decay('weights',
201 | shape=kernel_shape,
202 | use_xavier=use_xavier,
203 | stddev=stddev,
204 | wd=weight_decay)
205 | stride_h, stride_w = stride
206 |
207 | # from slim.convolution2d_transpose
208 | def get_deconv_dim(dim_size, stride_size, kernel_size, padding):
209 | dim_size *= stride_size
210 |
211 | if padding == 'VALID' and dim_size is not None:
212 | dim_size += max(kernel_size - stride_size, 0)
213 | return dim_size
214 |
215 | # caculate output shape
216 | batch_size = inputs.get_shape()[0].value
217 | height = inputs.get_shape()[1].value
218 | width = inputs.get_shape()[2].value
219 | out_height = get_deconv_dim(height, stride_h, kernel_h, padding)
220 | out_width = get_deconv_dim(width, stride_w, kernel_w, padding)
221 | output_shape = [batch_size, out_height, out_width, num_output_channels]
222 |
223 | outputs = tf.nn.conv2d_transpose(inputs, kernel, output_shape,
224 | [1, stride_h, stride_w, 1],
225 | padding=padding)
226 | biases = _variable_on_cpu('biases', [num_output_channels],
227 | tf.constant_initializer(0.0))
228 | outputs = tf.nn.bias_add(outputs, biases)
229 |
230 | if bn:
231 | outputs = batch_norm_for_conv2d(outputs, is_training,
232 | bn_decay=bn_decay, scope='bn')
233 |
234 | if activation_fn is not None:
235 | outputs = activation_fn(outputs)
236 | return outputs
237 |
238 |
239 | def gru_seq(inputs, n_units, batch_size, TIME, dropout, scope):
240 | with tf.variable_scope(scope) as _:
241 |
242 | gru = tf.contrib.rnn.GRUCell(n_units)
243 | if dropout:
244 | gru = tf.nn.rnn_cell.DropoutWrapper(gru, output_keep_prob=0.5)
245 |
246 | # print inputs.shape
247 | input = inputs[:, 0, :]
248 | state = input
249 | output, state = gru(input, state)
250 |
251 | for i in range(1,inputs.shape[1]):
252 | input = inputs[:, i, :]
253 | output, state = gru(input, state)
254 |
255 | input = inputs[:, 0, :]
256 | output, state = gru(input, state)
257 | outputs = output
258 | for i in range(1, inputs.shape[1]):
259 | input = inputs[:, i, :]
260 | input, state = gru(input, state)
261 | outputs = tf.concat([outputs, output], axis=1)
262 |
263 | output = tf.reshape(outputs, (batch_size, TIME, n_units))
264 |
265 | return output
266 |
267 |
268 | def gru_noseq(inputs,
269 | n_units,
270 | num_layers,
271 | batch_size, TIME,dropout,scope):
272 | with tf.variable_scope(scope) as sc:
273 |
274 | gru = tf.contrib.rnn.GRUCell(n_units)
275 | if dropout:
276 | gru = tf.nn.rnn_cell.DropoutWrapper(gru, output_keep_prob=0.5)
277 | #gru = tf.nn.rnn_cell.MultiRNNCell([gru] * num_layers)
278 |
279 |
280 | eol = tf.ones(inputs[:,0,:].shape)
281 |
282 | #print inputs.shape
283 | input = inputs[:,0,:]
284 | state = input
285 | output, state = gru(input, state)
286 | #outputs = output
287 | for i in range(1,inputs.shape[1]):
288 | input = inputs[:,i,:]
289 | output,state=gru(input,state)
290 | outputs = output
291 | for i in range(1,inputs.shape[1]):
292 | outputs=tf.concat([outputs,output],axis=1)
293 | #input = inputs[:, 0, :]
294 | #output,state = gru(input,state)
295 | #outputs = output
296 | #for i in range(1,inputs.shape[1]):
297 | # input = inputs[:, i, :]
298 | # input,state=gru(input,state)
299 | # output = tf.clip_by_value(output,0,100)
300 | # outputs = tf.concat([outputs, output], axis=1)
301 | #print output.shape
302 | output = tf.reshape(outputs, (batch_size / TIME, TIME, n_units))
303 |
304 | return output
305 |
306 |
307 | def conv3d(inputs,
308 | num_output_channels,
309 | kernel_size,
310 | scope,
311 | stride=[1, 1, 1],
312 | padding='SAME',
313 | use_xavier=True,
314 | stddev=1e-3,
315 | weight_decay=0.0,
316 | activation_fn=tf.nn.relu,
317 | bn=False,
318 | bn_decay=None,
319 | is_training=None):
320 | """ 3D convolution with non-linear operation.
321 |
322 | Args:
323 | inputs: 5-D tensor variable BxDxHxWxC
324 | num_output_channels: int
325 | kernel_size: a list of 3 ints
326 | scope: string
327 | stride: a list of 3 ints
328 | padding: 'SAME' or 'VALID'
329 | use_xavier: bool, use xavier_initializer if true
330 | stddev: float, stddev for truncated_normal init
331 | weight_decay: float
332 | activation_fn: function
333 | bn: bool, whether to use batch norm
334 | bn_decay: float or float tensor variable in [0,1]
335 | is_training: bool Tensor variable
336 |
337 | Returns:
338 | Variable tensor
339 | """
340 | with tf.variable_scope(scope) as sc:
341 | kernel_d, kernel_h, kernel_w = kernel_size
342 | num_in_channels = inputs.get_shape()[-1].value
343 | kernel_shape = [kernel_d, kernel_h, kernel_w,
344 | num_in_channels, num_output_channels]
345 | kernel = _variable_with_weight_decay('weights',
346 | shape=kernel_shape,
347 | use_xavier=use_xavier,
348 | stddev=stddev,
349 | wd=weight_decay)
350 | stride_d, stride_h, stride_w = stride
351 | outputs = tf.nn.conv3d(inputs, kernel,
352 | [1, stride_d, stride_h, stride_w, 1],
353 | padding=padding)
354 | biases = _variable_on_cpu('biases', [num_output_channels],
355 | tf.constant_initializer(0.0))
356 | outputs = tf.nn.bias_add(outputs, biases)
357 |
358 | if bn:
359 | outputs = batch_norm_for_conv3d(outputs, is_training,
360 | bn_decay=bn_decay, scope='bn')
361 |
362 | if activation_fn is not None:
363 | outputs = activation_fn(outputs)
364 | return outputs
365 |
366 |
367 | def fully_connected(inputs,
368 | num_outputs,
369 | scope,
370 | use_xavier=True,
371 | stddev=1e-3,
372 | weight_decay=0.0,
373 | activation_fn=tf.nn.relu,
374 | bn=False,
375 | bn_decay=None,
376 | is_training=None):
377 | """ Fully connected layer with non-linear operation.
378 |
379 | Args:
380 | inputs: 2-D tensor BxN
381 | num_outputs: int
382 |
383 | Returns:
384 | Variable tensor of size B x num_outputs.
385 | """
386 | with tf.variable_scope(scope) as sc:
387 | num_input_units = inputs.get_shape()[-1].value
388 | weights = _variable_with_weight_decay('weights',
389 | shape=[num_input_units, num_outputs],
390 | use_xavier=use_xavier,
391 | stddev=stddev,
392 | wd=weight_decay)
393 | outputs = tf.matmul(inputs, weights)
394 | biases = _variable_on_cpu('biases', [num_outputs],
395 | tf.constant_initializer(0.0))
396 | outputs = tf.nn.bias_add(outputs, biases)
397 |
398 | if bn:
399 | outputs = batch_norm_for_fc(outputs, is_training, bn_decay, 'bn')
400 |
401 | if activation_fn is not None:
402 | outputs = activation_fn(outputs)
403 | return outputs
404 |
405 |
406 | def max_pool2d(inputs,
407 | kernel_size,
408 | scope,
409 | stride=[2, 2],
410 | padding='VALID'):
411 | """ 2D max pooling.
412 |
413 | Args:
414 | inputs: 4-D tensor BxHxWxC
415 | kernel_size: a list of 2 ints
416 | stride: a list of 2 ints
417 |
418 | Returns:
419 | Variable tensor
420 | """
421 | with tf.variable_scope(scope) as sc:
422 | kernel_h, kernel_w = kernel_size
423 | stride_h, stride_w = stride
424 | outputs = tf.nn.max_pool(inputs,
425 | ksize=[1, kernel_h, kernel_w, 1],
426 | strides=[1, stride_h, stride_w, 1],
427 | padding=padding,
428 | name=sc.name)
429 | return outputs
430 |
431 | def avg_pool2d(inputs,
432 | kernel_size,
433 | scope,
434 | stride=[2, 2],
435 | padding='VALID'):
436 | """ 2D avg pooling.
437 |
438 | Args:
439 | inputs: 4-D tensor BxHxWxC
440 | kernel_size: a list of 2 ints
441 | stride: a list of 2 ints
442 |
443 | Returns:
444 | Variable tensor
445 | """
446 | with tf.variable_scope(scope) as sc:
447 | kernel_h, kernel_w = kernel_size
448 | stride_h, stride_w = stride
449 | outputs = tf.nn.avg_pool(inputs,
450 | ksize=[1, kernel_h, kernel_w, 1],
451 | strides=[1, stride_h, stride_w, 1],
452 | padding=padding,
453 | name=sc.name)
454 | return outputs
455 |
456 |
457 | def max_pool3d(inputs,
458 | kernel_size,
459 | scope,
460 | stride=[2, 2, 2],
461 | padding='VALID'):
462 | """ 3D max pooling.
463 |
464 | Args:
465 | inputs: 5-D tensor BxDxHxWxC
466 | kernel_size: a list of 3 ints
467 | stride: a list of 3 ints
468 |
469 | Returns:
470 | Variable tensor
471 | """
472 | with tf.variable_scope(scope) as sc:
473 | kernel_d, kernel_h, kernel_w = kernel_size
474 | stride_d, stride_h, stride_w = stride
475 | outputs = tf.nn.max_pool3d(inputs,
476 | ksize=[1, kernel_d, kernel_h, kernel_w, 1],
477 | strides=[1, stride_d, stride_h, stride_w, 1],
478 | padding=padding,
479 | name=sc.name)
480 | return outputs
481 |
482 | def avg_pool3d(inputs,
483 | kernel_size,
484 | scope,
485 | stride=[2, 2, 2],
486 | padding='VALID'):
487 | """ 3D avg pooling.
488 |
489 | Args:
490 | inputs: 5-D tensor BxDxHxWxC
491 | kernel_size: a list of 3 ints
492 | stride: a list of 3 ints
493 |
494 | Returns:
495 | Variable tensor
496 | """
497 | with tf.variable_scope(scope) as sc:
498 | kernel_d, kernel_h, kernel_w = kernel_size
499 | stride_d, stride_h, stride_w = stride
500 | outputs = tf.nn.avg_pool3d(inputs,
501 | ksize=[1, kernel_d, kernel_h, kernel_w, 1],
502 | strides=[1, stride_d, stride_h, stride_w, 1],
503 | padding=padding,
504 | name=sc.name)
505 | return outputs
506 |
507 |
508 | def batch_norm_template(inputs, is_training, scope, moments_dims, bn_decay):
509 | """ Batch normalization on convolutional maps and beyond...
510 | Ref.: http://stackoverflow.com/questions/33949786/how-could-i-use-batch-normalization-in-tensorflow
511 |
512 | Args:
513 | inputs: Tensor, k-D input ... x C could be BC or BHWC or BDHWC
514 | is_training: boolean tf.Varialbe, true indicates training phase
515 | scope: string, variable scope
516 | moments_dims: a list of ints, indicating dimensions for moments calculation
517 | bn_decay: float or float tensor variable, controling moving average weight
518 | Return:
519 | normed: batch-normalized maps
520 | """
521 | with tf.variable_scope(scope) as sc:
522 | num_channels = inputs.get_shape()[-1].value
523 | beta = tf.Variable(tf.constant(0.0, shape=[num_channels]),
524 | name='beta', trainable=True)
525 | gamma = tf.Variable(tf.constant(1.0, shape=[num_channels]),
526 | name='gamma', trainable=True)
527 | batch_mean, batch_var = tf.nn.moments(inputs, moments_dims, name='moments')
528 | decay = bn_decay if bn_decay is not None else 0.9
529 | ema = tf.train.ExponentialMovingAverage(decay=decay)
530 | # Operator that maintains moving averages of variables.
531 | ema_apply_op = tf.cond(is_training,
532 | lambda: ema.apply([batch_mean, batch_var]),
533 | lambda: tf.no_op())
534 |
535 | # Update moving average and return current batch's avg and var.
536 | def mean_var_with_update():
537 | with tf.control_dependencies([ema_apply_op]):
538 | return tf.identity(batch_mean), tf.identity(batch_var)
539 |
540 | # ema.average returns the Variable holding the average of var.
541 | mean, var = tf.cond(is_training,
542 | mean_var_with_update,
543 | lambda: (ema.average(batch_mean), ema.average(batch_var)))
544 | normed = tf.nn.batch_normalization(inputs, mean, var, beta, gamma, 1e-3)
545 | return normed
546 |
547 |
548 | def batch_norm_for_fc(inputs, is_training, bn_decay, scope):
549 | """ Batch normalization on FC data.
550 |
551 | Args:
552 | inputs: Tensor, 2D BxC input
553 | is_training: boolean tf.Varialbe, true indicates training phase
554 | bn_decay: float or float tensor variable, controling moving average weight
555 | scope: string, variable scope
556 | Return:
557 | normed: batch-normalized maps
558 | """
559 | return batch_norm_template(inputs, is_training, scope, [0,], bn_decay)
560 |
561 |
562 | def batch_norm_for_conv1d(inputs, is_training, bn_decay, scope):
563 | """ Batch normalization on 1D convolutional maps.
564 |
565 | Args:
566 | inputs: Tensor, 3D BLC input maps
567 | is_training: boolean tf.Varialbe, true indicates training phase
568 | bn_decay: float or float tensor variable, controling moving average weight
569 | scope: string, variable scope
570 | Return:
571 | normed: batch-normalized maps
572 | """
573 | return batch_norm_template(inputs, is_training, scope, [0,1], bn_decay)
574 |
575 |
576 | def batch_norm_for_conv2d(inputs, is_training, bn_decay, scope):
577 | """ Batch normalization on 2D convolutional maps.
578 |
579 | Args:
580 | inputs: Tensor, 4D BHWC input maps
581 | is_training: boolean tf.Varialbe, true indicates training phase
582 | bn_decay: float or float tensor variable, controling moving average weight
583 | scope: string, variable scope
584 | Return:
585 | normed: batch-normalized maps
586 | """
587 | return batch_norm_template(inputs, is_training, scope, [0,1,2], bn_decay)
588 |
589 |
590 | def batch_norm_for_conv3d(inputs, is_training, bn_decay, scope):
591 | """ Batch normalization on 3D convolutional maps.
592 |
593 | Args:
594 | inputs: Tensor, 5D BDHWC input maps
595 | is_training: boolean tf.Varialbe, true indicates training phase
596 | bn_decay: float or float tensor variable, controling moving average weight
597 | scope: string, variable scope
598 | Return:
599 | normed: batch-normalized maps
600 | """
601 | return batch_norm_template(inputs, is_training, scope, [0,1,2,3], bn_decay)
602 |
603 |
604 | def dropout(inputs,
605 | is_training,
606 | scope,
607 | keep_prob=0.5,
608 | noise_shape=None):
609 | """ Dropout layer.
610 |
611 | Args:
612 | inputs: tensor
613 | is_training: boolean tf.Variable
614 | scope: string
615 | keep_prob: float in [0,1]
616 | noise_shape: list of ints
617 |
618 | Returns:
619 | tensor variable
620 | """
621 | with tf.variable_scope(scope) as sc:
622 | outputs = tf.cond(is_training,
623 | lambda: tf.nn.dropout(inputs, keep_prob, noise_shape),
624 | lambda: inputs)
625 | return outputs
626 |
--------------------------------------------------------------------------------
/tools/tools.py:
--------------------------------------------------------------------------------
1 | """
2 | various helper functions to make life easier
3 | """
4 |
5 | import time
6 | import subprocess
7 | import os
8 | from pathlib import Path
9 | import sys
10 | from datetime import datetime
11 | import logging
12 | import re
13 | import string
14 | import random
15 |
16 |
17 | def id_generator(size=6, chars=string.ascii_uppercase + string.digits):
18 | return ''.join(random.choice(chars) for _ in range(size))
19 |
20 |
21 | def us2mc(x):
22 | """
23 | underscore to mixed-case notation
24 | from https://www.safaribooksonline.com/library/view/python-cookbook/0596001673/ch03s16.html
25 | """
26 | return re.sub(r'_([a-z])', lambda m: (m.group(1).upper()), x)
27 |
28 |
29 | def us2cw(x):
30 | """
31 | underscore to capwords notation
32 | from https://www.safaribooksonline.com/library/view/python-cookbook/0596001673/ch03s16.html
33 | """
34 | s = us2mc(x)
35 | return s[0].upper()+s[1:]
36 |
37 |
38 | def import_class(package_path, class_name):
39 | """
40 | dynamic import of a class from a given package
41 | :param package_path: path to the package
42 | :param class_name: class to be dynamically loaded
43 | :return: dynamically loaded class
44 | """
45 | try:
46 | logging.info(f"Loading {package_path}.{class_name} ...")
47 | module = __import__(f"{package_path}.{class_name}", fromlist=[class_name])
48 | return getattr(module, us2cw(class_name))
49 | except ModuleNotFoundError as exc:
50 | logging.error(f"{package_path}.{class_name} could not be found")
51 | exit(1)
52 |
53 |
54 | def setup_logger():
55 | """
56 | setup the logging mechanism where log messages are saved in time-encoded txt files as well as to the terminal
57 | :return: directory path in which logs are saved
58 | """
59 | directory_path = f"logs/{datetime.now():%Y-%m-%d@%H:%M:%S}_{id_generator()}"
60 |
61 | path = Path(directory_path)
62 | path.mkdir(parents=True, exist_ok=True)
63 |
64 | log_format = logging.Formatter("%(asctime)s [%(threadName)-12.12s] [%(levelname)-5.5s] [%(pathname)s:%(lineno)04d] %(message)s")
65 | logger = logging.getLogger()
66 | logger.setLevel(logging.DEBUG)
67 |
68 | file_handler = logging.FileHandler(f"{directory_path}/{datetime.now():%Y-%m-%d@%H:%M:%S}_{id_generator()}.log")
69 | file_handler.setFormatter(log_format)
70 | logger.addHandler(file_handler)
71 |
72 | file_handler = logging.StreamHandler(sys.stdout)
73 | file_handler.setFormatter(log_format)
74 | logger.addHandler(file_handler)
75 |
76 | logging.root = logger
77 |
78 | logging.info('START LOGGING')
79 | logging.info(f"Current Git Version: {git_version()}")
80 |
81 | return directory_path
82 |
83 |
84 | def pretty_print_arguments(args):
85 | """
86 | return a nicely formatted list of passed arguments
87 | :param args: arguments passed to the program via terminal
88 | :return: None
89 | """
90 | longest_key = max([len(key) for key in vars(args)])
91 |
92 | print('Program was launched with the following arguments:')
93 |
94 | for key, item in vars(args).items():
95 | print("~ {0:{s}} \t {1}".format(key, item, s=longest_key))
96 |
97 | print('')
98 | # Wait a bit until program execution continues
99 | time.sleep(0.1)
100 |
101 |
102 | def git_version():
103 | """
104 | return git revision such that it can be also logged to keep track of results
105 | :return: git revision hash
106 | """
107 | def _minimal_ext_cmd(cmd):
108 | # construct minimal environment
109 | env = {}
110 | for k in ['SYSTEMROOT', 'PATH']:
111 | v = os.environ.get(k)
112 | if v is not None:
113 | env[k] = v
114 | # LANGUAGE is used on win32
115 | env['LANGUAGE'] = 'C'
116 | env['LANG'] = 'C'
117 | env['LC_ALL'] = 'C'
118 | out = subprocess.Popen(cmd, stdout=subprocess.PIPE, env=env).communicate()[0]
119 | return out
120 |
121 | try:
122 | out = _minimal_ext_cmd(['git', 'rev-parse', 'HEAD'])
123 | git_revision = out.strip().decode('ascii')
124 | except OSError:
125 | git_revision = "Unknown"
126 |
127 | return git_revision
128 |
--------------------------------------------------------------------------------
/tools/viz.py:
--------------------------------------------------------------------------------
1 | import vtk
2 | import numpy as np
3 | import random
4 |
5 | print('Using', vtk.vtkVersion.GetVTKSourceVersion())
6 |
7 |
8 | class MyInteractorStyle(vtk.vtkInteractorStyleTrackballCamera):
9 | def __init__(self, parent, pointcloud):
10 | self.parent = parent
11 | self.pointcloud = pointcloud
12 | self.AddObserver("KeyPressEvent", self.keyPressEvent)
13 |
14 | def keyPressEvent(self, obj, event):
15 | key = self.parent.GetKeySym()
16 | if key == '+':
17 | point_size = self.pointcloud.vtkActor.GetProperty().GetPointSize()
18 | self.pointcloud.vtkActor.GetProperty().SetPointSize(point_size + 1)
19 | print(str(point_size) + " " + key)
20 | return
21 |
22 |
23 | class VtkPointCloud:
24 |
25 | def __init__(self, point_size=18, maxNumPoints=1e8):
26 | self.maxNumPoints = maxNumPoints
27 | self.vtkPolyData = vtk.vtkPolyData()
28 | self.clear_points()
29 |
30 | self.colors = vtk.vtkUnsignedCharArray()
31 | self.colors.SetNumberOfComponents(3)
32 | self.colors.SetName("Colors")
33 |
34 | mapper = vtk.vtkPolyDataMapper()
35 | mapper.SetInputData(self.vtkPolyData)
36 |
37 | self.vtkActor = vtk.vtkActor()
38 | self.vtkActor.SetMapper(mapper)
39 | self.vtkActor.GetProperty().SetPointSize(point_size)
40 |
41 | def add_point(self, point, color):
42 | if self.vtkPoints.GetNumberOfPoints() < self.maxNumPoints:
43 | pointId = self.vtkPoints.InsertNextPoint(point[:])
44 | self.colors.InsertNextTuple(color)
45 | self.vtkDepth.InsertNextValue(point[2])
46 | self.vtkCells.InsertNextCell(1)
47 | self.vtkCells.InsertCellPoint(pointId)
48 | else:
49 | print("VIZ: Reached max number of points!")
50 | r = random.randint(0, self.maxNumPoints)
51 | self.vtkPoints.SetPoint(r, point[:])
52 | self.vtkPolyData.GetPointData().SetScalars(self.colors)
53 | self.vtkCells.Modified()
54 | self.vtkPoints.Modified()
55 | self.vtkDepth.Modified()
56 |
57 | def clear_points(self):
58 | self.vtkPoints = vtk.vtkPoints()
59 | self.vtkCells = vtk.vtkCellArray()
60 | self.vtkDepth = vtk.vtkDoubleArray()
61 | self.vtkDepth.SetName('DepthArray')
62 | self.vtkPolyData.SetPoints(self.vtkPoints)
63 | self.vtkPolyData.SetVerts(self.vtkCells)
64 | self.vtkPolyData.GetPointData().SetScalars(self.vtkDepth)
65 | self.vtkPolyData.GetPointData().SetActiveScalars('DepthArray')
66 |
67 |
68 | def getActorCircle(radius_inner=100, radius_outer=99, color=(1,0,0)):
69 | """"""
70 | # create source
71 | source = vtk.vtkDiskSource()
72 | source.SetInnerRadius(radius_inner)
73 | source.SetOuterRadius(radius_outer)
74 | source.SetRadialResolution(100)
75 | source.SetCircumferentialResolution(100)
76 |
77 | # Transformer
78 | transform = vtk.vtkTransform()
79 | transform.RotateWXYZ(90, 1, 0, 0)
80 | transformFilter = vtk.vtkTransformPolyDataFilter()
81 | transformFilter.SetTransform(transform)
82 | transformFilter.SetInputConnection(source.GetOutputPort())
83 | transformFilter.Update()
84 |
85 | # mapper
86 | mapper = vtk.vtkPolyDataMapper()
87 | mapper.SetInputConnection(transformFilter.GetOutputPort())
88 |
89 | # actor
90 | actor = vtk.vtkActor()
91 | actor.GetProperty().SetColor(color)
92 | actor.SetMapper(mapper)
93 |
94 | return actor
95 |
96 |
97 | def show_pointclouds(points, colors, text=[], title="Default", png_path="", interactive=True):
98 | """
99 | Show multiple point clouds specified as lists. First clouds at the bottom.
100 | :param points: list of pointclouds, item: numpy (N x 3) XYZ
101 | :param colors: list of corresponding colors, item: numpy (N x 3) RGB [0..255]
102 | :param title: window title
103 | :param text: text per point cloud
104 | :param png_path: where to save png image
105 | :param interactive: wether to display window or not, useful if you only want to take screenshot
106 | :return: nothing
107 | """
108 |
109 | # make sure pointclouds is a list
110 | assert isinstance(points, type([])), \
111 | "Pointclouds argument must be a list"
112 |
113 | # make sure colors is a list
114 | assert isinstance(colors, type([])), \
115 | "Colors argument must be a list"
116 |
117 | # make sure number of pointclouds and colors are the same
118 | assert len(points) == len(colors), \
119 | "Number of pointclouds (%d) is different then number of colors (%d)" % (len(points), len(colors))
120 |
121 | while len(text)