Commit 583bc6ce authored by Daniel Scheffler's avatar Daniel Scheffler
Browse files

added console argument parser for COREG_LOCAL; some bugfixes and further developments

components.CoReg:
- imParams: mask is now calculated from only one band -> avoids reading the whole datacube

components.CoReg_local:
- COREG_LOCAL:
    - __init__(): some docstring modifications
    - added attribute '_success'
    - property quality_grid: now shows calculated coreg points figure in verbose mode
    - view_CoRegPoints(): added outlier filtering for points in figure
    - correct_shifts(): bugfix for trying to corrects shifts without GCPs

components.Geom_Quality_Grid.Geom_Quality_Grid:
- GCPList: bugfix for running to_GCPList() twice
- get_CoRegPoints_table(): disabled verbose mode during grid calculation
- to_GCPList(): now returns an empty list if no valid matches are available

MAIN:
- major revision; added subparsers; added argparser for COREG_LOCAL
- added run_global_coreg()
- added run_local_coreg()

- updated __version__
parent cc24e971
...@@ -9,7 +9,7 @@ from .components import utilities ...@@ -9,7 +9,7 @@ from .components import utilities
from .components import geometry from .components import geometry
__author__ = 'Daniel Scheffler' __author__ = 'Daniel Scheffler'
__version__= '2016-11-02_01' __version__= '2016-11-03_01'
__all__=['COREG', __all__=['COREG',
'COREG_LOCAL', 'COREG_LOCAL',
......
...@@ -99,7 +99,9 @@ class imParamObj(object): ...@@ -99,7 +99,9 @@ class imParamObj(object):
# set footprint polygon # set footprint polygon
#self.poly = get_footprint_polygon(self.corner_coord, fix_invalid=True) # this is the old algorithm #self.poly = get_footprint_polygon(self.corner_coord, fix_invalid=True) # this is the old algorithm
self.GeoArray.calc_mask_nodata(fromBand=self.band4match) # this avoids that are bands have to be read
self.poly = self.GeoArray.footprint_poly self.poly = self.GeoArray.footprint_poly
for XY in self.corner_coord: for XY in self.corner_coord:
assert self.GeoArray.box.mapPoly.contains(Point(XY)) or self.GeoArray.box.mapPoly.touches(Point(XY)), \ assert self.GeoArray.box.mapPoly.contains(Point(XY)) or self.GeoArray.box.mapPoly.touches(Point(XY)), \
"The corner position '%s' is outside of the %s." % (XY, self.imName) "The corner position '%s' is outside of the %s." % (XY, self.imName)
......
...@@ -29,15 +29,15 @@ class COREG_LOCAL(object): ...@@ -29,15 +29,15 @@ class COREG_LOCAL(object):
"""Applies the algorithm to detect spatial shifts to the whole overlap area of the input images. Spatial shifts """Applies the algorithm to detect spatial shifts to the whole overlap area of the input images. Spatial shifts
are calculated for each point in grid of which the parameters can be adjusted using keyword arguments. Shift are calculated for each point in grid of which the parameters can be adjusted using keyword arguments. Shift
correction performs a polynomial transformation using te calculated shifts of each point in the grid as GCPs. correction performs a polynomial transformation using the calculated shifts of each point in the grid as GCPs.
Thus 'COREG_LOCAL' can be used to correct for locally varying geometric distortions of the target image. Thus this class can be used to correct for locally varying geometric distortions of the target image.
:param im_ref(str, GeoArray): source path of reference image (any GDAL compatible image format is supported) :param im_ref(str, GeoArray): source path of reference image (any GDAL compatible image format is supported)
:param im_tgt(str, GeoArray): source path of image to be shifted (any GDAL compatible image format is supported) :param im_tgt(str, GeoArray): source path of image to be shifted (any GDAL compatible image format is supported)
:param grid_res: grid resolution in pixels of the target image :param grid_res: quality grid resolution in pixels of the target image
:param window_size(tuple): custom matching window size [pixels] (default: (256,256)) :param window_size(tuple): custom matching window size [pixels] (default: (256,256))
:param path_out(str): target path of the coregistered image :param path_out(str): target path of the coregistered image
- if None (default), the method correct_shifts() does not write to disk - if None (default), no output is written to disk
- if 'auto': /dir/of/im1/<im1>__shifted_to__<im0>.bsq - if 'auto': /dir/of/im1/<im1>__shifted_to__<im0>.bsq
:param fmt_out(str): raster file format for output file. ignored if path_out is None. can be any GDAL :param fmt_out(str): raster file format for output file. ignored if path_out is None. can be any GDAL
compatible raster file format (e.g. 'ENVI', 'GeoTIFF'; default: ENVI) compatible raster file format (e.g. 'ENVI', 'GeoTIFF'; default: ENVI)
...@@ -61,6 +61,9 @@ class COREG_LOCAL(object): ...@@ -61,6 +61,9 @@ class COREG_LOCAL(object):
:param v(bool): verbose mode (default: False) :param v(bool): verbose mode (default: False)
:param q(bool): quiet mode (default: False) :param q(bool): quiet mode (default: False)
""" """
# TODO outgsd, matchgsd
# assertions
assert fmt_out
self.params = dict([x for x in locals().items() if x[0] != "self" and not x[0].startswith('__')]) self.params = dict([x for x in locals().items() if x[0] != "self" and not x[0].startswith('__')])
...@@ -81,6 +84,7 @@ class COREG_LOCAL(object): ...@@ -81,6 +84,7 @@ class COREG_LOCAL(object):
self.outFillVal = outFillVal self.outFillVal = outFillVal
self.bin_ws = binary_ws self.bin_ws = binary_ws
self.CPUs = CPUs self.CPUs = CPUs
self.path_verbose_out = '' # TODO
self.v = v self.v = v
self.q = q if not v else False # overridden by v self.q = q if not v else False # overridden by v
self.progress = progress if not q else False # overridden by v self.progress = progress if not q else False # overridden by v
...@@ -112,6 +116,7 @@ class COREG_LOCAL(object): ...@@ -112,6 +116,7 @@ class COREG_LOCAL(object):
self._CoRegPoints_table = None # set by self.CoRegPoints_table self._CoRegPoints_table = None # set by self.CoRegPoints_table
self._coreg_info = None # set by self.coreg_info self._coreg_info = None # set by self.coreg_info
self.deshift_results = None # set by self.correct_shifts() self.deshift_results = None # set by self.correct_shifts()
self._success = None # set by self.success property
@property @property
...@@ -138,6 +143,8 @@ class COREG_LOCAL(object): ...@@ -138,6 +143,8 @@ class COREG_LOCAL(object):
self._quality_grid = Geom_Quality_Grid(self.COREG_obj, self.grid_res, outFillVal=self.outFillVal, self._quality_grid = Geom_Quality_Grid(self.COREG_obj, self.grid_res, outFillVal=self.outFillVal,
dir_out=self.projectDir, CPUs=self.CPUs, progress=self.progress, dir_out=self.projectDir, CPUs=self.CPUs, progress=self.progress,
v=self.v, q=self.q) v=self.v, q=self.q)
if self.v:
self.view_CoRegPoints(figsize=(10,10))
return self._quality_grid return self._quality_grid
...@@ -146,6 +153,14 @@ class COREG_LOCAL(object): ...@@ -146,6 +153,14 @@ class COREG_LOCAL(object):
return self.quality_grid.CoRegPoints_table return self.quality_grid.CoRegPoints_table
@property
def success(self):
self._success = self.quality_grid.GCPList != []
if not self._success and not self.q:
warnings.warn('No valid GCPs could by identified.')
return self._success
def view_CoRegPoints(self, attribute2plot='ABS_SHIFT', cmap=None, exclude_fillVals=True, backgroundIm='tgt', def view_CoRegPoints(self, attribute2plot='ABS_SHIFT', cmap=None, exclude_fillVals=True, backgroundIm='tgt',
figsize=None, savefigPath='', savefigDPI=96): figsize=None, savefigPath='', savefigDPI=96):
"""Shows a map of the calculated quality grid with the target image as background. """Shows a map of the calculated quality grid with the target image as background.
...@@ -190,15 +205,17 @@ class COREG_LOCAL(object): ...@@ -190,15 +205,17 @@ class COREG_LOCAL(object):
GDF['plt_XY'] = list(GDF['LonLat'].map(lambda ll: map2show(*ll))) GDF['plt_XY'] = list(GDF['LonLat'].map(lambda ll: map2show(*ll)))
GDF['plt_X'] = list(GDF['plt_XY'].map(lambda XY: XY[0])) GDF['plt_X'] = list(GDF['plt_XY'].map(lambda XY: XY[0]))
GDF['plt_Y'] = list(GDF['plt_XY'].map(lambda XY: XY[1])) GDF['plt_Y'] = list(GDF['plt_XY'].map(lambda XY: XY[1]))
vmin, vmax = np.percentile(GDF[attribute2plot], 0), np.percentile(GDF[attribute2plot], 95)
points = plt.scatter(GDF['plt_X'],GDF['plt_Y'], c=GDF[attribute2plot], points = plt.scatter(GDF['plt_X'],GDF['plt_Y'], c=GDF[attribute2plot],
cmap=palette, marker='o' if len(GDF)<10000 else '.', s=50, alpha=1.0) cmap=palette, marker='o' if len(GDF)<10000 else '.', s=50, alpha=1.0,
vmin=vmin, vmax=vmax)
# add colorbar # add colorbar
divider = make_axes_locatable(plt.gca()) divider = make_axes_locatable(plt.gca())
cax = divider.append_axes("right", size="2%", pad=0.1) # create axis on the right; size =2% of ax; padding = 0.1 inch cax = divider.append_axes("right", size="2%", pad=0.1) # create axis on the right; size =2% of ax; padding = 0.1 inch
plt.colorbar(points, cax=cax) plt.colorbar(points, cax=cax)
plt.show() plt.show(block=True)
if savefigPath: if savefigPath:
fig.savefig(savefigPath, dpi=savefigDPI) fig.savefig(savefigPath, dpi=savefigDPI)
...@@ -261,10 +278,11 @@ class COREG_LOCAL(object): ...@@ -261,10 +278,11 @@ class COREG_LOCAL(object):
'reference grid' : [ [self.imref.gt[0], self.imref.gt[0]+self.imref.gt[1]], 'reference grid' : [ [self.imref.gt[0], self.imref.gt[0]+self.imref.gt[1]],
[self.imref.gt[3], self.imref.gt[3]+self.imref.gt[5]] ], [self.imref.gt[3], self.imref.gt[3]+self.imref.gt[5]] ],
'reference extent' : {'cols':self.imref.xgsd, 'rows':self.imref.ygsd}, # FIXME not needed anymore 'reference extent' : {'cols':self.imref.xgsd, 'rows':self.imref.ygsd}, # FIXME not needed anymore
#'success' : self.success 'success' : self.success
} }
return self.coreg_info return self.coreg_info
def correct_shifts(self, max_GCP_count=None, cliptoextent=False): def correct_shifts(self, max_GCP_count=None, cliptoextent=False):
"""Performs a local shift correction using all points from the previously calculated geometric quality grid """Performs a local shift correction using all points from the previously calculated geometric quality grid
that contain valid matches as GCP points. that contain valid matches as GCP points.
...@@ -276,19 +294,23 @@ class COREG_LOCAL(object): ...@@ -276,19 +294,23 @@ class COREG_LOCAL(object):
coreg_info = self.coreg_info coreg_info = self.coreg_info
if max_GCP_count: if self.quality_grid.GCPList:
coreg_info['GCPList'] = coreg_info['GCPList'][:max_GCP_count] # TODO should be a random sample if max_GCP_count:
coreg_info['GCPList'] = coreg_info['GCPList'][:max_GCP_count] # TODO should be a random sample
DS = DESHIFTER(self.im2shift, coreg_info,
path_out = self.path_out, DS = DESHIFTER(self.im2shift, coreg_info,
fmt_out = self.fmt_out, path_out = self.path_out,
out_gsd = (self.im2shift.xgsd,self.im2shift.ygsd), fmt_out = self.fmt_out,
align_grids = True, out_gsd = (self.im2shift.xgsd,self.im2shift.ygsd),
cliptoextent = cliptoextent, align_grids = True,
#clipextent = self.im2shift.box.boxMapYX, cliptoextent = cliptoextent,
progress = self.progress, #clipextent = self.im2shift.box.boxMapYX,
v = self.v, progress = self.progress,
q = self.q) v = self.v,
q = self.q)
self.deshift_results = DS.correct_shifts()
return self.deshift_results self.deshift_results = DS.correct_shifts()
return self.deshift_results
else:
if not self.q:
warnings.warn('Correction of geometric shifts failed because the input GCP list is empty!')
...@@ -91,7 +91,7 @@ class Geom_Quality_Grid(object): ...@@ -91,7 +91,7 @@ class Geom_Quality_Grid(object):
return self._GCPList return self._GCPList
else: else:
self._GCPList = self.to_GCPList() self._GCPList = self.to_GCPList()
return self.to_GCPList() return self._GCPList
@GCPList.setter @GCPList.setter
...@@ -207,8 +207,8 @@ class Geom_Quality_Grid(object): ...@@ -207,8 +207,8 @@ class Geom_Quality_Grid(object):
'max_shift' : self.COREG_obj.max_shift, 'max_shift' : self.COREG_obj.max_shift,
'nodata' : (self.COREG_obj.ref.nodata, self.COREG_obj.shift.nodata), 'nodata' : (self.COREG_obj.ref.nodata, self.COREG_obj.shift.nodata),
'binary_ws' : self.COREG_obj.bin_ws, 'binary_ws' : self.COREG_obj.bin_ws,
'v' : self.v, # FIXME this could lead to massive console output 'v' : False, # otherwise this would lead to massive console output
'q' : True, # otherwise this would lead to massive console output 'q' : True, # otherwise this would lead to massive console output
'ignore_errors' : True 'ignore_errors' : True
} }
list_coreg_kwargs = (get_coreg_kwargs(i, self.XY_mapPoints[i]) for i in GDF.index) # generator list_coreg_kwargs = (get_coreg_kwargs(i, self.XY_mapPoints[i]) for i in GDF.index) # generator
...@@ -251,7 +251,7 @@ class Geom_Quality_Grid(object): ...@@ -251,7 +251,7 @@ class Geom_Quality_Grid(object):
GDF = GDF.merge(records, on='POINT_ID', how="inner") GDF = GDF.merge(records, on='POINT_ID', how="inner")
GDF = GDF.fillna(int(self.outFillVal)) GDF = GDF.fillna(int(self.outFillVal))
self.CoRegPoints_table = GDF # TODO catch GDF with no found matches self.CoRegPoints_table = GDF
return GDF return GDF
...@@ -268,13 +268,20 @@ class Geom_Quality_Grid(object): ...@@ -268,13 +268,20 @@ class Geom_Quality_Grid(object):
# get copy of quality grid without no data # get copy of quality grid without no data
GDF = self.CoRegPoints_table.loc[self.CoRegPoints_table.X_SHIFT_M != self.outFillVal, :].copy() GDF = self.CoRegPoints_table.loc[self.CoRegPoints_table.X_SHIFT_M != self.outFillVal, :].copy()
# calculate GCPs if getattr(GDF,'empty'): # GDF.empty returns AttributeError
GDF['X_UTM_new'] = GDF.X_UTM + GDF.X_SHIFT_M return []
GDF['Y_UTM_new'] = GDF.Y_UTM + GDF.Y_SHIFT_M else:
GDF['GCP'] = GDF.apply(lambda GDF_row: gdal.GCP(GDF_row.X_UTM_new, GDF_row.Y_UTM_new, 0, # calculate GCPs
GDF_row.X_IM, GDF_row.Y_IM), axis=1) GDF['X_UTM_new'] = GDF.X_UTM + GDF.X_SHIFT_M
self.GCPList = GDF.GCP.tolist() GDF['Y_UTM_new'] = GDF.Y_UTM + GDF.Y_SHIFT_M
return self.GCPList GDF['GCP'] = GDF.apply(lambda GDF_row: gdal.GCP(GDF_row.X_UTM_new, GDF_row.Y_UTM_new, 0,
GDF_row.X_IM, GDF_row.Y_IM), axis=1)
self.GCPList = GDF.GCP.tolist()
if not self.q:
print('Found %s valid GCPs.' %len(self.GCPList))
return self.GCPList
def test_if_singleprocessing_equals_multiprocessing_result(self): def test_if_singleprocessing_equals_multiprocessing_result(self):
......
...@@ -23,10 +23,10 @@ except ImportError: ...@@ -23,10 +23,10 @@ except ImportError:
# internal modules # internal modules
try: try:
from CoReg_Sat import COREG, __version__ from CoReg_Sat import COREG, COREG_LOCAL, __version__
except ImportError: except ImportError:
sys.path.append(os.path.abspath(os.path.join(os.path.dirname( __file__ ), '..'))) sys.path.append(os.path.abspath(os.path.join(os.path.dirname( __file__ ), '..')))
from CoReg_Sat import COREG, __version__ from CoReg_Sat import COREG, COREG_LOCAL, __version__
try: try:
import py_tools_ds import py_tools_ds
...@@ -36,6 +36,62 @@ except ImportError: ...@@ -36,6 +36,62 @@ except ImportError:
#sub-command functions
def run_global_coreg(args):
COREG_obj = COREG(args.path_im0,
args.path_im1,
path_out = args.path_out,
r_b4match = args.br,
s_b4match = args.bs,
wp = args.wp,
ws = args.ws,
max_iter = args.max_iter,
max_shift = args.max_shift,
align_grids = args.align_grids,
match_gsd = args.match_gsd,
out_gsd = args.out_gsd,
data_corners_im0 = args.cor0,
data_corners_im1 = args.cor1,
nodata = args.nodata,
calc_corners = args.calc_cor,
multiproc = args.mp,
binary_ws = args.bin_ws,
v = args.v,
path_verbose_out = args.vo,
q = args.q,
ignore_errors = args.ignore_errors)
COREG_obj.correct_shifts()
#sub-command functions
def run_local_coreg(args):
CRL = COREG_LOCAL(args.path_im0,
args.path_im1,
path_out = args.path_out,
fmt_out = args.fmt_out,
grid_res = args.grid_res,
r_b4match = args.br,
s_b4match = args.bs,
window_size = args.ws,
max_iter = args.max_iter,
max_shift = args.max_shift,
#align_grids = args.align_grids,
#match_gsd = args.match_gsd,
#out_gsd = args.out_gsd,
data_corners_im0 = args.cor0,
data_corners_im1 = args.cor1,
nodata = args.nodata,
calc_corners = args.calc_cor,
CPUs = None if args.mp else 1,
binary_ws = args.bin_ws,
progress = args.progress,
v = args.v,
q = args.q,
)
CRL.correct_shifts()
if __name__ == '__main__': if __name__ == '__main__':
import argparse import argparse
from socket import gethostname from socket import gethostname
...@@ -78,81 +134,155 @@ if __name__ == '__main__': ...@@ -78,81 +134,155 @@ if __name__ == '__main__':
"The following non-standard Python libraries are required: gdal, osr, ogr, geopandas, rasterio, pykrige, "\ "The following non-standard Python libraries are required: gdal, osr, ogr, geopandas, rasterio, pykrige, "\
"argparse and shapely. pyfftw is optional but will speed up calculation.") "argparse and shapely. pyfftw is optional but will speed up calculation.")
parser.add_argument('path_im0', parser.add_argument('--version', action='version', version=__version__)
type=str,
help='source path of reference image (any GDAL compatible image format is supported)') subparsers = parser.add_subparsers()
parser.add_argument('path_im1', # TODO add option to apply coreg results to multiple files
type=str, ### SUBPARSER FOR COREG
help='source path of image to be shifted (any GDAL compatible image format is supported)') parse_coreg_global = subparsers.add_parser('global',
description= None,
help='detect and correct global X/Y shifts (sub argument parser)')
parser.add_argument('-o', nargs='?', dest='path_out', type=str, gloArg = parse_coreg_global.add_argument
help="target path of the coregistered image (default: /dir/of/im1/<im1>__shifted_to__<im0>.bsq)", gloArg('path_im0', type=str, help='source path of reference image (any GDAL compatible image format is supported)')
default='auto')
parser.add_argument('-br', nargs='?',type=int, help='band of reference image to be used for matching '\ gloArg('path_im1', type=str, help='source path of image to be shifted (any GDAL compatible image format is supported)')
'(starts with 1; default: 1)', default=1)
parser.add_argument('-bs', nargs='?',type=int, help='band of shift image to be used for matching '\ gloArg('-o', nargs='?', dest='path_out', type=str, default='auto',
'(starts with 1; default: 1)', default=1) help="target path of the coregistered image (default: /dir/of/im1/<im1>__shifted_to__<im0>.bsq)")
parser.add_argument('-wp', nargs=2, metavar=('X', 'Y'),type=float,help="custom matching window position as map "\ gloArg('-br', nargs='?', type=int,
"values in the same projection like the reference image "\ help='band of reference image to be used for matching (starts with 1; default: 1)', default=1)
"(default: central position of image overlap)", default=(None,None))
parser.add_argument('-ws', nargs=2, metavar=('X size', 'Y size'),type=float, gloArg('-bs', nargs='?', type=int,
help="custom matching window size [pixels] (default: (512,512))", default=(512,512)) help='band of shift image to be used for matching (starts with 1; default: 1)', default=1)
parser.add_argument('-max_iter', nargs='?', type=int,help="maximum number of iterations for matching (default: 5)", gloArg('-wp', nargs=2, metavar=('X', 'Y'), type=float,
default=5) help="custom matching window position as map values in the same projection like the reference image "
"(default: central position of image overlap)", default=(None,None))
parser.add_argument('-max_shift', nargs='?', type=int,help="maximum shift distance in reference image pixel units "\ gloArg('-ws', nargs=2, metavar=('X size', 'Y size'), type=float,
"(default: 5 px)", default=5) help="custom matching window size [pixels] (default: (512,512))", default=(512,512))
parser.add_argument('-align_grids', nargs='?',type=int, help='align the coordinate grids of the output image to '\ gloArg('-max_iter', nargs='?', type=int, help="maximum number of iterations for matching (default: 5)", default=5)
'the reference image (default: 0)', default=0, choices=[0,1])
parser.add_argument('-match_gsd', nargs='?',type=int, help='match the output pixel size to the pixel size of the '\ gloArg('-max_shift', nargs='?', type=int,
'reference image (default: 0)', default=0, choices=[0,1]) help="maximum shift distance in reference image pixel units (default: 5 px)", default=5)
parser.add_argument('-out_gsd', nargs=2,type=float, help='xgsd ygsd: set the output pixel size in map units'\ gloArg('-align_grids', nargs='?', type=int, choices=[0, 1],
'(default: original pixel size of the image to be shifted)',metavar=('xgsd','ygsd')) help='align the coordinate grids of the output image to the reference image (default: 0)', default=0)
parser.add_argument('-cor0', nargs=8,type=float, help="map coordinates of data corners within reference image: ", gloArg('-match_gsd', nargs='?', type=int, choices=[0, 1],
metavar=tuple("UL-X UL-Y UR-X UR-Y LR-X LR-Y LL-X LL-Y".split(' ')),default=None) help='match the output pixel size to the pixel size of the reference image (default: 0)', default=0)
parser.add_argument('-cor1', nargs=8,type=float,help="map coordinates of data corners within image to be shifted: ", gloArg('-out_gsd', nargs=2, type=float, help='xgsd ygsd: set the output pixel size in map units'\
metavar=tuple("UL-X UL-Y UR-X UR-Y LR-X LR-Y LL-X LL-Y".split(' ')),default=None) '(default: original pixel size of the image to be shifted)', metavar=('xgsd','ygsd'))
parser.add_argument('-nodata', nargs=2,type=float, metavar=('im0','im1'), gloArg('-cor0', nargs=8, type=float, help="map coordinates of data corners within reference image: ",
help='no data values for reference image and image to be shifted',default=(None,None)) metavar=tuple("UL-X UL-Y UR-X UR-Y LR-X LR-Y LL-X LL-Y".split(' ')), default=None)
parser.add_argument('-calc_cor', nargs='?',type=int, choices=[0,1],default=1, help="calculate true positions of "\ gloArg('-cor1', nargs=8, type=float, help="map coordinates of data corners within image to be shifted: ",
"the dataset corners in order to get a useful matching window position within the actual "\ metavar=tuple("UL-X UL-Y UR-X UR-Y LR-X LR-Y LL-X LL-Y".split(' ')), default=None)
"image overlap (default: 1; deactivated if '-cor0' and '-cor1' are given")
parser.add_argument('-mp', nargs='?', type=int, help='enable multiprocessing (default: 1)', default=1, choices=[0,1]) gloArg('-nodata', nargs=2, type=float, metavar=('im0', 'im1'),
help='no data values for reference image and image to be shifted', default=(None,None))
parser.add_argument('-bin_ws', nargs='?', type=int, help='use binary X/Y dimensions for the matching window ' gloArg('-calc_cor', nargs='?', type=int, choices=[0, 1], default=1,
'(default: 1)', default=1, choices=[0, 1]) help="calculate true positions of the dataset corners in order to get a useful matching window position "
"within the actual image overlap (default: 1; deactivated if '-cor0' and '-cor1' are given")
parser.add_argument('-quadratic_win', nargs='?', type=int, help='force a quadratic matching window (default: 1)', gloArg('-mp', nargs='?', type=int, help='enable multiprocessing (default: 1)', default=1, choices=[0, 1])
default=1, choices=[0, 1])
parser.add_argument('-v', nargs='?',type=int, help='verbose mode (default: 0)', default=0, choices=[0,1]) gloArg('-bin_ws', nargs='?', type=int,
help='use binary X/Y dimensions for the matching window (default: 1)', default=1, choices=[0, 1])
parser.add_argument('-vo', nargs='?',type=str, help='an optional output directory for intermediate results ' gloArg('-quadratic_win', nargs='?', type=int,
'(if not given, no intermediate results are written to disk)', default=0, choices=[0,1]) help='force a quadratic matching window (default: 1)', default=1, choices=[0, 1])
parser.add_argument('-q', nargs='?',type=int, help='quiet mode (default: 0)', default=0, choices=[0,1]) gloArg('-v', nargs='?', type=int, help='verbose mode (default: 0)', default=0, choices=[0, 1])
gloArg('-vo', nargs='?', type=str, choices=[0, 1], help='an optional output directory for outputs of verbose mode'
'(if not given, no outputs are written to disk)', default=0, )
gloArg('-q', nargs='?', type=int, help='quiet mode (default: 0)', default=0, choices=[0, 1])
gloArg('-ignore_errors', nargs='?', type=int, help='Useful for batch processing. (default: 0) In case of error '
'COREG.success == False and COREG.x_shift_px/COREG.y_shift_px is None', default=0, choices=[0,1])
parse_coreg_global.set_defaults(func=run_global_coreg)
parser.add_argument('-ignore_errors', nargs='?',type=int, help='Useful for batch processing. (default: 0) '
'In case of error COREG.success == False and COREG.x_shift_px/COREG.y_shift_px is None',
default=0, choices=[0,1])
parser.add_argument('--version', action='version', version=__version__)
args = parser.parse_args() ### SUBPARSER FOR COREG LOCAL
parse_coreg_local = subparsers.add_parser('local',
description= 'Applies the algorithm to detect spatial shifts to the whole overlap area of the input images. '
'Spatial shifts are calculated for each point in grid of which the parameters can be adjusted '
'using keyword arguments. Shift correction performs a polynomial transformation using the '
'calculated shifts of each point in the grid as GCPs. Thus this class can be used to correct ' \
'for locally varying geometric distortions of the target image.',
help='detect and correct local shifts (sub argument parser)')
locArg = parse_coreg_local.add_argument
locArg('path_im0', type=str, help='source path of reference image (any GDAL compatible image format is supported)')
locArg('path_im1', type=str, help='source path of image to be shifted (any GDAL compatible image format is supported)')
locArg('grid_res', type=int, help='quality grid resolution in pixels of the target image')
locArg('-o', nargs='?', type=str, dest='path_out', default='auto',
help="target path of the coregistered image. If 'auto' (default): /dir/of/im1/<im1>__shifted_to__<im0>.bsq")
locArg('-fmt_out', nargs='?', type=str, help="raster file format for output file. ignored if path_out is None. can "
"be any GDAL compatible raster file format (e.g. 'ENVI', 'GeoTIFF'; default: ENVI)", default='ENVI')
locArg('-projectDir', nargs='?', type=str, help=None, default=None)
locArg('-ws', nargs=2, type=int, help='custom matching window size [pixels] (default: (256,256))')
locArg('-br', nargs='?', type=int,
help='band of reference image to be used for matching (starts with 1; default: 1)', default=1)
locArg('-bs', nargs='?', type=int,
help='band of shift image to be used for matching (starts with 1; default: 1)', default=1)
locArg('-max_iter', nargs='?', type=int, help="maximum number of iterations for matching (default: 5)", default=5)
locArg('-max_shift', nargs='?', type=int,
help="maximum shift distance in reference image pixel units (default: 5 px)", default=5)
locArg('-cor0', nargs=8, type=float, help="map coordinates of data corners within reference image: ",
metavar=tuple("UL-X UL-Y UR-X UR-Y LR-X LR-Y LL-X LL-Y".split(' ')), default=None)
locArg('-cor1', nargs=8, type=float, help="map coordinates of data corners within image to be shifted: ",
metavar=tuple("UL-X UL-Y UR-X UR-Y LR-X LR-Y LL-X LL-Y".split(' ')), default=None)
locArg('-nodata', nargs=2, type=float, metavar=('im0', 'im1'),
help='no data values for reference image and image to be shifted', default=(None,None))