ISCE_INSAR/components/isceobj/Alos2Proc/runSwathOffset.py

394 lines
15 KiB
Python

#
# Author: Cunren Liang
# Copyright 2015-present, NASA-JPL/Caltech
#
import os
import glob
import logging
import datetime
import numpy as np
import isceobj
from isceobj.Alos2Proc.Alos2ProcPublic import multilook
logger = logging.getLogger('isce.alos2insar.runSwathOffset')
def runSwathOffset(self):
'''estimate swath offsets.
'''
if hasattr(self, 'doInSAR'):
if not self.doInSAR:
return
catalog = isceobj.Catalog.createCatalog(self._insar.procDoc.name)
self.updateParamemetersFromUser()
referenceTrack = self._insar.loadTrack(reference=True)
secondaryTrack = self._insar.loadTrack(reference=False)
for i, frameNumber in enumerate(self._insar.referenceFrames):
frameDir = 'f{}_{}'.format(i+1, frameNumber)
os.chdir(frameDir)
mosaicDir = 'mosaic'
os.makedirs(mosaicDir, exist_ok=True)
os.chdir(mosaicDir)
if not (
((self._insar.modeCombination == 21) or \
(self._insar.modeCombination == 22) or \
(self._insar.modeCombination == 31) or \
(self._insar.modeCombination == 32))
and
(self._insar.endingSwath-self._insar.startingSwath+1 > 1)
):
os.chdir('../../')
continue
#compute swath offset
offsetReference = swathOffset(referenceTrack.frames[i], self._insar.referenceSlc, self._insar.referenceSwathOffset,
crossCorrelation=self.swathOffsetMatching, numberOfAzimuthLooks=10)
#only use geometrical offset for secondary
offsetSecondary = swathOffset(secondaryTrack.frames[i], self._insar.secondarySlc, self._insar.secondarySwathOffset,
crossCorrelation=False, numberOfAzimuthLooks=10)
#initialization
if i == 0:
self._insar.swathRangeOffsetGeometricalReference = []
self._insar.swathAzimuthOffsetGeometricalReference = []
self._insar.swathRangeOffsetGeometricalSecondary = []
self._insar.swathAzimuthOffsetGeometricalSecondary = []
if self.swathOffsetMatching:
self._insar.swathRangeOffsetMatchingReference = []
self._insar.swathAzimuthOffsetMatchingReference = []
#self._insar.swathRangeOffsetMatchingSecondary = []
#self._insar.swathAzimuthOffsetMatchingSecondary = []
#append list directly, as the API support 2-d list
self._insar.swathRangeOffsetGeometricalReference.append(offsetReference[0])
self._insar.swathAzimuthOffsetGeometricalReference.append(offsetReference[1])
self._insar.swathRangeOffsetGeometricalSecondary.append(offsetSecondary[0])
self._insar.swathAzimuthOffsetGeometricalSecondary.append(offsetSecondary[1])
if self.swathOffsetMatching:
self._insar.swathRangeOffsetMatchingReference.append(offsetReference[2])
self._insar.swathAzimuthOffsetMatchingReference.append(offsetReference[3])
#self._insar.swathRangeOffsetMatchingSecondary.append(offsetSecondary[2])
#self._insar.swathAzimuthOffsetMatchingSecondary.append(offsetSecondary[3])
os.chdir('../../')
catalog.printToLog(logger, "runSwathOffset")
self._insar.procDoc.addAllFromCatalog(catalog)
def swathOffset(frame, image, outputfile, crossCorrelation=True, numberOfAzimuthLooks=10):
'''
compute swath offset
frame: frame object
image: image for doing matching
outputfile: output txt file for saving swath offset
crossCorrelation: whether do matching
numberOfAzimuthLooks: number of looks to take in azimuth before matching
'''
rangeOffsetGeometrical = []
azimuthOffsetGeometrical = []
rangeOffsetMatching = []
azimuthOffsetMatching = []
for j in range(len(frame.swaths)):
frameNumber = frame.frameNumber
swathNumber = frame.swaths[j].swathNumber
swathDir = 's{}'.format(swathNumber)
print('estimate offset frame {}, swath {}'.format(frameNumber, swathNumber))
if j == 0:
rangeOffsetGeometrical.append(0.0)
azimuthOffsetGeometrical.append(0.0)
rangeOffsetMatching.append(0.0)
azimuthOffsetMatching.append(0.0)
swathDirLast = swathDir
continue
image1 = os.path.join('../', swathDirLast, image)
image2 = os.path.join('../', swathDir, image)
swath0 = frame.swaths[0]
swath1 = frame.swaths[j-1]
swath2 = frame.swaths[j]
rangeScale1 = swath0.rangePixelSize / swath1.rangePixelSize
azimuthScale1 = swath0.azimuthLineInterval / swath1.azimuthLineInterval
rangeScale2 = swath0.rangePixelSize / swath2.rangePixelSize
azimuthScale2 = swath0.azimuthLineInterval / swath2.azimuthLineInterval
#offset from geometry
offsetGeometrical = computeSwathOffset(swath1, swath2, rangeScale1, azimuthScale1)
rangeOffsetGeometrical.append(offsetGeometrical[0])
azimuthOffsetGeometrical.append(offsetGeometrical[1])
#offset from cross-correlation
if crossCorrelation:
offsetMatching = estimateSwathOffset(swath1, swath2, image1, image2, rangeScale1,
azimuthScale1, rangeScale2, azimuthScale2, numberOfAzimuthLooks)
if offsetMatching != None:
rangeOffsetMatching.append(offsetMatching[0])
azimuthOffsetMatching.append(offsetMatching[1])
else:
print('******************************************************************')
print('WARNING: bad matching offset, we are forced to use')
print(' geometrical offset for swath mosaicking')
print('******************************************************************')
rangeOffsetMatching.append(offsetGeometrical[0])
azimuthOffsetMatching.append(offsetGeometrical[1])
swathDirLast = swathDir
if crossCorrelation:
offsetComp = "\n\ncomparision of offsets:\n\n"
offsetComp += "offset type i geometrical match difference\n"
offsetComp += "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"
for i, (offset1, offset2) in enumerate(zip(rangeOffsetGeometrical, rangeOffsetMatching)):
offsetComp += "range offset {:2d} {:13.3f} {:13.3f} {:13.3f}\n".format(i, offset1, offset2, offset1 - offset2)
for i, (offset1, offset2) in enumerate(zip(azimuthOffsetGeometrical, azimuthOffsetMatching)):
offsetComp += "azimuth offset {:2d} {:13.3f} {:13.3f} {:13.3f}\n".format(i, offset1, offset2, offset1 - offset2)
#write and report offsets
with open(outputfile, 'w') as f:
f.write(offsetComp)
print("{}".format(offsetComp))
if crossCorrelation:
return (rangeOffsetGeometrical, azimuthOffsetGeometrical, rangeOffsetMatching, azimuthOffsetMatching)
else:
return (rangeOffsetGeometrical, azimuthOffsetGeometrical)
def computeSwathOffset(swath1, swath2, rangeScale1=1, azimuthScale1=1):
rangeOffset = -(swath2.startingRange - swath1.startingRange) / swath1.rangePixelSize
azimuthOffset = -((swath2.sensingStart - swath1.sensingStart).total_seconds()) / swath1.azimuthLineInterval
rangeOffset /= rangeScale1
azimuthOffset /= azimuthScale1
return (rangeOffset, azimuthOffset)
def estimateSwathOffset(swath1, swath2, image1, image2, rangeScale1=1, azimuthScale1=1, rangeScale2=1, azimuthScale2=1, numberOfAzimuthLooks=10):
'''
estimate offset of two adjacent swaths using matching
'''
from osgeo import gdal
import isceobj
from contrib.alos2proc_f.alos2proc_f import rect_with_looks
from isceobj.Alos2Proc.Alos2ProcPublic import create_xml
from isceobj.Alos2Proc.Alos2ProcPublic import cullOffsets
from isceobj.Alos2Proc.Alos2ProcPublic import meanOffset
from mroipac.ampcor.Ampcor import Ampcor
#processing image 1
rangeOff1 = int((swath2.startingRange - swath1.startingRange) / swath1.rangePixelSize)
if rangeOff1 < 0:
rangeOff1 = 0
numberOfSamples1 = swath1.numberOfSamples - rangeOff1
numberOfSamplesRect1 = int(numberOfSamples1/rangeScale1)
numberOfLinesRect1 = int(swath1.numberOfLines/azimuthScale1)
numberOfSamplesLook1 = int(numberOfSamplesRect1/1)
numberOfLinesLook1 = int(numberOfLinesRect1/numberOfAzimuthLooks)
#get magnitude image whether complex or not
#ReadAsArray: https://pcjericks.github.io/py-gdalogr-cookbook/raster_layers.html
ds = gdal.Open(image1 + '.vrt', gdal.GA_ReadOnly)
data = ds.ReadAsArray(rangeOff1, 0, numberOfSamples1, swath1.numberOfLines)
ds = None
(np.absolute(data)).astype(np.float32).tofile('image1.float')
#rectify
if rangeScale1 == 1 and azimuthScale1 == 1:
os.rename('image1.float', 'image1_rect.float')
else:
rect_with_looks('image1.float',
'image1_rect.float',
numberOfSamples1, swath1.numberOfLines,
numberOfSamplesRect1, numberOfLinesRect1,
rangeScale1, 0.0,
0.0,azimuthScale1,
0.0,0.0,
1,1,
1,1,
'REAL',
'Bilinear')
os.remove('image1.float')
#take looks
if numberOfAzimuthLooks == 1:
os.rename('image1_rect.float', 'image1_look.float')
else:
data1 = np.fromfile('image1_rect.float', dtype=np.float32).reshape(numberOfLinesRect1, numberOfSamplesRect1)
data1 = np.sqrt(multilook(data1**2, numberOfAzimuthLooks, 1))
data1.astype(np.float32).tofile('image1_look.float')
os.remove('image1_rect.float')
create_xml('image1_look.float', numberOfSamplesLook1, numberOfLinesLook1, 'float')
#processing image 2
rangeOff2 = 0
numberOfSamples2 = int((swath1.startingRange + swath1.rangePixelSize * (swath1.numberOfSamples - 1) - swath2.startingRange) / swath2.rangePixelSize) + 1
if numberOfSamples2 > swath2.numberOfSamples:
numberOfSamples2 = swath2.numberOfSamples
numberOfSamplesRect2 = int(numberOfSamples2/rangeScale2)
numberOfLinesRect2 = int(swath2.numberOfLines/azimuthScale2)
numberOfSamplesLook2 = int(numberOfSamplesRect2/1)
numberOfLinesLook2 = int(numberOfLinesRect2/numberOfAzimuthLooks)
#get magnitude image whether complex or not
ds = gdal.Open(image2 + '.vrt', gdal.GA_ReadOnly)
data = ds.ReadAsArray(rangeOff2, 0, numberOfSamples2, swath2.numberOfLines)
ds = None
(np.absolute(data)).astype(np.float32).tofile('image2.float')
#rectify
if rangeScale2 == 1 and azimuthScale2 == 1:
os.rename('image2.float', 'image2_rect.float')
else:
rect_with_looks('image2.float',
'image2_rect.float',
numberOfSamples2, swath2.numberOfLines,
numberOfSamplesRect2, numberOfLinesRect2,
rangeScale2, 0.0,
0.0,azimuthScale2,
0.0,0.0,
1,1,
1,1,
'REAL',
'Bilinear')
os.remove('image2.float')
#take looks
if numberOfAzimuthLooks == 1:
os.rename('image2_rect.float', 'image2_look.float')
else:
data2 = np.fromfile('image2_rect.float', dtype=np.float32).reshape(numberOfLinesRect2, numberOfSamplesRect2)
data2 = np.sqrt(multilook(data2**2, numberOfAzimuthLooks, 1))
data2.astype(np.float32).tofile('image2_look.float')
os.remove('image2_rect.float')
create_xml('image2_look.float', numberOfSamplesLook2, numberOfLinesLook2, 'float')
#matching
ampcor = Ampcor(name='insarapp_slcs_ampcor')
ampcor.configure()
mMag = isceobj.createImage()
mMag.load('image1_look.float.xml')
mMag.setAccessMode('read')
mMag.createImage()
sMag = isceobj.createImage()
sMag.load('image2_look.float.xml')
sMag.setAccessMode('read')
sMag.createImage()
ampcor.setImageDataType1('real')
ampcor.setImageDataType2('real')
ampcor.setReferenceSlcImage(mMag)
ampcor.setSecondarySlcImage(sMag)
#MATCH REGION
rgoff = 0
azoff = int((swath1.sensingStart - swath2.sensingStart).total_seconds() / swath1.azimuthLineInterval / azimuthScale1 / numberOfAzimuthLooks)
#it seems that we cannot use 0, haven't look into the problem
if rgoff == 0:
rgoff = 1
if azoff == 0:
azoff = 1
firstSample = 1
if rgoff < 0:
firstSample = int(35 - rgoff)
firstLine = 1
if azoff < 0:
firstLine = int(35 - azoff)
ampcor.setAcrossGrossOffset(rgoff)
ampcor.setDownGrossOffset(azoff)
ampcor.setFirstSampleAcross(firstSample)
ampcor.setLastSampleAcross(numberOfSamplesLook1)
ampcor.setNumberLocationAcross(20)
ampcor.setFirstSampleDown(firstLine)
ampcor.setLastSampleDown(numberOfLinesLook1)
ampcor.setNumberLocationDown(100)
#MATCH PARAMETERS
ampcor.setWindowSizeWidth(32)
ampcor.setWindowSizeHeight(32)
#note this is the half width/length of search area, so number of resulting correlation samples: 8*2+1
ampcor.setSearchWindowSizeWidth(8)
ampcor.setSearchWindowSizeHeight(8)
#REST OF THE STUFF
ampcor.setAcrossLooks(1)
ampcor.setDownLooks(1)
ampcor.setOversamplingFactor(64)
ampcor.setZoomWindowSize(16)
#1. The following not set
#Matching Scale for Sample/Line Directions (-) = 1. 1.
#should add the following in Ampcor.py?
#if not set, in this case, Ampcor.py'value is also 1. 1.
#ampcor.setScaleFactorX(1.)
#ampcor.setScaleFactorY(1.)
#MATCH THRESHOLDS AND DEBUG DATA
#2. The following not set
#in roi_pac the value is set to 0 1
#in isce the value is set to 0.001 1000.0
#SNR and Covariance Thresholds (-) = {s1} {s2}
#should add the following in Ampcor?
#THIS SHOULD BE THE ONLY THING THAT IS DIFFERENT FROM THAT OF ROI_PAC
#ampcor.setThresholdSNR(0)
#ampcor.setThresholdCov(1)
ampcor.setDebugFlag(False)
ampcor.setDisplayFlag(False)
#in summary, only two things not set which are indicated by 'The following not set' above.
#run ampcor
ampcor.ampcor()
offsets = ampcor.getOffsetField()
refinedOffsets = cullOffsets(offsets)
#finalize image, and re-create it
#otherwise the file pointer is still at the end of the image
mMag.finalizeImage()
sMag.finalizeImage()
os.remove('image1_look.float')
os.remove('image1_look.float.vrt')
os.remove('image1_look.float.xml')
os.remove('image2_look.float')
os.remove('image2_look.float.vrt')
os.remove('image2_look.float.xml')
if refinedOffsets != None:
rangeOffset, azimuthOffset = meanOffset(refinedOffsets)
rangeOffset -= rangeOff1/rangeScale1
azimuthOffset *= numberOfAzimuthLooks
return (rangeOffset, azimuthOffset)
else:
return None