2020-04-28 22:28:58 +00:00
#!/usr/bin/env python3
2020-05-29 20:37:07 +00:00
# Author: Minyan Zhong, Lijun Zhu
2020-04-28 22:28:58 +00:00
import os
2020-05-29 20:37:07 +00:00
import argparse
import numpy as np
2020-04-28 22:28:58 +00:00
import isce
import isceobj
from isceobj . Util . decorators import use_api
from contrib . PyCuAmpcor . PyCuAmpcor import PyCuAmpcor
2020-05-29 20:37:07 +00:00
EXAMPLE = ''' example
2020-11-12 23:02:44 +00:00
cuDenseOffsets . py - r . / merged / SLC / 20151120 / 20151120. slc . full - s . / merged / SLC / 20151214 / 20151214. slc . full
- - outprefix . / merged / offsets / 20151120_20151214 / offset
2020-05-29 20:37:07 +00:00
- - ww 256 - - wh 256 - - oo 32 - - kw 300 - - kh 100 - - nwac 100 - - nwdc 1 - - sw 8 - - sh 8 - - gpuid 2
'''
2020-04-28 22:28:58 +00:00
def createParser ( ) :
'''
Command line parser .
'''
2020-07-02 22:03:10 +00:00
2020-05-29 20:37:07 +00:00
parser = argparse . ArgumentParser ( description = ' Generate offset field between two Sentinel slc ' ,
formatter_class = argparse . RawTextHelpFormatter ,
epilog = EXAMPLE )
2020-11-12 23:02:44 +00:00
# input/output
parser . add_argument ( ' -r ' , ' --reference ' , type = str , dest = ' reference ' , required = True ,
2020-07-02 22:03:10 +00:00
help = ' Reference image ' )
2020-07-02 19:40:49 +00:00
parser . add_argument ( ' -s ' , ' --secondary ' , type = str , dest = ' secondary ' , required = True ,
2020-07-02 22:03:10 +00:00
help = ' Secondary image ' )
2020-05-29 20:37:07 +00:00
parser . add_argument ( ' --op ' , ' --outprefix ' , ' --output-prefix ' , type = str , dest = ' outprefix ' ,
default = ' offset ' , required = True ,
help = ' Output prefix, default: offset. ' )
parser . add_argument ( ' --os ' , ' --outsuffix ' , type = str , dest = ' outsuffix ' , default = ' ' ,
help = ' Output suffix, default:. ' )
2020-11-12 23:02:44 +00:00
# window size settings
2020-04-28 22:28:58 +00:00
parser . add_argument ( ' --ww ' , type = int , dest = ' winwidth ' , default = 64 ,
2020-05-29 20:37:07 +00:00
help = ' Window width (default: %(default)s ). ' )
2020-04-28 22:28:58 +00:00
parser . add_argument ( ' --wh ' , type = int , dest = ' winhgt ' , default = 64 ,
2020-05-29 20:37:07 +00:00
help = ' Window height (default: %(default)s ). ' )
2020-11-12 23:02:44 +00:00
parser . add_argument ( ' --sw ' , type = int , dest = ' srcwidth ' , default = 20 ,
help = ' Half search range along width, (default: %(default)s , recommend: 4-32). ' )
parser . add_argument ( ' --sh ' , type = int , dest = ' srchgt ' , default = 20 ,
help = ' Half search range along height (default: %(default)s , recommend: 4-32). ' )
2020-04-28 22:28:58 +00:00
parser . add_argument ( ' --kw ' , type = int , dest = ' skipwidth ' , default = 64 ,
2020-05-29 20:37:07 +00:00
help = ' Skip across (default: %(default)s ). ' )
2020-04-28 22:28:58 +00:00
parser . add_argument ( ' --kh ' , type = int , dest = ' skiphgt ' , default = 64 ,
2020-05-29 20:37:07 +00:00
help = ' Skip down (default: %(default)s ). ' )
2020-11-12 23:02:44 +00:00
# determine the number of windows
# either specify the starting pixel and the number of windows,
# or by setting them to -1, let the script to compute these parameters
parser . add_argument ( ' --mm ' , type = int , dest = ' margin ' , default = 0 ,
help = ' Margin (default: %(default)s ). ' )
parser . add_argument ( ' --nwa ' , type = int , dest = ' numWinAcross ' , default = - 1 ,
help = ' Number of window across (default: %(default)s to be auto-determined). ' )
parser . add_argument ( ' --nwd ' , type = int , dest = ' numWinDown ' , default = - 1 ,
help = ' Number of window down (default: %(default)s ). ' )
parser . add_argument ( ' --startpixelac ' , dest = ' startpixelac ' , type = int , default = - 1 ,
help = ' Starting Pixel across of the reference image(default: %(default)s to be determined by margin and search range). ' )
parser . add_argument ( ' --startpixeldw ' , dest = ' startpixeldw ' , type = int , default = - 1 ,
help = ' Starting Pixel down of the reference image (default: %(default)s ). ' )
# cross-correlation algorithm
parser . add_argument ( ' --alg ' , ' --algorithm ' , dest = ' algorithm ' , type = int , default = 0 ,
help = ' cross-correlation algorithm (0 = frequency domain, 1 = time domain) (default: %(default)s ). ' )
2020-05-29 20:37:07 +00:00
parser . add_argument ( ' --raw-osf ' , ' --raw-over-samp-factor ' , type = int , dest = ' raw_oversample ' ,
default = 2 , choices = range ( 2 , 5 ) ,
2020-11-12 23:02:44 +00:00
help = ' anti-aliasing oversampling factor, equivalent to i_ovs in RIOPAC (default: %(default)s ). ' )
parser . add_argument ( ' --drmp ' , ' --deramp ' , dest = ' deramp ' , type = int , default = 0 ,
help = ' deramp method (0: mag for TOPS, 1:complex with linear ramp) (default: %(default)s ). ' )
2020-05-29 20:37:07 +00:00
2020-11-12 23:02:44 +00:00
# gross offset
2020-05-29 20:37:07 +00:00
gross = parser . add_argument_group ( ' Initial gross offset ' )
gross . add_argument ( ' -g ' , ' --gross ' , type = int , dest = ' gross ' , default = 0 ,
2020-11-12 23:02:44 +00:00
help = ' Use varying gross offset or not ' )
2020-05-29 20:37:07 +00:00
gross . add_argument ( ' --aa ' , type = int , dest = ' azshift ' , default = 0 ,
help = ' Gross azimuth offset (default: %(default)s ). ' )
gross . add_argument ( ' --rr ' , type = int , dest = ' rgshift ' , default = 0 ,
help = ' Gross range offset (default: %(default)s ). ' )
2020-11-12 23:02:44 +00:00
gross . add_argument ( ' --gf ' , ' --gross-file ' , type = str , dest = ' gross_offset_file ' ,
help = ' Varying gross offset input file ' )
2020-05-29 20:37:07 +00:00
corr = parser . add_argument_group ( ' Correlation surface ' )
2020-11-12 23:02:44 +00:00
corr . add_argument ( ' --corr-stat-size ' , type = int , dest = ' corr_stat_win_size ' , default = 21 ,
help = ' Zoom-in window size of the correlation surface for statistics(snr/variance) (default: %(default)s ). ' )
corr . add_argument ( ' --corr-srch-size ' , type = int , dest = ' corr_srch_size ' , default = 4 ,
help = ' (half) Zoom-in window size of the correlation surface for oversampling, ' \
' equivalent to i_srcp in RIOPAC (default: %(default)s ). ' )
2020-05-29 20:37:07 +00:00
corr . add_argument ( ' --corr-osf ' , ' --oo ' , ' --corr-over-samp-factor ' , type = int , dest = ' corr_oversample ' , default = 32 ,
help = ' Oversampling factor of the zoom-in correlation surface (default: %(default)s ). ' )
2020-11-12 23:02:44 +00:00
corr . add_argument ( ' --corr-osm ' , ' --corr-over-samp-method ' , type = int , dest = ' corr_oversamplemethod ' , default = 0 ,
help = ' Oversampling method for the correlation surface 0=fft, 1=sinc (default: %(default)s ). ' )
# gpu settings
proc = parser . add_argument_group ( ' Processing parameters ' )
2020-12-15 21:33:34 +00:00
proc . add_argument ( ' --gpuid ' , ' --gid ' , ' --gpu-id ' , dest = ' gpuid ' , type = int , default = 0 ,
2021-01-27 03:47:37 +00:00
help = ' GPU ID (default: %(default)s ). ' )
2020-11-12 23:02:44 +00:00
proc . add_argument ( ' --nstreams ' , dest = ' nstreams ' , type = int , default = 2 ,
help = ' Number of cuda streams (default: %(default)s ). ' )
proc . add_argument ( ' --usemmap ' , dest = ' usemmap ' , type = int , default = 1 ,
help = ' Whether to use memory map for loading image files (default: %(default)s ). ' )
proc . add_argument ( ' --mmapsize ' , dest = ' mmapsize ' , type = int , default = 8 ,
help = ' The memory map buffer size in GB (default: %(default)s ). ' )
proc . add_argument ( ' --nwac ' , type = int , dest = ' numWinAcrossInChunk ' , default = 10 ,
help = ' Number of window across in a chunk/batch (default: %(default)s ). ' )
proc . add_argument ( ' --nwdc ' , type = int , dest = ' numWinDownInChunk ' , default = 1 ,
help = ' Number of window down in a chunk/batch (default: %(default)s ). ' )
proc . add_argument ( ' --redo ' , dest = ' redo ' , action = ' store_true ' ,
2020-05-29 20:37:07 +00:00
help = ' To redo by force (ignore the existing offset fields). ' )
2020-04-28 22:28:58 +00:00
return parser
2020-05-29 20:37:07 +00:00
2020-04-28 22:28:58 +00:00
def cmdLineParse ( iargs = None ) :
parser = createParser ( )
inps = parser . parse_args ( args = iargs )
2020-06-02 18:15:57 +00:00
# check oversampled window size
2020-11-12 23:02:44 +00:00
if ( inps . winwidth + 2 * inps . srcwidth ) * inps . raw_oversample > 1024 :
msg = ' The oversampled window width, ' \
' as computed by (winwidth+2*srcwidth)*raw_oversample, ' \
' exceeds the current implementation limit of 1,024. ' \
f ' Please reduce winwidth: { inps . winwidth } , ' \
f ' srcwidth: { inps . srcwidth } , ' \
f ' or raw_oversample: { inps . raw_oversample } . '
2020-06-02 18:15:57 +00:00
raise ValueError ( msg )
2020-04-28 22:28:58 +00:00
return inps
2020-05-29 20:37:07 +00:00
2020-04-28 22:28:58 +00:00
@use_api
2020-07-02 19:40:49 +00:00
def estimateOffsetField ( reference , secondary , inps = None ) :
2020-04-28 22:28:58 +00:00
2020-07-02 19:40:49 +00:00
###Loading the secondary image object
2020-04-28 22:28:58 +00:00
sim = isceobj . createSlcImage ( )
2020-07-02 22:03:10 +00:00
sim . load ( secondary + ' .xml ' )
2020-04-28 22:28:58 +00:00
sim . setAccessMode ( ' READ ' )
sim . createImage ( )
2020-07-02 19:40:49 +00:00
###Loading the reference image object
2020-04-28 22:28:58 +00:00
sar = isceobj . createSlcImage ( )
2020-07-02 22:03:10 +00:00
sar . load ( reference + ' .xml ' )
2020-04-28 22:28:58 +00:00
sar . setAccessMode ( ' READ ' )
sar . createImage ( )
width = sar . getWidth ( )
length = sar . getLength ( )
2020-11-12 23:02:44 +00:00
# create a PyCuAmpcor instance
2020-04-28 22:28:58 +00:00
objOffset = PyCuAmpcor ( )
2020-05-29 20:37:07 +00:00
2020-11-12 23:02:44 +00:00
objOffset . algorithm = inps . algorithm
objOffset . deviceID = inps . gpuid
objOffset . nStreams = inps . nstreams #cudaStreams
2020-04-28 22:28:58 +00:00
objOffset . derampMethod = inps . deramp
2020-05-29 20:37:07 +00:00
print ( ' deramp method (0 for magnitude, 1 for complex): ' , objOffset . derampMethod )
2020-04-28 22:28:58 +00:00
2020-07-02 22:03:10 +00:00
objOffset . referenceImageName = reference + ' .vrt '
2020-07-02 19:40:49 +00:00
objOffset . referenceImageHeight = length
objOffset . referenceImageWidth = width
2020-07-02 22:03:10 +00:00
objOffset . secondaryImageName = secondary + ' .vrt '
2020-07-02 19:40:49 +00:00
objOffset . secondaryImageHeight = length
objOffset . secondaryImageWidth = width
2020-04-28 22:28:58 +00:00
print ( " image length: " , length )
print ( " image width: " , width )
2020-11-12 23:02:44 +00:00
# if using gross offset, adjust the margin
margin = max ( inps . margin , abs ( inps . azshift ) , abs ( inps . rgshift ) )
2020-04-28 22:28:58 +00:00
2020-11-12 23:02:44 +00:00
# determine the number of windows down and across
# that's also the size of the output offset field
objOffset . numberWindowDown = inps . numWinDown if inps . numWinDown > 0 \
else ( length - 2 * margin - 2 * inps . srchgt - inps . winhgt ) / / inps . skiphgt
objOffset . numberWindowAcross = inps . numWinAcross if inps . numWinAcross > 0 \
else ( width - 2 * margin - 2 * inps . srcwidth - inps . winwidth ) / / inps . skipwidth
print ( ' the number of windows: {} by {} ' . format ( objOffset . numberWindowDown , objOffset . numberWindowAcross ) )
2020-04-28 22:28:58 +00:00
# window size
objOffset . windowSizeHeight = inps . winhgt
objOffset . windowSizeWidth = inps . winwidth
2020-11-12 23:02:44 +00:00
print ( ' window size for cross-correlation: {} by {} ' . format ( objOffset . windowSizeHeight , objOffset . windowSizeWidth ) )
2020-05-29 20:37:07 +00:00
2020-04-28 22:28:58 +00:00
# search range
objOffset . halfSearchRangeDown = inps . srchgt
objOffset . halfSearchRangeAcross = inps . srcwidth
2020-11-12 23:02:44 +00:00
print ( ' initial search range: {} by {} ' . format ( inps . srchgt , inps . srcwidth ) )
2020-04-28 22:28:58 +00:00
# starting pixel
2020-11-12 23:02:44 +00:00
objOffset . referenceStartPixelDownStatic = inps . startpixeldw if inps . startpixeldw != - 1 \
else margin + objOffset . halfSearchRangeDown # use margin + halfSearchRange instead
objOffset . referenceStartPixelAcrossStatic = inps . startpixelac if inps . startpixelac != - 1 \
else margin + objOffset . halfSearchRangeAcross
print ( ' the first pixel in reference image is: ( {} , {} ) ' . format (
objOffset . referenceStartPixelDownStatic , objOffset . referenceStartPixelAcrossStatic ) )
2020-05-29 20:37:07 +00:00
2020-04-28 22:28:58 +00:00
# skip size
objOffset . skipSampleDown = inps . skiphgt
objOffset . skipSampleAcross = inps . skipwidth
2020-05-29 20:37:07 +00:00
print ( ' search step: {} by {} ' . format ( inps . skiphgt , inps . skipwidth ) )
# oversample raw data (SLC)
objOffset . rawDataOversamplingFactor = inps . raw_oversample
# correlation surface
2020-11-12 23:02:44 +00:00
objOffset . corrStatWindowSize = inps . corr_stat_win_size
2020-04-28 22:28:58 +00:00
2020-11-12 23:02:44 +00:00
corr_win_size = 2 * inps . corr_srch_size * inps . raw_oversample
objOffset . corrSurfaceZoomInWindow = corr_win_size
print ( ' correlation surface zoom-in window size: ' , corr_win_size )
objOffset . corrSurfaceOverSamplingMethod = inps . corr_oversamplemethod
2020-05-29 20:37:07 +00:00
objOffset . corrSurfaceOverSamplingFactor = inps . corr_oversample
print ( ' correlation surface oversampling factor: ' , inps . corr_oversample )
2020-04-28 22:28:58 +00:00
# output filenames
objOffset . offsetImageName = str ( inps . outprefix ) + str ( inps . outsuffix ) + ' .bip '
objOffset . grossOffsetImageName = str ( inps . outprefix ) + str ( inps . outsuffix ) + ' _gross.bip '
objOffset . snrImageName = str ( inps . outprefix ) + str ( inps . outsuffix ) + ' _snr.bip '
objOffset . covImageName = str ( inps . outprefix ) + str ( inps . outsuffix ) + ' _cov.bip '
print ( " offsetfield: " , objOffset . offsetImageName )
print ( " gross offsetfield: " , objOffset . grossOffsetImageName )
print ( " snr: " , objOffset . snrImageName )
print ( " cov: " , objOffset . covImageName )
2020-11-12 23:02:44 +00:00
offsetImageName = objOffset . offsetImageName
grossOffsetImageName = objOffset . grossOffsetImageName
snrImageName = objOffset . snrImageName
covImageName = objOffset . covImageName
2020-04-28 22:28:58 +00:00
2020-05-29 20:37:07 +00:00
if os . path . exists ( offsetImageName ) and not inps . redo :
2020-11-12 23:02:44 +00:00
print ( ' offsetfield file {} exists while the redo flag is {} . ' . format ( offsetImageName , inps . redo ) )
2020-05-29 20:37:07 +00:00
return 0
2020-04-28 22:28:58 +00:00
# generic control
objOffset . numberWindowDownInChunk = inps . numWinDownInChunk
objOffset . numberWindowAcrossInChunk = inps . numWinAcrossInChunk
2020-11-12 23:02:44 +00:00
objOffset . useMmap = inps . usemmap
objOffset . mmapSize = inps . mmapsize
# setup and check parameters
2020-04-28 22:28:58 +00:00
objOffset . setupParams ( )
2020-05-29 20:37:07 +00:00
2020-04-28 22:28:58 +00:00
## Set Gross Offset ###
2020-11-12 23:02:44 +00:00
if inps . gross == 0 : # use static grossOffset
print ( ' Set constant grossOffset ( {} , {} ) ' . format ( inps . azshift , inps . rgshift ) )
objOffset . setConstantGrossOffset ( inps . azshift , inps . rgshift )
else : # use varying offset
print ( " Set varying grossOffset from file {} " . format ( inps . gross_offset_file ) )
grossOffset = np . fromfile ( inps . gross_offset_file , dtype = np . int32 )
numberWindows = objOffset . numberWindowDown * objOffset . numberWindowAcross
if grossOffset . size != 2 * numberWindows :
print ( ' The input gross offsets do not match the number of windows {} by {} in int32 type ' . format ( objOffset . numberWindowDown , objOffset . numberWindowAcross ) )
return 0 ;
2020-11-30 18:30:23 +00:00
grossOffset = grossOffset . reshape ( numberWindows , 2 )
grossAzimuthOffset = grossOffset [ : , 0 ]
grossRangeOffset = grossOffset [ : , 1 ]
# enforce C-contiguous flag
grossAzimuthOffset = grossAzimuthOffset . copy ( order = ' C ' )
grossRangeOffset = grossRangeOffset . copy ( order = ' C ' )
# set varying gross offset
objOffset . setVaryingGrossOffset ( grossAzimuthOffset , grossRangeOffset )
2020-05-29 20:37:07 +00:00
# check
2020-04-28 22:28:58 +00:00
objOffset . checkPixelInImageRange ( )
# Run the code
print ( ' Running PyCuAmpcor ' )
2020-05-29 20:37:07 +00:00
objOffset . runAmpcor ( )
2020-04-28 22:28:58 +00:00
print ( ' Finished ' )
sar . finalizeImage ( )
sim . finalizeImage ( )
2020-05-29 20:37:07 +00:00
2020-04-28 22:28:58 +00:00
# Finalize the results
# offsetfield
outImg = isceobj . createImage ( )
outImg . setDataType ( ' FLOAT ' )
outImg . setFilename ( offsetImageName )
outImg . setBands ( 2 )
outImg . scheme = ' BIP '
outImg . setWidth ( objOffset . numberWindowAcross )
outImg . setLength ( objOffset . numberWindowDown )
outImg . setAccessMode ( ' read ' )
outImg . renderHdr ( )
# gross offsetfield
outImg = isceobj . createImage ( )
outImg . setDataType ( ' FLOAT ' )
outImg . setFilename ( grossOffsetImageName )
outImg . setBands ( 2 )
outImg . scheme = ' BIP '
outImg . setWidth ( objOffset . numberWindowAcross )
outImg . setLength ( objOffset . numberWindowDown )
outImg . setAccessMode ( ' read ' )
outImg . renderHdr ( )
# snr
snrImg = isceobj . createImage ( )
snrImg . setFilename ( snrImageName )
snrImg . setDataType ( ' FLOAT ' )
snrImg . setBands ( 1 )
snrImg . setWidth ( objOffset . numberWindowAcross )
snrImg . setLength ( objOffset . numberWindowDown )
snrImg . setAccessMode ( ' read ' )
snrImg . renderHdr ( )
# cov
covImg = isceobj . createImage ( )
covImg . setFilename ( covImageName )
covImg . setDataType ( ' FLOAT ' )
covImg . setBands ( 3 )
covImg . scheme = ' BIP '
covImg . setWidth ( objOffset . numberWindowAcross )
covImg . setLength ( objOffset . numberWindowDown )
covImg . setAccessMode ( ' read ' )
covImg . renderHdr ( )
2020-05-29 20:37:07 +00:00
return
def main ( iargs = None ) :
2020-04-28 22:28:58 +00:00
inps = cmdLineParse ( iargs )
outDir = os . path . dirname ( inps . outprefix )
print ( inps . outprefix )
2020-07-02 22:03:10 +00:00
2020-05-29 20:37:07 +00:00
os . makedirs ( outDir , exist_ok = True )
2020-07-02 19:40:49 +00:00
estimateOffsetField ( inps . reference , inps . secondary , inps )
2020-05-29 20:37:07 +00:00
return
2020-04-28 22:28:58 +00:00
if __name__ == ' __main__ ' :
2020-05-29 20:37:07 +00:00
2020-04-28 22:28:58 +00:00
main ( )