def outputToPnts(self): import py3dtiles.points.task.pnts_writer as writer if (len(self.children) == 0): totalPoints = [] for pnt in self.points: p = pnt if (p['x'] and p['y'] and p['z']): totalPoints.append(p['x']) totalPoints.append(p['y']) totalPoints.append(p['z']) for pnt in self.getPointIterator(): p = self.convertStringToPoint(pnt) if (p['x'] and p['y'] and p['z']): totalPoints.append(p['x']) totalPoints.append(p['y']) totalPoints.append(p['z']) writer.points_to_pnts('tile-' + self.id, np.array(totalPoints, dtype=np.float32).view(np.uint8), "output", False) for b in self.children: b.outputToPnts()
def outputToPnts(self): import py3dtiles.points.task.pnts_writer as writer if (self.id == "root"): totalPoints = [] totalColors = [] X = [] Y = [] Z = [] for pnt in errored: p = pnt if (p['x'] and p['y'] and p['z']): # coord = np.vstack((p['x'],p['y'],p['z'])).transpose() totalPoints.append(p['x']) totalPoints.append(p['y']) totalPoints.append(p['z']) totalColors.append(p['r']) totalColors.append(p['g']) totalColors.append(p['b']) arr = np.concatenate( (np.array(totalPoints, dtype=np.float32).view(np.uint8).ravel(), np.array(totalColors, dtype=np.uint8).ravel())) writer.points_to_pnts('errored', arr, "output", True) if (len(self.childrenBlocks) == 0): totalPoints = [] totalColors = [] X = [] Y = [] Z = [] for pnt in self.childrenPoints: p = pnt if (p['x'] and p['y'] and p['z']): # coord = np.vstack((p['x'],p['y'],p['z'])).transpose() totalPoints.append(p['x']) totalPoints.append(p['y']) totalPoints.append(p['z']) totalColors.append(p['r']) totalColors.append(p['g']) totalColors.append(p['b']) # np.array(totalPoints) # np.array(totalPoints).astype(np.float32) arr = np.concatenate((np.array(totalPoints, dtype=np.float32).view(np.uint8), np.array(totalColors, dtype=np.uint8))) writer.points_to_pnts('tile-' + self.id, arr, "output", True, self.rtc) self.exportToXYZ(self.childrenPoints) # pnts.outputToFile('tile-' + self.id, totalPoints, "output") for b in self.childrenBlocks: b.outputToPnts()
def exportToPnts(points=[]): import py3dtiles.points.task.pnts_writer as writer import numpy as np totalPoints = [] totalColors = [] for p in points: x, y, z, r, g, b = p totalPoints.append(x) totalPoints.append(y) totalPoints.append(z) totalColors.append(r) totalColors.append(g) totalColors.append(b) arr = np.concatenate( (np.array(totalPoints, dtype=np.float32).view(np.uint8).ravel(), np.array(totalColors, dtype=np.uint8).ravel())) writer.points_to_pnts( 'root', arr, "output", True)
def outputToFile(name, pointArray, folder): writer.points_to_pnts(name, pointArray, folder, False)
def to_tileset(executor, name, parent_aabb, parent_spacing, folder, scale): node = node_from_name(name, parent_aabb, parent_spacing) aabb = node.aabb ondisk_tile = name_to_filename(folder, name, '.pnts') xyz, rgb = None, None # Read tile's pnts file, if existing, we'll need it for: # - computing the real AABB (instead of the one based on the octree) # - merging this tile's small (<100 points) children if os.path.exists(ondisk_tile): tile = TileReader().read_file(ondisk_tile) fth = tile.body.feature_table.header xyz = tile.body.feature_table.body.positions_arr if fth.colors != SemanticPoint.NONE: rgb = tile.body.feature_table.body.colors_arr xyz_float = xyz.view(np.float32).reshape((fth.points_length, 3)) # update aabb based on real values aabb = np.array( [np.amin(xyz_float, axis=0), np.amax(xyz_float, axis=0)]) # geometricError is in meters, so we divide it by the scale tileset = {'geometricError': 10 * node.spacing / scale[0]} children = [] tile_needs_rewrite = False if os.path.exists(ondisk_tile): tileset['content'] = {'uri': os.path.relpath(ondisk_tile, folder)} for child in ['0', '1', '2', '3', '4', '5', '6', '7']: child_name = '{}{}'.format(name.decode('ascii'), child).encode('ascii') child_ondisk_tile = name_to_filename(folder, child_name, '.pnts') if os.path.exists(child_ondisk_tile): # See if we should merge this child in tile if xyz is not None: # Read pnts content tile = TileReader().read_file(child_ondisk_tile) fth = tile.body.feature_table.header # If this child is small enough, merge in the current tile if fth.points_length < 100: xyz = np.concatenate( (xyz, tile.body.feature_table.body.positions_arr)) if fth.colors != SemanticPoint.NONE: rgb = np.concatenate( (rgb, tile.body.feature_table.body.colors_arr)) # update aabb xyz_float = tile.body.feature_table.body.positions_arr.view( np.float32).reshape((fth.points_length, 3)) aabb[0] = np.amin( [aabb[0], np.min(xyz_float, axis=0)], axis=0) aabb[1] = np.amax( [aabb[1], np.max(xyz_float, axis=0)], axis=0) tile_needs_rewrite = True os.remove(child_ondisk_tile) continue # Add child to the to-be-processed list if it hasn't been merged if executor is not None: children += [(child_name, node.aabb, node.spacing, folder, scale)] else: children += [ Node.to_tileset(None, child_name, node.aabb, node.spacing, folder, scale) ] # If we merged at least one child tile in the current tile # the pnts file needs to be rewritten. if tile_needs_rewrite: os.remove(ondisk_tile) count, filename = points_to_pnts(name, np.concatenate((xyz, rgb)), folder, rgb is not None) center = ((aabb[0] + aabb[1]) * 0.5).tolist() half_size = ((aabb[1] - aabb[0]) * 0.5).tolist() tileset['boundingVolume'] = { 'box': [ center[0], center[1], center[2], half_size[0], 0, 0, 0, half_size[1], 0, 0, 0, half_size[2] ] } if executor is not None: children = [t for t in executor.map(node_to_tileset, children)] if children: tileset['children'] = children else: tileset['geometricError'] = 0.0 if len(name) > 0 and children: if len(json.dumps(tileset)) > 100000: tile_root = { 'asset': { 'version': '1.0', }, 'refine': 'ADD', 'geometricError': tileset['geometricError'], 'root': tileset } tileset_name = 'tileset.{}.json'.format(name.decode('ascii')) with open('{}/{}'.format(folder, tileset_name), 'w') as f: f.write(json.dumps(tile_root)) tileset['content'] = {'uri': tileset_name} tileset['children'] = [] return tileset
def build_tileset_quadtree(out_folder, aabb, tilesets, base_transform, inv_base_transform, name): insides = [] for tileset in tilesets: if is_tileset_inside(tileset, aabb): insides += [tileset] quadtree_diag = np.linalg.norm(aabb[1][:2] - aabb[0][:2]) if not insides: return None elif len(insides) == 1 or quadtree_diag < 1: # apply transform to boundingVolume box = _aabb_from_3dtiles_bounding_volume( insides[0]['root']['boundingVolume'], _get_root_transform(insides[0])) return { 'transform': inv_base_transform.T.reshape(16).tolist(), 'geometricError': insides[0]['root']['geometricError'], 'boundingVolume': _3dtiles_bounding_box_from_aabb(box), 'content': { 'uri': os.path.relpath(insides[0]['filename'], out_folder) } } else: tilesets = [t for t in tilesets if t['id'] not in insides] result = {'children': []} sub = 0 for quarter in quadtree_split(aabb): r = build_tileset_quadtree(out_folder, quarter, insides, base_transform, inv_base_transform, name + str(sub)) sub += 1 if r is not None: result['children'] += [r] union_aabb = _aabb_from_3dtiles_bounding_volume( insides[0]['root']['boundingVolume'], _get_root_transform(insides[0])) # take half points from our children xyz = np.zeros((0, 3), dtype=np.float32) rgb = np.zeros((0, 3), dtype=np.uint8) max_point_count = 50000 point_count = 0 for tileset in insides: root_tile = _get_root_tile(tileset, tileset['filename']) point_count += root_tile.body.feature_table.header.points_length ratio = min(0.5, max_point_count / point_count) for tileset in insides: root_tile = _get_root_tile(tileset, tileset['filename']) _xyz, _rgb = _get_tile_points(root_tile, _get_root_transform(tileset), inv_base_transform) select = np.random.choice(_xyz.shape[0], int(_xyz.shape[0] * ratio)) xyz = np.concatenate((xyz, _xyz[select])) if _rgb is not None: rgb = np.concatenate((rgb, _rgb[select])) ab = _aabb_from_3dtiles_bounding_volume( tileset['root']['boundingVolume'], _get_root_transform(tileset)) union_aabb[0] = np.minimum(union_aabb[0], ab[0]) union_aabb[1] = np.maximum(union_aabb[1], ab[1]) filename = points_to_pnts( name.encode('ascii'), np.concatenate((xyz.view(np.uint8).ravel(), rgb.ravel())), out_folder, rgb.shape[0] > 0)[1] result['content'] = {'uri': os.path.relpath(filename, out_folder)} result['geometricError'] = sum( [t['root']['geometricError'] for t in insides]) result['boundingVolume'] = _3dtiles_bounding_box_from_aabb( union_aabb, inv_base_transform) return result