Esempio n. 1
0
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
Esempio n. 2
0
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
Esempio n. 3
0
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
Esempio n. 4
0
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
Esempio n. 5
0
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
Esempio n. 6
0
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
Esempio n. 7
0
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