Source code for satproc.console.extract_chips

# -*- coding: utf-8 -*-
"""
This is a skeleton file that can serve as a starting point for a Python
console script. To run this script uncomment the following lines in the
[options.entry_points] section in setup.cfg:

    console_scripts =
         fibonacci = satproc.skeleton:run

Then run `python setup.py install` which will install the command `fibonacci`
inside your current environment.
Besides console scripts, the header (i.e. until _logger...) of this file can
also be used as template for Python modules.

Note: This skeleton file can be safely removed if not needed!
"""

import argparse
import logging
import sys

from satproc import __version__
from satproc.chips import extract_chips
from satproc.utils import get_raster_band_count

__author__ = "Damián Silvani"
__copyright__ = "Dymaxion Labs"
__license__ = "Apache-2.0"

_logger = logging.getLogger(__name__)


[docs]def parse_args(args): """Parse command line parameters Args: args ([str]): command line parameters as list of strings Returns: :obj:`argparse.Namespace`: command line parameters namespace """ parser = argparse.ArgumentParser( description="Extract chips from a raster file, " "and optionally generate mask chips", formatter_class=argparse.ArgumentDefaultsHelpFormatter, ) parser.add_argument("raster", nargs="+", help="input raster file(s)") parser.add_argument( "--size", type=int, default=256, help="size of image tiles, in pixels" ) parser.add_argument( "--step-size", type=int, default=None, help="step size (i.e. stride), in pixels (if None, same as size i.e. no overlap)" ) parser.add_argument( "--sliding-windows-mode", choices=["exact", "whole", "whole_overlap"], default="whole_overlap", help="mode of sliding windows", ) parser.add_argument("--labels", help="inpul label shapefile") parser.add_argument( "--label-property", default="class", help="label property to separate in classes", ) parser.add_argument( "--classes", nargs="+", type=str, help="specify classes order in result mask." ) parser.add_argument( "--masks", "-m", nargs="+", choices=["extent", "boundary", "distance"], default={"extent"}, ) parser.add_argument( "--mask-type", choices=["single", "class", "instance"], default="class" ) parser.add_argument("--aoi", help="Filter by AOI vector file") parser.add_argument( "--within", dest="within", action="store_true", help="only create chip is it is within AOI (if provided)", ) parser.add_argument( "--no-within", dest="within", default=False, action="store_false", help="create chip if it intersects with AOI (if provided)", ) parser.add_argument("-o", "--output-dir", help="output dir", default=".") parser.add_argument( "--rescale", dest="rescale", default=False, action="store_true", help="rescale intensity using percentiles (lower/upper cuts)", ) parser.add_argument( "--no-rescale", dest="rescale", action="store_false", help="do not rescale intensity", ) parser.add_argument( "--rescale-mode", default="percentiles", choices=["percentiles", "values", "s2_rgb_extra"], help="choose mode of intensity rescaling", ) parser.add_argument( "--lower-cut", type=float, default=2, help="(for 'percentiles' mode) lower cut of percentiles " "for cumulative count in intensity rescaling", ) parser.add_argument( "--upper-cut", type=float, default=98, help="(for 'percentiles' mode) upper cut of percentiles " "for cumulative count in intensity rescaling", ) parser.add_argument( "--min", type=float, help="(for 'values' mode) minimum value in intensity rescaling", ) parser.add_argument( "--max", type=float, help="(for 'values' mode) maximum value in intensity rescaling", ) parser.add_argument( "-b", "--bands", nargs="+", type=int, help="specify band indexes. If type is 'jpg', defaults to (1, 2, 3). " "If type is 'tif', defaults to the total band count.", ) parser.add_argument( "-t", "--type", help="output chip format", choices=["jpg", "tif"], default="tif" ) parser.add_argument( "--write-footprints", help="write a GeoJSON file of chip footprints", dest="write_footprints", action="store_true", default=False, ) parser.add_argument( "--no-write-footprints", help="do not write a GeoJSON file of chip footprints", dest="write_footprints", action="store_false", ) parser.add_argument("--crs", help="force CRS of input files") parser.add_argument( "--skip-existing", dest="skip_existing", default=True, action="store_true", help="skip already existing chips (and masks)", ) parser.add_argument( "--no-skip-existing", dest="skip_existing", action="store_false", help="do not skip already existing chips (and masks)", ) parser.add_argument( "--skip-low-contrast", dest="skip_low_contrast", default=False, action="store_true", help="skip image chips with low contrast", ) parser.add_argument( "--no-skip-low-contrast", dest="skip_low_contrast", action="store_false", help="do not skip image chips with low contrast", ) parser.add_argument( "--skip-with-empty-mask", action="store_true", default=True, help="skip chips with empty mask", ) parser.add_argument( "--no-skip-with-empty-mask", action="store_false", dest="skip_with_empty_mask", help="do not skip chips with empty mask", ) parser.add_argument( "--extent-no-border", action="store_true", help="do not include border in extent mask", ) parser.add_argument( "--version", action="version", version="satproc {ver}".format(ver=__version__) ) parser.add_argument( "-v", "--verbose", dest="loglevel", help="set loglevel to INFO", action="store_const", const=logging.INFO, ) parser.add_argument( "-vv", "--very-verbose", dest="loglevel", help="set loglevel to DEBUG", action="store_const", const=logging.DEBUG, ) return parser.parse_args(args), parser
[docs]def setup_logging(loglevel): """Setup basic logging Args: loglevel (int): minimum loglevel for emitting messages """ logformat = "[%(asctime)s] %(levelname)s:%(name)s:%(message)s" logging.basicConfig( level=loglevel, stream=sys.stdout, format=logformat, datefmt="%Y-%m-%d %H:%M:%S" )
[docs]def main(args): """Main entry point allowing external calls Args: args ([str]): command line parameter list """ args, parser = parse_args(args) setup_logging(args.loglevel) bands = args.bands if not bands: if args.type == "jpg": bands = [1, 2, 3] else: some_raster = args.raster[0] bands = list(range(1, get_raster_band_count(some_raster) + 1)) if type == "jpg" and len(bands) != 3: parser.error( f"--type is jpg, but --bands does not have 3 band indexes: {bands}" ) rescale_mode = args.rescale_mode if args.rescale else None if rescale_mode == "percentiles": rescale_range = (args.lower_cut, args.upper_cut) _logger.info("Rescale intensity with percentiles %s", rescale_range) elif rescale_mode == "values": rescale_range = (args.min, args.max) _logger.info("Rescale intensity with values %s", rescale_range) elif rescale_mode == "s2_rgb_extra": rescale_range = (args.lower_cut, args.upper_cut) _logger.info( "Rescale intensity of first 3 bands (RGB) to (0, 0.3) " "and the rest of the bands with percentiles %s", rescale_range, ) else: rescale_range = None _logger.info("No rescale intensity") _logger.info("Extract chips") extract_chips( args.raster, aoi=args.aoi, labels=args.labels, label_property=args.label_property, masks=args.masks, mask_type=args.mask_type, extent_no_border=args.extent_no_border, rescale_mode=rescale_mode, rescale_range=rescale_range, bands=bands, chip_type=args.type, within=args.within, write_footprints=args.write_footprints, classes=args.classes, crs=args.crs, skip_existing=args.skip_existing, skip_low_contrast=args.skip_low_contrast, skip_with_empty_mask=args.skip_with_empty_mask, size=args.size, step_size=args.step_size, windows_mode=args.sliding_windows_mode, output_dir=args.output_dir, )
[docs]def run(): """Entry point for console_scripts""" main(sys.argv[1:])
if __name__ == "__main__": run()