def multi_to_single(image, output=None, of='GTiff', co=None, no_data=None, overwrite=False): """ Split a multiband image into many singleband images. :param image: Input image :param output: Output file. The band number will be appended at the end ('*_X.*'). Defaults to the file name of the input image. :param of: The desired format of the output file as provided by the GDAL raster formats (see: http://www.gdal.org/formats_list.html). :param co: Advanced raster creation options such as band interleave or compression. <br> Example: co=['compress=lzw'] :param no_data: NoData-value, given as int or float (depending on the data type of the images). Defaults to the NoData-value of the first input image. :param overwrite: Overwrite output file in case it already exists. """ ds = gdal.Open(image, gdal.GA_ReadOnly) ds_props = get_raster_properties(image, dictionary=True) bandnum = ds.RasterCount for b in tqdm(xrange(1, ds_props['bandnum'] + 1), desc='Progress'): band = ds.GetRasterBand(b) if no_data is None: no_data = band.GetNoDataValue() if output: out_name = ''.join([ os.path.splitext(output)[0], '_', str(b).zfill(len(bandnum)), os.path.splitext(output)[1] ]) else: out_name = ''.join([ os.path.splitext(image)[0], '_', str(b).zfill(len(bandnum)), os.path.splitext(image)[1] ]) ds_out = create_ds(out_name, ds_props['cols'], ds_props['rows'], 1, band.DataType, of, co, overwrite) ds_out.SetProjection(ds_props['proj']) ds_out.SetGeoTransform(ds_props['geotrans']) band_out = ds_out.GetRasterBand(1) band_out.WriteArray(band.ReadRaster()) meta = {} name = '_'.join(['Band', str(b)]) meta[name] = band.GetDescription() band_out.SetDescription(band.GetDescription()) band_out.SetNoDataValue(no_data) band_out.WriteRaster() ds_out.SetMetadata(meta) band = None band_out = None ds_out = None ds = None return
def match_rasters(reference, warp, outfile, of='GTiff', co=None, overwrite=True): # type: (str, str, str, str, list, bool) -> object """ Map a raster to the projection, extent and resolution of a reference raster :param reference: Reference image that holds the target projection and extent :param warp: image to be mapped to the reference image :param outfile: Output image :param of: Output format as defined at http://www.gdal.org/formats_list.html :param co: Advanced raster creation options such as band interleave or compression. <br<> Example: co=['compress=lzw'] :param overwrite: Overwrite output file if it already exists :return: -- """ if os.path.exists(outfile) and overwrite is True: delete_ds(outfile) elif os.path.exists(outfile) and overwrite is False: raise IOError( 'File {f} already exists! To overwrite, use "overwrite=True"!'. format(f=outfile)) # read reference raster print('Reading reference raster {f}'.format(f=reference)) ref_ds = gdal.Open(reference, gdal.GA_ReadOnly) ref_geotrans = ref_ds.GetGeoTransform() ref_proj = ref_ds.GetProjection() ref_cols = ref_ds.RasterXSize ref_rows = ref_ds.RasterYSize ref_ds = None # read warp raster print('Reading source raster {f}'.format(f=warp)) warp_ds = gdal.Open(warp, gdal.GA_ReadOnly) warp_proj = warp_ds.GetProjection() warp_band_num = warp_ds.RasterCount warp_dtype = warp_ds.GetRasterBand(1).DataType # create output file drv = gdal.GetDriverByName(of) out_ds = create_ds(outfile, ref_cols, ref_rows, warp_band_num, warp_dtype, of, co, overwrite) out_ds.SetGeoTransform(ref_geotrans) out_ds.SetProjection(ref_proj) # project raster print('Matching rasters and writing output to {f}'.format(f=outfile)) gdal.ReprojectImage(warp_ds, out_ds, warp_proj, ref_proj, gdalconst.GRA_NearestNeighbour) warp_ds = None out_ds = None print('Done!') return True
def image_segmentation(image, scale_param=10, min_area=9, max_area=None, output=None, of='ESRI Shapefile', overwrite=True): # type: (str, int, int, int, str, str, bool) -> np.array """ Use the Felsenszwalb algorithm for image segmentation. \n Original publication here: http://vision.stanford.edu/teaching/cs231b_spring1415/papers/IJCV2004_FelzenszwalbHuttenlocher.pdf \n Implementation here: https://scikit-image.org/docs/0.14.x/api/skimage.segmentation.html#skimage.segmentation.felzenszwalb :param image: Input image :param scale_param: The number of produced segments as well as their size can only be controlled indirectly through this parameter. Higher values result in larger clusters. :param min_area: Minimum segment size (in pixels) :param max_area: Maximum segment size (in pixels) :param output: Output name in case segments shall be exported :param of: Output format according to OGR driver standard :param overwrite: Overwrite output file, if it already exists :return: """ img = io.imread(image) # img = exposure.equalize_hist(image) if get_raster_properties(image, dictionary=True)['bandnum'] > 1: segments = felzenszwalb(img, multichannel=True, scale=scale_param, min_size=min_area) else: segments = felzenszwalb(img, multichannel=False, scale=scale_param, min_size=min_area) if max_area: segments = remove_large_areas(segments, max_area) if output: from basic_functions.vector_tools import create_ds, close_rings temp = output.replace(os.path.splitext(output)[-1], '__TEMP.tif') export_image(temp, segments, image) ds = gdal.Open(temp) data_band = ds.GetRasterBand(1) sr = osr.SpatialReference() sr.ImportFromEPSG(get_epsg(image)) out_ds, out_lyr = create_ds(output, of, ogr.wkbPolygon, sr, overwrite) gdal.Polygonize(data_band, None, out_lyr, -1, [], callback=None) out_lyr = None out_ds = None ds = None close_rings(output) return segments
def calc_raster_stat(image, output, mode='mean', of='GTiff', co=None, no_data=None): # type: (str, str, str, str, list, int or float) -> None """ Calculate as statistical value per pixel over all bands contained in the input image and write that statistic to a new file. :param image: Input image :param output: Output image :param mode: Statistic to calculate. Possible values are: <br> 'min', 'max', 'mean', 'med', 'sum', 'std', 'all' :param of: the desired format of the output file as provided by the GDAL raster formats (see: http://www.gdal.org/formats_list.html). :param co: Advanced raster creation options such as band interleave or compression. <br> Example: co=['compress=lzw'] :param no_data: Nodata value, that will be ignored for calculation. Has to be the same in all bands. :return: -- """ print('Reading input file ...') ds = gdal.Open(image, gdal.GA_ReadOnly) cols = ds.RasterXSize rows = ds.RasterYSize proj = ds.GetProjection() geotrans = ds.GetGeoTransform() dt = ds.GetRasterBand(1).DataType data = ds.GetRasterBand(1).ReadAsArray() for b in range(2, ds.RasterCount + 1): data = np.dstack((data, ds.GetRasterBand(b).ReadAsArray())) if no_data: data = np.ma.masked_equal(data, no_data) ds = None # calculate desired statistic and write to output file if mode == 'all': modes = ['min', 'max', 'mean', 'med', 'sum', 'std'] out_bands = len(modes) else: out_bands = 1 ds_out = create_ds(output, cols, rows, out_bands, dt, of, co, overwrite=True) ds_out.SetProjection(proj) ds_out.SetGeoTransform(geotrans) print('Calculating statistic(s) ...') if mode == 'min': stat = np.nanmin(data, -1) elif mode == 'max': stat = np.nanmax(data, -1) elif mode == 'mean': stat = np.nanmean(data, -1) elif mode == 'med': stat = np.nanmedian(data, -1) elif mode == 'sum': stat = np.nansum(data, -1) elif mode == 'std': stat = np.nanstd(data, -1) elif mode == 'all': stats = [] stats.append = np.nanmin(data, -1) stats.append = np.nanmax(data, -1) stats.append = np.nanmean(data, -1) stats.append = np.nanmedian(data, -1) stats.append = np.nansum(data, -1) stats.append = np.nanstd(data, -1) else: raise ValueError('Invalid mode!') print('Writing output file ...') if out_bands == 1: if no_data: stat = np.ma.filled(stat, no_data) ds_out.GetRasterBand(1).WriteArray(stat) else: for b in range(out_bands): print('\t ... band {n} ...'.format(n=b + 1)) if no_data: stat = np.ma.filled(stats[b], no_data) else: stat = stats[b] ds_out.GetRasterBand(b + 1).WriteArray(stat) ds_out = None print('Done!') return
def apply_mask(image, mask, outfile, bands=None, of='GTiff', co=None, no_data=0, overwrite=False): # type: (str, str, str, tuple, str, list, int or float, bool) -> None """ Apply a mask containing 0s and 1s to a (multiband) image. :param image: Input image :param mask: Mask image, containing 0s for areas that shall be masked out and 1s for areas to be kept. :param outfile: Output image. :param bands: The desired band numbers of the input images which shall be used for stacking. By this, single bands from multiband-images can be used. Defaults to "None", which means that the first band will be used. If more than one band from the same multiband-image shall be used, the filename must be given again in the "images" list. :param of: the desired format of the output file as provided by the GDAL raster formats (see: http://www.gdal.org/formats_list.html). :param co: Advanced raster creation options such as band interleave or compression. <br> Example: co=['compress=lzw'] :param no_data: NoData-value, given as int or float (depending on the data type of the images). Defaults to the NoData-value of the first input image. <br> Example: co=['compress=lzw'] :param overwrite: Overwrite output file, if it already exists. :return: -- """ ds_img = gdal.Open(image, gdal.GA_ReadOnly) xres, yres = (ds_img.GetGeoTransform()[1], abs(ds_img.GetGeoTransform()[-1])) epsg_img = get_epsg(image) epsg_mask = get_epsg(mask) if epsg_img != epsg_mask: print( 'Image and mask do not share the same coordinate system! Warping mask to EPSG ' '{e}'.format(e=epsg_img)) xmin, xmax, ymin, ymax = get_raster_extent(image) temp = mask.replace( os.path.splitext(mask)[1], '__WARPED__' + os.path.splitext(mask)[1]) if os.path.exists(temp): delete_ds(temp) cmd = [ 'gdalwarp', '-of', 'GTiff', '-t_srs', 'epsg:{e}'.format(e=epsg_img), '-te', str(xmin), str(ymin), str(xmax), str(ymax) ] cmd += ['-tr', str(xres), str(yres), mask, temp] run_cmd(cmd) mask = temp ds_mask = gdal.Open(mask, gdal.GA_ReadOnly) if (ds_img.RasterXSize, ds_img.RasterYSize) != (ds_mask.RasterXSize, ds_mask.RasterYSize): raise AttributeError( 'Image and mask have different numbers of columns and rows! Please ' 'adjust and try again!') if not bands: bands = range(1, ds_img.RasterCount + 1) if not no_data: no_data = ds_img.GetRasterBand(1).GetNoDataValue() ds_out = create_ds(outfile, ds_img.RasterXSize, ds_img.RasterYSize, len(bands), ds_img.GetRasterBand(1).DataType, of, co, overwrite) ds_out.SetProjection(ds_img.GetProjection()) ds_out.SetGeoTransform(ds_img.GetGeoTransform()) ds_out.SetMetadata(ds_img.GetMetadata()) data_mask = ds_mask.GetRasterBand(1).ReadAsArray() if float(np.min(data_mask)) != 0.0: warnings.warn( 'Minimum value of mask is not 0, but {v}! Setting it to 0!'.format( v=np.min(data_mask))) data_mask[data_mask == np.min(data_mask)] = 0 print('Masking band ...') for i, b in enumerate(bands): print('\t...', b) data_img = ds_img.GetRasterBand(b).ReadAsArray() data = data_img * data_mask band_out = ds_out.GetRasterBand(i + 1) band_out.WriteArray(data) band_out.SetNoDataValue(no_data) band_out = None ds_mask = None ds_img = None print('Done!') return
def spectral_subset(image, bands, output, of='GTiff', co=None, no_data=None, band_names=None, overwrite=False): # type: (str, list or tuple, str, str, list, int or float, list, bool) -> None """ Create a spectral subset of a multiband image. :param image: Input image :param bands: List of integers of the desired band numbers to keep. Counting starts at 1. :param output: Output file :param of: The desired format of the output file as provided by the GDAL raster formats (see: http://www.gdal.org/formats_list.html). :param co: Advanced raster creation options such as band interleave or compression. <br> Example: co=['compress=lzw'] :param no_data: NoData-value, given as int or float (depending on the data type of the images). Defaults to the NoData-value of the first input image. :param band_names: List of band names for the output file. Defaults to the names of the input files. :param overwrite: Overwrite output file in case it already exists. """ bands = bands.split(',') try: bands = [int(b) for b in bands] except ValueError: print( 'Band list contains non-integer charaters or a different separator than ","!' ) sys.exit(1) ds = gdal.Open(image, gdal.GA_ReadOnly) if no_data is None: no_data = ds.GetRasterBand(1).GetNoDataValue() dtype = ds.GetRasterBand(1).DataType ds_props = get_raster_properties(image, dictionary=True) ds_out = create_ds(output, ds_props['cols'], ds_props['rows'], len(bands), dtype, of, co, overwrite) ds_out.SetProjection(ds_props['proj']) ds_out.SetGeoTransform(ds_props['geotrans']) meta = {} for b in tqdm(xrange(1, len(bands) + 1), desc='Progress'): band = ds.GetRasterBand(int(bands[b - 1])) band_out = ds_out.GetRasterBand(b) band_out.WriteRaster(band.ReadRaster()) if not band_names: name = '_'.join(['Band', str(bands[b - 1])]) meta[name] = band.GetDescription() band_out.SetDescription(band.GetDescription()) else: name = '_'.join(['Band', str(band_names[b - 1])]) meta[name] = band_names[b] band_out.SetDescription(band_names[b - 1]) band_out.SetNoDataValue(no_data) band = None ds_out.SetMetadata(meta) ds_out = None ds = None print('Done!') return
def stack_images(images, outfile, of='GTiff', co=None, no_data=0, overwrite=False): # type: (tuple, str, str, list, int or float, bool) -> None """ Create a layerstack from the given images. They have to share the same spatial reference system, data type and dimensions. :param images: Input images. Will be stacked in the given order. :param outfile: Output image. :param of: the desired format of the output file as provided by the GDAL raster formats (see: http://www.gdal.org/formats_list.html). :param co: Advanced raster creation options such as band interleave or compression. <br> Example: co=['compress=lzw'] :param no_data: NoData-value, given as int or float (depending on the data type of the images). Defaults to the NoData-value of the first input image. :param overwrite: Overwrite output file in case it already exists. :return: -- """ if os.path.exists(outfile) and overwrite is True: delete_ds(outfile) elif os.path.exists(outfile) and overwrite is False: raise IOError( 'File {f} already exists! To overwrite, use "overwrite=True"!'. format(f=outfile)) print('Stacking {n} images to {s} ...'.format(n=len(images), s=outfile)) cols, rows, __bandnum, dtype, proj, geotrans = get_raster_properties( images[0]) ds = gdal.Open(images[0], gdal.GA_ReadOnly) band = ds.GetRasterBand(1) if no_data is None: nodata = band.GetNoDataValue() else: nodata = no_data band = None ds = None total_band_count = 0 for i in images: ds = gdal.Open(i, gdal.GA_ReadOnly) total_band_count += ds.RasterCount ds = None ds_out = create_ds(outfile, cols, rows, total_band_count, dtype, of, co, overwrite) ds_out.SetProjection(proj) ds_out.SetGeoTransform(geotrans) # loop through all files and stack them: band_index = 1 for i, name in enumerate(images): print('\t... Processing {img}'.format(img=name)) ds = gdal.Open(name, gdal.GA_ReadOnly) for b in range(1, ds.RasterCount + 1): print('\t\t... band {b}'.format(b=b)) band = ds.GetRasterBand(b) data = band.ReadAsArray() band_out = ds_out.GetRasterBand(band_index) # create new band names (either from original image or from user input): band_out.SetDescription(band.GetDescription()) band_out.SetNoDataValue(nodata) band_out.WriteArray(data, 0, 0) band_index += 1 band = None ds = None ds_out = None print('Done!') return