def _get_root_tile(tileset, filename): folder = os.path.dirname(filename) pnts_filename = os.path.join( folder, tileset['root']['content']['uri']) return TileContentReader.read_file(pnts_filename)
def main(args): tile = TileContentReader.read_file(args.filename) magic = tile.header.magic_value if magic == "pnts": print_pnts_info(tile) elif magic == "b3dm": print_b3dm_info(tile) else: raise RuntimeError("Unsupported format " + magic)
def write_tileset(in_folder, out_folder, octree_metadata, offset, scale, projection, rotation_matrix, include_rgb): # compute tile transform matrix if rotation_matrix is None: transform = np.identity(4) else: transform = inverse_matrix(rotation_matrix) transform = np.dot(transform, scale_matrix(1.0 / scale[0])) transform = np.dot(translation_matrix(offset), transform) # build fake points if True: root_node = Node('', octree_metadata.aabb, octree_metadata.spacing * 2) root_node.children = [] inv_aabb_size = (1.0 / (octree_metadata.aabb[1] - octree_metadata.aabb[0])).astype(np.float32) for child in ['0', '1', '2', '3', '4', '5', '6', '7']: ondisk_tile = name_to_filename(out_folder, child.encode('ascii'), '.pnts') if os.path.exists(ondisk_tile): tile_content = TileContentReader.read_file(ondisk_tile) fth = tile_content.body.feature_table.header xyz = tile_content.body.feature_table.body.positions_arr.view(np.float32).reshape((fth.points_length, 3)) if include_rgb: rgb = tile_content.body.feature_table.body.colors_arr.reshape((fth.points_length, 3)) else: rgb = np.zeros(xyz.shape, dtype=np.uint8) root_node.grid.insert( octree_metadata.aabb[0].astype(np.float32), inv_aabb_size, xyz.copy(), rgb) pnts_writer.node_to_pnts(''.encode('ascii'), root_node, out_folder, include_rgb) executor = concurrent.futures.ProcessPoolExecutor() root_tileset = Node.to_tileset(executor, ''.encode('ascii'), octree_metadata.aabb, octree_metadata.spacing, out_folder, scale) executor.shutdown() root_tileset['transform'] = transform.T.reshape(16).tolist() root_tileset['refine'] = 'REPLACE' for child in root_tileset['children']: child['refine'] = 'ADD' tileset = { 'asset': { 'version': '1.0', }, 'geometricError': np.linalg.norm( octree_metadata.aabb[1] - octree_metadata.aabb[0]) / scale[0], 'root': root_tileset, } with open('{}/tileset.json'.format(out_folder), 'w') as f: f.write(json.dumps(tileset))
def test_read(self): tile = TileContentReader().read_file('tests/dragon_low.b3dm') self.assertEqual(tile.header.version, 1.0) self.assertEqual(tile.header.tile_byte_length, 47246) self.assertEqual(tile.header.ft_json_byte_length, 20) self.assertEqual(tile.header.ft_bin_byte_length, 0) self.assertEqual(tile.header.bt_json_byte_length, 0) self.assertEqual(tile.header.bt_bin_byte_length, 0) with open('tests/dragon_low_gltf_header.json', 'r') as f: gltf_header = json.loads(f.read()) self.assertDictEqual(gltf_header, tile.body.glTF.header)
def test_read(self): tile = TileContentReader().read_file('tests/pointCloudRGB.pnts') self.assertEqual(tile.header.version, 1.0) self.assertEqual(tile.header.tile_byte_length, 15176) self.assertEqual(tile.header.ft_json_byte_length, 148) self.assertEqual(tile.header.ft_bin_byte_length, 15000) self.assertEqual(tile.header.bt_json_byte_length, 0) self.assertEqual(tile.header.bt_bin_byte_length, 0) feature_table = tile.body.feature_table feature = feature_table.feature(0) dcol_res = {'Red': 44, 'Blue': 209, 'Green': 243} self.assertDictEqual(dcol_res, feature.colors)
def test_build(self): tread = TileContentReader().read_file('tests/pointCloudRGB.pnts') # numpy dtype for positions and colors pdt = np.dtype([('X', '<f4'), ('Y', '<f4'), ('Z', '<f4')]) cdt = np.dtype([('Red', 'u1'), ('Green', 'u1'), ('Blue', 'u1')]) # create features features = [] for i in range(0, tread.body.feature_table.header.points_length): f = tread.body.feature_table.feature(i) p = f.positions c = f.colors pos = np.array([(p['X'], p['Y'], p['Z'])], dtype=pdt).view('uint8') col = np.array([(c['Red'], c['Green'], c['Blue'])], dtype=cdt).view('uint8') newf = Feature.from_array(pdt, pos, cdt, col) features.append(newf) # create a tile t = Pnts.from_features(pdt, cdt, features) # configure the tile rtc = [1215012.8828876738, -4736313.051199594, 4081605.22126042] t.body.feature_table.header.rtc = rtc # get an array tile_arr = t.to_array() t2 = Pnts.from_array(tile_arr) self.assertEqual(t2.header.version, 1.0) self.assertEqual(t2.header.tile_byte_length, 15176) self.assertEqual(t2.header.ft_json_byte_length, 148) self.assertEqual(t2.header.ft_bin_byte_length, 15000) self.assertEqual(t2.header.bt_json_byte_length, 0) self.assertEqual(t2.header.bt_bin_byte_length, 0) feature_table = t.body.feature_table feature = feature_table.feature(0) dcol_res = {'Red': 44, 'Blue': 209, 'Green': 243} self.assertDictEqual(dcol_res, feature.colors)
def test_build_without_colors(self): tread = TileContentReader().read_file('tests/pointCloudRGB.pnts') f0_ref = tread.body.feature_table.feature(0).positions # numpy dtype for positions and colors pdt = np.dtype([('X', '<f4'), ('Y', '<f4'), ('Z', '<f4')]) # create features features = [] for i in range(0, tread.body.feature_table.header.points_length): f = tread.body.feature_table.feature(i) p = f.positions pos = np.array([(p['X'], p['Y'], p['Z'])], dtype=pdt).view('uint8') newf = Feature.from_array(pdt, pos) features.append(newf) # create a tile t = Pnts.from_features(pdt, None, features) # configure the tile rtc = [1215012.8828876738, -4736313.051199594, 4081605.22126042] t.body.feature_table.header.rtc = rtc # get an array tile_arr = t.to_array() t2 = Pnts.from_array(tile_arr) self.assertEqual(t2.header.version, 1.0) self.assertEqual(t2.header.tile_byte_length, 12152) self.assertEqual(t2.header.ft_json_byte_length, 124) self.assertEqual(t2.header.ft_bin_byte_length, 12000) self.assertEqual(t2.header.bt_json_byte_length, 0) self.assertEqual(t2.header.bt_bin_byte_length, 0) feature_table = t.body.feature_table f0 = feature_table.feature(0).positions self.assertAlmostEqual(f0_ref['X'], f0['X']) self.assertAlmostEqual(f0_ref['Y'], f0['Y']) self.assertAlmostEqual(f0_ref['Z'], f0['Z'])
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 = TileContentReader.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 = TileContentReader.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