├── IP.py ├── LICENSE ├── QP.py ├── README.md ├── RecordReader.py ├── RecordWriterCustom.py ├── RecordWriterTango.py ├── augmentation_tf.py ├── checkpoint └── floornet_hybrid1_branch0123_wsf │ ├── checkpoint │ ├── checkpoint.ckpt.data-00000-of-00001 │ ├── checkpoint.ckpt.index │ └── checkpoint.ckpt.meta ├── evaluate.py ├── floorplan_utils.py ├── icons ├── bathtub.jpg ├── bed.jpg ├── cabinet.jpg ├── cooking_counter.jpg ├── refrigerator.jpg ├── sofa.jpg ├── table.jpg ├── toilet.jpg └── washing_basin.jpg ├── train.py └── utils.py /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Chen Liu 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 | # FloorNet: A Unified Framework for Floorplan Reconstruction from 3D Scans 2 | By Chen Liu\*, Jiaye Wu\*, and Yasutaka Furukawa (\* indicates equal contribution) 3 | 4 | ## Introduction 5 | 6 | This paper proposes FloorNet, a novel neural network, to turn RGBD videos of indoor spaces into vector-graphics floorplans. FloorNet consists of three branches, PointNet branch, Floorplan branch, and Image branch. For more details, please refer to our ECCV 2018 [paper](https://arxiv.org/abs/1804.00090) or visit our [project website](http://art-programmer.github.io/floornet.html). This is a follow-up work of our floorplan transformation project which you can find [here](https://github.com/art-programmer/FloorplanTransformation). 7 | 8 | ## Updates 9 | [12/22/2018] We now provide a free IP solver (not relying on Gurobi) at IP.py. The functionality of IP.py should be similar to QP.py which uses Gurobi to solve the IP problem. You might want to consider the free solver if you don't have a Gurobi license. 10 | 11 | ## Dependencies 12 | Python 2.7, TensorFlow (>= 1.3), numpy, opencv 3, CUDA (>= 8.0), Gurobi (free only for academic usages). 13 | 14 | ## Data 15 | 16 | ### Dataset used in the paper 17 | 18 | We collect 155 scans of residential units and annotated corresponding floorplan information. Among 155 scans, 135 are used for training and 20 are for testing. We convert data to tfrecords files which can be downloaded [here](https://drive.google.com/open?id=16lyX_xTiALUzKyst86WJHlhpTDr8XPF_) (or [here](https://mega.nz/#F!5yQy0b5T!ykkR4dqwGO9J5EwnKT_GBw) if you cannot access the previous one). Please put the downloaded files under folder *data/*. 19 | 20 | Here are the links to the raw [point clouds](https://drive.google.com/open?id=1JJlD0qsgMpiU5Jq9TNm3uDPjvqi88aZn), [annotations](https://drive.google.com/open?id=1hYDE2SXLA8Cq7LEK67xO-UMeTSPJ5rcB), and their [associations](https://drive.google.com/open?id=125TAmYWk22EyzCdlbGIfX4Z4DRMhru_V). Please refer to **RecordWriterTango.py** to see how to convert the raw data and annotations to tfrecords files. 21 | 22 | 23 | ### Using custom data 24 | 25 | To generate training/testing data from other data source, the data should be converted to tfrecords as what we did in **RecordWriterTango.py** (an example of our raw data before processed by **RecordWriterTango.py** is provided [here](https://mega.nz/#!dnohjKZa!I3NJZ806vNK-UYp-ap7OynGnS5E-E5AK_z5WsX8n1Ls)). Please refer to [this guide](http://warmspringwinds.github.io/tensorflow/tf-slim/2016/12/21/tfrecords-guide/) for how to generate and read tfrecords. 26 | 27 | Basically, every data sample(tf.train.Example) should at least contain the following components: 28 | 29 | 30 | 1. Inputs: 31 | 32 | - a point cloud (randomly sampled 50,000 points) 33 | - a mapping from point cloud's 3D space to 2D space of the 256x256 top-view density image. 34 | - It contains 50,000 indices, one for each point. 35 | - For point (x, y, z), index = round((y - min(Y) + padding) / (maxRange + 2 * padding) * 256) * 256 + round((x - min(X) + padding) / (maxRange + 2 * padding) * 256). 36 | - maxRange = max(max(X) - min(X), max(Y) - min(Y)) 37 | - padding could be any small value, say 0.05 maxRange 38 | - optional: image features of the RGB video stream, if the image branch is enabled 39 | 40 | 2. Labels: 41 | 42 | - Corners and their corresponding types 43 | - Total number of corners 44 | - A ground-truth icon segmentation map 45 | - A ground-truth room segmentation map 46 | 47 | Again, please refer to **RecordWriterTango.py** for exact details. 48 | 49 | **NEW:** We added a template file, **RecordWriterCustom.py** for using custom data. 50 | 51 | 52 | ## Annotator 53 | For reference, a similar (but not the same) annotator written in Python is [here](https://github.com/art-programmer/FloorplanAnnotator). You need to make some changes to annotate your own data. 54 | 55 | ## Training 56 | To train the network from scratch, please run: 57 | ```bash 58 | python train.py --restore=0 59 | ``` 60 | 61 | ## Evaluation 62 | To evaluate the performance of our trained model, please run: 63 | ```bash 64 | python train.py --task=evaluate --separateIconLoss 65 | ``` 66 | 67 | ## Generate 3D models 68 | We can popup the reconstructed floorplan to generate 3D models. Please refer to our previous project, [FloorplanTransformation](https://github.com/art-programmer/FloorplanTransformation), for more details. 69 | 70 | ## Contact 71 | 72 | If you have any questions, please contact me at chenliu@wustl.edu. 73 | -------------------------------------------------------------------------------- /RecordReader.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | import numpy as np 3 | import threading 4 | import PIL.Image as Image 5 | from functools import partial 6 | from multiprocessing import Pool 7 | import cv2 8 | import sys 9 | import os 10 | import functools 11 | from floorplan_utils import * 12 | from augmentation_tf import * 13 | from utils import * 14 | #sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 15 | 16 | NUM_THREADS = 8 17 | 18 | def parse_fn(example, augmentation, readImageFeatures = False, convertCorners=False, kernelSize=11): 19 | if readImageFeatures: 20 | features = tf.parse_single_example( 21 | example, 22 | # Defaults are not specified since both keys are required. 23 | features={ 24 | 'image_path': tf.FixedLenFeature([], tf.string), 25 | 'points': tf.FixedLenFeature([NUM_POINTS * (NUM_INPUT_CHANNELS - 1)], tf.float32), 26 | 'point_indices': tf.FixedLenFeature([NUM_POINTS], tf.int64), 27 | 'corner': tf.FixedLenFeature([MAX_NUM_CORNERS * 3], tf.int64), 28 | 'num_corners': tf.FixedLenFeature([], tf.int64), 29 | 'icon': tf.FixedLenFeature([], tf.string), 30 | 'room': tf.FixedLenFeature([], tf.string), 31 | 'image': tf.FixedLenFeature(sum([size * size * numChannels for size, numChannels in zip(SIZES, NUM_CHANNELS)[1:]]), tf.float32), 32 | 'flags': tf.FixedLenFeature([2], tf.int64), 33 | }) 34 | else: 35 | features = tf.parse_single_example( 36 | example, 37 | # Defaults are not specified since both keys are required. 38 | features={ 39 | 'image_path': tf.FixedLenFeature([], tf.string), 40 | 'points': tf.FixedLenFeature([NUM_POINTS * (NUM_INPUT_CHANNELS - 1)], tf.float32), 41 | 'point_indices': tf.FixedLenFeature([NUM_POINTS], tf.int64), 42 | 'corner': tf.FixedLenFeature([MAX_NUM_CORNERS * 3], tf.int64), 43 | 'num_corners': tf.FixedLenFeature([], tf.int64), 44 | 'icon': tf.FixedLenFeature([], tf.string), 45 | 'room': tf.FixedLenFeature([], tf.string), 46 | 'flags': tf.FixedLenFeature([2], tf.int64), 47 | }) 48 | pass 49 | 50 | points = tf.reshape(features['points'], (NUM_POINTS, NUM_INPUT_CHANNELS - 1)) 51 | point_indices = tf.cast(features['point_indices'], tf.int32) 52 | #point_indices = features['point_indices'] 53 | 54 | corners = tf.cast(tf.reshape(features['corner'], [MAX_NUM_CORNERS, 3]), tf.int32) 55 | numCorners = features['num_corners'] 56 | corners = corners[:numCorners] 57 | iconSegmentation = tf.reshape(tf.decode_raw(features['icon'], tf.uint8), (HEIGHT, WIDTH, 1)) 58 | roomSegmentation = tf.reshape(tf.decode_raw(features['room'], tf.uint8), (HEIGHT, WIDTH, 1)) 59 | heatmaps = tf.stack([iconSegmentation, roomSegmentation], axis=0) 60 | 61 | if readImageFeatures: 62 | imageFeature = features['image'] 63 | imageFeatures = {} 64 | offset = 0 65 | for index, (size, numChannels) in enumerate(zip(SIZES, NUM_CHANNELS)[1:]): 66 | imageFeatures[index] = tf.reshape(imageFeature[offset:offset + size * size * numChannels], (size, size, numChannels)) 67 | offset += size * size * numChannels 68 | continue 69 | else: 70 | imageFeatures = {} 71 | pass 72 | 73 | flags = features['flags'] 74 | if 'w' in augmentation: 75 | point_indices, corners, heatmaps = tf.cond(tf.logical_or(tf.equal(flags[0], 0), tf.equal(flags[0], 4)), lambda: augmentWarping(point_indices, corners, heatmaps, gridStride=32, randomScale=2), lambda: (point_indices, corners, heatmaps)) 76 | #point_indices, corners, heatmaps = augmentWarping(point_indices, corners, heatmaps, gridStride=32, randomScale=4) 77 | pass 78 | if 's' in augmentation: 79 | points, point_indices, corners, heatmaps, imageFeatures = augmentScaling(points, point_indices, corners, heatmaps, imageFeatures) 80 | pass 81 | if 'f' in augmentation: 82 | points, point_indices, corners, heatmaps, imageFeatures = augmentFlipping(points, point_indices, corners, heatmaps, imageFeatures) 83 | pass 84 | 85 | iconSegmentation = tf.cast(tf.squeeze(heatmaps[0]), tf.int32) 86 | roomSegmentation = tf.cast(tf.squeeze(heatmaps[1]), tf.int32) 87 | 88 | roomSegmentation = tf.minimum(roomSegmentation, NUM_ROOMS - 1) 89 | 90 | # point_indices_stack = getCoarseIndicesMaps(point_indices, WIDTH, HEIGHT, 0) 91 | 92 | corners = tf.reshape(tf.concat([corners, tf.zeros((MAX_NUM_CORNERS - tf.shape(corners)[0], 3), dtype=tf.int32)], axis=0), (MAX_NUM_CORNERS, 3)) 93 | if convertCorners: 94 | cornerSegmentation = tf.stack([tf.sparse_to_dense(tf.stack([corners[:, 1], corners[:, 0]], axis=1), (HEIGHT, WIDTH), corners[:, 2], validate_indices=False)], axis=0) 95 | cornerHeatmaps = tf.one_hot(cornerSegmentation, depth=NUM_CORNERS + 1, axis=-1)[:, :, :, 1:] 96 | #kernel_size = kernelSize 97 | #neighbor_kernel_array = disk(kernel_size) 98 | #neighbor_kernel = tf.constant(neighbor_kernel_array.reshape(-1), shape=neighbor_kernel_array.shape, dtype=tf.float32) 99 | #neighbor_kernel = tf.reshape(neighbor_kernel, [kernel_size, kernel_size, 1, 1]) 100 | #cornerHeatmaps = tf.nn.depthwise_conv2d(cornerHeatmaps, tf.tile(neighbor_kernel, [1, 1, NUM_CORNERS, 1]), strides=[1, 1, 1, 1], padding='SAME') 101 | corners = tf.cast(cornerHeatmaps > 0.5, tf.float32) 102 | 103 | # cornerSegmentation = tf.sparse_to_dense(tf.stack([corners[:, 1], corners[:, 0]], axis=1), (HEIGHT, WIDTH), corners[:, 2], validate_indices=False) 104 | # cornerHeatmaps = tf.one_hot(cornerSegmentation, depth=NUM_CORNERS, axis=-1) 105 | # kernel = tf.tile(tf.expand_dims(tf.constant(disk(11)), -1), [1, 1, NUM_CORNERS]) 106 | # cornerHeatmaps = tf.nn.dilation2d(tf.expand_dims(cornerHeatmaps, 0), kernel, [1, 1, 1, 1], [1, 1, 1, 1], 'SAME')[0] 107 | 108 | imagePath = features['image_path'] 109 | 110 | points = tf.concat([points, tf.ones((NUM_POINTS, 1))], axis=1) 111 | 112 | if readImageFeatures: 113 | input_dict = {'points': points, 'point_indices': point_indices, 'image_features': imageFeatures, 'image_path': imagePath, 'flags': flags} 114 | else: 115 | input_dict = {'points': points, 'point_indices': point_indices, 'image_path': imagePath, 'flags': flags} 116 | pass 117 | gt_dict = {'corner': corners, 'icon': iconSegmentation, 'room': roomSegmentation, 'num_corners': numCorners} 118 | return input_dict, gt_dict 119 | 120 | # class RecordReader(): 121 | # def __init__(self): 122 | # return 123 | 124 | # def getBatch(self, filename_queues, batchSize, augmentation, min_after_dequeue = 1000, random=True, getLocal=False, getSegmentation=False, test=True): 125 | # reader = tf.TFRecordReader() 126 | # queueIndex = tf.cast(tf.random_uniform([len(filename_queues)], maxval=5), tf.int32)[0] 127 | # #filename_queue = tf.cond(tf.equal(queueIndex, 0), lambda: filename_queues[0], lambda: tf.cond(tf.equal(queueIndex, 1), lambda: filename_queues[1], lambda: tf.cond(tf.equal(queueIndex, 2), lambda: filename_queues[2], lambda: tf.cond(tf.equal(queueIndex, 3), lambda: filename_queues[3], lambda: filename_queues[4])))) 128 | 129 | # _, serialized_example = reader.read(filename_queues[queueIndex]) 130 | 131 | # features = tf.parse_single_example( 132 | # serialized_example, 133 | # # Defaults are not specified since both keys are required. 134 | # features={ 135 | # 'image_path': tf.FixedLenFeature([], tf.string), 136 | # 'points': tf.FixedLenFeature([NUM_POINTS * 7], tf.float32), 137 | # 'point_indices': tf.FixedLenFeature([NUM_POINTS], tf.int32), 138 | # 'corner': tf.FixedLenFeature([MAX_NUM_CORNERS * 3], tf.int32), 139 | # 'num_corners': tf.FixedLenFeature([], tf.int32), 140 | # 'icon': tf.FixedLenFeature([], tf.string), 141 | # 'room': tf.FixedLenFeature([], tf.string), 142 | # 'image': tf.FixedLenFeature(sum([size * size * numChannels for size, numChannels in zip(SIZES, NUM_CHANNELS)[1:]]), tf.float32), 143 | # 'flags': tf.FixedLenFeature([2], tf.int32), 144 | # }) 145 | 146 | 147 | # points = tf.reshape(features['points'], (NUM_POINTS, NUM_INPUT_CHANNELS)) 148 | # point_indices = tf.reshape(features['point_indices'], (NUM_POINTS)) 149 | # corners = tf.reshape(features['corner'], [MAX_NUM_CORNERS, 3]) 150 | # numCorners = tf.cast(features['num_corners'], tf.int32) 151 | # corners = corners[:numCorners] 152 | # iconSegmentation = tf.reshape(tf.decode_raw(features['icon'], tf.uint8), (HEIGHT, WIDTH, 1)) 153 | # roomSegmentation = tf.reshape(tf.decode_raw(features['room'], tf.uint8), (HEIGHT, WIDTH, 1)) 154 | # heatmaps = tf.stack([icon, room], axis=0) 155 | 156 | # flags = features['flags'] 157 | 158 | # if 'w' in augmentation: 159 | # point_indices, corners, heatmaps = tf.cond(flags[0] == 0 or flags[0] == 4, lambda: augmentWarping(point_indices, corners, heatmaps, stride=32, randomScale=4), lambda: pointcloudIndices, corners, heatmaps) 160 | # pass 161 | # if 's' in augmentation: 162 | # points, point_indices, corners, heatmaps = augmentScaling(points, point_indices, corners, heatmaps) 163 | # pass 164 | # if 'f' in augmentation: 165 | # points, point_indices, corners, heatmaps = augmentFlipping(points, point_indices, corners, heatmaps) 166 | # pass 167 | 168 | # iconSegmentation = tf.squeeze(heatmaps[0]) 169 | # roomSegmentation = tf.squeeze(heatmaps[1]) 170 | 171 | # point_indices_stack = getCoarseIndicesMaps(point_indices, WIDTH, HEIGHT, 0) 172 | 173 | # cornerSegmentation = tf.sparse_to_dense(tf.stack([corners[:, 1], corners[:, 0]], axis=1), (HEIGHT, WIDTH), corners[:, 2]) 174 | # cornerHeatmaps = tf.one_hot(cornerSegmentation, depth=NUM_CORNERS, axis=-1) 175 | 176 | # kernel = tf.tile(tf.expand_dims(tf.constant(disk(11)), -1), [1, 1, NUM_CORNERS]) 177 | # cornerHeatmaps = nn.diltion2d(cornerHeatmaps, kernel, [1, 1, 1, 1], [1, 1, 1, 1], 'VALID') 178 | 179 | # imagePath = features['image_path'] 180 | 181 | # imageFeatures = [] 182 | # imageFeature = tf.reduce_sum(tf.reshape(features['image'], [2, -1]), axis=0) 183 | # offset = 0 184 | # for size, numChannels in zip(SIZES, NUM_CHANNELS)[1:]: 185 | # imageFeatures.append(tf.reshape(imageFeature[offset:offset + size * size * numChannels], (size[1], size, numChannels))) 186 | # offset += size * size * numChannels 187 | # continue 188 | 189 | # if random: 190 | # points_inp, point_indices_inp, corner_gt, icon_gt, room_gt, image_features_inp, image_path_inp, flags_inp = tf.train.shuffle_batch([points, point_indices_stack, cornerHeatmaps, iconSegmentation, roomSegmentation, imageFeatures, imagePath, flags], batch_size=batchSize, capacity=min_after_dequeue + (NUM_THREADS + 2) * batchSize, num_threads=NUM_THREADS, min_after_dequeue=min_after_dequeue) 191 | # else: 192 | # points_inp, point_indices_inp, corner_gt, icon_gt, room_gt, image_features_inp, image_path_inp, flags_inp = tf.train.batch([points, point_indices_stack, cornerHeatmaps, iconSegmentation, roomSegmentation, imageFeatures, imagePath, flags], batch_size=batchSize, capacity=min_after_dequeue + (NUM_THREADS + 2) * batchSize, num_threads=1) 193 | # pass 194 | 195 | # input_dict = {'points': points_inp, 'point_indices': point_indices_inp, 'image_features': image_features_inp, 'image_path': image_path_inp, 'flags': flags_inp} 196 | # gt_dict = {'corner': corner_gt, 'icon': icon_gt, 'room': room_gt} 197 | # return input_dict, gt_dict 198 | 199 | 200 | def getDatasetTrain(filenames, augmentation, readImageFeatures, batchSize): 201 | if len(filenames) == 1: 202 | return tf.data.TFRecordDataset(filenames[0]).repeat().map(functools.partial(parse_fn, augmentation=augmentation, readImageFeatures=readImageFeatures), num_parallel_calls=NUM_THREADS).batch(batchSize).prefetch(1) 203 | else: 204 | return tf.data.Dataset.from_tensor_slices(filenames).interleave(lambda x: tf.data.TFRecordDataset(x).repeat().map(functools.partial(parse_fn, augmentation=augmentation), num_parallel_calls=NUM_THREADS), cycle_length=len(filenames), block_length=batchSize).batch(batchSize).prefetch(100 / len(filenames)).shuffle(100 / len(filenames)) 205 | 206 | 207 | def getDatasetVal(filenames, augmentation, readImageFeatures, batchSize): 208 | #return tf.data.Dataset.from_tensor_slices(filenames).interleave(lambda x: tf.data.TFRecordDataset(x).map(functools.partial(parse_fn, augmentation=augmentation, readImageFeatures=readImageFeatures), num_parallel_calls=NUM_THREADS), cycle_length=len(filenames), block_length=batchSize).apply(tf.contrib.data.batch_and_drop_remainder(batchSize)).prefetch(1) 209 | if len(filenames) == 1: 210 | return tf.data.TFRecordDataset(filenames[0]).repeat().map(functools.partial(parse_fn, augmentation=augmentation, readImageFeatures=readImageFeatures), num_parallel_calls=NUM_THREADS).batch(batchSize).prefetch(1) 211 | else: 212 | return tf.data.Dataset.from_tensor_slices(filenames).interleave(lambda x: tf.data.TFRecordDataset(x).map(functools.partial(parse_fn, augmentation=augmentation, readImageFeatures=readImageFeatures), num_parallel_calls=NUM_THREADS), cycle_length=len(filenames), block_length=batchSize).apply(tf.contrib.data.batch_and_drop_remainder(batchSize)).prefetch(1) 213 | -------------------------------------------------------------------------------- /RecordWriterCustom.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import numpy as np 3 | import cv2 4 | import os 5 | import tensorflow as tf 6 | 7 | HEIGHT = 256 8 | WIDTH = 256 9 | NUM_POINTS = 50000 10 | NUM_CHANNELS = [7, 64, 64, 64, 128, 256] 11 | SIZES = [WIDTH, WIDTH // 2, WIDTH // 4, WIDTH // 8, WIDTH // 16, WIDTH // 32] 12 | 13 | def _bytes_feature(value): 14 | return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value])) 15 | 16 | def _int64_feature(value): 17 | return tf.train.Feature(int64_list=tf.train.Int64List(value=value)) 18 | 19 | def _float_feature(value): 20 | return tf.train.Feature(float_list=tf.train.FloatList(value=value)) 21 | 22 | 23 | def fitPlane(points): 24 | if points.shape[0] == points.shape[1]: 25 | return np.linalg.solve(points, np.ones(points.shape[0])) 26 | else: 27 | return np.linalg.lstsq(points, np.ones(points.shape[0]))[0] 28 | 29 | class RecordWriterCustom(): 30 | def __init__(self, filename_list, numPoints=50000, numInputChannels=6): 31 | """filename_list: a list of filename dictionaries Each element is a dict of filenames for data entries, which must contains 'point_cloud' 32 | numPoints: the number of sample points 33 | numInputChannels: [3: XYZ, 6:XYZ + RGB, 9: XYZ + RGB + Normals] 34 | """ 35 | self.numPoints = numPoints 36 | self.numInputChannels = numInputChannels 37 | #super(RecordWriterTango, self).__init__() 38 | self.writer = tf.python_io.TFRecordWriter('data/' + filename + '_' + options.task + '.tfrecords') 39 | for filename_dict in filename_list: 40 | self.writeExample(filename_dict) 41 | continue 42 | self.writer.close() 43 | return 44 | 45 | 46 | def loadPoints(self, filename): 47 | """Function to load the point cloud""" 48 | assert(False, "Not implemented") 49 | return 50 | 51 | def rotatePoints(self, points, rotation_matrix=None): 52 | """Function to rotate the point cloud to be axis-aligned. 53 | Recommand annotate the rotation by adapting the annotator here: https://github.com/art-programmer/FloorplanAnnotator. 54 | If the annotation is not available we use heuristics to rotate the point cloud 55 | """ 56 | #assert(False, "Not implemented") 57 | if transformation: 58 | rotated_points = points.copy() 59 | rotated_points[:, :3] = np.matmul(points[:, :3], rotation_matrix.transpose()) 60 | if points.shape[-1] >= 9: 61 | rotated_points[:, 6:9] = np.matmul(points[:, 6:9], rotation_matrix.transpose()) 62 | pass 63 | return rotated_points 64 | 65 | if points.shape[-1] >= 9: 66 | ## Point normal exists 67 | normals = points[:, 6:9] 68 | else: 69 | ## Compute the point normal by ourselves. Consider using PCL if available. If not we sample some points and compute their normals 70 | sampled_points = points[np.random.choice(np.arange(len(points), dtype=np.int32), 100)] 71 | scale = (points[:, :3].max(0) - points[:, :3].min(0)).max() 72 | normals = [] 73 | for sampled_point in sampled_points: 74 | neighbor_points = points[np.linalg.norm(points - sampled_point, axis=1) < scale * 0.02] 75 | if len(neighbor_points) >= 3: 76 | ## Consider to use try catch for fitPlane as it might encounter singular cases 77 | plane = fitPlane(neighbor_points) 78 | normal = plane / max(np.linalg.norm(plane), 1e-4) 79 | normals.append(normal) 80 | pass 81 | continue 82 | normals = np.stack(normals, axis=0) 83 | pass 84 | 85 | 86 | polarAngles = np.arange(16) * np.pi / 2 / 16 87 | azimuthalAngles = np.arange(64) * np.pi * 2 / 64 88 | polarAngles = np.expand_dims(polarAngles, -1) 89 | azimuthalAngles = np.expand_dims(azimuthalAngles, 0) 90 | 91 | normalBins = np.stack([np.sin(polarAngles) * np.cos(azimuthalAngles), np.tile(np.cos(polarAngles), [1, azimuthalAngles.shape[1]]), -np.sin(polarAngles) * np.sin(azimuthalAngles)], axis=2) 92 | normalBins = np.reshape(normalBins, [-1, 3]) 93 | numBins = normalBins.shape[0] 94 | 95 | 96 | normalDiff = np.tensordot(normals, normalBins, axes=([1], [1])) 97 | normalDiffSign = np.sign(normalDiff) 98 | normalDiff = np.maximum(normalDiff, -normalDiff) 99 | normalMask = one_hot(np.argmax(normalDiff, axis=-1), numBins) 100 | bins = normalMask.sum(0) 101 | maxNormals = np.expand_dims(normals, 1) * np.expand_dims(normalMask, -1) 102 | maxNormals *= np.expand_dims(normalDiffSign, -1) 103 | averageNormals = maxNormals.sum(0) / np.maximum(np.expand_dims(bins, -1), 1e-4) 104 | averageNormals /= np.maximum(np.linalg.norm(averageNormals, axis=-1, keepdims=True), 1e-4) 105 | dominantNormal_1 = averageNormals[np.argmax(bins)] 106 | 107 | dotThreshold_1 = np.cos(np.deg2rad(100)) 108 | dotThreshold_2 = np.cos(np.deg2rad(80)) 109 | 110 | dot_1 = np.tensordot(normalBins, dominantNormal_1, axes=([1], [0])) 111 | bins[np.logical_or(dot_1 < dotThreshold_1, dot_1 > dotThreshold_2)] = 0 112 | dominantNormal_2 = averageNormals[np.argmax(bins)] 113 | 114 | # dot_2 = np.tensordot(normalBins, dominantNormal_2, axes=([1], [0])) 115 | # bins[np.logical_or(dot_2 < dotThreshold_1, dot_2 > dotThreshold_2)] = 0 116 | # dominantNormal_3 = averageNormals[np.argmax(bins)] 117 | dominantNormal_3 = np.cross(dominant_normal_1, dominant_normal_2) 118 | dominantNormal_2 = np.cross(dominant_normal_3, dominant_normal_1) 119 | rotation_matrix = np.stack([dominantNormal_1, dominantNormal_2, dominantNormal_3], axis=0) 120 | rotated_points = points.copy() 121 | rotated_points[:, :3] = np.matmul(points[:, :3], rotation_matrix.transpose()) 122 | if points.shape[-1] >= 9: 123 | rotated_points[:, 6:9] = np.matmul(points[:, 6:9], rotation_matrix.transpose()) 124 | pass 125 | ## Rectify the rotation matrix 126 | return rotated_points 127 | 128 | def scalePoints(self, points, rotation_matrix=None): 129 | """Function to scale the point cloud to range [0, 1].""" 130 | XYZ = points[:, :3] 131 | mins = XYZ.min(0, keepdims=True) 132 | maxs = XYZ.max(0, keepdims=True) 133 | maxRange = (maxs - mins)[:, :2].max() 134 | padding = maxRange * 0.05 135 | maxRange += padding * 2 136 | mins = (maxs + mins) / 2 - maxRange / 2 137 | 138 | XYZ = (XYZ - mins) / maxRange 139 | points[:, :3] = XYZ 140 | return points 141 | 142 | def computeCoordinates(self, points): 143 | """Compute the image coordinate for each point""" 144 | coordinates = np.minimum(np.maximum(np.round(points[:, :2] * imageSize).astype(np.int32), 0), imageSize - 1) 145 | coordinates = coordinates[:, 1] * WIDTH + coordinates[:, 0] 146 | return coordinates 147 | 148 | def load(self, name, filename): 149 | """Function to load info""" 150 | assert(False, "Not implemented") 151 | return 152 | 153 | def writeExample(self, filename_dict): 154 | """Write one data entry""" 155 | assert('point_cloud' in filename_dict) 156 | 157 | ## Implement a function to load the point cloud as a numpy array of size [NxC] where N is the number of points and C is the number of channels 158 | points = loadPoints(filename_dict['point_cloud']) 159 | 160 | points = points[:, :self.numInputChannels] 161 | if len(points) < self.numPoints: 162 | indices = np.arange(len(points)) 163 | points = np.concatenate([points, points[np.random.choice(indices, self.numPoints - len(points))]], axis=0) 164 | elif len(points) > self.numPoints: 165 | sampledInds = np.arange(len(points)) 166 | points = points[np.random.choice(indices, self.numPoints)] 167 | pass 168 | 169 | points = self.rotatedPoints(points) 170 | points = self.scalePoints(points) 171 | 172 | imageSize = np.array([WIDTH, HEIGHT]) 173 | indicesMap = self.computeCoordinates() 174 | 175 | info_dict = {} 176 | for info_name in ['corner_gt', 'icon_gt', 'room_gt', 'image_feature']: 177 | if info_name in filename_dict: 178 | info = self.load(info_name, filename_dict[info_name]) 179 | else: 180 | if info_name == 'corner_gt': 181 | info = np.zeros((1, 3)) 182 | elif info_name in ['icon_gt', 'room_gt']: 183 | info = np.zeros((HEIGHT, WIDTH), dtype=uint8) 184 | else: 185 | info = np.zeros(sum([size * size * numChannels for size, numChannels in zip(SIZES, NUM_CHANNELS)[1:]])) 186 | pass 187 | pass 188 | info_dict[info_name] = info 189 | continue 190 | 191 | example = tf.train.Example(features=tf.train.Features(feature={ 192 | 'image_path': _bytes_feature(filename_dict['point_cloud']), 193 | 'points': _float_feature(points.reshape(-1)), 194 | 'point_indices': _int64_feature(indicesMap.reshape(-1)), 195 | 'corner': _int64_feature(info['corner_gt'].reshape(-1)), 196 | 'num_corners': _int64_feature([len(info['corner_gt'])]), 197 | 'icon': _bytes_feature(info['icon_gt'].tostring()), 198 | 'room': _bytes_feature(info['room_gt'].tostring()), 199 | 'image': _float_feature(info['image_feature'].reshape(-1)), 200 | 'flags': _int64_feature(np.zeros((2, dtype=np.int64))), 201 | })) 202 | self.writer.write(example.SerializeToString()) 203 | return 204 | 205 | 206 | if __name__ == "__main__": 207 | RecordWriterCustom() 208 | exit(1) 209 | -------------------------------------------------------------------------------- /RecordWriterTango.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import numpy as np 3 | import cv2 4 | import random 5 | import math 6 | import os 7 | import time 8 | import zlib 9 | import socket 10 | import threading 11 | import Queue 12 | import sys 13 | import cPickle as pickle 14 | import PIL 15 | import json 16 | import glob 17 | import torchfile 18 | import math 19 | import os.path 20 | import zipfile 21 | import lupa 22 | #from drnseg import DRNSeg 23 | from utils import * 24 | from floorplan_utils import * 25 | import argparse 26 | from skimage import measure 27 | from augmentation import augment 28 | import tensorflow as tf 29 | 30 | #import show3d 31 | sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 32 | 33 | 34 | #BATCH_SIZE=16 35 | FETCH_BATCH_SIZE=16 36 | HEIGHT=256 37 | WIDTH=256 38 | NUM_POINTS = 50000 39 | ROOT_FOLDER = '/home/chenliu/Projects/Data/Tango/' 40 | #CORNER_POINTS_FOLDER = '../../../Data/Floorplan_syn/test_pntnetpp_new/data/oldway/real/combined_out/' 41 | 42 | TEST_INDICES = [153, 102, 115, 57, 104, 156, 154, 310, 70, 134, 98, 8, 51, 76, 87, 36, 18, 106, 10, 73] 43 | 44 | 45 | def _bytes_feature(value): 46 | return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value])) 47 | 48 | def _int64_feature(value): 49 | return tf.train.Feature(int64_list=tf.train.Int64List(value=value)) 50 | 51 | def _float_feature(value): 52 | return tf.train.Feature(float_list=tf.train.FloatList(value=value)) 53 | 54 | def load_pnt(pnt_file): 55 | all_pnts = [] 56 | texture_file = pnt_file.replace('.obj', '.mtl') 57 | texture_exists = os.path.exists(texture_file) 58 | print(pnt_file, texture_exists) 59 | if texture_exists: 60 | with open(texture_file) as f: 61 | for line in f: 62 | values = line.split(' ') 63 | if values[0] == 'map_Kd': 64 | paths = texture_file.split('/') 65 | paths[-1] = values[1].strip() 66 | image_file = '/'.join(paths) 67 | #print(image_file) 68 | texture = cv2.imread(image_file) 69 | texture_width = texture.shape[1] 70 | texture_height = texture.shape[0] 71 | pass 72 | continue 73 | pass 74 | pass 75 | 76 | with open(pnt_file) as f: 77 | vertex_index = 0 78 | for line in f: 79 | eles = line.split() 80 | if len(eles) == 0: 81 | continue 82 | if eles[0] == "v": 83 | if texture_exists: 84 | pnt = [float(eles[1]), float(eles[2]), float(eles[3])] 85 | else: 86 | assert len(eles) >= 7, str(vertex_index) + line 87 | pnt = [float(eles[1]), float(eles[2]), float(eles[3]), float(eles[4]), float(eles[5]), float(eles[6])] 88 | pass 89 | all_pnts.append(pnt) 90 | elif eles[0] == 'vt' and texture_exists: 91 | u = float(eles[1]) 92 | v = float(eles[2]) 93 | if u < 0 or u > 1 or v < 0 or v > 1: 94 | color = [0, 0, 0] 95 | else: 96 | u = int(min(np.round(u * texture_width), texture_width - 1)) 97 | v = int(min(np.round((1 - v) * texture_height), texture_height - 1)) 98 | color = texture[v][u] 99 | color = color.astype(np.float32) / 255 100 | pass 101 | all_pnts[vertex_index] += [color[2], color[1], color[0]] 102 | vertex_index += 1 103 | pass 104 | continue 105 | pass 106 | #if texture_exists: 107 | #writePointCloud('test/pointcloud.ply', all_pnts) 108 | #exit(1) 109 | # print(all_pnts) 110 | return all_pnts 111 | 112 | class PntMapper: 113 | def __init__(self, start, size, newstart, newsize): 114 | self.start = start 115 | self.size = size 116 | self.newsize = newsize 117 | self.newstart=newstart 118 | def map_pnts(self, x, y): 119 | # print(self.size) 120 | return (x - self.start[0])/(self.size[0]-1)*(self.newsize[0]-1) + self.newstart[0], (y- self.start[1])/(self.size[1]-1)*(self.newsize[1]-1) + self.newstart[1] 121 | def pnts_to_heatmap_transformation_matrix(pnts, is_binary, label, transform=None, img=None, bd_box=None): 122 | if not isinstance(pnts, np.ndarray): 123 | pnts = np.array(pnts) 124 | if pnts.shape[0] == 0: 125 | return None 126 | if transform is not None: 127 | if not isinstance(transform, np.ndarray): 128 | transform = np.array(transform) 129 | aug_pnts = np.ones((pnts.shape[0], 6)) 130 | aug_pnts[:, :-1] = pnts 131 | prj_pnts = np.transpose(np.dot(transform, np.transpose(aug_pnts, (1, 0))), (1, 0)) 132 | prj_pnts /= prj_pnts[:, -1][:,np.newaxis] 133 | else: 134 | prj_pnts = pnts 135 | if bd_box==None: 136 | x_max = prj_pnts[:, 0].max() 137 | x_min = prj_pnts[:, 0].min() 138 | y_max = prj_pnts[:, 1].max() 139 | y_min = prj_pnts[:, 1].min() 140 | else: 141 | x_min = bd_box[0] 142 | y_min = bd_box[1] 143 | x_max = bd_box[2] 144 | y_max = bd_box[3] 145 | #print(x_min, y_min, x_max, y_max) 146 | x_size = math.ceil(x_max - x_min + 1) 147 | y_size = math.ceil(y_max - y_min + 1) 148 | 149 | #print(x_min, y_min, x_size, y_size) 150 | mx_size = max(x_size, y_size) 151 | 152 | pnt_mapper = PntMapper((x_min, y_min), (x_size, y_size), (0,0), (HEIGHT, WIDTH)) 153 | if img is None: 154 | img = np.zeros((HEIGHT, WIDTH), dtype=np.uint8) 155 | for pnt in prj_pnts: 156 | # print(pnt) 157 | m_pnt = pnt_mapper.map_pnts(pnt[0], pnt[1]) 158 | #if len(pnts) < 30: 159 | # print(pnt, m_pnt, (x_min, y_min, x_max, y_max)) 160 | try: 161 | if is_binary: 162 | img[int(round(m_pnt[1])), int(round(m_pnt[0]))] = label 163 | else: 164 | img[int(round(m_pnt[1])), int(round(m_pnt[0]))] += 1 165 | except Exception as e: 166 | print(pnt, m_pnt, e) 167 | raise e 168 | return img, (x_min, y_min, x_max, y_max) 169 | 170 | def getZRotationMatrix(deg): 171 | return np.array([[math.cos(deg), -math.sin(deg), 0], 172 | [-math.sin(deg), -math.cos(deg), 0], 173 | [0, 0, 1]]) 174 | 175 | class RecordWriterTango(): 176 | def __init__(self, options): 177 | #super(RecordWriterTango, self).__init__() 178 | self.dataPath = 'data/' 179 | self.stopped=False 180 | self.split = options.task 181 | self.imagePaths = self.loadImagePaths() 182 | 183 | self.numImages = len(self.imagePaths) 184 | self.numPoints = options.numPoints 185 | self.numInputChannels = options.numInputChannels 186 | 187 | self.labelMap = self.loadLabelMap() 188 | self.gap = 3 189 | 190 | self.roomLabelMap = getRoomLabelMap() 191 | 192 | # numTrainingImages = int(round(self.numImages * 0.9)) 193 | # if options.task == 'train': 194 | # self.imagePaths = self.imagePaths[:numTrainingImages] 195 | # else: 196 | # self.imagePaths = self.imagePaths[numTrainingImages:] 197 | # pass 198 | 199 | if options.task == 'train': 200 | self.imagePaths = [imagePath for imagePath in self.imagePaths if int(imagePath[5:]) not in TEST_INDICES] 201 | else: 202 | self.imagePaths = [imagePath for imagePath in self.imagePaths if int(imagePath[5:]) in TEST_INDICES] 203 | pass 204 | 205 | filename = os.path.basename(__file__).rstrip('.py')[12:] 206 | self.writer = tf.python_io.TFRecordWriter('data/' + filename + '_' + options.task + '.tfrecords') 207 | for imagePath in self.imagePaths: 208 | self.writeExample(imagePath) 209 | continue 210 | self.writer.close() 211 | return 212 | 213 | def getNumBatchesTesting(self): 214 | #print(self.numBatches) 215 | #print(self.numBatchesTraining) 216 | return self.numBatches - self.numBatchesTraining 217 | 218 | def loadImagePaths(self): 219 | scene_ids = [] 220 | for scene_id in os.listdir(ROOT_FOLDER): 221 | annotation_filename = ROOT_FOLDER + scene_id + '/annotation/floorplan.txt' 222 | exists1 = os.path.exists(annotation_filename) 223 | filename_glob = ROOT_FOLDER + scene_id + '/' + 'dataset/mesh/*.obj' 224 | filename = list(glob.glob(filename_glob)) 225 | exists2 = len(filename) > 0 226 | if exists1 and exists2: 227 | scene_ids.append(scene_id) 228 | else: 229 | print(scene_id) 230 | pass 231 | continue 232 | return scene_ids 233 | 234 | def loadLabelMap(self): 235 | roomMap = getRoomLabelMap() 236 | 237 | iconMap = getIconLabelMap() 238 | 239 | # iconMap['washing_basin'] = iconMap['washbasin'] 240 | # iconMap['sofa'] = 6 241 | # iconMap['chair'] = 5 242 | # iconMap['TV'] = iconMap['tv'] 243 | #for icon in ['cooking_counter', 'bathtub', 'toilet', 'washing_basin', 'sofa', 'cabinet', 'bed', 'table', 'desk', 'refrigerator', 'TV', 'entrance', 'chair']: 244 | 245 | labelMap = {} 246 | for icon in ['cooking_counter', 'bathtub', 'toilet', 'washing_basin', 'sofa', 'cabinet', 'bed', 'table', 'desk', 'refrigerator', 'TV', 'entrance', 'chair']: 247 | if icon not in iconMap: 248 | print(icon) 249 | exit(1) 250 | labelMap[icon] = ('icons', iconMap[icon]) 251 | continue 252 | for room in ['living_room', 'kitchen', 'bedroom', 'bathroom', 'office', 'closet', 'balcony', 'corridor', 'dining_room', 'stairs']: 253 | if room not in roomMap: 254 | print(room) 255 | exit(1) 256 | labelMap[room] = ('rooms', roomMap[room]) 257 | continue 258 | 259 | labelMap['door'] = 11 260 | labelMap['window'] = 12 261 | return labelMap 262 | 263 | def writeExample(self, scene_id): 264 | filename_glob = ROOT_FOLDER + scene_id + '/' + 'dataset/mesh/*.obj' 265 | filename = list(glob.glob(filename_glob))[0] 266 | points = load_pnt(filename) 267 | points = np.array(points) 268 | 269 | 270 | #segmentation = segmentation[sampledInds[:NUM_POINTS]] 271 | filename = ROOT_FOLDER + scene_id + '/annotation/metadata.t7' 272 | metadata = torchfile.load(filename) 273 | topDownViewTransformation = metadata["topDownTransformation"] 274 | 275 | #degree = metadata["topDownViewAngle"] 276 | rotMat = getZRotationMatrix(-metadata["topDownViewAngle"]) 277 | #print(rotMat, topDownViewTransformation) 278 | #exit(1) 279 | 280 | #XYZ_rotated = np.transpose(np.dot(rotMat, np.transpose(points[:, :3]))) 281 | #XYZ = np.tensordot(np.concatenate([points[:, :3], np.ones((points.shape[0], 1))], axis=1), topDownViewTransformation, axes=((1), (1))) 282 | #XYZ[:, 2] = points[:, 2] 283 | 284 | #ratio_1 = (XYZ[:, 0].max() - XYZ[:, 0].min()) / (XYZ_rotated[:, 0].max() - XYZ_rotated[:, 0].min()) 285 | #ratio_2 = (XYZ[:, 1].max() - XYZ[:, 1].min()) / (XYZ_rotated[:, 1].max() - XYZ_rotated[:, 1].min()) 286 | 287 | #XYZ[2, 2] *= np.sqrt(ratio_1 * ratio_2) 288 | 289 | #ratio = pow(np.abs(rotMat[0][0] / topDownViewTransformation[0][0] * rotMat[1][0] / topDownViewTransformation[1][0] * rotMat[0][1] / topDownViewTransformation[0][1] * rotMat[1][1] / topDownViewTransformation[1][1]), 0.25) 290 | ratio = 0 291 | for i in xrange(2): 292 | for j in xrange(2): 293 | if rotMat[i][j] != 0: 294 | ratio = max(ratio, np.abs(topDownViewTransformation[i][j] / rotMat[i][j])) 295 | pass 296 | continue 297 | continue 298 | 299 | globalTransformation = topDownViewTransformation 300 | globalTransformation[2, 2] = ratio 301 | globalTransformation[2, 3] = 0 302 | globalTransformation = np.concatenate([globalTransformation, np.zeros((1, 4))], axis=0) 303 | globalTransformation[3, 3] = 1 304 | 305 | 306 | XYZ = np.tensordot(np.concatenate([points[:, :3], np.ones((points.shape[0], 1))], axis=1), globalTransformation, axes=((1), (1))) 307 | XYZ = XYZ[:, :3] / XYZ[:, 3:] 308 | 309 | 310 | mins = XYZ.min(0, keepdims=True) 311 | maxs = XYZ.max(0, keepdims=True) 312 | maxRange = (maxs - mins)[:, :2].max() 313 | padding = maxRange * 0.05 314 | mins = (maxs + mins) / 2 - maxRange / 2 315 | mins -= padding 316 | maxRange += padding * 2 317 | minXY = mins[:, :2] 318 | 319 | #XYZ[:, :2] = (XYZ[:, :2] - minXY) / maxRange 320 | XYZ = (XYZ - mins) / maxRange 321 | points[:, :3] = XYZ 322 | 323 | originalWidth = 700. 324 | 325 | if points.shape[0] < NUM_POINTS: 326 | indices = np.arange(points.shape[0]) 327 | points = np.concatenate([points, points[np.random.choice(indices, NUM_POINTS - points.shape[0])]], axis=0) 328 | elif points.shape[0] > NUM_POINTS: 329 | sampledInds = np.arange(points.shape[0]) 330 | np.random.shuffle(sampledInds) 331 | points = points[sampledInds[:NUM_POINTS]] 332 | pass 333 | 334 | points[:, 3:] = points[:, 3:] / 255 - 0.5 335 | 336 | coordinates = np.clip(np.round(points[:, :2] * HEIGHT).astype(np.int32), 0, HEIGHT - 1) 337 | 338 | self.indicesMaps = np.zeros((NUM_POINTS), dtype=np.int64) 339 | self.projectIndices(np.concatenate([coordinates, np.arange(NUM_POINTS).reshape(-1, 1)], axis=1), 0, WIDTH, 0, HEIGHT) 340 | 341 | 342 | filename = ROOT_FOLDER + scene_id + '/annotation/floorplan.txt' 343 | walls = [] 344 | doors = [] 345 | windows = [] 346 | semantics = {} 347 | def transformPoint(v, c): 348 | return max(min(round((float(v) - minXY[0, c]) / maxRange * WIDTH), WIDTH - 1), 0) 349 | 350 | with open(filename) as info_file: 351 | line_index = 0 352 | for line in info_file: 353 | line = line.split('\t') 354 | if line[4] == 'wall': 355 | walls.append(((transformPoint(line[0], 0), transformPoint(line[1], 1)), (transformPoint(line[2], 0), transformPoint(line[3], 1)))) 356 | elif line[4] == 'door': 357 | doors.append(((transformPoint(line[0], 0), transformPoint(line[1], 1)), (transformPoint(line[2], 0), transformPoint(line[3], 1)))) 358 | elif line[4] == 'window': 359 | windows.append(((transformPoint(line[0], 0), transformPoint(line[1], 1)), (transformPoint(line[2], 0), transformPoint(line[3], 1)))) 360 | else: 361 | if line[4] not in semantics: 362 | semantics[line[4]] = [] 363 | pass 364 | semantics[line[4]].append(((transformPoint(line[0], 0), transformPoint(line[1], 1)), (transformPoint(line[2], 0), transformPoint(line[3], 1)))) 365 | pass 366 | continue 367 | pass 368 | 369 | 370 | roomSegmentation = np.zeros((HEIGHT, WIDTH), dtype=np.uint8) 371 | for line in walls: 372 | cv2.line(roomSegmentation, (int(round(line[0][0])), int(round(line[0][1]))), (int(round(line[1][0])), int(round(line[1][1]))), color = 15 + calcLineDirection(line), thickness=self.gap) 373 | #cv2.line(roomSegmentation, (int(round(line[0][0])), int(round(line[0][1]))), (int(round(line[1][0])), int(round(line[1][1]))), color = 15, thickness=self.gap) 374 | continue 375 | 376 | rooms = measure.label(roomSegmentation == 0, background=0) 377 | 378 | 379 | corners = lines2Corners(walls, gap=self.gap) 380 | #corner_gt = np.zeros((HEIGHT, WIDTH), dtype=np.uint8) 381 | corner_gt = [] 382 | for corner in corners: 383 | #corner_gt[int(round(corner[0][1])), int(round(corner[0][0]))] = corner[1] + 1 384 | corner_gt.append((int(round(corner[0][0])), int(round(corner[0][1])), corner[1] + 1)) 385 | continue 386 | 387 | openingCornerMap = [[3, 1], [0, 2]] 388 | for openingType, openings in enumerate([doors, windows]): 389 | for opening in openings: 390 | direction = calcLineDirection(opening) 391 | for cornerIndex, corner in enumerate(opening): 392 | #corner_gt[int(round(corner[1])), int(round(corner[0]))] = 14 + openingCornerMap[direction][cornerIndex] 393 | corner_gt.append((int(round(corner[0])), int(round(corner[1])), 14 + openingCornerMap[direction][cornerIndex])) 394 | continue 395 | continue 396 | continue 397 | 398 | 399 | wallIndex = rooms.min() 400 | for pixel in [(0, 0), (0, HEIGHT - 1), (WIDTH - 1, 0), (WIDTH - 1, HEIGHT - 1)]: 401 | backgroundIndex = rooms[pixel[1]][pixel[0]] 402 | if backgroundIndex != wallIndex: 403 | break 404 | continue 405 | 406 | iconSegmentation = np.zeros((HEIGHT, WIDTH), dtype=np.uint8) 407 | for line in doors: 408 | cv2.line(iconSegmentation, (int(round(line[0][0])), int(round(line[0][1]))), (int(round(line[1][0])), int(round(line[1][1]))), color = self.labelMap['door'], thickness=self.gap - 1) 409 | continue 410 | for line in windows: 411 | cv2.line(iconSegmentation, (int(round(line[0][0])), int(round(line[0][1]))), (int(round(line[1][0])), int(round(line[1][1]))), color = self.labelMap['window'], thickness=self.gap - 1) 412 | continue 413 | 414 | roomLabelMap = {} 415 | for semantic, items in semantics.iteritems(): 416 | group, label = self.labelMap[semantic] 417 | for corners in items: 418 | if group == 'icons': 419 | if label == 0: 420 | continue 421 | cv2.rectangle(iconSegmentation, (int(round(corners[0][0])), int(round(corners[0][1]))), (int(round(corners[1][0])), int(round(corners[1][1]))), color=label, thickness=-1) 422 | # corner_gt[int(round(corners[0][1])), int(round(corners[0][0]))] = 18 + 2 423 | # corner_gt[int(round(corners[1][1])), int(round(corners[0][0]))] = 18 + 1 424 | # corner_gt[int(round(corners[0][1])), int(round(corners[1][0]))] = 18 + 3 425 | # corner_gt[int(round(corners[1][1])), int(round(corners[1][0]))] = 18 + 0 426 | corner_gt.append((int(round(corners[0][0])), int(round(corners[0][1])), 18 + 2)) 427 | corner_gt.append((int(round(corners[0][0])), int(round(corners[1][1])), 18 + 1)) 428 | corner_gt.append((int(round(corners[1][0])), int(round(corners[0][1])), 18 + 3)) 429 | corner_gt.append((int(round(corners[1][0])), int(round(corners[1][1])), 18 + 0)) 430 | else: 431 | roomIndex = rooms[int(round((corners[0][1] + corners[1][1]) / 2))][int(round((corners[0][0] + corners[1][0]) / 2))] 432 | if roomIndex == wallIndex or roomIndex == backgroundIndex: 433 | print('label on background') 434 | exit(1) 435 | pass 436 | if roomIndex in roomLabelMap: 437 | print('room has more than one labels', label) 438 | exit(1) 439 | pass 440 | roomLabelMap[roomIndex] = label 441 | roomSegmentation[rooms == roomIndex] = label 442 | pass 443 | continue 444 | continue 445 | for roomIndex in xrange(rooms.min(), rooms.max() + 1): 446 | if roomIndex == wallIndex or roomIndex == backgroundIndex: 447 | continue 448 | if roomIndex not in roomLabelMap: 449 | print('room has no label') 450 | print(roomIndex, rooms.max()) 451 | pass 452 | continue 453 | flags = np.zeros(2, np.int64) 454 | flags[0] = 1 455 | 456 | corner_feature_file = ROOT_FOLDER + scene_id + '/corner_acc.npy' 457 | icon_feature_file = ROOT_FOLDER + scene_id + '/topdown_acc.npy' 458 | image_features = [[], []] 459 | if os.path.exists(corner_feature_file) and os.path.exists(icon_feature_file): 460 | flags[1] = 1 461 | corner_feature = np.load(corner_feature_file).reshape((HEIGHT, WIDTH, -1)) 462 | icon_feature = np.load(icon_feature_file).reshape((HEIGHT, WIDTH, -1)) 463 | for featureIndex, feature in enumerate([corner_feature, icon_feature]): 464 | for size, numChannels in zip(SIZES, NUM_CHANNELS)[1:]: 465 | feature = cv2.resize(feature, (size, size)) 466 | image_features[featureIndex].append(feature.reshape((size, size, numChannels, -1)).mean(-1).reshape(-1)) 467 | continue 468 | image_features[featureIndex] = np.concatenate(image_features[featureIndex], axis=0) 469 | continue 470 | image_features = image_features[0] + image_features[1] 471 | else: 472 | image_features = np.zeros(sum([size * size * numChannels for size, numChannels in zip(SIZES, NUM_CHANNELS)[1:]])) 473 | pass 474 | 475 | if False: 476 | cv2.imwrite('test/density.png', drawDensityImage(getDensity(points, HEIGHT, WIDTH))) 477 | cv2.imwrite('test/density_indices.png', drawDensityImage(getDensityFromIndices(self.indicesMaps, HEIGHT, WIDTH))) 478 | cv2.imwrite('test/icon_segmentation.png', drawSegmentationImage(iconSegmentation)) 479 | cv2.imwrite('test/room_segmentation.png', drawSegmentationImage(roomSegmentation)) 480 | cv2.imwrite('test/corner_segmentation.png', drawSegmentationImage(corner_gt, blackIndex=0)) 481 | if flags[1]: 482 | cv2.imwrite('test/topdown_corner.png', cv2.imread(ROOT_FOLDER + scene_id + '/corner_pred.png')) 483 | cv2.imwrite('test/topdown_icon.png', cv2.imread(ROOT_FOLDER + scene_id + '/topdown_pred_nonzero.png')) 484 | exit(1) 485 | pass 486 | pass 487 | 488 | corner_gt = np.array(corner_gt, dtype=np.int64) 489 | numCorners = len(corner_gt) 490 | print('num corners', numCorners) 491 | if numCorners > MAX_NUM_CORNERS: 492 | exit(1) 493 | elif numCorners < MAX_NUM_CORNERS: 494 | corner_gt = np.concatenate([corner_gt, np.zeros((MAX_NUM_CORNERS - numCorners, 3), dtype=np.int64)], axis=0) 495 | pass 496 | 497 | example = tf.train.Example(features=tf.train.Features(feature={ 498 | 'image_path': _bytes_feature(scene_id), 499 | 'points': _float_feature(points.reshape(-1)), 500 | 'point_indices': _int64_feature(self.indicesMaps.reshape(-1)), 501 | 'corner': _int64_feature(corner_gt.reshape(-1)), 502 | 'num_corners': _int64_feature([numCorners]), 503 | 'icon': _bytes_feature(iconSegmentation.tostring()), 504 | 'room': _bytes_feature(roomSegmentation.tostring()), 505 | 'image': _float_feature(image_features.reshape(-1)), 506 | 'flags': _int64_feature(flags), 507 | })) 508 | self.writer.write(example.SerializeToString()) 509 | return 510 | 511 | 512 | def projectSegmentation(self, pointSegmentation, min_x, max_x, min_y, max_y): 513 | if max_x - min_x == 1 and max_y - min_y == 1: 514 | segments, counts = np.unique(pointSegmentation[:, 2], return_counts=True) 515 | segmentList = zip(segments.tolist(), counts.tolist()) 516 | segmentList = [segment for segment in segmentList if segment[0] not in [0, 2]] 517 | label = 0 518 | if 2 in segments: 519 | label = 2 520 | pass 521 | if len(segmentList) > 0: 522 | segment = max(segmentList, key=lambda x: x[1]) 523 | if segment[1] > 0: 524 | label = segment[0] 525 | pass 526 | pass 527 | self.segmentation[min_y][min_x] = label 528 | elif max_x - min_x >= max_y - min_y: 529 | middle_x = int((max_x + min_x + 1) / 2) 530 | mask_1 = pointSegmentation[:, 1] < middle_x 531 | self.projectSegmentation(pointSegmentation[mask_1], min_x, middle_x, min_y, max_y) 532 | mask_2 = pointSegmentation[:, 1] >= middle_x 533 | self.projectSegmentation(pointSegmentation[mask_2], middle_x, max_x, min_y, max_y) 534 | else: 535 | middle_y = int((max_y + min_y + 1) / 2) 536 | mask_1 = pointSegmentation[:, 0] < middle_y 537 | self.projectSegmentation(pointSegmentation[mask_1], min_x, max_x, min_y, middle_y) 538 | mask_2 = pointSegmentation[:, 0] >= middle_y 539 | self.projectSegmentation(pointSegmentation[mask_2], min_x, max_x, middle_y, max_y) 540 | pass 541 | return 542 | 543 | 544 | def projectIndices(self, pointSegmentation, min_x, max_x, min_y, max_y): 545 | # for strideIndex in xrange(6): 546 | # stride = pow(2, strideIndex) 547 | # if max_x - min_x == stride and max_y - min_y == stride: 548 | # self.indicesMaps[strideIndex][pointSegmentation[:, 2]] = min_y / stride * WIDTH / stride + min_x / stride + self.batchIndex * (HEIGHT / stride * WIDTH / stride) 549 | # pass 550 | # continue 551 | 552 | if max_x - min_x == 1 and max_y - min_y == 1: 553 | self.indicesMaps[pointSegmentation[:, 2]] = min_y * WIDTH + min_x 554 | return 555 | elif max_x - min_x >= max_y - min_y: 556 | middle_x = int((max_x + min_x + 1) / 2) 557 | mask_1 = pointSegmentation[:, 0] < middle_x 558 | self.projectIndices(pointSegmentation[mask_1], min_x, middle_x, min_y, max_y) 559 | mask_2 = pointSegmentation[:, 0] >= middle_x 560 | self.projectIndices(pointSegmentation[mask_2], middle_x, max_x, min_y, max_y) 561 | else: 562 | middle_y = int((max_y + min_y + 1) / 2) 563 | mask_1 = pointSegmentation[:, 1] < middle_y 564 | self.projectIndices(pointSegmentation[mask_1], min_x, max_x, min_y, middle_y) 565 | mask_2 = pointSegmentation[:, 1] >= middle_y 566 | self.projectIndices(pointSegmentation[mask_2], min_x, max_x, middle_y, max_y) 567 | pass 568 | return 569 | 570 | if __name__ == "__main__": 571 | parser = argparse.ArgumentParser(description='Planenet') 572 | args = parser.parse_args() 573 | args.numPoints = 50000 574 | args.numInputChannels = 7 575 | args.augmentation = '' 576 | for split in ['val']: 577 | print(split) 578 | args.task = split 579 | RecordWriterTango(args) 580 | continue 581 | exit(1) 582 | 583 | # reader = BatchFetcher('train') 584 | # for i in xrange(12): 585 | # print(i) 586 | # reader.work(i) 587 | # continue 588 | # exit(1) 589 | -------------------------------------------------------------------------------- /augmentation_tf.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from utils import * 3 | import tensorflow as tf 4 | from floorplan_utils import * 5 | 6 | 7 | def warpIndices(xs, ys, gridStride, gridWidth, gridHeight, width, height, gridXsTarget, gridYsTarget): 8 | numPoints = xs.shape[0] 9 | 10 | minXs = xs / gridStride 11 | minYs = ys / gridStride 12 | maxXs = xs / gridStride + 1 13 | maxYs = ys / gridStride + 1 14 | 15 | topLeft = tf.expand_dims(minYs * gridWidth + minXs, -1) 16 | topRight = tf.expand_dims(minYs * gridWidth + maxXs, -1) 17 | bottomLeft = tf.expand_dims(maxYs * gridWidth + minXs, -1) 18 | bottomRight = tf.expand_dims(maxYs * gridWidth + maxXs, -1) 19 | 20 | topLeftXsTarget = tf.gather_nd(gridXsTarget, topLeft) 21 | topLeftYsTarget = tf.gather_nd(gridYsTarget, topLeft) 22 | topRightXsTarget = tf.gather_nd(gridXsTarget, topRight) 23 | topRightYsTarget = tf.gather_nd(gridYsTarget, topRight) 24 | bottomLeftXsTarget = tf.gather_nd(gridXsTarget, bottomLeft) 25 | bottomLeftYsTarget = tf.gather_nd(gridYsTarget, bottomLeft) 26 | bottomRightXsTarget = tf.gather_nd(gridXsTarget, bottomRight) 27 | bottomRightYsTarget = tf.gather_nd(gridYsTarget, bottomRight) 28 | 29 | ratioX = tf.cast(xs - minXs * gridStride, tf.float32) / float(gridStride) 30 | ratioY = tf.cast(ys - minYs * gridStride, tf.float32) / float(gridStride) 31 | topLeftRatio = (1 - ratioX) * (1 - ratioY) 32 | topRightRatio = ratioX * (1 - ratioY) 33 | bottomLeftRatio = (1 - ratioX) * ratioY 34 | bottomRightRatio = ratioX * ratioY 35 | 36 | xsTarget = topLeftXsTarget * topLeftRatio + topRightXsTarget * topRightRatio + bottomLeftXsTarget * bottomLeftRatio + bottomRightXsTarget * bottomRightRatio 37 | ysTarget = topLeftYsTarget * topLeftRatio + topRightYsTarget * topRightRatio + bottomLeftYsTarget * bottomLeftRatio + bottomRightYsTarget * bottomRightRatio 38 | 39 | xsTarget = tf.clip_by_value(tf.cast(tf.round(xsTarget), tf.int32), 0, width - 1) 40 | ysTarget = tf.clip_by_value(tf.cast(tf.round(ysTarget), tf.int32), 0, height - 1) 41 | return xsTarget, ysTarget 42 | 43 | def scaleIndices(xs, ys, min_x, min_y, max_x, max_y, width, height): 44 | xsTarget = (tf.cast(xs, tf.float32) - min_x) / (max_x - min_x + 1) * width 45 | ysTarget = (tf.cast(ys, tf.float32) - min_y) / (max_y - min_y + 1) * height 46 | xsTarget = tf.clip_by_value(tf.cast(tf.round(xsTarget), tf.int32), 0, width - 1) 47 | ysTarget = tf.clip_by_value(tf.cast(tf.round(ysTarget), tf.int32), 0, height - 1) 48 | return xsTarget, ysTarget 49 | 50 | #Get coarse indices maps from 256x256 indices map 51 | def getCoarseIndicesMaps(indicesMap, width=256, height=256, batchIndex=0): 52 | indicesMaps = [] 53 | for strideIndex in xrange(6): 54 | stride = pow(2, strideIndex) 55 | if strideIndex == 0: 56 | indicesMaps.append(indicesMap + batchIndex * width * height) 57 | else: 58 | indicesMaps.append(indicesMap / (width * stride) * (width / stride) + indicesMap % width / stride + batchIndex * width / stride * height / stride) 59 | pass 60 | #print(indicesMaps) 61 | continue 62 | indicesMaps = tf.stack(indicesMaps, axis=0) 63 | return indicesMaps 64 | 65 | #Get coarse indices maps from 256x256 indices map 66 | def getCoarseIndicesMapsBatch(indicesMap, width=256, height=256): 67 | indicesMaps = [] 68 | for strideIndex in xrange(6): 69 | stride = pow(2, strideIndex) 70 | if strideIndex == 0: 71 | indicesMaps.append(indicesMap) 72 | else: 73 | indicesMaps.append(indicesMap / (width * stride) * (width / stride) + indicesMap % width / stride) 74 | pass 75 | #print(indicesMaps) 76 | continue 77 | indicesMaps = tf.stack(indicesMaps, axis=0) 78 | return indicesMaps 79 | 80 | 81 | def augmentWarping(pointcloudIndices, corners, heatmaps, gridStride=16, randomScale=4): 82 | width = WIDTH 83 | height = HEIGHT 84 | gridWidth = int(width / gridStride + 1) 85 | gridHeight = int(height / gridStride + 1) 86 | 87 | gridXs = tf.reshape(tf.tile(tf.expand_dims(tf.range(gridWidth) * gridStride, 0), [gridHeight, 1]), [-1]) 88 | gridYs= tf.reshape(tf.tile(tf.expand_dims(tf.range(gridHeight) * gridStride, -1), [1, gridWidth]), [-1]) 89 | 90 | gridXsTarget = tf.cast(gridXs, tf.float32) + tf.random_normal(stddev=randomScale, shape=[gridHeight * gridWidth]) 91 | gridYsTarget = tf.cast(gridYs, tf.float32) + tf.random_normal(stddev=randomScale, shape=[gridHeight * gridWidth]) 92 | 93 | xsTarget, ysTarget = warpIndices(pointcloudIndices % width, pointcloudIndices / width, gridStride, gridWidth, gridHeight, width, height, gridXsTarget, gridYsTarget) 94 | 95 | newPointcloudIndices = tf.clip_by_value(ysTarget, 0, height - 1) * width + tf.clip_by_value(xsTarget, 0, width - 1) 96 | 97 | xsTarget, ysTarget = warpIndices(corners[:, 0], corners[:, 1], gridStride, gridWidth, gridHeight, width, height, gridXsTarget, gridYsTarget) 98 | newCorners = tf.stack([xsTarget, ysTarget, corners[:, 2]], axis=1) 99 | 100 | return newPointcloudIndices, newCorners, heatmaps 101 | 102 | def augmentScaling(pointcloud, pointcloudIndices, corners, heatmaps, imageFeatures): 103 | width = WIDTH 104 | height = HEIGHT 105 | xs = pointcloudIndices % width 106 | ys = pointcloudIndices / width 107 | 108 | imageSize = tf.constant((height, width), dtype=np.float32) 109 | #randomScale = pow(2.0, tf.random.uniform([1]) - 1) 110 | randomScale = tf.random_uniform(shape=[1], minval=0.5, maxval=1.5)[0] 111 | xsTarget = tf.clip_by_value(tf.cast(tf.round((tf.cast(xs, tf.float32) - width / 2) * randomScale + width / 2), tf.int32), 0, width - 1) 112 | ysTarget = tf.clip_by_value(tf.cast(tf.round((tf.cast(ys, tf.float32) - height / 2) * randomScale + height / 2), tf.int32), 0, height - 1) 113 | 114 | 115 | newPointcloudIndices = ysTarget * width + xsTarget 116 | 117 | pointcloud = (pointcloud - 0.5) * randomScale + 0.5 118 | 119 | newHeatmaps = tf.image.resize_nearest_neighbor(heatmaps, size = tf.cast(tf.round(imageSize * randomScale), tf.int32)) 120 | newHeatmaps = tf.image.resize_image_with_crop_or_pad(newHeatmaps, height, width) 121 | 122 | xsTarget = tf.cast(tf.round((tf.cast(corners[:, 0], tf.float32) - width / 2) * randomScale + width / 2), tf.int32) 123 | ysTarget = tf.cast(tf.round((tf.cast(corners[:, 1], tf.float32) - height / 2) * randomScale + height / 2), tf.int32) 124 | newCorners = tf.stack([xsTarget, ysTarget, corners[:, 2]], axis=1) 125 | validMask = tf.logical_and(tf.logical_and(tf.greater_equal(newCorners[:, 0], 0), tf.greater_equal(newCorners[:, 1], 0)), tf.logical_and(tf.less(newCorners[:, 0], WIDTH), tf.less(newCorners[:, 1], HEIGHT))) 126 | newCorners = tf.boolean_mask(newCorners, validMask) 127 | 128 | for index, (featureSize, numChannels) in enumerate(zip(SIZES, NUM_CHANNELS)[1:]): 129 | if index in imageFeatures: 130 | imageFeatures[index] = tf.image.resize_image_with_crop_or_pad(tf.image.resize_nearest_neighbor(tf.expand_dims(imageFeatures[index], 0), size = tf.cast(tf.round(tf.constant((featureSize, featureSize), dtype=tf.float32) * randomScale), tf.int32)), featureSize, featureSize)[0] 131 | pass 132 | continue 133 | 134 | return pointcloud, newPointcloudIndices, newCorners, newHeatmaps, imageFeatures 135 | 136 | 137 | def augmentFlipping(pointcloud, pointcloudIndices, corners, heatmaps, imageFeatures): 138 | width = WIDTH 139 | height = HEIGHT 140 | 141 | orientation = tf.cast(tf.random_uniform(shape=[1], maxval=4)[0], tf.int32) 142 | 143 | #if orientation == 0: 144 | #return pointcloud, pointcloudIndices, newCorners, heatmaps 145 | 146 | xsTarget = pointcloudIndices % width 147 | ysTarget = pointcloudIndices / width 148 | 149 | reverseChannelsY = tf.constant([-1, 2, 1, 0, 3, 7, 6, 5, 4, 10, 9, 8, 11, 12, 15, 14, 13, 16, 20, 19, 18, 17], dtype=tf.int32) + 1 150 | reverseChannelsX = tf.constant([-1, 0, 3, 2, 1, 5, 4, 7, 6, 8, 11, 10, 9, 12, 13, 16, 15, 14, 18, 17, 20, 19], dtype=tf.int32) + 1 151 | 152 | xsTarget = tf.cond(orientation >= 2, lambda: width - 1 - xsTarget, lambda: xsTarget) 153 | pointcloud = tf.cond(orientation >= 2, lambda: tf.concat([1 - pointcloud[:, 0:1], pointcloud[:, 1:]], axis=1), lambda: pointcloud) 154 | heatmaps = tf.cond(orientation >= 2, lambda: heatmaps[:, :, ::-1], lambda: heatmaps) 155 | corners = tf.cond(orientation >= 2, lambda: tf.stack([width - 1 - corners[:, 0], corners[:, 1], tf.gather_nd(reverseChannelsX, corners[:, 2:3])], axis=1), lambda: corners) 156 | 157 | ysTarget = tf.cond(tf.equal(orientation % 2, 1), lambda: height - 1 - ysTarget, lambda: ysTarget) 158 | pointcloud = tf.cond(tf.equal(orientation % 2, 1), lambda: tf.concat([pointcloud[:, :1], 1 - pointcloud[:, 1:2], pointcloud[:, 2:]], axis=1), lambda: pointcloud) 159 | heatmaps = tf.cond(tf.equal(orientation % 2, 1), lambda: heatmaps[:, ::-1], lambda: heatmaps) 160 | corners = tf.cond(tf.equal(orientation % 2, 1), lambda: tf.stack([corners[:, 0], height - 1 - corners[:, 1], tf.gather_nd(reverseChannelsY, corners[:, 2:3])], axis=1), lambda: corners) 161 | 162 | 163 | for index, (size, numChannels) in enumerate(zip(SIZES, NUM_CHANNELS)[1:]): 164 | if index in imageFeatures: 165 | imageFeatures[index] = tf.cond(orientation >= 2, lambda: imageFeatures[index][:, ::-1], lambda: imageFeatures[index]) 166 | imageFeatures[index] = tf.cond(tf.equal(orientation % 2, 1), lambda: imageFeatures[index][::-1], lambda: imageFeatures[index]) 167 | pass 168 | continue 169 | 170 | newPointcloudIndices = ysTarget * width + xsTarget 171 | 172 | return pointcloud, newPointcloudIndices, corners, heatmaps, imageFeatures 173 | 174 | 175 | def augmentDropping(pointcloud, pointcloud_indices, changeIndices): 176 | p = tf.random.random() * 0.5 + 0.5 177 | indices = tf.range(pointcloud.shape[0], dtype='int32') 178 | out_shape = int(pointcloud.shape[0] * p) 179 | chosen_indices = tf.random.choice(indices, (out_shape, ), replace=True) 180 | rest_mask = tf.ones(indices.shape, dtype=tf.bool) 181 | rest_mask[chosen_indices] = 0 182 | rest_indices = indices[rest_mask] 183 | #rest_indices = tf.array(list(set(indices) - set(chosen_indices))) 184 | #rest = pointcloud[rest_indices] 185 | 186 | #rest_chosen_indices = tf.random.choice(rest_indices, (pointcloud.shape[0] - out_shape, ), replace=True) 187 | #rest_chosen = rest[rest_chosen_indices] 188 | #aug_pointcloud = pointcloud[chosen_indices] = rest_chosen 189 | 190 | rest_indices = tf.random.choice(rest_indices, chosen_indices.shape[0], replace=True) 191 | pointcloud[chosen_indices] = pointcloud[rest_indices] 192 | if changeIndices: 193 | pointcloud_indices[chosen_indices] = pointcloud_indices[rest_indices] 194 | pass 195 | return pointcloud, pointcloud_indices 196 | 197 | 198 | 199 | def augment(pointcloud_inp, pointcloud_indices_0_inp, heatmapBatches, augmentation, numPoints=50000, numInputChannels=7): 200 | pointcloud_indices_inp = tf.zeros((FETCH_BATCH_SIZE, 6, NUM_POINTS),dtype='int32') 201 | newHeatmapBatches = [[] for heatmapIndex in xrange(len(heatmapBatches))] 202 | 203 | for imageIndex in xrange(pointcloud_inp.shape[0]): 204 | # pointcloud = pointcloud_inp[imageIndex] 205 | # pointcloud_indices_0 = pointcloud_indices_0_inp[imageIndex] 206 | # corner = corner_gt[imageIndex] 207 | # icon = icon_gt[imageIndex] 208 | # room = room_gt[imageIndex] 209 | # feature = feature_inp[imageIndex] 210 | # if 'w' in augmentation: 211 | # pointcloud_indices_0, [corner, icon, room, feature] = augmentWarping(pointcloud_indices_0, [corner, icon, room, feature], gridStride=32., randomScale=4) 212 | # pass 213 | # if 's' in augmentation: 214 | # pointcloud_indices_0, [corner, icon, room, feature] = augmentScaling(pointcloud_indices_0, [corner, icon, room, feature], randomScale=0) 215 | # pass 216 | # if 'f' in augmentation: 217 | # pointcloud_indices_0, [corner, icon, room, feature] = augmentFlipping(pointcloud_indices_0, [corner, icon, room, feature]) 218 | # pass 219 | # if 'd' in augmentation: 220 | # pointcloud, pointcloud_indices_0 = augmentDropping(pointcloud, pointcloud_indices_0, changeIndices=True) 221 | # pass 222 | # if 'p' in augmentation: 223 | # pointcloud, pointcloud_indices_0 = augmentDropping(pointcloud, pointcloud_indices_0, changeIndices=False) 224 | # pass 225 | 226 | # pointcloud_inp[imageIndex] = pointcloud 227 | # pointcloud_indices_inp[imageIndex] = getCoarseIndicesMaps(pointcloud_indices_0, WIDTH, HEIGHT, 0) 228 | # corner_gt[imageIndex] = corner 229 | # icon_gt[imageIndex] = icon 230 | # room_gt[imageIndex] = room 231 | # feature_inp[imageIndex] = feature 232 | 233 | 234 | newHeatmaps = [heatmapBatch[imageIndex] for heatmapBatch in heatmapBatches] 235 | if 'w' in augmentation: 236 | pointcloud_indices_0_inp[imageIndex], newHeatmaps = augmentWarping(pointcloud_indices_0_inp[imageIndex], newHeatmaps, gridStride=32, randomScale=4) 237 | pass 238 | if 's' in augmentation: 239 | pointcloud_inp[imageIndex], pointcloud_indices_0_inp[imageIndex], newHeatmaps = augmentScaling(pointcloud_inp[imageIndex], pointcloud_indices_0_inp[imageIndex], newHeatmaps) 240 | pass 241 | if 'f' in augmentation: 242 | pointcloud_inp[imageIndex], pointcloud_indices_0_inp[imageIndex], newHeatmaps = augmentFlipping(pointcloud_inp[imageIndex], pointcloud_indices_0_inp[imageIndex], newHeatmaps) 243 | pass 244 | if 'd' in augmentation: 245 | pointcloud_inp[imageIndex], pointcloud_indices_0_inp[imageIndex] = augmentDropping(pointcloud_inp[imageIndex], pointcloud_indices_0_inp[imageIndex], changeIndices=True) 246 | pass 247 | if 'p' in augmentation: 248 | pointcloud_inp[imageIndex], pointcloud_indices_0_inp[imageIndex] = augmentDropping(pointcloud_inp[imageIndex], pointcloud_indices_0_inp[imageIndex], changeIndices=False) 249 | pass 250 | 251 | #print(pointcloud_indices_0_inp[imageIndex].shape, pointcloud_indices_inp[imageIndex].shape) 252 | pointcloud_indices_inp[imageIndex] = getCoarseIndicesMaps(pointcloud_indices_0_inp[imageIndex], WIDTH, HEIGHT, 0) 253 | for heatmapIndex, newHeatmap in enumerate(newHeatmaps): 254 | newHeatmapBatches[heatmapIndex].append(newHeatmap) 255 | continue 256 | continue 257 | newHeatmapBatches = [tf.array(newHeatmapBatch) for newHeatmapBatch in newHeatmapBatches] 258 | pointcloud_inp = tf.concatenate([pointcloud_inp, tf.ones((FETCH_BATCH_SIZE, NUM_POINTS, 1))], axis=2) 259 | #print(pointcloud_itf.shape) 260 | #writePointCloud('test/pointcloud.ply', pointcloud_inp[0, :, :6]) 261 | #exit(1) 262 | 263 | if numPoints < pointcloud_itf.shape[1]: 264 | sampledInds = tf.range(pointcloud_itf.shape[1]) 265 | tf.random.shuffle(sampledInds) 266 | sampledInds = sampledInds[:numPoints] 267 | pointcloud_inp = pointcloud_inp[:, sampledInds] 268 | pointcloud_indices_inp = pointcloud_indices_inp[:, :, sampledInds] 269 | pass 270 | 271 | if numInputChannels == 4: 272 | pointcloud_inp = tf.concatenate([pointcloud_inp[:, :, :3], pointcloud_inp[:, :, 6:]], axis=2) 273 | pass 274 | 275 | 276 | return pointcloud_inp, pointcloud_indices_inp, newHeatmapBatches 277 | -------------------------------------------------------------------------------- /checkpoint/floornet_hybrid1_branch0123_wsf/checkpoint: -------------------------------------------------------------------------------- 1 | model_checkpoint_path: "checkpoint.ckpt" 2 | all_model_checkpoint_paths: "checkpoint.ckpt" 3 | -------------------------------------------------------------------------------- /checkpoint/floornet_hybrid1_branch0123_wsf/checkpoint.ckpt.data-00000-of-00001: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/art-programmer/FloorNet/93db5bdf12f8b60c9d788aa8e124fbe852e50b4a/checkpoint/floornet_hybrid1_branch0123_wsf/checkpoint.ckpt.data-00000-of-00001 -------------------------------------------------------------------------------- /checkpoint/floornet_hybrid1_branch0123_wsf/checkpoint.ckpt.index: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/art-programmer/FloorNet/93db5bdf12f8b60c9d788aa8e124fbe852e50b4a/checkpoint/floornet_hybrid1_branch0123_wsf/checkpoint.ckpt.index -------------------------------------------------------------------------------- /checkpoint/floornet_hybrid1_branch0123_wsf/checkpoint.ckpt.meta: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/art-programmer/FloorNet/93db5bdf12f8b60c9d788aa8e124fbe852e50b4a/checkpoint/floornet_hybrid1_branch0123_wsf/checkpoint.ckpt.meta -------------------------------------------------------------------------------- /evaluate.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import cv2 3 | from QP import reconstructFloorplan, findMatches 4 | from RecordReader import * 5 | from train import * 6 | 7 | # Disable 8 | def blockPrint(): 9 | return 10 | sys.stdout = open(os.devnull, 'w') 11 | 12 | # Restore 13 | def enablePrint(): 14 | return 15 | sys.stdout = sys.__stdout__ 16 | 17 | def evaluate(options): 18 | if not os.path.exists(options.test_dir): 19 | os.system("mkdir -p %s"%options.test_dir) 20 | pass 21 | if os.path.exists(options.test_dir + '/dummy'): 22 | #os.rmdir(options.test_dir + '/dummy') 23 | pass 24 | else: 25 | os.mkdir(options.test_dir + '/dummy') 26 | pass 27 | 28 | if options.useCache == 2 and os.path.exists(options.test_dir + '/dummy/gt_dict.npy') and os.path.exists(options.test_dir + '/dummy/pred_dict.npy'): 29 | return 30 | 31 | if options.useCache == 1 and os.path.exists(options.test_dir + '/dummy/gt_dict.npy') and os.path.exists(options.test_dir + '/dummy/pred_dict.npy'): 32 | evaluateBatch(options) 33 | return 34 | 35 | if not os.path.exists(options.test_dir): 36 | os.system("mkdir -p %s"%options.test_dir) 37 | pass 38 | 39 | #print(options.checkpoint_dir) 40 | tf.reset_default_graph() 41 | 42 | filenames = [] 43 | if '0' in options.dataset: 44 | filenames.append('data/Syn_val.tfrecords') 45 | if '1' in options.dataset: 46 | filenames.append('data/Tango_val.tfrecords') 47 | pass 48 | if '2' in options.dataset: 49 | filenames.append('data/ScanNet_val.tfrecords') 50 | pass 51 | if '3' in options.dataset: 52 | filenames.append('data/Matterport_val.tfrecords') 53 | pass 54 | if '4' in options.dataset: 55 | filenames.append('data/SUNCG_val.tfrecords') 56 | pass 57 | 58 | dataset = getDatasetVal(filenames, '', '4' in options.branches, options.batchSize) 59 | 60 | 61 | iterator = dataset.make_one_shot_iterator() 62 | input_dict, gt_dict = iterator.get_next() 63 | 64 | 65 | pred_dict, debug_dict = build_graph(options, input_dict) 66 | dataset_flag = input_dict['flags'][0, 0] 67 | flags = input_dict['flags'][:, 1] 68 | loss, loss_list = build_loss(options, pred_dict, gt_dict, dataset_flag, debug_dict, input_dict['flags']) 69 | 70 | var_to_restore = [v for v in tf.global_variables()] 71 | 72 | config = tf.ConfigProto() 73 | config.gpu_options.allow_growth=True 74 | config.allow_soft_placement=True 75 | #config.log_device_placement=True 76 | 77 | statisticsSum = {k: [0.0, 0.0, 0.0] for k in ['wall', 'door', 'icon', 'room']} 78 | 79 | numbers = {} 80 | 81 | with tf.Session(config=config) as sess: 82 | sess.run(tf.global_variables_initializer()) 83 | tflearn.is_training(False) 84 | #var_to_restore = [v for v in var_to_restore if 'pred_room' not in v.name] 85 | var_to_restore = [v for v in var_to_restore if 'is_training' not in v.name] 86 | loader = tf.train.Saver(var_to_restore) 87 | if options.startIteration <= 0: 88 | loader.restore(sess,"%s/checkpoint.ckpt"%(options.checkpoint_dir)) 89 | else: 90 | loader.restore(sess,"%s/checkpoint_%d.ckpt"%(options.checkpoint_dir, options.startIteration)) 91 | pass 92 | 93 | #if tf.train.checkpoint_exists("%s/%s.ckpt"%(dumpdir,keyname)): 94 | #saver.restore(sess,"%s/%s.ckpt"%(dumpdir,keyname)) 95 | #pass 96 | 97 | MOVING_AVERAGE_DECAY = 1 98 | losses = [0., 0., 0.] 99 | acc = [1e-4, 1e-4, 1e-4] 100 | 101 | cornerCounters = {} 102 | for cornerType in CORNER_RANGES.keys(): 103 | cornerCounters[cornerType] = np.zeros(3) 104 | pass 105 | 106 | globalCornerCounter = np.zeros(3) 107 | iconCounter = np.zeros(2) 108 | roomCounter = np.zeros(2) 109 | 110 | numImages = 0 111 | try: 112 | for iteration in xrange(options.numTestingImages): 113 | total_loss, losses, dataset, image_flags, gt, pred, debug, inp = sess.run([loss, loss_list, dataset_flag, flags, gt_dict, pred_dict, debug_dict, input_dict]) 114 | 115 | for lossIndex, value in enumerate(losses): 116 | losses[lossIndex] = losses[lossIndex] * MOVING_AVERAGE_DECAY + value 117 | acc[lossIndex] = acc[lossIndex] * MOVING_AVERAGE_DECAY + 1 118 | continue 119 | print('testing', losses[0] / acc[0], losses[1] / acc[1], losses[2] / acc[2]) 120 | 121 | gt = {'corner': gt['corner'], 'corner_values': gt['corner_values'], 'icon': gt['icon'], 'room': gt['room'], 'density': debug['x0_topdown'][:, :, :, -1], 'image_path': inp['image_path'], 'num_corners': gt['num_corners'], 'image_flags': image_flags} 122 | if iteration == 0: 123 | gtAll = gt 124 | predAll = pred 125 | else: 126 | for k, v in gt.iteritems(): 127 | gtAll[k] = np.concatenate([gtAll[k], v], axis=0) 128 | continue 129 | for k, v in pred.iteritems(): 130 | predAll[k] = np.concatenate([predAll[k], v], axis=0) 131 | continue 132 | pass 133 | continue 134 | except tf.errors.OutOfRangeError: 135 | print('Finish testing') 136 | pass 137 | 138 | pass 139 | 140 | if options.useCache != -1: 141 | np.save(options.test_dir + '/dummy/gt_dict.npy', gtAll) 142 | np.save(options.test_dir + '/dummy/pred_dict.npy', predAll) 143 | pass 144 | if options.useCache == -2: 145 | return 146 | 147 | evaluateBatch(options, gtAll, predAll) 148 | return 149 | 150 | def evaluateBatch(options, gt_dict=None, pred_dict=None): 151 | datasetFlag = 1 152 | if options.useCache != -1: 153 | if options.loss != '5': 154 | gt_dict = np.load(options.test_dir + '/dummy/gt_dict.npy')[()] 155 | pred_dict = np.load(options.test_dir + '/dummy/pred_dict.npy')[()] 156 | else: 157 | gt_dict = np.load(options.test_dir.replace('loss5', 'loss0') + '/dummy/gt_dict.npy')[()] 158 | pred_wc = np.load(options.test_dir.replace('loss5', 'loss0') + '/dummy/pred_dict.npy')[()]['corner'][:, :, :, :NUM_WALL_CORNERS] 159 | pred_oc = np.load(options.test_dir.replace('loss5', 'loss1') + '/dummy/pred_dict.npy')[()]['corner'][:, :, :, NUM_WALL_CORNERS:NUM_WALL_CORNERS + 4] 160 | pred_ic = np.load(options.test_dir.replace('loss5', 'loss2').replace('hybrid14', 'hybrid1') + '/dummy/pred_dict.npy')[()]['corner'][:, :, :, NUM_WALL_CORNERS + 4:NUM_WALL_CORNERS + 8] 161 | pred_icon = np.load(options.test_dir.replace('loss5', 'loss3').replace('hybrid14', 'hybrid1') + '/dummy/pred_dict.npy')[()]['icon'] 162 | pred_room = np.load(options.test_dir.replace('loss5', 'loss4').replace('hybrid14', 'hybrid1') + '/dummy/pred_dict.npy')[()]['room'] 163 | pred_dict = {'corner': np.concatenate([pred_wc, pred_oc, pred_ic], axis=-1), 'icon': pred_icon, 'room': pred_room} 164 | pass 165 | 166 | if options.separateIconLoss: 167 | pred_icon_separate = softmax(np.load(options.test_dir.replace('wsf', 'wsf_loss3') + '/dummy/pred_dict.npy')[()]['icon']) 168 | pass 169 | #pred_dict['icon'] = np.load(options.test_dir.replace('wsf', 'wsf_loss3').replace('hybrid1', 'hybrid14').replace('dataset_1', '') + '/dummy/pred_dict.npy')[()]['icon'] 170 | #pred_dict['corner'][:, :, :, NUM_WALL_CORNERS + 4:NUM_WALL_CORNERS + 8] = np.load(options.test_dir.replace('wsf', 'wsf_loss2') + '/dummy/pred_dict.npy')[()]['corner'][:, :, :, NUM_WALL_CORNERS + 4:NUM_WALL_CORNERS + 8] 171 | #pass 172 | 173 | if options.cornerLossType != 'mse': 174 | threshold = np.ones((HEIGHT, WIDTH, 1)) * 0.5 175 | else: 176 | threshold = np.ones((HEIGHT, WIDTH, 1)) * 0.5# HEATMAP_SCALE / 2 177 | pass 178 | 179 | statisticsSum = {k: [0.0, 0.0, 0.0] for k in ['wall', 'door', 'icon', 'room', 'neighbor', 'neighbor_all']} 180 | #print(pred_dict['corner'].max()) 181 | pred_wc = pred_dict['corner'][:, :, :, :NUM_WALL_CORNERS] 182 | pred_oc = pred_dict['corner'][:, :, :, NUM_WALL_CORNERS:NUM_WALL_CORNERS + 4] 183 | pred_ic = pred_dict['corner'][:, :, :, NUM_WALL_CORNERS + 4:NUM_WALL_CORNERS + 8] 184 | 185 | if options.branches != '5': 186 | pred_wc = sigmoid(pred_wc) 187 | pred_oc = sigmoid(pred_oc) 188 | pred_ic = sigmoid(pred_ic) 189 | else: 190 | threshold = np.ones((HEIGHT, WIDTH, 1)) * 0.3 191 | pass 192 | 193 | gt_wc = gt_dict['corner'][:, :, :, :NUM_WALL_CORNERS] 194 | gt_oc = gt_dict['corner'][:, :, :, NUM_WALL_CORNERS:NUM_WALL_CORNERS + 4] 195 | gt_ic = gt_dict['corner'][:, :, :, NUM_WALL_CORNERS + 4:NUM_WALL_CORNERS + 8] 196 | 197 | names = [] 198 | 199 | for batchIndex in xrange(gt_dict['corner'].shape[0]): 200 | #if batchIndex == 0: 201 | #continue 202 | 203 | #if options.branches == '4' and gt_dict['image_flags'][batchIndex] == 0: 204 | if options.evaluateImage and gt_dict['image_flags'][batchIndex] == 0: 205 | continue 206 | 207 | density = np.minimum(gt_dict['density'][batchIndex] * 255, 255).astype(np.uint8) 208 | density = np.stack([density, density, density], axis=2) 209 | 210 | pred_icon = softmax(pred_dict['icon'][batchIndex]) 211 | pred_room = softmax(pred_dict['room'][batchIndex]) 212 | if options.separateIconLoss: 213 | pred_icon[:, :, :-2] = pred_icon_separate[batchIndex][:, :, :-2] 214 | #pred_icon = pred_icon_separate[batchIndex] 215 | pass 216 | 217 | if False: 218 | #print('batch index', batchIndex) 219 | cv2.imwrite(options.test_dir + '/' + str(batchIndex) + '_density.png', density) 220 | #print('heatmap max value', pred_wc[batchIndex].max()) 221 | 222 | if datasetFlag in [0, 1, 4]: 223 | cornerImage = drawSegmentationImage(np.concatenate([threshold, pred_wc[batchIndex]], axis=2), blackIndex=0) 224 | cornerImage[cornerImage == 0] = density[cornerImage == 0] 225 | cv2.imwrite(options.test_dir + '/' + str(batchIndex) + '_corner_pred.png', cornerImage) 226 | 227 | cornerImage = drawSegmentationImage(np.concatenate([threshold, gt_wc[batchIndex]], axis=2), blackIndex=0) 228 | cornerImage[cornerImage == 0] = density[cornerImage == 0] 229 | cv2.imwrite(options.test_dir + '/' + str(batchIndex) + '_corner_gt.png', cornerImage) 230 | pass 231 | 232 | 233 | if False: 234 | corner_heat = np.max(pred_wc[batchIndex], axis=-1) 235 | #print('corner_shape', corner_heat.shape) 236 | cmap = plt.get_cmap('jet') 237 | corner_rgba_img = cmap(corner_heat) 238 | corner_rgb_img = np.delete(corner_rgba_img, 3, 2) 239 | #print('rgb_out', corner_rgb_img.shape, corner_rgb_img.max(), corner_rgb_img.min()) 240 | corner_rgb_img = (corner_rgb_img * 255).round().astype('uint8') 241 | #print('rgb_out', corner_rgb_img.shape, corner_rgb_img.max()) 242 | cv2.imwrite(options.test_dir + '/' + str(batchIndex) + '_corner_heatmap.png', corner_rgb_img) 243 | pass 244 | 245 | if datasetFlag in [1, 4]: 246 | cornerImage = drawSegmentationImage(np.concatenate([threshold, pred_oc[batchIndex]], axis=2), blackIndex=0) 247 | cornerImage[cornerImage == 0] = density[cornerImage == 0] 248 | cv2.imwrite(options.test_dir + '/' + str(batchIndex) + '_opening_corner_pred.png', cornerImage) 249 | 250 | cornerImage = drawSegmentationImage(np.concatenate([threshold, pred_ic[batchIndex]], axis=2), blackIndex=0) 251 | cornerImage[cornerImage == 0] = density[cornerImage == 0] 252 | cv2.imwrite(options.test_dir + '/' + str(batchIndex) + '_icon_corner_pred.png', cornerImage) 253 | 254 | 255 | cornerImage = drawSegmentationImage(np.concatenate([threshold, gt_oc[batchIndex]], axis=2), blackIndex=0) 256 | cornerImage[cornerImage == 0] = density[cornerImage == 0] 257 | cv2.imwrite(options.test_dir + '/' + str(batchIndex) + '_opening_corner_gt.png', cornerImage) 258 | 259 | cornerImage = drawSegmentationImage(np.concatenate([threshold, gt_ic[batchIndex]], axis=2), blackIndex=0) 260 | cornerImage[cornerImage == 0] = density[cornerImage == 0] 261 | cv2.imwrite(options.test_dir + '/' + str(batchIndex) + '_icon_corner_gt.png', cornerImage) 262 | pass 263 | 264 | 265 | if datasetFlag in [1, 2, 3, 4]: 266 | icon_density = drawSegmentationImage(gt_dict['icon'][batchIndex], blackIndex=0) 267 | icon_density[icon_density == 0] = density[icon_density == 0] 268 | cv2.imwrite(options.test_dir + '/' + str(batchIndex) + '_icon_gt.png', icon_density) 269 | 270 | icon_density = drawSegmentationImage(pred_dict['icon'][batchIndex], blackIndex=0) 271 | icon_density[icon_density == 0] = density[icon_density == 0] 272 | cv2.imwrite(options.test_dir + '/' + str(batchIndex) + '_icon_pred.png', icon_density) 273 | pass 274 | 275 | if datasetFlag in [1, 3, 4]: 276 | room_density = drawSegmentationImage(gt_dict['room'][batchIndex], blackIndex=0) 277 | room_density[room_density == 0] = density[room_density == 0] 278 | cv2.imwrite(options.test_dir + '/' + str(batchIndex) + '_room_gt.png', room_density) 279 | 280 | room_density = drawSegmentationImage(pred_dict['room'][batchIndex], blackIndex=0) 281 | room_density[room_density == 0] = density[room_density == 0] 282 | cv2.imwrite(options.test_dir + '/' + str(batchIndex) + '_room_pred.png', room_density) 283 | pass 284 | 285 | 286 | if batchIndex == 0 and False: 287 | for c in xrange(22): 288 | cv2.imwrite(options.test_dir + '/mask_' + str(c) + '.png', cv2.dilate(drawMaskImage(corner_segmentation[batchIndex] == c), np.ones((3, 3)), 3)) 289 | continue 290 | continue 291 | 292 | 293 | if batchIndex < options.visualizeReconstruction or True: 294 | if options.debug >= 0 and batchIndex != options.debug: 295 | continue 296 | names.append((batchIndex, gt_dict['image_path'][batchIndex])) 297 | print(batchIndex, 'start reconstruction', gt_dict['image_path'][batchIndex]) 298 | if True: 299 | if options.debug == -1: 300 | blockPrint() 301 | pass 302 | 303 | # gtHeatmaps = gt_dict['corner'][batchIndex] 304 | #result_gt = reconstructFloorplan(gtHeatmaps[:, :, :NUM_WALL_CORNERS], gtHeatmaps[:, :, NUM_WALL_CORNERS:NUM_WALL_CORNERS + 4], gtHeatmaps[:, :, NUM_WALL_CORNERS + 4:NUM_WALL_CORNERS + 8], segmentation2Heatmaps(gt_dict['icon'][batchIndex], NUM_ICONS), segmentation2Heatmaps(gt_dict['room'][batchIndex], NUM_ROOMS), density[:, :, 0], gt=True) 305 | orientationCorners = getOrientationCorners(gt_dict['corner_values'][batchIndex][:gt_dict['num_corners'][batchIndex]]) 306 | result_gt = reconstructFloorplan(orientationCorners[:NUM_WALL_CORNERS], orientationCorners[NUM_WALL_CORNERS:NUM_WALL_CORNERS + 4], orientationCorners[NUM_WALL_CORNERS + 4:NUM_WALL_CORNERS + 8], segmentation2Heatmaps(gt_dict['icon'][batchIndex], NUM_ICONS), segmentation2Heatmaps(gt_dict['room'][batchIndex], NUM_ROOMS), density[:, :, 0], gt=True) 307 | 308 | #if batchIndex == 1: 309 | #exit(1) 310 | 311 | 312 | #pred_debug_dir = options.test_dir + '/' + str(batchIndex) + '_debug' 313 | pred_debug_dir = options.test_dir 314 | try: 315 | os.mkdir(pred_debug_dir) 316 | pass 317 | except OSError as e: 318 | pass 319 | 320 | result_pred = reconstructFloorplan(pred_wc[batchIndex], pred_oc[batchIndex], pred_ic[batchIndex], pred_icon, pred_room, density[:, :, 0], gt_dict=result_gt, gt=False, debug_prefix=pred_debug_dir) 321 | 322 | if True: 323 | try: 324 | newWidth = newHeight = 1000 325 | resizeResult(result_gt, newWidth, newHeight, WIDTH, HEIGHT) 326 | resultImageGT = drawResultImageFinal(newWidth, newHeight, result_gt) 327 | #cv2.imwrite(options.test_dir + '/' + str(batchIndex) + '_result_gt.png', resultImageGT) 328 | cv2.imwrite(options.test_dir + '/' + gt_dict['image_path'][batchIndex] + '_gt.png', resultImageGT) 329 | 330 | resizeResult(result_pred, newWidth, newHeight, WIDTH, HEIGHT) 331 | resultImagePred = drawResultImageFinal(newWidth, newHeight, result_pred) 332 | cv2.imwrite(options.test_dir + '/' + gt_dict['image_path'][batchIndex] + '_pred.png', resultImagePred) 333 | except: 334 | continue 335 | continue 336 | 337 | if 'wall' not in result_pred or 'wall' not in result_gt: 338 | print('invalid result') 339 | continue 340 | 341 | statistics = findMatches(result_pred, result_gt, distanceThreshold=10) 342 | 343 | if options.drawFinal: 344 | newWidth = newHeight = 1000 345 | resizeResult(result_gt, newWidth, newHeight, WIDTH, HEIGHT) 346 | resultImageGT = drawResultImageFinal(newWidth, newHeight, result_gt) 347 | cv2.imwrite(options.test_dir + '/' + str(batchIndex) + '_result_gt.png', resultImageGT) 348 | 349 | resizeResult(result_pred, newWidth, newHeight, WIDTH, HEIGHT) 350 | resultImagePred = drawResultImageFinal(newWidth, newHeight, result_pred) 351 | cv2.imwrite(options.test_dir + '/' + str(batchIndex) + '_result_pred.png', resultImagePred) 352 | 353 | writeRepresentation('popup/data/floorplan_' + str(batchIndex) + '_gt.txt', newWidth, newHeight, result_gt) 354 | writeRepresentation('popup/data/floorplan_' + str(batchIndex) + '_pred.txt', newWidth, newHeight, result_pred) 355 | cv2.imwrite('popup/data/floorplan_' + str(batchIndex) + '_gt.png', resultImageGT) 356 | cv2.imwrite('popup/data/floorplan_' + str(batchIndex) + '_pred.png', resultImagePred) 357 | exit(1) 358 | else: 359 | resultImage, iconImage = drawResultImage(WIDTH, HEIGHT, result_gt) 360 | cv2.imwrite(options.test_dir + '/' + str(batchIndex) + '_reconstruction_wall_gt.png', resultImage) 361 | iconImage[iconImage == 0] = density[iconImage == 0] 362 | cv2.imwrite(options.test_dir + '/' + str(batchIndex) + '_reconstruction_icon_gt.png', iconImage) 363 | resultImage, iconImage = drawResultImage(WIDTH, HEIGHT, result_pred) 364 | cv2.imwrite(options.test_dir + '/' + str(batchIndex) + '_reconstruction_wall_pred.png', resultImage) 365 | iconImage[iconImage == 0] = density[iconImage == 0] 366 | cv2.imwrite(options.test_dir + '/' + str(batchIndex) + '_reconstruction_icon_pred.png', iconImage) 367 | pass 368 | 369 | 370 | if options.debug == -1: 371 | enablePrint() 372 | pass 373 | if len(result_pred) == 0: 374 | continue 375 | 376 | # print(result_pred) 377 | # print(result_pred['door']) 378 | # print('gt') 379 | # print(result_gt) 380 | # print(result_gt['door']) 381 | # exit(1) 382 | 383 | print('find predictions among ground-truths') 384 | #print(result_pred['wall'][2]) 385 | #statistics = findMatches(result_pred, result_gt, distanceThreshold=10) 386 | #statistics = findMatches(result_gt, result_pred, distanceThreshold=10) 387 | 388 | #print('find ground-truths among predictions') 389 | #statistics = findMatches(result_gt, result_pred, distanceThreshold=10) 390 | #print(statistics) 391 | print('statistics', [(k, float(v[0]) / max(v[1], 1), float(v[0]) / max(v[2], 1)) for k, v in statistics.iteritems()]) 392 | #print('topology statistics', [(k, float(v[0]) / max(v[1], 1), float(v[0]) / max(v[2], 1)) for k, v in topologyStatistics.iteritems()]) 393 | print('finish reconstruction', gt_dict['image_path'][batchIndex]) 394 | for k, v in statistics.iteritems(): 395 | if k in statisticsSum: 396 | for c in xrange(3): 397 | statisticsSum[k][c] += v[c] 398 | continue 399 | else: 400 | print(k, 'not in', statisticsSum) 401 | continue 402 | if options.debug >= 0: 403 | exit(1) 404 | pass 405 | pass 406 | 407 | # except Exception as e: 408 | # #traceback.print_tb(e) 409 | # print('exception-----------: ', e) 410 | # #raise e 411 | continue 412 | print(names) 413 | print('final statistics', [(k, float(v[0]) / max(v[1], 1), float(v[0]) / max(v[2], 1)) for k, v in statisticsSum.iteritems()]) 414 | np.save(options.test_dir + '/numbers.npy', statisticsSum) 415 | #print(statisticsSum) 416 | return 417 | -------------------------------------------------------------------------------- /floorplan_utils.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from skimage import measure 3 | import cv2 4 | import copy 5 | from utils import * 6 | 7 | NUM_WALL_CORNERS = 13 8 | NUM_CORNERS = 21 9 | CORNER_RANGES = {'wall': (0, 13), 'opening': (13, 17), 'icon': (17, 21)} 10 | MAX_NUM_CORNERS = 300 11 | 12 | NUM_FINAL_ICONS = 10 13 | NUM_FINAL_ROOMS = 15 14 | NUM_ICONS = 13 15 | NUM_ROOMS = 16 16 | HEIGHT=256 17 | WIDTH=256 18 | NUM_POINTS = 50000 19 | 20 | NUM_INPUT_CHANNELS = 7 21 | NUM_CHANNELS = [7, 64, 64, 64, 128, 256] 22 | SIZES = [WIDTH, WIDTH // 2, WIDTH // 4, WIDTH // 8, WIDTH // 16, WIDTH // 32] 23 | 24 | 25 | 26 | POINT_ORIENTATIONS = [[(2, ), (3, ), (0, ), (1, )], [(0, 3), (0, 1), (1, 2), (2, 3)], [(1, 2, 3), (0, 2, 3), (0, 1, 3), (0, 1, 2)], [(0, 1, 2, 3)]] 27 | 28 | 29 | def getOrientationRanges(width, height): 30 | orientationRanges = [[width, 0, 0, 0], [width, height, width, 0], [width, height, 0, height], [0, height, 0, 0]] 31 | return orientationRanges 32 | 33 | def getIconNames(): 34 | iconNames = [] 35 | iconLabelMap = getIconLabelMap() 36 | for iconName, _ in iconLabelMap.iteritems(): 37 | iconNames.append(iconName) 38 | continue 39 | return iconNames 40 | 41 | def getRoomLabelMap(): 42 | labelMap = {} 43 | labelMap['living_room'] = 1 44 | labelMap['kitchen'] = 2 45 | labelMap['bedroom'] = 3 46 | labelMap['bathroom'] = 4 47 | labelMap['restroom'] = 4 48 | labelMap['office'] = 3 49 | labelMap['closet'] = 6 50 | labelMap['balcony'] = 7 51 | labelMap['corridor'] = 8 52 | labelMap['dining_room'] = 9 53 | labelMap['laundry_room'] = 10 54 | labelMap['garage'] = 11 55 | labelMap['recreation_room'] = 12 56 | labelMap['stairs'] = 13 57 | labelMap['other'] = 14 58 | labelMap['wall'] = 15 59 | return labelMap 60 | 61 | def getLabelRoomMap(): 62 | labelMap = {} 63 | labelMap[1] = 'living room' 64 | labelMap[2] = 'kitchen' 65 | labelMap[3] = 'bedroom' 66 | labelMap[4] = 'bathroom' 67 | labelMap[6] = 'closet' 68 | labelMap[7] = 'balcony' 69 | labelMap[8] = 'corridor' 70 | labelMap[9] = 'dining room' 71 | return labelMap 72 | 73 | def getIconLabelMap(): 74 | labelMap = {} 75 | labelMap['cooking_counter'] = 1 76 | labelMap['bathtub'] = 2 77 | labelMap['toilet'] = 3 78 | labelMap['washing_basin'] = 4 79 | labelMap['sofa'] = 5 80 | labelMap['cabinet'] = 6 81 | labelMap['bed'] = 7 82 | labelMap['table'] = 8 83 | labelMap['desk'] = 8 84 | labelMap['refrigerator'] = 9 85 | labelMap['TV'] = 0 86 | labelMap['entrance'] = 0 87 | labelMap['chair'] = 0 88 | labelMap['door'] = 11 89 | labelMap['window'] = 12 90 | return labelMap 91 | 92 | def getLabelIconMap(): 93 | labelMap = {} 94 | labelMap[1] = 'cooking_counter' 95 | labelMap[2] = 'bathtub' 96 | labelMap[3] = 'toilet' 97 | labelMap[4] = 'washing_basin' 98 | labelMap[5] = 'sofa' 99 | labelMap[6] = 'cabinet' 100 | labelMap[7] = 'bed' 101 | labelMap[8] = 'table' 102 | labelMap[9] = 'refrigerator' 103 | return labelMap 104 | 105 | def getLabelMapNYU40(): 106 | labelMap = {} 107 | labelMap[1] = 'wall' 108 | labelMap[2] = 'floor' 109 | labelMap[3] = 'cabinet' 110 | labelMap[4] = 'bed' 111 | labelMap[5] = 'chair' 112 | labelMap[6] = 'sofa' 113 | labelMap[7] = 'table' 114 | labelMap[8] = 'door' 115 | labelMap[9] = 'window' 116 | labelMap[10] = 'bookshelf' 117 | labelMap[11] = 'picture' 118 | labelMap[12] = 'cooking_counter' 119 | labelMap[13] = 'blinds' 120 | labelMap[14] = 'desk' 121 | labelMap[15] = 'shelf' 122 | labelMap[16] = 'curtain' 123 | labelMap[17] = 'dresser' 124 | labelMap[18] = 'pillow' 125 | labelMap[19] = 'mirror' 126 | labelMap[20] = 'entrance' #mat 127 | labelMap[21] = 'clothes' 128 | labelMap[22] = 'ceiling' 129 | labelMap[23] = 'book' 130 | labelMap[24] = 'refrigerator' 131 | labelMap[25] = 'TV' 132 | labelMap[26] = 'paper' 133 | labelMap[27] = 'towel' 134 | labelMap[28] = 'shower_curtain' 135 | labelMap[29] = 'box' 136 | labelMap[30] = 'whiteboard' 137 | labelMap[31] = 'person' 138 | labelMap[32] = 'nightstand' 139 | labelMap[33] = 'toilet' 140 | labelMap[34] = 'washing_basin' 141 | labelMap[35] = 'lamp' 142 | labelMap[36] = 'bathtub' 143 | labelMap[37] = 'bag' 144 | labelMap[38] = 'otherprop' 145 | labelMap[39] = 'otherstructure' 146 | labelMap[40] = 'unannotated' 147 | return labelMap 148 | 149 | def getNYUScanNetMap(): 150 | labelMap = np.zeros(41, dtype=np.int32) 151 | labelMap[1] = 1 152 | labelMap[2] = 2 153 | labelMap[3] = 19 154 | labelMap[4] = 6 155 | labelMap[5] = 3 156 | labelMap[6] = 8 157 | labelMap[7] = 4 158 | labelMap[8] = 14 159 | labelMap[9] = 15 160 | labelMap[10] = 7 161 | labelMap[11] = 18 162 | labelMap[12] = 13 163 | labelMap[13] = 12 #20 Blinds 164 | labelMap[14] = 5 165 | labelMap[15] = 7 166 | labelMap[16] = 12 167 | labelMap[17] = 19 168 | labelMap[18] = 20 169 | labelMap[19] = 20 170 | labelMap[20] = 20 171 | labelMap[21] = 20 172 | labelMap[22] = 1 173 | labelMap[23] = 20 174 | labelMap[24] = 17 175 | labelMap[25] = 20 176 | labelMap[26] = 20 177 | labelMap[27] = 20 178 | labelMap[28] = 16 179 | labelMap[29] = 20 180 | labelMap[30] = 20 181 | labelMap[31] = 20 182 | labelMap[32] = 20 183 | labelMap[33] = 11 184 | labelMap[34] = 9 185 | labelMap[35] = 20 186 | labelMap[36] = 10 187 | labelMap[37] = 20 188 | labelMap[38] = 20 189 | labelMap[39] = 20 190 | labelMap[40] = 0 191 | return labelMap 192 | 193 | def getMatterportClassMap(): 194 | classMap = { 195 | 'a': 1, 196 | 'b': 2, 197 | 'c': 3, 198 | 'd': 4, 199 | 'e': 5, 200 | 'f': 6, 201 | 'g': 7, 202 | 'h': 8, 203 | 'i': 9, 204 | 'j': 10, 205 | 'k': 11, 206 | 'l': 12, 207 | 'm': 13, 208 | 'n': 14, 209 | 'o': 15, 210 | 'p': 16, 211 | 'r': 17, 212 | 's': 18, 213 | 't': 19, 214 | 'u': 20, 215 | 'v': 21, 216 | 'w': 22, 217 | 'x': 23, 218 | 'y': 24, 219 | 'z': 25, 220 | 'B': 26, 221 | 'C': 27, 222 | 'D': 28, 223 | 'S': 29, 224 | 'Z': 30 225 | } 226 | return classMap 227 | 228 | def calcLineDim(points, line): 229 | point_1 = points[line[0]] 230 | point_2 = points[line[1]] 231 | if abs(point_2[0] - point_1[0]) > abs(point_2[1] - point_1[1]): 232 | lineDim = 0 233 | else: 234 | lineDim = 1 235 | return lineDim 236 | 237 | def calcLineDirection(line): 238 | return int(abs(line[0][0] - line[1][0]) < abs(line[0][1] - line[1][1])) 239 | 240 | def calcLineDirectionPoints(points, line): 241 | point_1 = points[line[0]] 242 | point_2 = points[line[1]] 243 | if isinstance(point_1[0], tuple): 244 | point_1 = point_1[0] 245 | pass 246 | if isinstance(point_2[0], tuple): 247 | point_2 = point_2[0] 248 | pass 249 | return calcLineDirection((point_1, point_2)) 250 | 251 | def pointDistance(point_1, point_2): 252 | #return np.sqrt(pow(point_1[0] - point_2[0], 2) + pow(point_1[1] - point_2[1], 2)) 253 | return max(abs(point_1[0] - point_2[0]), abs(point_1[1] - point_2[1])) 254 | 255 | def sortLines(lines): 256 | newLines = [] 257 | for line in lines: 258 | direction = calcLineDirection(line) 259 | if line[0][direction] < line[1][direction]: 260 | newLines.append((line[0], line[1])) 261 | else: 262 | newLines.append((line[1], line[0])) 263 | pass 264 | continue 265 | return newLines 266 | 267 | def lineRange(line): 268 | direction = calcLineDirection(line) 269 | fixedValue = (line[0][1 - direction] + line[1][1 - direction]) / 2 270 | minValue = min(line[0][direction], line[1][direction]) 271 | maxValue = max(line[0][direction], line[1][direction]) 272 | return direction, fixedValue, minValue, maxValue 273 | 274 | def findConnections(line_1, line_2, gap): 275 | connection_1 = -1 276 | connection_2 = -1 277 | pointConnected = False 278 | for c_1 in xrange(2): 279 | if pointConnected: 280 | break 281 | for c_2 in xrange(2): 282 | if pointDistance(line_1[c_1], line_2[c_2]) > gap: 283 | continue 284 | 285 | connection_1 = c_1 286 | connection_2 = c_2 287 | connectionPoint = ((line_1[c_1][0] + line_2[c_2][0]) / 2, (line_1[c_1][1] + line_2[c_2][1]) / 2) 288 | pointConnected = True 289 | break 290 | continue 291 | if pointConnected: 292 | return [connection_1, connection_2], connectionPoint 293 | direction_1, fixedValue_1, min_1, max_1 = lineRange(line_1) 294 | direction_2, fixedValue_2, min_2, max_2 = lineRange(line_2) 295 | if direction_1 == direction_2: 296 | return [-1, -1], (0, 0) 297 | 298 | #print(fixedValue_1, min_1, max_1, fixedValue_2, min_2, max_2) 299 | if min(fixedValue_1, max_2) < max(fixedValue_1, min_2) - gap or min(fixedValue_2, max_1) < max(fixedValue_2, min_1) - gap: 300 | return [-1, -1], (0, 0) 301 | 302 | if abs(min_1 - fixedValue_2) <= gap: 303 | return [0, 2], (fixedValue_2, fixedValue_1) 304 | if abs(max_1 - fixedValue_2) <= gap: 305 | return [1, 2], (fixedValue_2, fixedValue_1) 306 | if abs(min_2 - fixedValue_1) <= gap: 307 | return [2, 0], (fixedValue_2, fixedValue_1) 308 | if abs(max_2 - fixedValue_1) <= gap: 309 | return [2, 1], (fixedValue_2, fixedValue_1) 310 | return [2, 2], (fixedValue_2, fixedValue_1) 311 | 312 | def lines2Corners(lines, gap, getSingularCorners=False): 313 | corners = [] 314 | lineConnections = [] 315 | for _ in xrange(len(lines)): 316 | lineConnections.append({}) 317 | continue 318 | 319 | connectionCornerMap = {} 320 | connectionCornerMap[(1, 1)] = 4 321 | connectionCornerMap[(0, 1)] = 5 322 | connectionCornerMap[(0, 0)] = 6 323 | connectionCornerMap[(1, 0)] = 7 324 | connectionCornerMap[(2, 0)] = 8 325 | connectionCornerMap[(1, 2)] = 9 326 | connectionCornerMap[(2, 1)] = 10 327 | connectionCornerMap[(0, 2)] = 11 328 | connectionCornerMap[(2, 2)] = 12 329 | corners = [] 330 | for lineIndex_1, line_1 in enumerate(lines): 331 | for lineIndex_2, line_2 in enumerate(lines): 332 | if lineIndex_2 == lineIndex_1: 333 | continue 334 | connections, connectionPoint = findConnections(line_1, line_2, gap=gap) 335 | if connections[0] == -1 and connections[1] == -1: 336 | continue 337 | if calcLineDirection(line_1) == calcLineDirection(line_2): 338 | print('overlap', line_1, line_2, connections) 339 | exit(1) 340 | pass 341 | if calcLineDirection(line_1) == 1: 342 | continue 343 | 344 | indices = [lineIndex_1, lineIndex_2] 345 | #print(lineIndex_1, lineIndex_2, connections) 346 | for c in xrange(2): 347 | if connections[c] in [0, 1] and connections[c] in lineConnections[indices[c]]: 348 | print('duplicate corner', line_1, line_2, connections) 349 | exit(1) 350 | pass 351 | lineConnections[indices[c]][connections[c]] = True 352 | continue 353 | corners.append((connectionPoint, connectionCornerMap[tuple(connections)])) 354 | continue 355 | continue 356 | 357 | if getSingularCorners: 358 | singularCorners = [] 359 | for lineIndex, connections in enumerate(lineConnections): 360 | if 0 not in connections: 361 | print('single corner', lines[lineIndex], connections) 362 | singularCorners.append((lineIndex, 0)) 363 | pass 364 | if 1 not in connections: 365 | print('single corner', lines[lineIndex], connections) 366 | singularCorners.append((lineIndex, 1)) 367 | pass 368 | continue 369 | return corners, singularCorners 370 | 371 | return corners 372 | 373 | def drawWallMask(walls, width, height, thickness=3, indexed=False): 374 | if indexed: 375 | wallMask = np.full((height, width), -1, dtype=np.int32) 376 | for wallIndex, wall in enumerate(walls): 377 | cv2.line(wallMask, (int(wall[0][0]), int(wall[0][1])), (int(wall[1][0]), int(wall[1][1])), color=wallIndex, thickness=thickness) 378 | continue 379 | else: 380 | wallMask = np.zeros((height, width), dtype=np.int32) 381 | for wall in walls: 382 | cv2.line(wallMask, (int(wall[0][0]), int(wall[0][1])), (int(wall[1][0]), int(wall[1][1])), color=1, thickness=thickness) 383 | continue 384 | wallMask = wallMask.astype(np.bool) 385 | pass 386 | return wallMask 387 | 388 | def mergeLines(line_1, line_2): 389 | direction_1, fixedValue_1, min_1, max_1 = lineRange(line_1) 390 | direction_2, fixedValue_2, min_2, max_2 = lineRange(line_2) 391 | fixedValue = (fixedValue_1 + fixedValue_2) / 2 392 | if direction_1 == 0: 393 | return [[min(min_1, min_2), fixedValue], [max(max_1, max_2), fixedValue]] 394 | else: 395 | return [[fixedValue, min(min_1, min_2)], [fixedValue, max(max_1, max_2)]] 396 | return 397 | 398 | def findIntersection(line_1, line_2): 399 | direction_1, fixedValue_1, min_1, max_1 = lineRange(line_1) 400 | direction_2, fixedValue_2, min_2, max_2 = lineRange(line_2) 401 | if direction_1 == 0: 402 | return (fixedValue_2, fixedValue_1) 403 | else: 404 | return (fixedValue_1, fixedValue_2) 405 | return 406 | 407 | def extendLine(line, point): 408 | direction, fixedValue, min_value, max_value = lineRange(line) 409 | if direction == 0: 410 | return ((min(min_value, point[direction]), fixedValue), (max(max_value, point[direction]), fixedValue)) 411 | else: 412 | return ((fixedValue, min(min_value, point[direction])), (fixedValue, max(max_value, point[direction]))) 413 | return 414 | 415 | def divideWalls(walls): 416 | horizontalWalls = [] 417 | verticalWalls = [] 418 | for wall in walls: 419 | if calcLineDirection(wall) == 0: 420 | horizontalWalls.append(wall) 421 | else: 422 | verticalWalls.append(wall) 423 | pass 424 | continue 425 | return horizontalWalls, verticalWalls 426 | 427 | 428 | def connectWalls(walls, roomSegmentation, gap=3): 429 | width = roomSegmentation.shape[1] 430 | height = roomSegmentation.shape[0] 431 | roomBoundary = np.zeros(roomSegmentation.shape, dtype=np.bool) 432 | for direction in xrange(2): 433 | for shift in [-1, 1]: 434 | roomBoundary = np.logical_or(roomBoundary, roomSegmentation != np.roll(roomSegmentation, shift, axis=direction)) 435 | continue 436 | continue 437 | roomBoundary = roomBoundary.astype(np.uint8) 438 | roomBoundary[0] = roomBoundary[-1] = roomBoundary[:, 0] = roomBoundary[:, -1] = 0 439 | 440 | uncoveredBoundary = roomBoundary.copy() 441 | wallGroups = divideWalls(walls) 442 | wallMasks = [drawWallMask(walls, width, height, indexed=True, thickness=gap * 2) for walls in wallGroups] 443 | 444 | uncoveredBoundary[wallMasks[0] >= 0] = 0 445 | uncoveredBoundary[wallMasks[1] >= 0] = 0 446 | uncoveredBoundary = cv2.dilate(uncoveredBoundary, np.ones((3, 3)), iterations=gap) 447 | components = measure.label(uncoveredBoundary, background=0) 448 | 449 | connectedWalls = [] 450 | 451 | for walls, wallMask in zip(wallGroups, wallMasks): 452 | 453 | newWalls = copy.deepcopy(walls) 454 | invalidWallIndices = [] 455 | for label in xrange(components.min() + 1, components.max() + 1): 456 | mask = components == label 457 | wallIndices = np.unique(wallMask[mask]).tolist() 458 | if -1 in wallIndices: 459 | wallIndices.remove(-1) 460 | pass 461 | if len(wallIndices) != 2: 462 | continue 463 | 464 | wall_1 = newWalls[wallIndices[0]] 465 | wall_2 = newWalls[wallIndices[1]] 466 | 467 | direction_1, fixedValue_1, min_1, max_1 = lineRange(wall_1) 468 | direction_2, fixedValue_2, min_2, max_2 = lineRange(wall_2) 469 | 470 | if direction_1 == direction_2: 471 | if abs(fixedValue_1 - fixedValue_2) < gap: 472 | newWallIndex = len(newWalls) 473 | wallMask[wallMask == wallIndices[0]] = newWallIndex 474 | wallMask[wallMask == wallIndices[1]] = newWallIndex 475 | newWall = mergeLines(wall_1, wall_2) 476 | newWalls.append(newWall) 477 | invalidWallIndices.append(wallIndices[0]) 478 | invalidWallIndices.append(wallIndices[1]) 479 | pass 480 | pass 481 | # else: 482 | # print(wall_1, wall_2) 483 | # ys, xs = mask.nonzero() 484 | # newWall = [[xs.min(), ys.min()], [xs.max(), ys.max()]] 485 | # newWallDirection = calcLineDirection(newWall) 486 | # if newWallDirection != direction_1 and newWall[1][1 - newWallDirection] - newWall[0][1 - newWallDirection] < gap * 2 + 1: 487 | # fixedValue = (newWall[1][1 - newWallDirection] + newWall[0][1 - newWallDirection]) / 2 488 | # newWall[1][1 - newWallDirection] = newWall[0][1 - newWallDirection] = fixedValue 489 | # newWalls.append(newWall) 490 | # pass 491 | # pass 492 | # else: 493 | # assert(False) 494 | # intersectionPoint = findIntersection(wall_1, wall_2) 495 | # newWalls[wallIndices[0]] = extendLine(wall_1, intersectionPoint) 496 | # newWalls[wallIndices[1]] = extendLine(wall_2, intersectionPoint) 497 | # pass 498 | continue 499 | 500 | #print(invalidWallIndices) 501 | invalidWallIndices = sorted(invalidWallIndices, key=lambda x: -x) 502 | for index in invalidWallIndices: 503 | del newWalls[index] 504 | continue 505 | connectedWalls += newWalls 506 | continue 507 | 508 | newWalls = connectedWalls 509 | wallMask = drawWallMask(newWalls, width, height, indexed=True, thickness=gap * 2) 510 | uncoveredBoundary = roomBoundary.copy() 511 | uncoveredBoundary[wallMask >= 0] = 0 512 | uncoveredBoundary = cv2.dilate(uncoveredBoundary, np.ones((3, 3)), iterations=gap) 513 | components = measure.label(uncoveredBoundary, background=0) 514 | 515 | #cv2.imwrite('test/segmentation.png', drawSegmentationImage(components)) 516 | 517 | for label in xrange(components.min() + 1, components.max() + 1): 518 | mask = components == label 519 | #cv2.imwrite('test/mask_' + str(label) + '.png', drawMaskImage(mask)) 520 | wallIndices = np.unique(wallMask[mask]).tolist() 521 | if -1 in wallIndices: 522 | wallIndices.remove(-1) 523 | pass 524 | 525 | lines = [newWalls[index] for index in wallIndices] 526 | #cv2.imwrite('test/mask_' + str(label) + '_segment.png', drawMaskImage(mask)) 527 | #cv2.imwrite('test/mask_' + str(label) + '.png', drawMaskImage(drawWallMask(lines, width, height))) 528 | 529 | horizontalLines, verticalLines = divideWalls(lines) 530 | if len(horizontalLines) > 0 and len(verticalLines) > 0: 531 | continue 532 | #print(label, wallIndices, len(horizontalLines), len(verticalLines)) 533 | for direction, lines in enumerate([horizontalLines, verticalLines]): 534 | if len(lines) < 2: 535 | continue 536 | #wall_1 = lines[0] 537 | #wall_2 = lines[1] 538 | #print(wall_1, wall_2) 539 | #direction_1, fixedValue_1, min_1, max_1 = lineRange(wall_1) 540 | #direction_2, fixedValue_2, min_2, max_2 = lineRange(wall_2) 541 | #values = [line[direction] for line in lines] 542 | 543 | #print(wall_1, wall_2) 544 | ys, xs = mask.nonzero() 545 | newWall = [[xs.min(), ys.min()], [xs.max(), ys.max()]] 546 | newWallDirection = calcLineDirection(newWall) 547 | #print(label, wallIndices, newWallDirection, direction, newWall[1][1 - newWallDirection] - newWall[0][1 - newWallDirection]) 548 | if newWallDirection != direction and newWall[1][1 - newWallDirection] - newWall[0][1 - newWallDirection] <= (gap * 2 + 2) * 2: 549 | fixedValue = (newWall[1][1 - newWallDirection] + newWall[0][1 - newWallDirection]) / 2 550 | newWall[1][1 - newWallDirection] = newWall[0][1 - newWallDirection] = fixedValue 551 | values = [line[0][newWallDirection] for line in lines] + [line[1][newWallDirection] for line in lines] 552 | min_value = min(values) 553 | max_value = max(values) 554 | newWall[0][newWallDirection] = min_value 555 | newWall[1][newWallDirection] = max_value 556 | 557 | newWalls.append(newWall) 558 | #print('new orthogonal wall', newWall) 559 | pass 560 | 561 | continue 562 | continue 563 | 564 | wallMask = drawWallMask(newWalls, width, height, indexed=True, thickness=gap * 2) 565 | uncoveredBoundary = roomBoundary.copy() 566 | uncoveredBoundary[wallMask >= 0] = 0 567 | uncoveredBoundary = cv2.dilate(uncoveredBoundary, np.ones((3, 3)), iterations=gap) 568 | components = measure.label(uncoveredBoundary, background=0) 569 | 570 | for label in xrange(components.min() + 1, components.max() + 1): 571 | mask = components == label 572 | wallIndices = np.unique(wallMask[mask]).tolist() 573 | if -1 in wallIndices: 574 | wallIndices.remove(-1) 575 | pass 576 | if len(wallIndices) != 2: 577 | continue 578 | 579 | wall_1 = newWalls[wallIndices[0]] 580 | wall_2 = newWalls[wallIndices[1]] 581 | 582 | #print(wall_1, wall_2) 583 | 584 | direction_1 = calcLineDirection(wall_1) 585 | direction_2 = calcLineDirection(wall_2) 586 | 587 | if direction_1 != direction_2: 588 | intersectionPoint = findIntersection(wall_1, wall_2) 589 | newWalls[wallIndices[0]] = extendLine(wall_1, intersectionPoint) 590 | newWalls[wallIndices[1]] = extendLine(wall_2, intersectionPoint) 591 | pass 592 | continue 593 | 594 | # try: 595 | # _, singularCorners = lines2Corners(newWalls, gap=gap, getSingularCorners=True) 596 | # for _, singularCorner_1 in enumerate(singularCorners): 597 | # for singularCorner_2 in singularCorners[_ + 1:]: 598 | # wall_1 = newWalls[singularCorner_1[0]] 599 | # wall_2 = newWalls[singularCorner_2[0]] 600 | # corner_1 = wall_1[singularCorner_1[1]] 601 | # corner_2 = wall_2[singularCorner_2[1]] 602 | # if pointDistance(corner_1, corner_2) < (gap * 2 + 1) * 2: 603 | # intersectionPoint = findIntersection(wall_1, wall_2) 604 | # newWalls[singularCorner_1[0]] = extendLine(wall_1, intersectionPoint) 605 | # newWalls[singularCorner_2[0]] = extendLine(wall_2, intersectionPoint) 606 | # pass 607 | # continue 608 | # continue 609 | # except: 610 | # pass 611 | 612 | return newWalls 613 | 614 | def extractLines(lineMask, lengthThreshold=11, widthThreshold=5): 615 | lines = [] 616 | components = measure.label(lineMask, background=0) 617 | for label in xrange(components.min() + 1, components.max() + 1): 618 | mask = components == label 619 | ys, xs = mask.nonzero() 620 | line = [[xs.min(), ys.min()], [xs.max(), ys.max()]] 621 | direction = calcLineDirection(line) 622 | if abs(line[1][1 - direction] - line[0][1 - direction]) > widthThreshold or abs(line[1][direction] - line[0][direction]) < lengthThreshold: 623 | continue 624 | fixedValue = (line[1][1 - direction] + line[0][1 - direction]) / 2 625 | line[1][1 - direction] = line[0][1 - direction] = fixedValue 626 | lines.append(line) 627 | continue 628 | return lines 629 | 630 | 631 | def drawPoints(filename, width, height, points, backgroundImage=None, pointSize=5, pointColor=None): 632 | colorMap = ColorPalette(NUM_CORNERS).getColorMap() 633 | if np.all(np.equal(backgroundImage, None)): 634 | image = np.zeros((height, width, 3), np.uint8) 635 | else: 636 | if backgroundImage.ndim == 2: 637 | image = np.tile(np.expand_dims(backgroundImage, -1), [1, 1, 3]) 638 | else: 639 | image = backgroundImage 640 | pass 641 | pass 642 | no_point_color = pointColor is None 643 | for point in points: 644 | if no_point_color: 645 | pointColor = colorMap[point[2] * 4 + point[3]] 646 | pass 647 | #print('used', pointColor) 648 | #print('color', point[2] , point[3]) 649 | image[max(int(round(point[1])) - pointSize, 0):min(int(round(point[1])) + pointSize, height), max(int(round(point[0])) - pointSize, 0):min(int(round(point[0])) + pointSize, width)] = pointColor 650 | continue 651 | 652 | if filename != '': 653 | cv2.imwrite(filename, image) 654 | return 655 | else: 656 | return image 657 | 658 | def drawPointsSeparately(path, width, height, points, backgroundImage=None, pointSize=5): 659 | if np.all(np.equal(backgroundImage, None)): 660 | image = np.zeros((height, width, 13), np.uint8) 661 | else: 662 | image = np.tile(np.expand_dims(backgroundImage, -1), [1, 1, 13]) 663 | pass 664 | 665 | for point in points: 666 | image[max(int(round(point[1])) - pointSize, 0):min(int(round(point[1])) + pointSize, height), max(int(round(point[0])) - pointSize, 0):min(int(round(point[0])) + pointSize, width), int(point[2] * 4 + point[3])] = 255 667 | continue 668 | for channel in xrange(13): 669 | cv2.imwrite(path + '_' + str(channel) + '.png', image[:, :, channel]) 670 | continue 671 | return 672 | 673 | def drawLineMask(width, height, points, lines, lineWidth = 5, backgroundImage = None): 674 | lineMask = np.zeros((height, width)) 675 | 676 | for lineIndex, line in enumerate(lines): 677 | point_1 = points[line[0]] 678 | point_2 = points[line[1]] 679 | direction = calcLineDirectionPoints(points, line) 680 | 681 | fixedValue = int(round((point_1[1 - direction] + point_2[1 - direction]) / 2)) 682 | minValue = int(min(point_1[direction], point_2[direction])) 683 | maxValue = int(max(point_1[direction], point_2[direction])) 684 | if direction == 0: 685 | lineMask[max(fixedValue - lineWidth, 0):min(fixedValue + lineWidth + 1, height), minValue:maxValue + 1] = 1 686 | else: 687 | lineMask[minValue:maxValue + 1, max(fixedValue - lineWidth, 0):min(fixedValue + lineWidth + 1, width)] = 1 688 | pass 689 | continue 690 | return lineMask 691 | 692 | 693 | 694 | def drawLines(filename, width, height, points, lines, lineLabels = [], backgroundImage = None, lineWidth = 5, lineColor = None): 695 | colorMap = ColorPalette(len(lines)).getColorMap() 696 | if backgroundImage is None: 697 | image = np.ones((height, width, 3), np.uint8) * 0 698 | else: 699 | if backgroundImage.ndim == 2: 700 | image = np.stack([backgroundImage, backgroundImage, backgroundImage], axis=2) 701 | else: 702 | image = backgroundImage 703 | pass 704 | pass 705 | 706 | for lineIndex, line in enumerate(lines): 707 | point_1 = points[line[0]] 708 | point_2 = points[line[1]] 709 | direction = calcLineDirectionPoints(points, line) 710 | 711 | 712 | fixedValue = int(round((point_1[1 - direction] + point_2[1 - direction]) / 2)) 713 | minValue = int(round(min(point_1[direction], point_2[direction]))) 714 | maxValue = int(round(max(point_1[direction], point_2[direction]))) 715 | if len(lineLabels) == 0: 716 | if np.any(lineColor == None): 717 | lineColor = np.random.rand(3) * 255 718 | pass 719 | if direction == 0: 720 | image[max(fixedValue - lineWidth, 0):min(fixedValue + lineWidth + 1, height), minValue:maxValue + 1, :] = lineColor 721 | else: 722 | image[minValue:maxValue + 1, max(fixedValue - lineWidth, 0):min(fixedValue + lineWidth + 1, width), :] = lineColor 723 | else: 724 | labels = lineLabels[lineIndex] 725 | isExterior = False 726 | if direction == 0: 727 | for c in xrange(3): 728 | image[max(fixedValue - lineWidth, 0):min(fixedValue, height), minValue:maxValue, c] = colorMap[labels[0]][c] 729 | image[max(fixedValue, 0):min(fixedValue + lineWidth + 1, height), minValue:maxValue, c] = colorMap[labels[1]][c] 730 | continue 731 | else: 732 | for c in xrange(3): 733 | image[minValue:maxValue, max(fixedValue - lineWidth, 0):min(fixedValue, width), c] = colorMap[labels[1]][c] 734 | image[minValue:maxValue, max(fixedValue, 0):min(fixedValue + lineWidth + 1, width), c] = colorMap[labels[0]][c] 735 | continue 736 | pass 737 | pass 738 | continue 739 | 740 | if filename == '': 741 | return image 742 | else: 743 | cv2.imwrite(filename, image) 744 | 745 | 746 | def drawRectangles(filename, width, height, points, rectangles, labels, lineWidth = 2, backgroundImage = None, rectangleColor = None): 747 | colorMap = ColorPalette(NUM_ICONS).getColorMap() 748 | if backgroundImage is None: 749 | image = np.ones((height, width, 3), np.uint8) * 0 750 | else: 751 | image = backgroundImage 752 | pass 753 | 754 | for rectangleIndex, rectangle in enumerate(rectangles): 755 | point_1 = points[rectangle[0]] 756 | point_2 = points[rectangle[1]] 757 | point_3 = points[rectangle[2]] 758 | point_4 = points[rectangle[3]] 759 | 760 | 761 | if len(labels) == 0: 762 | if rectangleColor is None: 763 | color = np.random.rand(3) * 255 764 | else: 765 | color = rectangleColor 766 | else: 767 | color = colorMap[labels[rectangleIndex]] 768 | pass 769 | 770 | x_1 = int(round((point_1[0] + point_3[0]) / 2)) 771 | x_2 = int(round((point_2[0] + point_4[0]) / 2)) 772 | y_1 = int(round((point_1[1] + point_2[1]) / 2)) 773 | y_2 = int(round((point_3[1] + point_4[1]) / 2)) 774 | 775 | cv2.rectangle(image, (x_1, y_1), (x_2, y_2), color=tuple(color.tolist()), thickness = 2) 776 | 777 | # point_1 = (int(point_1[0]), int(point_1[1])) 778 | # point_2 = (int(point_2[0]), int(point_2[1])) 779 | # point_3 = (int(point_3[0]), int(point_3[1])) 780 | # point_4 = (int(point_4[0]), int(point_4[1])) 781 | 782 | # image[max(point_1[1] - lineWidth, 0):min(point_1[1] + lineWidth, height), point_1[0]:point_2[0] + 1, :] = color 783 | # image[max(point_3[1] - lineWidth, 0):min(point_3[1] + lineWidth, height), point_3[0]:point_4[0] + 1, :] = color 784 | # image[point_1[1]:point_3[1] + 1, max(point_1[0] - lineWidth, 0):min(point_1[0] + lineWidth, width), :] = color 785 | # image[point_2[1]:point_4[1] + 1, max(point_2[0] - lineWidth, 0):min(point_2[0] + lineWidth, width), :] = color 786 | 787 | continue 788 | 789 | if filename == '': 790 | return image 791 | else: 792 | cv2.imwrite(filename, image) 793 | pass 794 | 795 | 796 | def drawResultImage(width, height, result): 797 | resultImage = drawLines('', width, height, result['wall'][0], result['wall'][1], result['wall'][2], None, lineWidth=3) 798 | resultImage = drawLines('', width, height, result['door'][0], result['door'][1], [], resultImage, lineWidth=2, lineColor=0) 799 | iconImage = drawRectangles('', width, height, result['icon'][0], result['icon'][1], result['icon'][2], lineWidth=2) 800 | return resultImage, iconImage 801 | 802 | def resizeResult(result, width, height, oriWidth=256, oriHeight=256): 803 | result['wall'][0] = [[float(point[0]) / oriWidth * width, float(point[1]) / oriHeight * height, point[2], point[3]] for point in result['wall'][0]] 804 | result['door'][0] = [[float(point[0]) / oriWidth * width, float(point[1]) / oriHeight * height, point[2], point[3]] for point in result['door'][0]] 805 | result['icon'][0] = [[float(point[0]) / oriWidth * width, float(point[1]) / oriHeight * height, point[2], point[3]] for point in result['icon'][0]] 806 | #result['room'][0] = [(cv2.resize(mask, (width, height), interpolation=cv2.INTER_NEAREST) > 0).astype(np.uint8) for mask in result['room'][0]] 807 | return 808 | 809 | def drawResultImageFinal(width, height, result): 810 | colorMap = np.array([(224, 255, 192), (255, 160, 96), (255, 224, 128), (192, 255, 255), (192, 255, 255), (192, 255, 255), (192, 192, 224), (224, 255, 192), (255, 224, 224), (224, 224, 224)]) 811 | borderColorMap = np.array([(128, 192, 64), (192, 64, 64), (192, 128, 64), (0, 128, 192), (0, 128, 192), (0, 128, 192), (128, 64, 160), (128, 192, 64), (192, 64, 0), (255, 255, 255)]) 812 | colorMap = np.concatenate([np.full(shape=(1, 3), fill_value=0), colorMap, borderColorMap], axis=0).astype(np.uint8) 813 | colorMap = colorMap[:, ::-1] 814 | 815 | labelRoomMap = getLabelRoomMap() 816 | 817 | roomSegmentation = np.zeros((height, width), dtype=np.int32) 818 | 819 | roomsInfo = [] 820 | wall_dict = result['wall'] 821 | wallMask = drawWallMask([(wall_dict[0][line[0]], wall_dict[0][line[1]]) for line in wall_dict[1]], width, height, thickness=3) 822 | roomRegions = measure.label(1 - wallMask, background=0) 823 | #cv2.imwrite('test/' + str(dictIndex) + '_segmentation_regions.png', drawSegmentationImage(roomRegions)) 824 | backgroundIndex = roomRegions.min() 825 | wallPoints = wall_dict[0] 826 | roomLabels = {} 827 | sizes = np.array([width, height]) 828 | for wallIndex, wallLabels in enumerate(wall_dict[2]): 829 | wallLine = wall_dict[1][wallIndex] 830 | lineDim = calcLineDim(wallPoints, wallLine) 831 | #print('wall', wallIndex, wallPoints[wallLine[0]][:2], wallPoints[wallLine[1]][:2]) 832 | center = np.round((np.array(wallPoints[wallLine[0]][:2]) + np.array(wallPoints[wallLine[1]][:2])) / 2).astype(np.int32) 833 | 834 | for c in xrange(2): 835 | direction = c * 2 - 1 836 | if lineDim == 1: 837 | direction *= -1 838 | pass 839 | point = center 840 | for offset in xrange(10): 841 | point[1 - lineDim] += direction 842 | if point[lineDim] < 0 or point[lineDim] >= sizes[lineDim]: 843 | break 844 | roomIndex = roomRegions[point[1], point[0]] 845 | if roomIndex != backgroundIndex: 846 | #print(roomIndex, wallLabels[c], wallLabels, point.tolist()) 847 | #mask = roomRegions == roomIndex 848 | #mask = cv2.dilate(mask.astype(np.uint8), np.ones((3, 3)), iterations=1) 849 | #roomSegmentation[mask] = wallLabels[c] 850 | #rooms[wallLabels[c]].append(cv2.dilate(mask.astype(np.uint8), np.ones((3, 3)), iterations=wallLineWidth)) 851 | #roomRegions[mask] = backgroundIndex 852 | if roomIndex not in roomLabels: 853 | roomLabels[roomIndex] = {} 854 | pass 855 | roomLabels[roomIndex][wallLabels[c]] = True 856 | break 857 | continue 858 | continue 859 | continue 860 | 861 | 862 | rooms = [] 863 | indexMap = {} 864 | for roomIndex, labels in roomLabels.iteritems(): 865 | #print(roomIndex, labels) 866 | if roomIndex == roomRegions[0][0]: 867 | continue 868 | indexMap[roomIndex] = len(rooms) 869 | mask = roomRegions == roomIndex 870 | mask = cv2.dilate(mask.astype(np.uint8), np.ones((3, 3)), iterations=3) 871 | 872 | # if 7 in labels and 2 not in labels: 873 | # labels[2] = True 874 | # pass 875 | # if 5 in labels and 3 not in labels: 876 | # labels[3] = True 877 | # pass 878 | # if 9 in labels and 1 not in labels: 879 | # labels[1] = True 880 | # pass 881 | rooms.append((mask, labels)) 882 | continue 883 | 884 | wallLineWidth = 5 885 | # foregroundMask = roomSegmentation > 0 886 | # foregroundMask = cv2.dilate(foregroundMask, np.ones((3, 3)), iterations=wallLineWidth) 887 | # roomSegmentation[foregroundMask] = 888 | for mask, labels in rooms: 889 | label = min([label for label in labels]) 890 | if label < 0: 891 | continue 892 | kernel = np.zeros((3, 3)) 893 | kernel[1:, 1:] = 1 894 | #mask = cv2.erode(mask.astype(np.uint8), kernel.astype(np.uint8), iterations=1) 895 | erodedMask = cv2.erode(mask, np.ones((3, 3)), iterations=wallLineWidth) 896 | roomSegmentation[mask.astype(np.bool)] = label + 10 897 | roomSegmentation[erodedMask.astype(np.bool)] = label 898 | 899 | continue 900 | image = colorMap[roomSegmentation.reshape(-1)].reshape((height, width, 3)) 901 | 902 | pointColor = tuple((np.array([0.3, 0.3, 0.9]) * 255).astype(np.uint8).tolist()) 903 | for wallLine in result['wall'][1]: 904 | for pointIndex in wallLine: 905 | point = result['wall'][0][pointIndex] 906 | cv2.circle(image, (int(point[0]), int(point[1])), color=pointColor, radius=8, thickness=-1) 907 | cv2.circle(image, (int(point[0]), int(point[1])), color=(255, 255, 255), radius=4, thickness=-1) 908 | continue 909 | continue 910 | 911 | lineSegmentLength = 20.0 912 | for doorLine in result['door'][1]: 913 | point_1 = np.array(result['door'][0][doorLine[0]][:2]).astype(np.float32) 914 | point_2 = np.array(result['door'][0][doorLine[1]][:2]).astype(np.float32) 915 | lineDim = calcLineDim(result['door'][0], doorLine) 916 | for i in xrange(int(abs(point_1[lineDim] - point_2[lineDim]) / lineSegmentLength + 1)): 917 | ratio = i * lineSegmentLength / abs(point_1[lineDim] - point_2[lineDim]) 918 | if ratio >= 1: 919 | break 920 | startPoint = point_1 + ratio * (point_2 - point_1) 921 | ratio = (i + 0.5) * lineSegmentLength / abs(point_1[lineDim] - point_2[lineDim]) 922 | ratio = min(ratio, 1) 923 | endPoint = point_1 + ratio * (point_2 - point_1) 924 | cv2.line(image, (startPoint[0], startPoint[1]), (endPoint[0], endPoint[1]), color=(0, 0, 0), thickness=4) 925 | continue 926 | for point in [point_1, point_2]: 927 | startPoint = point.copy() 928 | startPoint[1 - lineDim] += lineSegmentLength / 2 929 | endPoint = point.copy() 930 | endPoint[1 - lineDim] -= lineSegmentLength / 2 931 | cv2.line(image, (startPoint[0], startPoint[1]), (endPoint[0], endPoint[1]), color=(0, 0, 0), thickness=2) 932 | continue 933 | continue 934 | 935 | 936 | 937 | labelIconMap = getLabelIconMap() 938 | iconPos = [] 939 | for iconIndex, (icon, label) in enumerate(zip(result['icon'][1], result['icon'][2])): 940 | name = labelIconMap[label + 1] 941 | iconImage = cv2.imread('icons/' + name + '.jpg') 942 | 943 | points = [result['icon'][0][pointIndex] for pointIndex in icon] 944 | x_1 = int(round((points[0][0] + points[2][0]) / 2)) 945 | x_2 = int(round((points[1][0] + points[3][0]) / 2)) 946 | y_1 = int(round((points[0][1] + points[1][1]) / 2)) 947 | y_2 = int(round((points[2][1] + points[3][1]) / 2)) 948 | 949 | iconSize = iconImage.shape #(y, x) 950 | #print('icon_size', iconSize) 951 | icon_is_landscape = iconSize[1] > iconSize[0] 952 | 953 | slot_size = (x_2 - x_1 + 1, y_2 - y_1 + 1) 954 | slot_center = np.array((x_1 + slot_size[0]/2, y_1 + slot_size[1] / 2)) 955 | slot_is_landscape = slot_size[0] > slot_size[1] 956 | 957 | min_dist = float('inf') 958 | line = None 959 | close_line_dim = 0 960 | for wallIndex, wallLabels in enumerate(wall_dict[2]): 961 | wallLine = wall_dict[1][wallIndex] 962 | lineDim = calcLineDim(wallPoints, wallLine) 963 | center = np.round((np.array(wallPoints[wallLine[0]][:2]) + np.array(wallPoints[wallLine[1]][:2])) / 2).astype(np.int32) 964 | point1=np.array(wallPoints[wallLine[0]][:2]) 965 | point2 = np.array(wallPoints[wallLine[1]][:2]) 966 | n = point2 - point1 967 | dist = np.dot((point1 - slot_center) - np.dot((point1 - slot_center), n), n) 968 | #print('indices', wallIndex, wallLabels, wallLine) 969 | # print('points', wallPoints[wallLine[0]], wallPoints[wallLine[1]]) 970 | # pass 971 | 972 | if dist < 5: 973 | min_dist = dist 974 | line = (point1, point2) 975 | close_line_dim = lineDim 976 | pass 977 | pass 978 | 979 | #sys.stderr.write("{}, {}, {}, {}, {}\n".format(y_1, y_2, x_1, x_2, iconImage.shape)) 980 | print('has line: ', line, name, close_line_dim) 981 | if name == "toilet": 982 | if line is not None: 983 | if close_line_dim == 0: #x 984 | y_pos = (line[0][1] + line[1][1]) / 2 985 | if y_pos > y_2: #toilet is below 986 | print('first case rot') 987 | iconImage = rotateImage(iconImage, 2) 988 | elif y_pos < y_1: # toilet is above 989 | pass # do nothing 990 | else: 991 | print("bad case", x_1, x_2, y_1, y_2, line) 992 | pass 993 | else: #y 994 | x_pos = (line[0][0] + line[1][0])/2 995 | print('here', x_pos, x_1, x_2) 996 | if x_pos > x_2: #toilet is to the left 997 | pass # do nothing 998 | elif x_pos < x_1: # toilet is to the right 999 | print(slot_is_landscape, icon_is_landscape) 1000 | if slot_is_landscape: 1001 | iconImage = rotateImage(iconImage, 2) 1002 | pass # do nothing 1003 | else: 1004 | print("bad case", x_1, x_2, y_1, y_2, line) 1005 | pass 1006 | pass 1007 | elif name == "washing_basin": 1008 | if line is not None: 1009 | if close_line_dim == 0: #x 1010 | 1011 | y_pos = (line[0][1] + line[1][1]) / 2 1012 | print(y_pos, y_1, y_2, 'y') 1013 | if y_pos > y_2: #toilet is below 1014 | iconImage = rotateImage(iconImage, 2) 1015 | pass 1016 | elif y_pos < y_1: # toilet is above 1017 | pass # do nothing 1018 | else: 1019 | print("bad case", x_1, x_2, y_1, y_2, line) 1020 | pass 1021 | else: #y 1022 | x_pos = (line[0][0] + line[1][0])/2 1023 | print(x_pos, x_1, x_2 , 'x') 1024 | if x_pos > x_2: #toilet is to the left 1025 | pass # do nothing 1026 | elif x_pos < x_1: # toilet is to the right 1027 | if not slot_is_landscape: 1028 | iconImage = rotateImage(iconImage, 2) 1029 | pass # do nothing 1030 | pass 1031 | else: 1032 | print("bad case", x_1, x_2, y_1, y_2, line) 1033 | pass 1034 | pass 1035 | pass 1036 | pass 1037 | pass 1038 | if slot_is_landscape != icon_is_landscape: 1039 | iconImage = rotateImage(iconImage, 1) 1040 | 1041 | 1042 | iconImage = cv2.resize(iconImage, slot_size) 1043 | 1044 | 1045 | image[y_1:y_2 + 1, x_1:x_2 + 1] = iconImage 1046 | if name == "washing_basin": 1047 | print('basin pose', [x_1, y_1, x_2, y_2]) 1048 | iconPos.append([x_1,y_1, x_2, y_2]) 1049 | continue 1050 | 1051 | fontSize = 0.7 1052 | for mask, labels in rooms: 1053 | label = min([label for label in labels]) 1054 | if label <= 0: 1055 | continue 1056 | ys, xs = mask.nonzero() 1057 | print(xs.mean(), ys.mean(), label) 1058 | 1059 | #label_half_size_x = int(fontSize * len(labelRoomMap[label]) / 2 * 20) 1060 | #label_half_size_y = int(fontSize / 2 * 20) 1061 | ret, baseline = cv2.getTextSize(labelRoomMap[label], fontFace=cv2.FONT_HERSHEY_TRIPLEX, fontScale=fontSize, thickness=1) 1062 | print(labelRoomMap[label]) 1063 | #print('ret', ret) 1064 | center = findBestTextLabelCenter(iconPos, xs, ys, ret[0]/2, ret[1]/2) 1065 | print('comp', [xs.mean(), ys.mean()], center) 1066 | #center = np.round([xs.mean(), ys.mean()]).astype(np.int32) 1067 | if center is not None: 1068 | cv2.putText(image, labelRoomMap[label], (center[0] - ret[0]/2, center[1] + ret[1]/2), fontFace=cv2.FONT_HERSHEY_TRIPLEX, fontScale=fontSize, color=(0, 0, 0), thickness=1) 1069 | else: 1070 | if label != 4: 1071 | import sys 1072 | sys.stderr.write("panic! I cannot find valid position to put label in room: {}, {}\n".format(label, labelRoomMap[label])) 1073 | continue 1074 | print('end draw') 1075 | #cv2.imwrite('test/result.png', image) 1076 | #exit(1) 1077 | #cv2.imwrite('test/region.png', drawSegmentationImage(roomRegions)) 1078 | # for regionIndex in xrange(roomRegions.max() + 1): 1079 | # cv2.imwrite('test/mask_' + str(regionIndex) + '.png', drawMaskImage(roomRegions == regionIndex)) 1080 | # continue 1081 | #resultImage = drawLines('', width, height, result['wall'][0], result['wall'][1], result['wall'][2], None, lineWidth=3) 1082 | #resultImage = drawLines('', width, height, result['door'][0], result['door'][1], [], resultImage, lineWidth=2, lineColor=0) 1083 | #iconImage = drawRectangles('', width, height, result['icon'][0], result['icon'][1], result['icon'][2], lineWidth=2) 1084 | return image 1085 | def findBestTextLabelCenter( icon_pos, xs, ys, label_half_size_x, label_half_size_y): 1086 | center = np.array([xs.mean(), ys.mean()]) 1087 | icon_pos = np.array(icon_pos) 1088 | room_points = np.array([xs, ys]).transpose() 1089 | min_point = room_points.min(axis=0, keepdims=True) 1090 | max_point = room_points.max(axis=0, keepdims=True) 1091 | size = np.array([label_half_size_x, label_half_size_y]) 1092 | print('size', size) 1093 | avail_min_point = min_point + size 1094 | avail_max_point = max_point - size 1095 | 1096 | avail_points = np.logical_and(room_points > avail_min_point, room_points < avail_max_point) 1097 | avail_points = np.all(avail_points, axis=1) 1098 | 1099 | room_points_aug = np.tile(room_points[:, :, np.newaxis], [1, 1, icon_pos.shape[0]]) 1100 | 1101 | room_points_gt_tl_x = room_points_aug[:, 0, :] > icon_pos[:, 0] - size[0] - 5 1102 | room_points_lt_br_x = room_points_aug[:, 0, :] < icon_pos[:, 2] + size[0] + 5 1103 | 1104 | room_points_gt_tl_y = room_points_aug[:, 1, :] > icon_pos[:, 1] - size[1] - 5 1105 | room_points_lt_br_y = room_points_aug[:, 1, :] < icon_pos[:, 3] + size[1] + 5 1106 | 1107 | room_points_in_square = np.logical_and(room_points_gt_tl_x, room_points_lt_br_x) 1108 | room_points_in_square = np.logical_and(room_points_in_square, room_points_gt_tl_y) 1109 | room_points_in_square = np.logical_and(room_points_in_square, room_points_lt_br_y) 1110 | 1111 | #room_points_in_square = np.all(room_points_in_square, axis=1) 1112 | room_points_in_square = np.any(room_points_in_square, axis=1) 1113 | 1114 | room_points_not_in_square = np.logical_not(room_points_in_square) 1115 | 1116 | good_points_mask = np.logical_and(avail_points, room_points_not_in_square) 1117 | good_points = room_points[good_points_mask] 1118 | good_points_center_dist = np.linalg.norm(good_points - center, axis=1) 1119 | #good_points_center_dist = np.sum(np.abs(good_points - center), axis=1) 1120 | #print('icon_pos') 1121 | #print(icon_pos) 1122 | #print('goodpoints') 1123 | #print(center) 1124 | #print(good_points) 1125 | #print(good_points_center_dist) 1126 | if len(good_points) == 0: 1127 | #print('give up') 1128 | return None 1129 | #return np.round(center).astype(np.int32) 1130 | best_point_idx = np.argmin(good_points_center_dist, axis=0) 1131 | #print('cost', good_points_center_dist[best_point_idx]) 1132 | #print('best points') 1133 | #print(good_points[best_point_idx]) 1134 | return good_points[best_point_idx] 1135 | 1136 | def rotateImage(image, orientation): 1137 | if orientation == 0: 1138 | return image 1139 | elif orientation == 1: 1140 | return np.flip(image.transpose((1, 0, 2)), axis=0) 1141 | elif orientation == 2: 1142 | return np.flip(np.flip(image, axis=1), axis=0) 1143 | else: 1144 | return np.flip(image.transpose(1, 0, 2), axis=1) 1145 | return 1146 | 1147 | def projectIndices(pointIndices, pointSegmentation, min_x, max_x, min_y, max_y): 1148 | if max_x - min_x == 1 and max_y - min_y == 1: 1149 | pointIndices[pointSegmentation[:, 2]] = min_y * WIDTH + min_x 1150 | return 1151 | elif max_x - min_x >= max_y - min_y: 1152 | middle_x = int((max_x + min_x + 1) / 2) 1153 | mask_1 = pointSegmentation[:, 0] < middle_x 1154 | projectIndices(pointIndices, pointSegmentation[mask_1], min_x, middle_x, min_y, max_y) 1155 | mask_2 = pointSegmentation[:, 0] >= middle_x 1156 | projectIndices(pointIndices, pointSegmentation[mask_2], middle_x, max_x, min_y, max_y) 1157 | else: 1158 | middle_y = int((max_y + min_y + 1) / 2) 1159 | mask_1 = pointSegmentation[:, 1] < middle_y 1160 | projectIndices(pointIndices, pointSegmentation[mask_1], min_x, max_x, min_y, middle_y) 1161 | mask_2 = pointSegmentation[:, 1] >= middle_y 1162 | projectIndices(pointIndices, pointSegmentation[mask_2], min_x, max_x, middle_y, max_y) 1163 | pass 1164 | return 1165 | 1166 | def drawCornerSegmentation(corners, radius=1, width=WIDTH, height=HEIGHT): 1167 | cornerSegmentation = np.zeros((height, width), dtype=np.int64) 1168 | for corner in corners: 1169 | cornerSegmentation[max(corner[1] - radius + 1, 0):min(corner[1] + radius, height - 1), max(corner[0] - radius + 1, 0):min(corner[0] + radius, width - 1)] = corner[2] 1170 | continue 1171 | return cornerSegmentation 1172 | 1173 | def getOrientationCorners(corners, cornerSize=3): 1174 | orientationCorners = [[] for _ in xrange(NUM_CORNERS)] 1175 | for corner in corners: 1176 | orientationCorners[corner[2] - 1].append(((corner[0], corner[1]), (corner[0] - cornerSize, corner[1] - cornerSize), (corner[0] + cornerSize, corner[1] + cornerSize))) 1177 | continue 1178 | return orientationCorners 1179 | 1180 | def getGTPrimitives(gt_dict): 1181 | result_dict = {'wall': [wallPoints, filteredWallLines, filteredWallLabels], 'door': [doorPoints, filteredDoorLines, []], 'icon': [iconPoints, filteredIcons, filteredIconTypes]} 1182 | return 1183 | 1184 | def writeRepresentation(filename, width, height, result_dict): 1185 | labelMap = [11, 1, 2, 3, 4, 3, 6, 7, 8, 2,] 1186 | labelIconMap = getLabelIconMap() 1187 | with open(filename, 'w') as f: 1188 | f.write(str(width) + '\t' + str(height) + '\n') 1189 | f.write(str(len(result_dict['wall'][1])) + '\n') 1190 | for wallLine, wallLabels in zip(result_dict['wall'][1], result_dict['wall'][2]): 1191 | point_1 = result_dict['wall'][0][wallLine[0]] 1192 | point_2 = result_dict['wall'][0][wallLine[1]] 1193 | lineDim = calcLineDim(result_dict['wall'][0], wallLine) 1194 | if point_1[lineDim] > point_2[lineDim]: 1195 | point_1[lineDim], point_2[lineDim] = point_2[lineDim], point_1[lineDim] 1196 | pass 1197 | f.write(str(int(point_1[0])) + '\t' + str(int(point_1[1])) + '\t' + str(int(point_2[0])) + '\t' + str(int(point_2[1])) + '\t' + str(labelMap[wallLabels[0]]) + '\t' + str(labelMap[wallLabels[1]]) + '\n') 1198 | continue 1199 | for doorLine in result_dict['door'][1]: 1200 | point_1 = result_dict['door'][0][doorLine[0]] 1201 | point_2 = result_dict['door'][0][doorLine[1]] 1202 | lineDim = calcLineDim(result_dict['door'][0], doorLine) 1203 | if point_1[lineDim] > point_2[lineDim]: 1204 | point_1[lineDim], point_2[lineDim] = point_2[lineDim], point_1[lineDim] 1205 | pass 1206 | f.write(str(int(point_1[0])) + '\t' + str(int(point_1[1])) + '\t' + str(int(point_2[0])) + '\t' + str(int(point_2[1])) + '\tdoor\t1\t1\n') 1207 | continue 1208 | #print(len(result_dict['icon'][1])) 1209 | for icon, iconLabel in zip(result_dict['icon'][1], result_dict['icon'][2]): 1210 | #print(iconLabel, labelIconMap[iconLabel + 1]) 1211 | points = np.array([result_dict['icon'][0][pointIndex][:2] for pointIndex in icon]).astype(np.int32) 1212 | mins = points.min(0) 1213 | maxs = points.max(0) 1214 | f.write(str(int(mins[0])) + '\t' + str(int(mins[1])) + '\t' + str(int(maxs[0])) + '\t' + str(int(maxs[1])) + '\t' + labelIconMap[iconLabel + 1] + '\t1\t1\n') 1215 | continue 1216 | f.close() 1217 | pass 1218 | return 1219 | -------------------------------------------------------------------------------- /icons/bathtub.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/art-programmer/FloorNet/93db5bdf12f8b60c9d788aa8e124fbe852e50b4a/icons/bathtub.jpg -------------------------------------------------------------------------------- /icons/bed.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/art-programmer/FloorNet/93db5bdf12f8b60c9d788aa8e124fbe852e50b4a/icons/bed.jpg -------------------------------------------------------------------------------- /icons/cabinet.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/art-programmer/FloorNet/93db5bdf12f8b60c9d788aa8e124fbe852e50b4a/icons/cabinet.jpg -------------------------------------------------------------------------------- /icons/cooking_counter.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/art-programmer/FloorNet/93db5bdf12f8b60c9d788aa8e124fbe852e50b4a/icons/cooking_counter.jpg -------------------------------------------------------------------------------- /icons/refrigerator.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/art-programmer/FloorNet/93db5bdf12f8b60c9d788aa8e124fbe852e50b4a/icons/refrigerator.jpg -------------------------------------------------------------------------------- /icons/sofa.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/art-programmer/FloorNet/93db5bdf12f8b60c9d788aa8e124fbe852e50b4a/icons/sofa.jpg -------------------------------------------------------------------------------- /icons/table.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/art-programmer/FloorNet/93db5bdf12f8b60c9d788aa8e124fbe852e50b4a/icons/table.jpg -------------------------------------------------------------------------------- /icons/toilet.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/art-programmer/FloorNet/93db5bdf12f8b60c9d788aa8e124fbe852e50b4a/icons/toilet.jpg -------------------------------------------------------------------------------- /icons/washing_basin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/art-programmer/FloorNet/93db5bdf12f8b60c9d788aa8e124fbe852e50b4a/icons/washing_basin.jpg -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import glob 3 | import cv2 4 | if 'xrange' not in globals(): 5 | xrange = range 6 | 7 | #consistent color mapping 8 | class ColorPalette: 9 | def __init__(self, numColors): 10 | np.random.seed(1) 11 | #self.colorMap = np.random.randint(255, size = (numColors, 3), dtype=np.uint8) 12 | #self.colorMap[0] = 0 13 | #self.colorMap[0] = np.maximum(self.colorMap[0], 1) 14 | 15 | # [128, 128, 128], 16 | # [0, 0, 255], 17 | # [64, 128, 192], 18 | # [0, 128, 0], 19 | # [192, 0, 0], 20 | # [128, 0, 128], 21 | # [128, 128, 192], 22 | # [128, 192, 192], 23 | # [0, 128, 0], 24 | # [0, 0, 128], 25 | # [128, 128, 0], 26 | # [0, 128, 128] 27 | 28 | self.colorMap = np.array([[255, 0, 0], 29 | [50, 150, 0], 30 | [0, 0, 255], 31 | [80, 128, 255], 32 | [255, 230, 180], 33 | [255, 0, 255], 34 | [0, 255, 255], 35 | [255, 255, 0], 36 | [0, 255, 0], 37 | [200, 255, 255], 38 | [255, 200, 255], 39 | [100, 0, 0], 40 | [0, 100, 0], 41 | [128, 128, 80], 42 | [0, 50, 128], 43 | [0, 100, 100], 44 | [0, 255, 128], 45 | [0, 128, 255], 46 | [255, 0, 128], 47 | [128, 0, 255], 48 | [255, 128, 0], 49 | [128, 255, 0], 50 | ], dtype=np.uint8) 51 | 52 | self.colorMap = np.maximum(self.colorMap, 1) 53 | 54 | if numColors > self.colorMap.shape[0]: 55 | self.colorMap = np.concatenate([self.colorMap, np.random.randint(255, size = (numColors, 3), dtype=np.uint8)], axis=0) 56 | pass 57 | 58 | return 59 | 60 | def getColorMap(self): 61 | return self.colorMap 62 | 63 | def getColor(self, index): 64 | if index >= colorMap.shape[0]: 65 | return np.random.randint(255, size = (3), dtype=np.uint8) 66 | else: 67 | return self.colorMap[index] 68 | pass 69 | 70 | def sigmoid(values): 71 | return 1 / (1 + np.exp(-values)) 72 | 73 | def softmax(values): 74 | values_exp = np.exp(values) 75 | return values_exp / np.sum(values_exp, axis=-1, keepdims=True) 76 | 77 | 78 | #Draw density image 79 | def drawDensityImage(density, maxDensity=-1, nChannels=1): 80 | if maxDensity < 0: 81 | maxDensity = density.max() / 2 82 | pass 83 | densityImage = np.minimum(np.round(density / maxDensity * 255).astype(np.uint8), 255) 84 | if nChannels == 3: 85 | densityImage = np.stack([densityImage, densityImage, densityImage], axis=2) 86 | pass 87 | return densityImage 88 | 89 | #Draw segmentation image. The input could be either HxW or HxWxC 90 | def drawSegmentationImage(segmentations, numColors=42, blackIndex=-1): 91 | if segmentations.ndim == 2: 92 | numColors = max(numColors, segmentations.max() + 2) 93 | else: 94 | numColors = max(numColors, segmentations.shape[2] + 2) 95 | pass 96 | randomColor = ColorPalette(numColors).getColorMap() 97 | if blackIndex >= 0: 98 | randomColor[blackIndex] = 0 99 | pass 100 | width = segmentations.shape[1] 101 | height = segmentations.shape[0] 102 | if segmentations.ndim == 3: 103 | #segmentation = (np.argmax(segmentations, 2) + 1) * (np.max(segmentations, 2) > 0.5) 104 | segmentation = np.argmax(segmentations, 2) 105 | else: 106 | segmentation = segmentations 107 | pass 108 | segmentation = segmentation.astype(np.int) 109 | return randomColor[segmentation.reshape(-1)].reshape((height, width, 3)) 110 | 111 | def drawMaskImage(mask): 112 | return (np.clip(mask * 255, 0, 255)).astype(np.uint8) 113 | 114 | 115 | 116 | def projectIndices(indicesMap, pointSegmentation, min_x, max_x, min_y, max_y, width): 117 | if max_x - min_x == 1 and max_y - min_y == 1: 118 | indicesMap[pointSegmentation[:, 2]] = min_y * width + min_x 119 | return 120 | elif max_x - min_x >= max_y - min_y: 121 | middle_x = int((max_x + min_x + 1) / 2) 122 | mask_1 = pointSegmentation[:, 0] < middle_x 123 | projectIndices(indicesMap, pointSegmentation[mask_1], min_x, middle_x, min_y, max_y, width) 124 | mask_2 = pointSegmentation[:, 0] >= middle_x 125 | projectIndices(indicesMap, pointSegmentation[mask_2], middle_x, max_x, min_y, max_y, width) 126 | else: 127 | middle_y = int((max_y + min_y + 1) / 2) 128 | mask_1 = pointSegmentation[:, 1] < middle_y 129 | projectIndices(indicesMap, pointSegmentation[mask_1], min_x, max_x, min_y, middle_y, width) 130 | mask_2 = pointSegmentation[:, 1] >= middle_y 131 | projectIndices(indicesMap, pointSegmentation[mask_2], min_x, max_x, middle_y, max_y, width) 132 | pass 133 | return 134 | 135 | 136 | #Extract corners from heatmaps 137 | def extractCornersFromHeatmaps(heatmaps, heatmapThreshold=0.5, numPixelsThreshold=5, returnRanges=True): 138 | from skimage import measure 139 | heatmaps = (heatmaps > heatmapThreshold).astype(np.float32) 140 | orientationPoints = [] 141 | #kernel = np.ones((3, 3), np.float32) 142 | for heatmapIndex in xrange(0, heatmaps.shape[-1]): 143 | heatmap = heatmaps[:, :, heatmapIndex] 144 | #heatmap = cv2.dilate(cv2.erode(heatmap, kernel), kernel) 145 | components = measure.label(heatmap, background=0) 146 | points = [] 147 | for componentIndex in xrange(components.min() + 1, components.max() + 1): 148 | ys, xs = (components == componentIndex).nonzero() 149 | if ys.shape[0] <= numPixelsThreshold: 150 | continue 151 | #print(heatmapIndex, xs.shape, ys.shape, componentIndex) 152 | if returnRanges: 153 | points.append(((xs.mean(), ys.mean()), (xs.min(), ys.min()), (xs.max(), ys.max()))) 154 | else: 155 | points.append((xs.mean(), ys.mean())) 156 | pass 157 | continue 158 | orientationPoints.append(points) 159 | continue 160 | return orientationPoints 161 | 162 | #Extract corners from heatmaps 163 | def extractCornersFromSegmentation(segmentation, cornerTypeRange=[0, 13]): 164 | from skimage import measure 165 | orientationPoints = [] 166 | for heatmapIndex in xrange(cornerTypeRange[0], cornerTypeRange[1]): 167 | heatmap = segmentation == heatmapIndex 168 | #heatmap = cv2.dilate(cv2.erode(heatmap, kernel), kernel) 169 | components = measure.label(heatmap, background=0) 170 | points = [] 171 | for componentIndex in xrange(components.min()+1, components.max() + 1): 172 | ys, xs = (components == componentIndex).nonzero() 173 | points.append((xs.mean(), ys.mean())) 174 | continue 175 | orientationPoints.append(points) 176 | continue 177 | return orientationPoints 178 | 179 | #Extract corners from heatmaps 180 | def getSegmentationFromCorners(width, height, orientationCorners): 181 | segmentation = np.zeros((height, width)) 182 | for orientation, corners in enumerate(orientationCorners): 183 | for corner in corners: 184 | segmentation[int(round(corner[1]))][int(round(corner[0]))] = orientation + 1 185 | continue 186 | continue 187 | return segmentation 188 | 189 | #Evaluate corner predictions 190 | def evaluateCorners(cornersPred, cornersGT, distanceThreshold = 15): 191 | numGT = 0 192 | numPred = 0 193 | numMatches = 0 194 | for cornerType, gt_c in enumerate(cornersGT): 195 | pred_c = cornersPred[cornerType] 196 | gt_c = np.array(gt_c) 197 | pred_c = np.array(pred_c) 198 | numGT += gt_c.shape[0] 199 | numPred += pred_c.shape[0] 200 | if gt_c.shape[0] == 0 or pred_c.shape[0] == 0: 201 | continue 202 | diff = np.linalg.norm(np.expand_dims(gt_c, 1) - np.expand_dims(pred_c, 0), axis=2) 203 | numMatches += (diff.min(axis=1) < distanceThreshold).sum() 204 | continue 205 | return np.array([numMatches, numGT, numPred]) 206 | 207 | 208 | #Evaluate segmentation predictions 209 | def evaluateSegmentation(segmentationPred, segmentationGT, numSegments = 12): 210 | #print("hack in evaluate! remove this!") 211 | #print(segmentationGT.shape) 212 | #segmentationGT = segmentationGT.transpose(1, 0) 213 | height = segmentationPred.shape[0] 214 | width = segmentationPred.shape[1] 215 | nonemptyMask = segmentationGT > 0 216 | correctMask = segmentationPred == segmentationGT 217 | #accuracy = float(correctMask.sum()) / (width * height) 218 | accuracy = float(correctMask[nonemptyMask].sum()) / max(nonemptyMask.sum(), 1) 219 | #(width * height) 220 | 221 | sumIOU = 0. 222 | numIOU = 0 223 | for segmentIndex in xrange(numSegments): 224 | gt_s = segmentationGT == segmentIndex 225 | pred_s = segmentationPred == segmentIndex 226 | union = np.logical_or(pred_s, gt_s) 227 | unionSum = union.sum() 228 | if unionSum == 0: 229 | continue 230 | intersection = np.logical_and(pred_s, gt_s) 231 | IOU = float(intersection.sum()) / unionSum 232 | sumIOU += IOU 233 | numIOU += 1 234 | continue 235 | meanIOU = sumIOU / numIOU 236 | return np.array([accuracy, meanIOU]) 237 | 238 | def evaluateDetection(segmentationPred, segmentationGT, numSegments = 12, IOUThreshold = 0.5): 239 | from skimage import measure 240 | numGT = 0 241 | numPred = 0 242 | numMatches = 0 243 | 244 | for segmentIndex in xrange(numSegments): 245 | gt_s = segmentationGT == segmentIndex 246 | if gt_s.sum() > 0: 247 | numGT += 1 248 | pass 249 | pred_s = segmentationPred == segmentIndex 250 | if pred_s.sum() > 0: 251 | numPred += 1 252 | pass 253 | IOU = float(np.logical_and(pred_s, gt_s).sum()) / np.logical_or(pred_s, gt_s).sum() 254 | if IOU > IOUThreshold: 255 | numMatches += 1 256 | pass 257 | continue 258 | return (numMatches, numGT, numPred) 259 | 260 | def fitPlane(points): 261 | if points.shape[0] == points.shape[1]: 262 | return np.linalg.solve(points, np.ones(points.shape[0])) 263 | else: 264 | return np.linalg.lstsq(points, np.ones(points.shape[0]))[0] 265 | 266 | def rotatePoints(points, segmentation, numSampledPoints = 10000): 267 | sampledInds = np.arange(points.shape[0]) 268 | np.random.shuffle(sampledInds) 269 | sampledPoints = points[sampledInds[:numSampledPoints]] 270 | sampledSegmentation = segmentation[sampledInds[:numSampledPoints]] 271 | segments = np.unique(sampledSegmentation).tolist() 272 | binSize = 3 273 | numAngleBins = 90 // 3 + 1 274 | angleSums = np.zeros(numAngleBins) 275 | angleCounts = np.zeros(numAngleBins) 276 | for segmentIndex in segments: 277 | segmentPoints = sampledPoints[sampledSegmentation == segmentIndex] 278 | if segmentPoints.shape[0] < 3: 279 | continue 280 | try: 281 | plane = fitPlane(segmentPoints) 282 | except: 283 | continue 284 | if np.argmax(np.abs(plane)) == 2: 285 | continue 286 | angle = np.arctan2(plane[0], plane[1]) 287 | angle = np.rad2deg(angle) % 90 288 | numPoints = segmentPoints.shape[0] 289 | angleSums[int(np.round(angle / 3))] += angle * numPoints 290 | angleCounts[int(np.round(angle / 3))] += numPoints 291 | continue 292 | angles = angleSums / np.maximum(angleCounts, 1) 293 | angle = angles[np.argmax(angleCounts)] 294 | angle = np.deg2rad(angle) 295 | rotationMatrix = np.zeros((2, 2)) 296 | rotationMatrix[0][0] = np.cos(angle) 297 | rotationMatrix[0][1] = np.sin(angle) 298 | rotationMatrix[1][0] = -np.sin(angle) 299 | rotationMatrix[1][1] = np.cos(angle) 300 | 301 | points[:, :2] = np.matmul(points[:, :2], rotationMatrix) 302 | return points 303 | 304 | def rotatePointsWithMatrix(points, segmentation, numSampledPoints = 10000): 305 | sampledInds = np.arange(points.shape[0]) 306 | np.random.shuffle(sampledInds) 307 | sampledPoints = points[sampledInds[:numSampledPoints]] 308 | sampledSegmentation = segmentation[sampledInds[:numSampledPoints]] 309 | segments = np.unique(sampledSegmentation).tolist() 310 | binSize = 3 311 | numAngleBins = 90 // 3 + 1 312 | #print("rotate!", numAngleBins, flush=True) 313 | assert isinstance(numAngleBins, int), numAngleBins 314 | 315 | angleSums = np.zeros(numAngleBins) 316 | angleCounts = np.zeros(numAngleBins) 317 | for segmentIndex in segments: 318 | segmentPoints = sampledPoints[sampledSegmentation == segmentIndex] 319 | if segmentPoints.shape[0] < 3: 320 | continue 321 | try: 322 | plane = fitPlane(segmentPoints) 323 | except: 324 | continue 325 | if np.argmax(np.abs(plane)) == 2: 326 | continue 327 | angle = np.arctan2(plane[0], plane[1]) 328 | angle = np.rad2deg(angle) % 90 329 | numPoints = segmentPoints.shape[0] 330 | angleSums[int(np.round(angle / 3))] += angle * numPoints 331 | angleCounts[int(np.round(angle / 3))] += numPoints 332 | continue 333 | angles = angleSums / np.maximum(angleCounts, 1) 334 | angle = angles[np.argmax(angleCounts)] 335 | angle = np.deg2rad(angle) 336 | rotationMatrix = np.zeros((2, 2)) 337 | rotationMatrix[0][0] = np.cos(angle) 338 | rotationMatrix[0][1] = np.sin(angle) 339 | rotationMatrix[1][0] = -np.sin(angle) 340 | rotationMatrix[1][1] = np.cos(angle) 341 | 342 | points[:, :2] = np.matmul(points[:, :2], rotationMatrix) 343 | return points, rotationMatrix 344 | 345 | 346 | def drawTopDownView(points, width, height): 347 | coordinates = points[:, :2] 348 | mins = coordinates.min(0, keepdims=True) 349 | maxs = coordinates.max(0, keepdims=True) 350 | ranges = maxs - mins 351 | padding = ranges * 0.05 352 | mins -= padding 353 | ranges += padding * 2 354 | 355 | maxRange = ranges.max() 356 | mins = (maxs + mins) / 2 - maxRange / 2 357 | 358 | coordinates = ((coordinates - mins) / ranges * height).astype(np.int32) 359 | coordinates = np.minimum(coordinates, height - 1) 360 | image = np.zeros((height, width)) 361 | for coordinate in coordinates: 362 | image[coordinate[1]][coordinate[0]] += 1 363 | continue 364 | print(image.max()) 365 | image /= min(image.max(), 300) 366 | image = (np.minimum(image * 255, 255)).astype(np.uint8) 367 | return image 368 | 369 | def writePointCloud(filename, pointCloud): 370 | with open(filename, 'w') as f: 371 | header = """ply 372 | format ascii 1.0 373 | element vertex """ 374 | header += str(len(pointCloud)) 375 | header += """ 376 | property float x 377 | property float y 378 | property float z 379 | property uchar red { start of vertex color } 380 | property uchar green 381 | property uchar blue 382 | end_header 383 | """ 384 | f.write(header) 385 | for point in pointCloud: 386 | for valueIndex, value in enumerate(point): 387 | if valueIndex < 3: 388 | f.write(str(value) + ' ') 389 | else: 390 | f.write(str(int(value * 255)) + ' ') 391 | pass 392 | continue 393 | f.write('\n') 394 | continue 395 | f.close() 396 | pass 397 | return 398 | 399 | 400 | # def convertPointCloudIndices(folder, width=256, height=256): 401 | # filenames = glob.glob(folder + '/pointcloud_indices_*.npy') 402 | # for filename in filenames: 403 | # pointcloud_indices = np.load(filename) 404 | # new_pointcloud_indices = [] 405 | # for imageIndex in xrange(pointcloud_indices.shape[0]): 406 | # pointcloud_indices_0 = pointcloud_indices[imageIndex][0] - imageIndex * width * height 407 | # new_pointcloud_indices.append(pointcloud_indices_0) 408 | # continue 409 | # np.save(filename, np.stack(new_pointcloud_indices, 0)) 410 | # continue 411 | # return 412 | 413 | 414 | def getDensity(points, width=256, height=256): 415 | imageSizes = np.array([width, height]).reshape((-1, 2)) 416 | # mins = points.min(0, keepdims=True) 417 | # maxs = points.max(0, keepdims=True) 418 | # maxRange = (maxs - mins)[:, :2].max() 419 | # padding = maxRange * 0.05 420 | # mins = (maxs + mins) / 2 - maxRange / 2 421 | # mins -= padding 422 | # maxRange += padding * 2 423 | # coordinates = np.round((points - mins) / maxRange * imageSizes).astype(np.int32) 424 | coordinates = np.round(points[:, :2] * imageSizes).astype(np.int32) 425 | coordinates = np.minimum(np.maximum(coordinates, 0), imageSizes - 1) 426 | density = np.zeros((height, width)) 427 | for uv in coordinates: 428 | density[uv[1], uv[0]] += 1 429 | continue 430 | return density 431 | 432 | def getDensityFromIndices(indices, width=256, height=256): 433 | density = np.zeros((height, width)) 434 | for index in indices: 435 | #print(index, index / width, index % width) 436 | density[index / width, index % width] += 1 437 | continue 438 | return density 439 | 440 | def drawCornerImages(segmentations, numColors=42, blackIndex=0): 441 | if segmentations.ndim == 2: 442 | numColors = max(numColors, segmentations.max() + 2) 443 | else: 444 | numColors = max(numColors, segmentations.shape[2] + 2) 445 | pass 446 | randomColor = ColorPalette(numColors).getColorMap() 447 | if blackIndex >= 0: 448 | randomColor[blackIndex] = 0 449 | pass 450 | width = segmentations.shape[1] 451 | height = segmentations.shape[0] 452 | if segmentations.ndim == 3: 453 | #segmentation = (np.argmax(segmentations, 2) + 1) * (np.max(segmentations, 2) > 0.5) 454 | segmentation = np.argmax(segmentations, 2) 455 | else: 456 | segmentation = segmentations 457 | pass 458 | segmentation = segmentation.astype(np.int32).reshape(-1) 459 | images = [] 460 | print(np.unique(segmentation)) 461 | for segment in [(1, 14), (14, 18), (18, 22)]: 462 | colorMap = randomColor.copy() 463 | colorMap[:segment[0]] = 0 464 | colorMap[segment[1]:] = 0 465 | image = colorMap[segmentation].reshape((height, width, 3)) 466 | image = cv2.dilate(image, np.ones((3, 3), dtype=np.uint8), iterations=3) 467 | images.append(image) 468 | continue 469 | return images 470 | 471 | def segmentation2Heatmaps(segmentation, numLabels): 472 | width = segmentation.shape[1] 473 | height = segmentation.shape[0] 474 | labels = np.arange(numLabels, dtype=np.int32).reshape((1, 1, -1)) 475 | heatmaps = (np.expand_dims(segmentation, -1) == labels).astype(np.float32) 476 | return heatmaps 477 | 478 | def heatmaps2Segmentation(heatmaps): 479 | return np.argmax(heatmaps, axis=2) 480 | 481 | def calcIOU(rectangle_1, rectangle_2): 482 | # mins_1 = rectangle_1.min(0) 483 | # maxs_1 = rectangle_1.max(0) 484 | # area_1 = (maxs_1[0] - mins_1[0] + 1) * (maxs_1[1] - mins_1[1] + 1) 485 | # mins_2 = rectangle_2.min(0) 486 | # maxs_2 = rectangle_2.max(0) 487 | # area_2 = (maxs_2[0] - mins_2[0] + 1) * (maxs_2[1] - mins_2[1] + 1) 488 | # intersection = (min(maxs_1[0], maxs_2[0]) - max(mins_1[0], mins_2[0]) + 1) * (min(maxs_1[1], maxs_2[1]) - max(mins_1[1], mins_2[1]) + 1) 489 | 490 | rectangles = [rectangle_1, rectangle_2] 491 | 492 | x_1 = max([int(round((rectangle[0][0] + rectangle[2][0]) / 2)) for rectangle in rectangles]) 493 | x_2 = min([int(round((rectangle[1][0] + rectangle[3][0]) / 2)) for rectangle in rectangles]) 494 | y_1 = max([int(round((rectangle[0][1] + rectangle[1][1]) / 2)) for rectangle in rectangles]) 495 | y_2 = min([int(round((rectangle[2][1] + rectangle[3][1]) / 2)) for rectangle in rectangles]) 496 | if x_1 >= x_2 or y_1 >= y_2: 497 | return 0 498 | intersection = (x_2 - x_1 + 1) * (y_2 - y_1 + 1) 499 | 500 | area_1, area_2 = ((int(round((rectangle[1][0] + rectangle[3][0]) / 2)) - int(round((rectangle[0][0] + rectangle[2][0]) / 2)) + 1) * (int(round((rectangle[2][1] + rectangle[3][1]) / 2)) - int(round((rectangle[0][1] + rectangle[1][1]) / 2)) + 1) for rectangle in rectangles) 501 | 502 | union = area_1 + area_2 - intersection 503 | return float(intersection) / union 504 | 505 | def calcIOUMask(mask_1, mask_2): 506 | intersection = (mask_1 * mask_2).sum() 507 | union = mask_1.sum() + mask_2.sum() - intersection 508 | return float(intersection) / max(union, 1) 509 | 510 | 511 | def gaussian(k=5, sig=0): 512 | """ 513 | creates gaussian kernel with side length l and a sigma of sig 514 | v """ 515 | if sig == 0: 516 | sig = 0.3 * ((k - 1) * 0.5 - 1) + 0.8 517 | pass 518 | 519 | ax = np.arange(-k // 2 + 1., k // 2 + 1.) 520 | xx, yy = np.meshgrid(ax, ax) 521 | 522 | kernel = np.exp(-(xx**2 + yy**2) / (2. * sig**2)) 523 | 524 | return kernel / np.sum(kernel) 525 | 526 | def disk(k): 527 | """ 528 | creates gaussian kernel with side length l and a sigma of sig 529 | """ 530 | ax = np.arange(-k // 2 + 1., k // 2 + 1.) 531 | xx, yy = np.meshgrid(ax, ax) 532 | 533 | kernel = (np.sqrt(pow(xx, 2) + pow(yy, 2)) <= (k - 1) / 2).astype(np.float32) 534 | 535 | return kernel 536 | 537 | if __name__ == '__main__': 538 | rect_1 = np.array([[54.56637168141593, 91.65781710914455], [85.51592356687898, 90.92993630573248], [64.36440677966101, 123.17372881355932], [84.18115942028986, 109.3840579710145]]) 539 | rect_2 = np.array([[64.0, 92.0, 1, 2], [86.0, 92.0, 1, 3], [64.0, 111.0, 1, 1], [86.0, 111.0, 1, 0]]) 540 | print('IOU', calcIOU(rect_1, rect_2)) 541 | pass 542 | --------------------------------------------------------------------------------