def loadCloudLAS(path): """ Loads a LAS file from the specified path. """ try: import laspy except: assert False, "Please install laspy (pip install laspy) to load LAS." # look for/load header file if one exists header, data = matchHeader(path) if not header is None: header = loadHeader(header) # load header file f = laspy.file.File(data, mode='r') # get data xyz = np.array([f.x, f.y, f.z], dtype=f.x.dtype).T # try to get colour info try: rgb = np.array([f.get_red(), f.get_green(), f.get_blue()], dtype=f.get_blue().dtype) except: rgb = None # construct cloud return HyCloud(xyz, rgb=rgb, header=header)
def _merge(chunks, shape): """ Merge a list of HyData objects into a combined one (aka. do the opposite of split(...)). *Arguments*: - chunks = a list of HyData chunks to merge. - shape = the output data shape. *Returns*: a single merged HyData instance (of the same type as the input). The header of this instance will be a copy of chunks[0].header. """ # merge data X = np.vstack([c.data for c in chunks]) X = X.reshape((*shape, -1)) if not isinstance(chunks[0], HyCloud): # easy! # make copy out = chunks[0].copy(data=False) out.data = X out.header = chunks[0].header.copy() return out else: # less easy xyz = np.vstack([c.xyz for c in chunks]) rgb = None if chunks[0].has_rgb(): rgb = np.vstack([c.rgb for c in chunks]) normals = None if chunks[0].has_normals(): normals = np.vstack([c.normals for c in chunks]) return HyCloud(xyz, rgb=rgb, normals=normals, bands=X, header=chunks[0].header.copy())
def _split(data, nchunks): """ Split the specified HyCloud instance into a number of chunks. *Arguments*: - data = the complete HyData object to copy and split. - nchunks = the number of chunks to split into. *Returns*: - a list of split """ if isinstance( data, HyCloud ): # special case for hyperclouds - split xyz, rgb and normals too chunksize = int(np.floor(data.point_count() / nchunks)) chunks = [(i * chunksize, (i + 1) * chunksize) for i in range(nchunks)] chunks[-1] = (chunks[-1][0], data.point_count() ) # expand last chunk to include remainder # split points xyz = [data.xyz[c[0]:c[1], :].copy() for c in chunks] # split data bands = [None for c in chunks] if data.has_bands(): X = data.get_raveled().copy() bands = [X[c[0]:c[1], :] for c in chunks] # split rgb rgb = [None for c in chunks] if data.has_rgb(): rgb = [data.rgb[c[0]:c[1], :].copy() for c in chunks] # split normals normals = [None for c in chunks] if data.has_normals(): normals = [data.normals[c[0]:c[1], :].copy() for c in chunks] return [ HyCloud(xyz[i], rgb=rgb[i], normals=normals[i], bands=bands[i], header=data.header.copy()) for i in range(len(chunks)) ] else: # just split data (for HyImage and other types) X = data.get_raveled().copy() chunksize = int(np.floor(X.shape[0] / nchunks)) chunks = [(i * chunksize, (i + 1) * chunksize) for i in range(nchunks)] chunks[-1] = (chunks[-1][0], X.shape[0] ) # expand last chunk to include remainder out = [] for c in chunks: _o = data.copy(data=False) # create copy _o.data = X[c[0]:c[1], :][:, None, :] out.append(_o) return out
def genCloud(npoints=1000, nbands=10, data=True, normals=True, rgb=True): header = genHeader() xyz = np.random.rand(npoints, 3) if rgb: rgb = np.random.rand(npoints, 3) if normals: normals = np.random.rand(npoints, 3) normals = normals / np.linalg.norm(normals, axis=1)[:, None] if data: data = np.random.rand(npoints, nbands) # point cloud cloud = HyCloud(xyz, rgb=rgb, normals=normals, bands=data, header=header) cloud.set_wavelengths(np.linspace(500.0, 1000.0, nbands)) cloud.set_band_names(["Band %d" % (i + 1) for i in range(nbands)]) cloud.set_fwhm([1.0 for i in range(nbands)]) cloud.push_to_header() # push info on dims return cloud
def loadCloudDEM(pathDEM, pathRGB=None): """ Combines a DEM file and an Orthophoto from the specified path to a cloud. """ # load files DEMdata = loadWithGDAL(pathDEM) if not pathRGB is None: RGBdata = loadWithGDAL(pathRGB) # create list of pixel coordinates pixgrid = np.meshgrid(np.arange(0,DEMdata.xdim(),1),np.arange(0,DEMdata.ydim(),1)) pixlist = np.vstack(((pixgrid[0]).flatten(),(pixgrid[1]).flatten())) # transform pixel coordinates to world xy coordinates fx=[] fy=[] for i in range(pixlist.shape[1]): tx,ty= DEMdata.pix_to_world(int(pixlist[0,i]),int(pixlist[1,i])) fx.append(tx) fy.append(ty) # add DEM data as z information and create xyz cloud list fz = DEMdata.data.flatten() xyz = np.array([fx, fy, fz]).T # if orthophoto is specified, extract RGB values for each DEM point and colorize cloud if not pathRGB is None: rgb=[] for i in range(len(xyz)): wx,wy = RGBdata.world_to_pix(xyz[i,0],xyz[i,1]) rgb.append(RGBdata.data[wx,wy,:]) # otherwise fill rgb info with zeros else: rgb = np.zeros((len(xyz),3)) # return HyCloud return HyCloud(xyz, rgb=rgb)
def loadCloudCSV(path, delimiter=' ', order='xyzrgbklm'): """ Loads a point cloud from a csv (or other formatted text) file. *Arguments*: - path = the file path - delimeter = the delimeter used (e.g. ' ', ',', ';'). Default is ' '. - order = A string defining the order of data in the csv. Each character maps to the following: -'x' = point x coordinate -'y' = point y coordinate -'z' = point z coordinate -'r' = point r coordinate -'g' = point g coordinate -'b' = point b coordinate -'k' = point normal (x) -'l' = point normal (y) -'m' = point normal (z). Default is 'xyzrgbklm'. """ # look for/load header file if one exists header, data = matchHeader(path) if not header is None: header = loadHeader(header) # load header file # load points points = np.genfromtxt(data, delimiter=delimiter, skip_header=1) assert points.shape[1] >= 3, "Error - dataset must at-least contain x,y,z point coordinates." # parse order string order = order.lower() xyz = [order.find('x'), order.find('y'), order.find('z')] assert not -1 in xyz, "Error - order must include x,y,z data." rgb = [order.find('r'), order.find('g'), order.find('b')] norm = [order.find('k'), order.find('l'), order.find('m')] notS = [] # columns that have already been parsed (so are not scalar fields) # get xyz notS += xyz xyz = points[:, xyz] # try get rgb and normals if max(rgb) < points.shape[1] and not -1 in rgb: notS += rgb rgb = points[:, rgb] # if RGB is 0-255 store as uint8 to minimise memory usage if (rgb > 1.0).any(): rgb = rgb.astype(np.uint8) else: rgb = None if max(norm) < points.shape[1] and not -1 in norm: notS += norm norm = points[:, norm].astype(np.float32) #store norm as float32 rather than float64 (default) else: norm = None # get remaining data as scalar field scal = [i for i in range(points.shape[1]) if i not in notS] if len(scal) > 0: scal = points[:, scal] else: scal = None # return point cloud return HyCloud(xyz, rgb=rgb, normals=norm, bands=scal, header=header)
def loadCloudPLY(path): """ Loads a PLY file from the specified path. """ try: from plyfile import PlyData, PlyElement except: assert False, "Please install plyfile (pip install plyfile) to load PLY." # look for/load header file if one exists header, data = matchHeader(path) if not header is None: header = loadHeader(header) # load header file # load file data = PlyData.read(data) # extract data xyz = None rgb = None norm = None scalar = [] scalar_names = [] for e in data.elements: if 'vert' in e.name.lower(): # vertex data xyz = np.array([e['x'], e['y'], e['z']]).T if len(e.properties) > 3: # vertices have more than just position names = e.data.dtype.names # colour? if 'red' in names and 'green' in names and 'blue' in names: rgb = np.array([e['red'], e['green'], e['blue']], dtype=e['red'].dtype).T # normals? if 'nx' in names and 'ny' in names and 'nz' in names: norm = np.array([e['nx'], e['ny'], e['nz']], dtype=e['nx'].dtype).T # load others as scalar mask = ['red', 'green', 'blue', 'nx', 'ny', 'nz', 'x', 'y', 'z'] for n in names: if not n in mask: scalar_names.append(n) scalar.append(e[n]) elif 'color' in e.name.lower(): # rgb data rgb = np.array([e['r'], e['g'], e['b']], dtype=e['r'].dtype).T elif 'normal' in e.name.lower(): # normal data norm = np.array([e['x'], e['y'], e['z']], dtype=e['z'].dtype).T else: # scalar data scalar_names.append(e.properties[0].name) scalar.append(np.array(e[e.properties[0].name], dtype=e[e.properties[0].name].dtype)) if len(scalar) == 0: scalar = None scalar_names = None else: scalar = np.vstack(scalar).T assert (not xyz is None) and (xyz.shape[0] > 0), "Error - cloud has not points?" # return HyCloud return HyCloud(xyz, rgb=rgb, normals=norm, bands=scalar, band_names=scalar_names, header=header)