Skip to content

Commit 38a142c

Browse files
Virginia Brancatovbrancat
authored andcommitted
Fine resampling with geometry + dense offsets (#796)
* Discriminate between coarse and fine resample in InSAR schema/defaults * Modify yaml_argparse to accomodate coarse/fine resampling * Modify resample runconfig to accomodate resample_type * Introduce resample_type in resample_slc * Remove resample_type check in yaml_argparse * Make dense_offsets point to coarse_resample_slc diretory * Modify resample_slc to accomodate coarse and fine resampling * Compute crossmul with coarse/fine co-registered secondary SLCs * Accomodate coarse/fine resampling in InSAR workflow * Turn off fine resampling in insar.py unit test * Correct path to resampled slc in resample_slc unit test * Fix pep8 notation and isort * Correct resample_slc unit test * Better address co-pol dense offsets selection for fine resampling * Change the default value of lines_per_tile in default insar schema * Replace if/else using resample_type with f-string * More fstring reformatting * More if/else replace with fstring * Correct lines_per_tile value in insar test yaml file * Moving the enabled flag only to fine_resampling * Isort inputs and format to pep8 notation * Remove for loop to check for pol validity * Add explicit check for range/azimuth offset file existence Co-authored-by: vbrancat <[email protected]>
1 parent e7aeec1 commit 38a142c

File tree

15 files changed

+186
-65
lines changed

15 files changed

+186
-65
lines changed

python/packages/pybind_nisar/workflows/crossmul.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from pybind_nisar.workflows.yaml_argparse import YamlArgparse
1616

1717

18-
def run(cfg: dict, output_hdf5: str = None):
18+
def run(cfg: dict, output_hdf5: str = None, resample_type='coarse'):
1919
'''
2020
run crossmul
2121
'''
@@ -104,8 +104,8 @@ def run(cfg: dict, output_hdf5: str = None):
104104
if coregistered_is_file:
105105
raster_str = f'HDF5:{sec_hdf5}:/{sec_slc.slcPath(freq, pol)}'
106106
else:
107-
raster_str = str(coregistered_slc_path / \
108-
f'resample_slc/freq{freq}/{pol}/coregistered_secondary.slc')
107+
raster_str = str(coregistered_slc_path / f'{resample_type}_resample_slc/'
108+
f'freq{freq}/{pol}/coregistered_secondary.slc')
109109

110110
sec_slc_raster = isce3.io.Raster(raster_str)
111111

@@ -155,11 +155,13 @@ def run(cfg: dict, output_hdf5: str = None):
155155
run crossmul from command line
156156
'''
157157
# load command line args
158-
crossmul_parser = YamlArgparse()
158+
crossmul_parser = YamlArgparse(resample_type=True)
159159
args = crossmul_parser.parse()
160+
# extract resample type
161+
resample_type = args.resample_type
160162
# get a runconfig dict from command line args
161-
crossmul_runconfig = CrossmulRunConfig(args)
163+
crossmul_runconfig = CrossmulRunConfig(args, resample_type)
162164
# prepare RIFG HDF5
163165
out_paths = h5_prep.run(crossmul_runconfig.cfg)
164166
# run crossmul
165-
run(crossmul_runconfig.cfg, out_paths['RIFG'])
167+
run(crossmul_runconfig.cfg, out_paths['RIFG'], resample_type)

python/packages/pybind_nisar/workflows/crossmul_runconfig.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,16 @@
66

77

88
class CrossmulRunConfig(RunConfig):
9-
def __init__(self, args):
9+
def __init__(self, args, resample_type='coarse'):
1010
# all insar submodules share a commmon `insar` schema
1111
super().__init__(args, 'insar')
1212

1313
if self.args.run_config_path is not None:
1414
self.load_geocode_yaml_to_dict()
1515
self.geocode_common_arg_load()
16-
self.yaml_check()
16+
self.yaml_check(resample_type)
1717

18-
def yaml_check(self):
18+
def yaml_check(self, resample_type):
1919
'''
2020
Check crossmul specifics from YAML
2121
'''
@@ -34,11 +34,17 @@ def yaml_check(self):
3434
raise ValueError(err_str)
3535

3636
# check if required coregistered frequency/polarization rasters exist in directory or HDF5 file
37+
# Distinguish between coarse and fine resample_slc directories
3738
freq_pols = self.cfg['processing']['input_subset']['list_of_frequencies']
3839
frequencies = freq_pols.keys()
3940
if os.path.isdir(coregistered_slc_path):
40-
helpers.check_mode_directory_tree(coregistered_slc_path, 'resample_slc',\
41-
frequencies, freq_pols)
41+
if resample_type not in ['coarse', 'fine']:
42+
err_str = f"{resample_type} not a valid resample slc type"
43+
error_channel.log(err_str)
44+
raise ValueError(err_str)
45+
helpers.check_mode_directory_tree(coregistered_slc_path,
46+
f'{resample_type}_resample_slc',
47+
frequencies, freq_pols)
4248
else:
4349
helpers.check_hdf5_freq_pols(coregistered_slc_path, freq_pols)
4450

python/packages/pybind_nisar/workflows/dense_offsets.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ def run(cfg: dict):
8686
format='ENVI')
8787
else:
8888
sec_raster_path = str(coregistered_slc_path /
89-
f'resample_slc/freq{freq}/{pol}/coregistered_secondary.slc')
89+
f'coarse_resample_slc/freq{freq}/{pol}/coregistered_secondary.slc')
9090
sec_raster = isce3.io.Raster(sec_raster_path)
9191
ampcor.secondaryImageName = sec_raster_path
9292
ampcor.secondaryImageHeight = sec_raster.length

python/packages/pybind_nisar/workflows/dense_offsets_runconfig.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,7 @@ def yaml_check(self):
4444

4545
if os.path.isdir(coregistered_slc_path):
4646
helpers.check_mode_directory_tree(coregistered_slc_path,
47-
'resample_slc',
47+
'coarse_resample_slc',
4848
frequencies, freq_pols)
4949
else:
5050
helpers.check_hdf5_freq_pols(coregistered_slc_path, freq_pols)
51-

python/packages/pybind_nisar/workflows/insar.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ def run(cfg: dict, out_paths: dict, run_steps: dict):
1515
Run INSAR workflow with parameters in cfg dictionary
1616
'''
1717
info_channel = journal.info("insar.run")
18-
error_channel = journal.error('insar.run')
1918
info_channel.log("starting INSAR")
2019

2120
t_all = time.time()
@@ -26,8 +25,8 @@ def run(cfg: dict, out_paths: dict, run_steps: dict):
2625
if run_steps['geo2rdr']:
2726
geo2rdr.run(cfg)
2827

29-
if run_steps['resample']:
30-
resample_slc.run(cfg)
28+
if run_steps['coarse_resample']:
29+
resample_slc.run(cfg, 'coarse')
3130

3231
if (run_steps['dense_offsets']) and \
3332
(cfg['processing']['dense_offsets']['enabled']):
@@ -37,8 +36,18 @@ def run(cfg: dict, out_paths: dict, run_steps: dict):
3736
cfg['processing']['rubbersheet']['enabled']:
3837
rubbersheet.run(cfg, out_paths['RIFG'])
3938

39+
# If enabled, run fine_resampling
40+
if run_steps['fine_resample'] and \
41+
cfg['processing']['fine_resample']['enabled']:
42+
resample_slc.run(cfg, 'fine')
43+
44+
# If fine_resampling is enabled, use fine-coregistered SLC
45+
# to run crossmul
4046
if run_steps['crossmul']:
41-
crossmul.run(cfg, out_paths['RIFG'])
47+
if cfg['processing']['fine_resample']['enabled']:
48+
crossmul.run(cfg, out_paths['RIFG'], 'fine')
49+
else:
50+
crossmul.run(cfg, out_paths['RIFG'], 'coarse')
4251

4352
if run_steps['unwrap'] and 'RUNW' in out_paths:
4453
unwrap.run(cfg, out_paths['RIFG'], out_paths['RUNW'])

python/packages/pybind_nisar/workflows/insar_runconfig.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import journal
22
import numpy as np
3+
34
from pybind_nisar.workflows.geo2rdr_runconfig import Geo2rdrRunConfig
45

56

@@ -15,23 +16,31 @@ def yaml_check(self):
1516
Check submodule paths from YAML
1617
'''
1718
scratch_path = self.cfg['ProductPathGroup']['ScratchPath']
18-
error_channel = journal.error('InsarRunConfig.yaml_check')
19+
error_channel = journal.error('InsarRunconfig.yaml_check')
1920

2021
# If dense_offsets is disabled and rubbersheet is enabled
2122
# throw an exception and do not run the workflow
2223
if not self.cfg['processing']['dense_offsets']['enabled'] and \
2324
self.cfg['processing']['rubbersheet']['enabled']:
2425
err_str = "Dense_offsets must be enabled to run rubbersheet"
2526
error_channel.log(err_str)
26-
raise RuntimeError(err_str)
27+
raise ValueError(err_str)
28+
29+
# If rubbersheet is disabled but fine_resampling is enabled,
30+
# throw an exception and stop insar.py execution
31+
if not self.cfg['processing']['rubbersheet']['enabled'] and \
32+
self.cfg['processing']['fine_resample']['enabled']:
33+
err_str = "Rubbersheet must be enabled to run fine SLC resampling"
34+
error_channel.log(err_str)
35+
raise ValueError(err_str)
2736

2837
# for each submodule check if user path for input data assigned
2938
# if not assigned, assume it'll be in scratch
3039
if 'topo_path' not in self.cfg['processing']['geo2rdr']:
3140
self.cfg['processing']['geo2rdr']['topo_path'] = scratch_path
3241

33-
if 'offset_dir' not in self.cfg['processing']['resample']:
34-
self.cfg['processing']['resample']['offset_dir'] = scratch_path
42+
if self.cfg['processing']['coarse_resample']['offsets_dir'] is None:
43+
self.cfg['processing']['coarse_resample']['offsets_dir'] = scratch_path
3544

3645
if self.cfg['processing']['dense_offsets']['coregistered_slc_path'] is None:
3746
self.cfg['processing']['dense_offsets'][
@@ -47,6 +56,9 @@ def yaml_check(self):
4756
self.cfg['processing']['rubbersheet'][
4857
'geo2rdr_offsets_path'] = scratch_path
4958

59+
if self.cfg['processing']['fine_resample']['offsets_dir'] is None:
60+
self.cfg['processing']['fine_resample']['offsets_dir'] = scratch_path
61+
5062
if 'coregistered_slc_path' not in self.cfg['processing']['crossmul']:
5163
self.cfg['processing']['crossmul'][
5264
'coregistered_slc_path'] = scratch_path

python/packages/pybind_nisar/workflows/persistence.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ class Persistence():
77
basic class that determines InSAR persistence
88
'''
99
# init InSAR steps in reverse chronological run order
10-
insar_steps = ['geocode', 'unwrap', 'crossmul', 'rubbersheet',
11-
'dense_offsets', 'resample', 'geo2rdr', 'rdr2geo', 'h5_prep']
10+
insar_steps = ['geocode', 'unwrap', 'crossmul', 'fine_resample', 'rubbersheet',
11+
'dense_offsets', 'coarse_resample', 'geo2rdr', 'rdr2geo', 'h5_prep']
1212

1313
def __init__(self, restart=False):
1414
# bool flag that determines if insar.run is called

python/packages/pybind_nisar/workflows/resample_slc.py

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
wrapper for resample
55
'''
66

7+
import os
78
import pathlib
89
import time
910

@@ -16,15 +17,16 @@
1617
from pybind_nisar.workflows.yaml_argparse import YamlArgparse
1718

1819

19-
def run(cfg):
20+
def run(cfg, resample_type):
2021
'''
2122
run resample_slc
2223
'''
2324
input_hdf5 = cfg['InputFileGroup']['SecondaryFilePath']
2425
scratch_path = pathlib.Path(cfg['ProductPathGroup']['ScratchPath'])
2526
freq_pols = cfg['processing']['input_subset']['list_of_frequencies']
2627

27-
resamp_args = cfg['processing']['resample']
28+
# According to the type of resampling, choose proper resample cfg
29+
resamp_args = cfg['processing'][f'{resample_type}_resample']
2830

2931
# Get SLC parameters
3032
slc = SLC(hdf5file=input_hdf5)
@@ -48,8 +50,29 @@ def run(cfg):
4850
radar_grid = slc.getRadarGrid(frequency=freq)
4951
native_doppler = slc.getDopplerCentroid(frequency=freq)
5052

51-
# create separate directory within scratch for resample_slc
52-
resample_slc_scratch_path = scratch_path / 'resample_slc' / f'freq{freq}'
53+
# Open offsets
54+
offsets_dir = pathlib.Path(resamp_args['offsets_dir'])
55+
56+
# Create separate directories for coarse and fine resample
57+
# Open corresponding range/azimuth offsets
58+
resample_slc_scratch_path = scratch_path / \
59+
f'{resample_type}_resample_slc' / f'freq{freq}'
60+
if resample_type == 'coarse':
61+
offsets_path = offsets_dir / 'geo2rdr' / f'freq{freq}'
62+
rg_off = isce3.io.Raster(str(offsets_path / 'range.off'))
63+
az_off = isce3.io.Raster(str(offsets_path / 'azimuth.off'))
64+
else:
65+
# We checked the existence of HH/VV offsets in resample_slc_runconfig.py
66+
# Select the first offsets available between HH and VV
67+
freq_offsets_path = offsets_dir / 'rubbersheet_offsets' / f'freq{freq}'
68+
if os.path.isdir(str(freq_offsets_path/'HH')):
69+
offsets_path = freq_offsets_path/'HH'
70+
else:
71+
offsets_path = freq_offsets_path/'VV'
72+
rg_off = isce3.io.Raster(str(offsets_path / 'range.off.vrt'))
73+
az_off = isce3.io.Raster(str(offsets_path / 'azimuth.off.vrt'))
74+
75+
# Create resample slc directory
5376
resample_slc_scratch_path.mkdir(parents=True, exist_ok=True)
5477

5578
# Initialize CPU or GPU resample object accordingly
@@ -64,15 +87,7 @@ def run(cfg):
6487
if resamp_args['lines_per_tile']:
6588
resamp_obj.lines_per_tile = resamp_args['lines_per_tile']
6689

67-
# Open offsets
68-
offset_dir = pathlib.Path(cfg['processing']['resample']['offset_dir'])
69-
geo2rdr_off_path = offset_dir / 'geo2rdr' / f'freq{freq}'
70-
71-
# Open offsets
72-
rg_off = isce3.io.Raster(str(geo2rdr_off_path / 'range.off'))
73-
az_off = isce3.io.Raster(str(geo2rdr_off_path / 'azimuth.off'))
74-
75-
# Get polarization list to process
90+
# Get polarization list for which resample SLCs
7691
pol_list = freq_pols[freq]
7792

7893
for pol in pol_list:
@@ -101,11 +116,14 @@ def run(cfg):
101116
'''
102117

103118
# load command line args
104-
resample_slc_parser = YamlArgparse()
119+
resample_slc_parser = YamlArgparse(resample_type=True)
105120
args = resample_slc_parser.parse()
106121

122+
# Extract resample_type
123+
resample_type = args.resample_type
124+
107125
# Get a runconfig dictionary from command line args
108-
resample_slc_runconfig = ResampleSlcRunConfig(args)
126+
resample_slc_runconfig = ResampleSlcRunConfig(args, resample_type)
109127

110128
# Run resample_slc
111-
run(resample_slc_runconfig.cfg)
129+
run(resample_slc_runconfig.cfg, resample_type)
Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,68 @@
1+
import os
2+
3+
import journal
14
import pybind_nisar.workflows.helpers as helpers
25
from pybind_nisar.workflows.runconfig import RunConfig
36

47

58
class ResampleSlcRunConfig(RunConfig):
6-
def __init__(self, args):
9+
def __init__(self, args, resample_type='coarse'):
710
# InSAR submodules have a common InSAR schema
811
super().__init__(args, 'insar')
912

1013
if self.args.run_config_path is not None:
1114
super().load_geocode_yaml_to_dict()
1215
super().geocode_common_arg_load()
13-
self.yaml_check()
16+
self.yaml_check(resample_type)
1417

15-
def yaml_check(self):
18+
def yaml_check(self, resample_type):
1619
'''
1720
Check resample specifics from YAML.
1821
'''
19-
# Use scratch as offset_dir if none given in YAML
20-
if 'offset_dir' not in self.cfg['processing']['resample']:
21-
self.cfg['processing']['resample']['offset_dir'] = self.cfg['ProductPathGroup']['ScratchPath']
22+
error_channel = journal.error('ResampleSlcRunConfig.yaml_check')
2223

23-
# Check offsets directory structure
24-
off_dir = self.cfg['processing']['resample']['offset_dir']
25-
freq_pols = self.cfg['processing']['input_subset']['list_of_frequencies']
24+
# Extract frequency
25+
freq_pols = self.cfg['processing']['input_subset'][
26+
'list_of_frequencies']
2627
frequencies = freq_pols.keys()
27-
helpers.check_mode_directory_tree(off_dir, 'geo2rdr', frequencies)
28+
29+
if resample_type not in ['coarse', 'fine']:
30+
err_str = f"{resample_type} is not a valid resample mode"
31+
error_channel.log(err_str)
32+
raise ValueError(err_str)
33+
34+
# For insar.py, offsets_dir comes from the previous step of the
35+
# workflow through scratch_path
36+
resample_key = f'{resample_type}_resample'
37+
if self.cfg['processing'][resample_key]['offsets_dir'] is None:
38+
self.cfg['processing'][resample_key]['offsets_dir'] = \
39+
self.cfg['ProductPathGroup']['ScratchPath']
40+
offsets_dir = self.cfg['processing'][resample_key]['offsets_dir']
41+
42+
# Check directory structure and existence of offset files depending on
43+
# the selected resample type
44+
if resample_type == 'coarse':
45+
helpers.check_mode_directory_tree(offsets_dir, 'geo2rdr',
46+
frequencies)
47+
for freq in frequencies:
48+
rg_off = os.path.join(offsets_dir, 'geo2rdr', f'freq{freq}',
49+
'range.off')
50+
az_off = rg_off.replace('range', 'azimuth')
51+
if not os.path.exists(rg_off) or not os.path.exists(az_off):
52+
err_str = f'{rg_off} and {az_off} offsets files do not exist'
53+
error_channel.log(err_str)
54+
raise FileNotFoundError(err_str)
55+
else:
56+
# use the HH or VV rubbersheeted offsets to fine
57+
# resample the secondary SLC. Check for the offsets existence
58+
for freq in frequencies:
59+
for pol in ['HH', 'VV']:
60+
rg_off = os.path.join(offsets_dir,
61+
'rubbersheeted_offsets',
62+
f'freq{freq}', pol, 'range.off.vrt')
63+
az_off = rg_off.replace('range', 'azimuth')
64+
if not os.path.exists(rg_off) or not os.path.exists(az_off):
65+
err_str = f"{rg_off} and {az_off} files do not exists. HH and" \
66+
f"VV rubbersheet offsets required to run fine resampling"
67+
error_channel.log(err_str)
68+
raise FileNotFoundError(err_str)

python/packages/pybind_nisar/workflows/yaml_argparse.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class YamlArgparse():
3939
Class for parsing Yaml path from CLI
4040
'''
4141

42-
def __init__(self):
42+
def __init__(self, resample_type = False):
4343
self.parser = argparse.ArgumentParser(description='',
4444
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
4545

@@ -49,6 +49,11 @@ def __init__(self):
4949
default=True, help='Disable logging to file. Log to file on by default.')
5050
self.parser.add_argument('--restart', action='store_true', default=False,
5151
help='Restart the InSAR workflow from the beginning and ignore the previous run. False by default.')
52+
if resample_type:
53+
self.parser.add_argument('--resample-type', dest='resample_type', default='coarse',
54+
help='Type of offsets (coregistered slc) to use in resample_slc (crossmul).\n'
55+
'Coarse: geometry offsets (secondary SLC resampled with geometry offsets)\n'
56+
'Fine: rubbersheeted dense offsets (secondary slc resampled with rubbersheet dense offsets')
5257
self.args = argparse.Namespace()
5358

5459
def parse(self):

0 commit comments

Comments
 (0)