429 lines
14 KiB
Python
Executable File
429 lines
14 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# Author: Piyush Agram
|
|
# Copyright 2016
|
|
#
|
|
# Heresh Fattahi, updated for stack processing
|
|
|
|
|
|
import os
|
|
import glob
|
|
import datetime
|
|
import logging
|
|
import argparse
|
|
import numpy as np
|
|
|
|
import isce
|
|
import isceobj
|
|
from isceobj.Util.ImageUtil import ImageLib as IML
|
|
from isceobj.Util.decorators import use_api
|
|
import s1a_isce_utils as ut
|
|
|
|
|
|
def createParser():
|
|
'''
|
|
Create command line parser.
|
|
'''
|
|
|
|
parser = argparse.ArgumentParser( description='Generate offset field between two Sentinel swaths')
|
|
parser.add_argument('-i', '--inp_reference', type=str, dest='reference', required=True,
|
|
help='Directory with the reference image')
|
|
|
|
parser.add_argument('-s', '--stack', type=str, dest='stack', default = None,
|
|
help='Directory with the stack xml files which includes the common valid region of the stack')
|
|
|
|
parser.add_argument('-d', '--dirname', type=str, dest='dirname', required=True,
|
|
help='directory with products to merge')
|
|
|
|
parser.add_argument('-o', '--outfile', type=str, dest='outfile', required=True,
|
|
help='Output merged file')
|
|
|
|
parser.add_argument('-m', '--method', type=str, dest='method', default='avg',
|
|
help='Method: top / bot/ avg')
|
|
|
|
parser.add_argument('-a', '--aligned', action='store_true', dest='isaligned', default=False,
|
|
help='Use reference information instead of coreg for merged grid.')
|
|
|
|
parser.add_argument('-l', '--multilook', action='store_true', dest='multilook', default=False,
|
|
help='Multilook the merged products. True or False')
|
|
|
|
parser.add_argument('-A', '--azimuth_looks', type=str, dest='numberAzimuthLooks', default=3, help='azimuth looks')
|
|
|
|
parser.add_argument('-R', '--range_looks', type=str, dest='numberRangeLooks', default=9, help='range looks')
|
|
|
|
parser.add_argument('-n', '--name_pattern', type=str, dest='namePattern', default='fine*int',
|
|
help='a name pattern of burst products that will be merged. '
|
|
'default: fine. it can be lat, lon, los, burst, hgt, shadowMask, incLocal')
|
|
|
|
parser.add_argument('-v', '--valid_only', action='store_true', dest='validOnly', default=False,
|
|
help='True for SLC, int and coherence. False for geometry files (lat, lon, los, hgt, shadowMask, incLocal).')
|
|
|
|
parser.add_argument('-u', '--use_virtual_files', action='store_true', dest='useVirtualFiles', default=False,
|
|
help='writing only a vrt of merged file. Default: True.')
|
|
|
|
parser.add_argument('-M', '--multilook_tool', type=str, dest='multilookTool', default='isce',
|
|
help='The tool used for multi-looking')
|
|
|
|
parser.add_argument('-N', '--no_data_value', type=float, dest='noData', default=None,
|
|
help='no data value when gdal is used for multi-looking')
|
|
|
|
return parser
|
|
|
|
|
|
def cmdLineParse(iargs = None):
|
|
'''
|
|
Command line parser.
|
|
'''
|
|
|
|
parser = createParser()
|
|
inps = parser.parse_args(args=iargs)
|
|
if inps.method not in ['top', 'bot', 'avg']:
|
|
raise Exception('Merge method should be in top / bot / avg')
|
|
|
|
return inps
|
|
|
|
def mergeBurstsVirtual(frame, referenceFrame, fileList, outfile, validOnly=True):
|
|
'''
|
|
Merging using VRTs.
|
|
'''
|
|
|
|
from VRTManager import Swath, VRTConstructor
|
|
|
|
|
|
swaths = [Swath(x) for x in frame]
|
|
refSwaths = [Swath(x) for x in referenceFrame]
|
|
###Identify the 4 corners and dimensions
|
|
#topSwath = min(swaths, key = lambda x: x.sensingStart)
|
|
#botSwath = max(swaths, key = lambda x: x.sensingStop)
|
|
#leftSwath = min(swaths, key = lambda x: x.nearRange)
|
|
#rightSwath = max(swaths, key = lambda x: x.farRange)
|
|
topSwath = min(refSwaths, key = lambda x: x.sensingStart)
|
|
botSwath = max(refSwaths, key = lambda x: x.sensingStop)
|
|
leftSwath = min(refSwaths, key = lambda x: x.nearRange)
|
|
rightSwath = max(refSwaths, key = lambda x: x.farRange)
|
|
|
|
|
|
totalWidth = int(np.round((rightSwath.farRange - leftSwath.nearRange)/leftSwath.dr + 1))
|
|
totalLength = int(np.round((botSwath.sensingStop - topSwath.sensingStart).total_seconds()/topSwath.dt + 1 ))
|
|
|
|
|
|
###Determine number of bands and type
|
|
img = isceobj.createImage()
|
|
img.load( fileList[0][0] + '.xml')
|
|
bands = img.bands
|
|
dtype = img.dataType
|
|
img.filename = outfile
|
|
|
|
|
|
#####Start the builder
|
|
###Now start building the VRT and then render it
|
|
builder = VRTConstructor(totalLength, totalWidth)
|
|
builder.setReferenceTime( topSwath.sensingStart)
|
|
builder.setReferenceRange( leftSwath.nearRange)
|
|
builder.setTimeSpacing( topSwath.dt )
|
|
builder.setRangeSpacing( leftSwath.dr)
|
|
builder.setDataType( dtype.upper())
|
|
|
|
builder.initVRT()
|
|
|
|
|
|
####Render XML and default VRT. VRT will be overwritten.
|
|
img.width = totalWidth
|
|
img.length =totalLength
|
|
img.renderHdr()
|
|
|
|
|
|
for bnd in range(1,bands+1):
|
|
builder.initBand(band = bnd)
|
|
|
|
for ind, swath in enumerate(swaths):
|
|
####Relative path
|
|
relfilelist = [os.path.relpath(x,
|
|
os.path.dirname(outfile)) for x in fileList[ind]]
|
|
|
|
builder.addSwath(swath, relfilelist, band=bnd, validOnly=validOnly)
|
|
|
|
builder.finishBand()
|
|
builder.finishVRT()
|
|
|
|
with open(outfile + '.vrt', 'w') as fid:
|
|
fid.write(builder.vrt)
|
|
|
|
|
|
|
|
def mergeBursts(frame, fileList, outfile,
|
|
method='top'):
|
|
'''
|
|
Merge burst products into single file.
|
|
Simple numpy based stitching
|
|
'''
|
|
|
|
###Check against metadata
|
|
if frame.numberOfBursts != len(fileList):
|
|
print('Warning : Number of burst products does not appear to match number of bursts in metadata')
|
|
|
|
|
|
t0 = frame.bursts[0].sensingStart
|
|
dt = frame.bursts[0].azimuthTimeInterval
|
|
width = frame.bursts[0].numberOfSamples
|
|
|
|
#######
|
|
tstart = frame.bursts[0].sensingStart
|
|
tend = frame.bursts[-1].sensingStop
|
|
nLines = int( np.round((tend - tstart).total_seconds() / dt)) + 1
|
|
print('Expected total nLines: ', nLines)
|
|
|
|
|
|
img = isceobj.createImage()
|
|
img.load( fileList[0] + '.xml')
|
|
bands = img.bands
|
|
scheme = img.scheme
|
|
npType = IML.NUMPY_type(img.dataType)
|
|
|
|
azReferenceOff = []
|
|
for index in range(frame.numberOfBursts):
|
|
burst = frame.bursts[index]
|
|
soff = burst.sensingStart + datetime.timedelta(seconds = (burst.firstValidLine*dt))
|
|
start = int(np.round((soff - tstart).total_seconds() / dt))
|
|
end = start + burst.numValidLines
|
|
|
|
azReferenceOff.append([start,end])
|
|
|
|
print('Burst: ', index, [start,end])
|
|
|
|
if index == 0:
|
|
linecount = start
|
|
|
|
outMap = IML.memmap(outfile, mode='write', nchannels=bands,
|
|
nxx=width, nyy=nLines, scheme=scheme, dataType=npType)
|
|
|
|
for index in range(frame.numberOfBursts):
|
|
curBurst = frame.bursts[index]
|
|
curLimit = azReferenceOff[index]
|
|
|
|
curMap = IML.mmapFromISCE(fileList[index], logging)
|
|
|
|
#####If middle burst
|
|
if index > 0:
|
|
topBurst = frame.bursts[index-1]
|
|
topLimit = azReferenceOff[index-1]
|
|
topMap = IML.mmapFromISCE(fileList[index-1], logging)
|
|
|
|
olap = topLimit[1] - curLimit[0]
|
|
|
|
print("olap: ", olap)
|
|
|
|
if olap <= 0:
|
|
raise Exception('No Burst Overlap')
|
|
|
|
|
|
for bb in range(bands):
|
|
topData = topMap.bands[bb][topBurst.firstValidLine: topBurst.firstValidLine + topBurst.numValidLines,:]
|
|
|
|
curData = curMap.bands[bb][curBurst.firstValidLine: curBurst.firstValidLine + curBurst.numValidLines,:]
|
|
|
|
im1 = topData[-olap:,:]
|
|
im2 = curData[:olap,:]
|
|
|
|
if method=='avg':
|
|
data = 0.5*(im1 + im2)
|
|
elif method == 'top':
|
|
data = im1
|
|
elif method == 'bot':
|
|
data = im2
|
|
else:
|
|
raise Exception('Method should be top/bot/avg')
|
|
|
|
outMap.bands[bb][linecount:linecount+olap,:] = data
|
|
|
|
tlim = olap
|
|
else:
|
|
tlim = 0
|
|
|
|
linecount += tlim
|
|
|
|
if index != (frame.numberOfBursts-1):
|
|
botBurst = frame.bursts[index+1]
|
|
botLimit = azReferenceOff[index+1]
|
|
|
|
olap = curLimit[1] - botLimit[0]
|
|
|
|
if olap < 0:
|
|
raise Exception('No Burst Overlap')
|
|
|
|
blim = botLimit[0] - curLimit[0]
|
|
else:
|
|
blim = curBurst.numValidLines
|
|
|
|
lineout = blim - tlim
|
|
|
|
for bb in range(bands):
|
|
curData = curMap.bands[bb][curBurst.firstValidLine: curBurst.firstValidLine + curBurst.numValidLines,:]
|
|
outMap.bands[bb][linecount:linecount+lineout,:] = curData[tlim:blim,:]
|
|
|
|
linecount += lineout
|
|
curMap = None
|
|
topMap = None
|
|
|
|
IML.renderISCEXML(outfile, bands,
|
|
nLines, width,
|
|
img.dataType, scheme)
|
|
|
|
oimg = isceobj.createImage()
|
|
oimg.load(outfile + '.xml')
|
|
oimg.imageType = img.imageType
|
|
oimg.renderHdr()
|
|
try:
|
|
outMap.bands[0].base.base.flush()
|
|
except:
|
|
pass
|
|
|
|
|
|
def multilook(infile, outname=None, alks=5, rlks=15, multilook_tool="isce", no_data=None):
|
|
'''
|
|
Take looks.
|
|
'''
|
|
|
|
if multilook_tool=="gdal":
|
|
|
|
from osgeo import gdal
|
|
|
|
print("multi looking using gdal ...")
|
|
if outname is None:
|
|
spl = os.path.splitext(infile)
|
|
ext = '.{0}alks_{1}rlks'.format(alks, rlks)
|
|
outname = spl[0] + ext + spl[1]
|
|
|
|
print(infile)
|
|
ds = gdal.Open(infile + ".vrt", gdal.GA_ReadOnly)
|
|
|
|
xSize = ds.RasterXSize
|
|
ySize = ds.RasterYSize
|
|
|
|
outXSize = xSize/int(rlks)
|
|
outYSize = ySize/int(alks)
|
|
|
|
if no_data:
|
|
gdalTranslateOpts = gdal.TranslateOptions(format="ENVI", width=outXSize, height=outYSize, noData=no_data)
|
|
else:
|
|
gdalTranslateOpts = gdal.TranslateOptions(format="ENVI", width=outXSize, height=outYSize)
|
|
|
|
gdal.Translate(outname, ds, options=gdalTranslateOpts)
|
|
ds = None
|
|
|
|
|
|
ds = gdal.Open(outname, gdal.GA_ReadOnly)
|
|
gdal.Translate(outname+".vrt", ds, options=gdal.TranslateOptions(format="VRT"))
|
|
ds = None
|
|
|
|
else:
|
|
from mroipac.looks.Looks import Looks
|
|
|
|
print('Multilooking {0} ...'.format(infile))
|
|
|
|
inimg = isceobj.createImage()
|
|
inimg.load(infile + '.xml')
|
|
|
|
if outname is None:
|
|
spl = os.path.splitext(inimg.filename)
|
|
ext = '.{0}alks_{1}rlks'.format(alks, rlks)
|
|
outname = spl[0] + ext + spl[1]
|
|
|
|
lkObj = Looks()
|
|
lkObj.setDownLooks(alks)
|
|
lkObj.setAcrossLooks(rlks)
|
|
lkObj.setInputImage(inimg)
|
|
lkObj.setOutputFilename(outname)
|
|
lkObj.looks()
|
|
|
|
return outname
|
|
|
|
|
|
|
|
def main(iargs=None):
|
|
'''
|
|
Merge burst products to make it look like stripmap.
|
|
Currently will merge interferogram, lat, lon, z and los.
|
|
'''
|
|
inps=cmdLineParse(iargs)
|
|
virtual = inps.useVirtualFiles
|
|
|
|
swathList = ut.getSwathList(inps.reference)
|
|
referenceFrames = []
|
|
frames=[]
|
|
fileList = []
|
|
namePattern = inps.namePattern.split('*')
|
|
|
|
for swath in swathList:
|
|
ifg = ut.loadProduct(os.path.join(inps.reference , 'IW{0}.xml'.format(swath)))
|
|
if inps.stack:
|
|
stack = ut.loadProduct(os.path.join(inps.stack , 'IW{0}.xml'.format(swath)))
|
|
if inps.isaligned:
|
|
reference = ifg.reference
|
|
|
|
# checking inconsistent number of bursts in the secondary acquisitions
|
|
if reference.numberOfBursts != ifg.numberOfBursts:
|
|
raise ValueError('{} has different number of bursts ({}) than the reference ({})'.format(
|
|
inps.reference, ifg.numberOfBursts, reference.numberOfBursts))
|
|
|
|
else:
|
|
reference = ifg
|
|
|
|
minBurst = ifg.bursts[0].burstNumber
|
|
maxBurst = ifg.bursts[-1].burstNumber
|
|
|
|
|
|
if minBurst==maxBurst:
|
|
print('Skipping processing of swath {0}'.format(swath))
|
|
continue
|
|
|
|
if inps.stack:
|
|
minStack = stack.bursts[0].burstNumber
|
|
print('Updating the valid region of each burst to the common valid region of the stack')
|
|
for ii in range(minBurst, maxBurst + 1):
|
|
ifg.bursts[ii-minBurst].firstValidLine = stack.bursts[ii-minStack].firstValidLine
|
|
ifg.bursts[ii-minBurst].firstValidSample = stack.bursts[ii-minStack].firstValidSample
|
|
ifg.bursts[ii-minBurst].numValidLines = stack.bursts[ii-minStack].numValidLines
|
|
ifg.bursts[ii-minBurst].numValidSamples = stack.bursts[ii-minStack].numValidSamples
|
|
|
|
frames.append(ifg)
|
|
referenceFrames.append(reference)
|
|
print('bursts: ', minBurst, maxBurst)
|
|
fileList.append([os.path.join(inps.dirname, 'IW{0}'.format(swath), namePattern[0] + '_%02d.%s'%(x,namePattern[1]))
|
|
for x in range(minBurst, maxBurst+1)])
|
|
|
|
mergedir = os.path.dirname(inps.outfile)
|
|
os.makedirs(mergedir, exist_ok=True)
|
|
|
|
suffix = '.full'
|
|
if (inps.numberRangeLooks == 1) and (inps.numberAzimuthLooks==1):
|
|
suffix=''
|
|
####Virtual flag is ignored for multi-swath data
|
|
if (not virtual):
|
|
print('User requested for multi-swath stitching.')
|
|
print('Virtual files are the only option for this.')
|
|
print('Proceeding with virtual files.')
|
|
|
|
mergeBurstsVirtual(frames, referenceFrames, fileList, inps.outfile+suffix, validOnly=inps.validOnly)
|
|
|
|
if (not virtual):
|
|
print('writing merged file to disk ...')
|
|
cmd = 'gdal_translate -of ENVI -co INTERLEAVE=BIL ' + inps.outfile + suffix + '.vrt ' + inps.outfile + suffix
|
|
os.system(cmd)
|
|
|
|
print(inps.multilook)
|
|
if inps.multilook:
|
|
multilook(inps.outfile+suffix,
|
|
outname=inps.outfile,
|
|
alks=inps.numberAzimuthLooks,
|
|
rlks=inps.numberRangeLooks,
|
|
multilook_tool=inps.multilookTool,
|
|
no_data=inps.noData)
|
|
else:
|
|
print('Skipping multi-looking ....')
|
|
|
|
if __name__ == '__main__' :
|
|
'''
|
|
Merge products burst-by-burst.
|
|
'''
|
|
main()
|