Skip to content

Commit 71b1598

Browse files
Virginia BrancatoGitHub Enterprise
authored andcommitted
ISO metadata for InSAR product (#935)
* Add ISO metadata to RIFG layers * Add ISO metadata to RUNW layers * Generate stats in RUNW instead of copying * Create external compute_stats function * Use external compute_stats inside workflow * Reusing the compute_stats function in SLC writer
1 parent eccfb9d commit 71b1598

File tree

6 files changed

+95
-43
lines changed

6 files changed

+95
-43
lines changed

python/packages/nisar/products/writers/SLC.py

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
from nisar.types import complex32
1212
from nisar.products.readers.Raw import Raw
1313
from nisar.workflows.h5_prep import add_geolocation_grid_cubes_to_hdf5
14+
from nisar.workflows.compute_stats import compute_stats_complex_data
15+
1416

1517
log = logging.getLogger("SLCWriter")
1618

@@ -378,17 +380,7 @@ def compute_stats(self, frequency, pol):
378380
'''
379381
h5_ds = self.swath(frequency)[pol]
380382
raster = isce3.io.Raster(f"IH5:::ID={h5_ds.id.id}".encode("utf-8"))
381-
stats_obj = isce3.math.compute_raster_stats_real_imag(raster)[0]
382-
h5_ds.attrs.create('min_real_value', data=stats_obj.min_real)
383-
h5_ds.attrs.create('mean_real_value', data=stats_obj.mean_real)
384-
h5_ds.attrs.create('max_real_value', data=stats_obj.max_real)
385-
h5_ds.attrs.create('sample_stddev_real',
386-
data=stats_obj.sample_stddev_real)
387-
h5_ds.attrs.create('min_imag_value', data=stats_obj.min_imag)
388-
h5_ds.attrs.create('mean_imag_value', data=stats_obj.mean_imag)
389-
h5_ds.attrs.create('max_imag_value', data=stats_obj.max_imag)
390-
h5_ds.attrs.create('sample_stddev_imag',
391-
data=stats_obj.sample_stddev_imag)
383+
compute_stats_complex_data(raster, h5_ds)
392384

393385

394386
def add_calibration_section(self, frequency, pol,
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import isce3
2+
from osgeo import gdal
3+
4+
5+
def compute_stats_complex_data(raster, h5_ds):
6+
"""
7+
Compute raster statistics for complex datasets
8+
9+
raster: isce3.io.Raster
10+
ISCE3 Raster object
11+
h5_ds: h5py.File
12+
h5py file
13+
"""
14+
stats_obj = isce3.math.compute_raster_stats_real_imag(raster)[0]
15+
h5_ds.attrs.create('min_real_value', data=stats_obj.min_real)
16+
h5_ds.attrs.create('mean_real_value', data=stats_obj.mean_real)
17+
h5_ds.attrs.create('max_real_value', data=stats_obj.max_real)
18+
h5_ds.attrs.create('sample_stddev_real',
19+
data=stats_obj.sample_stddev_real)
20+
h5_ds.attrs.create('min_imag_value', data=stats_obj.min_imag)
21+
h5_ds.attrs.create('mean_imag_value', data=stats_obj.mean_imag)
22+
h5_ds.attrs.create('max_imag_value', data=stats_obj.max_imag)
23+
h5_ds.attrs.create('sample_stddev_imag',
24+
data=stats_obj.sample_stddev_imag)
25+
26+
27+
def compute_stats_real_data(raster, h5_ds):
28+
"""
29+
Compute raster statistics for real datasets
30+
31+
raster: isce3.io.Raster
32+
ISCE3 Raster object
33+
h5_ds: h5py.File
34+
h5py file
35+
"""
36+
if raster.datatype() == gdal.GDT_Float64:
37+
stats_obj = isce3.math.compute_raster_stats_float64(raster)[0]
38+
else:
39+
stats_obj = isce3.math.compute_raster_stats_float32(raster)[0]
40+
h5_ds.attrs.create('min_value', data=stats_obj.min)
41+
h5_ds.attrs.create('mean_value', data=stats_obj.mean)
42+
h5_ds.attrs.create('max_value', data=stats_obj.max)
43+
h5_ds.attrs.create('sample_stddev',
44+
data=stats_obj.sample_stddev)

python/packages/nisar/workflows/crossmul.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,13 @@
99
import h5py
1010
import journal
1111
import isce3
12+
13+
from osgeo import gdal
1214
from nisar.products.readers import SLC
1315
from nisar.workflows import h5_prep
1416
from nisar.workflows.crossmul_runconfig import CrossmulRunConfig
1517
from nisar.workflows.yaml_argparse import YamlArgparse
18+
from nisar.workflows.compute_stats import compute_stats_real_data
1619

1720

1821
def run(cfg: dict, output_hdf5: str = None, resample_type='coarse'):
@@ -137,19 +140,45 @@ def run(cfg: dict, output_hdf5: str = None, resample_type='coarse'):
137140
crossmul.crossmul(ref_slc_raster, sec_slc_raster,
138141
igram_raster, coherence_raster)
139142

143+
# Allocate raster statistics for coherence
144+
compute_stats_real_data(coherence_raster, coherence_dataset)
145+
140146
del coherence_raster
141147
else:
142148
# no coherence without multilook
143149
crossmul.crossmul(ref_slc_raster, sec_slc_raster,
144150
igram_raster)
145-
146151
del igram_raster
147152

153+
# Allocate stats for rubbersheet offsets
154+
stats_offsets(dst_h5, freq, pol)
155+
148156
t_all_elapsed = time.time() - t_all
149157
info_channel.log(
150158
f"successfully ran crossmul in {t_all_elapsed:.3f} seconds")
151159

152160

161+
def stats_offsets(h5_ds, freq, pol):
162+
"""
163+
Allocate statistics for dense offsets
164+
h5_ds: h5py.File
165+
h5py File
166+
freq: str
167+
Frequency to process (A or B)
168+
pol: str
169+
Polarization to process (HH, HV, VH, VV)
170+
"""
171+
path = f'science/LSAR/RIFG/swaths/frequency{freq}/pixelOffsets/{pol}/'
172+
offset_layer = ['slantRangeOffset', 'alongTrackOffset']
173+
174+
for layer in offset_layer:
175+
offset_path = f'{path}/{layer}'
176+
offset_dataset = h5_ds[offset_path]
177+
offset_raster = isce3.io.Raster(
178+
f"IH5:::ID={offset_dataset.id.id}".encode("utf-8"))
179+
compute_stats_real_data(offset_raster, offset_dataset)
180+
181+
153182
if __name__ == "__main__":
154183
'''
155184
run crossmul from command line

python/packages/nisar/workflows/geocode_insar.py

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from nisar.workflows.geocode_insar_runconfig import \
2020
GeocodeInsarRunConfig
2121
from nisar.workflows.yaml_argparse import YamlArgparse
22+
from nisar.workflows.compute_stats import compute_stats_real_data
2223

2324

2425
def run(cfg, runw_hdf5, output_hdf5):
@@ -357,7 +358,7 @@ def cpu_run(cfg, runw_hdf5, output_hdf5):
357358
if (dataset_name != "layoverShadowMask"):
358359
# Layover/shadow masks dont't have min/max/mean/stddev
359360
# stats attributes
360-
_compute_stats(geocoded_raster, geocoded_dataset)
361+
compute_stats_real_data(geocoded_raster, geocoded_dataset)
361362

362363
# spec for NISAR GUNW does not require freq B so skip radar cube
363364
if freq.upper() == 'B':
@@ -565,8 +566,7 @@ def gpu_run(cfg, runw_hdf5, output_hdf5):
565566
if (dataset_name != "layoverShadowMask"):
566567
# Layover/shadow masks dont't have min/max/mean/stddev
567568
# stats attributes
568-
_compute_stats(geocoded_raster, geocoded_dataset)
569-
569+
compute_stats_real_data(geocoded_raster, geocoded_dataset)
570570

571571
if gunw_datasets['layoverShadowMask']:
572572
skip_layover_shadow = True
@@ -581,18 +581,6 @@ def gpu_run(cfg, runw_hdf5, output_hdf5):
581581
info_channel.log(f"Successfully ran geocode in {t_all_elapsed:.3f} seconds")
582582

583583

584-
def _compute_stats(raster, h5_ds):
585-
if raster.datatype() == gdal.GDT_Float64:
586-
stats_obj = isce3.math.compute_raster_stats_float64(raster)[0]
587-
else:
588-
stats_obj = isce3.math.compute_raster_stats_float32(raster)[0]
589-
h5_ds.attrs.create('min_value', data=stats_obj.min)
590-
h5_ds.attrs.create('mean_value', data=stats_obj.mean)
591-
h5_ds.attrs.create('max_value', data=stats_obj.max)
592-
h5_ds.attrs.create('sample_stddev',
593-
data=stats_obj.sample_stddev)
594-
595-
596584
if __name__ == "__main__":
597585
"""
598586
run geocode from command line

python/packages/nisar/workflows/gslc.py

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from nisar.workflows.h5_prep import add_radar_grid_cubes_to_hdf5
1313
from nisar.workflows.yaml_argparse import YamlArgparse
1414
from nisar.workflows.gslc_runconfig import GSLCRunConfig
15+
from nisar.workflows.compute_stats import compute_stats_complex_data
1516

1617

1718
def run(cfg):
@@ -89,7 +90,7 @@ def run(cfg):
8990

9091
# output_raster_ref = f'HDF5:{output_hdf5}:/{dataset_path}'
9192
gslc_raster = isce3.io.Raster(f"IH5:::ID={gslc_dataset.id.id}".encode("utf-8"))
92-
_compute_stats(gslc_raster, gslc_dataset)
93+
compute_stats_complex_data(gslc_raster, gslc_dataset)
9394

9495
t_pol_elapsed = time.time() - t_pol
9596
info_channel.log(f'polarization {polarization} ran in {t_pol_elapsed:.3f} seconds')
@@ -123,20 +124,6 @@ def run(cfg):
123124
info_channel.log(f"successfully ran geocode SLC in {t_all_elapsed:.3f} seconds")
124125

125126

126-
def _compute_stats(raster, h5_ds):
127-
stats_obj = isce3.math.compute_raster_stats_real_imag(raster)[0]
128-
h5_ds.attrs.create('min_real_value', data=stats_obj.min_real)
129-
h5_ds.attrs.create('mean_real_value', data=stats_obj.mean_real)
130-
h5_ds.attrs.create('max_real_value', data=stats_obj.max_real)
131-
h5_ds.attrs.create('sample_stddev_real',
132-
data=stats_obj.sample_stddev_real)
133-
h5_ds.attrs.create('min_imag_value', data=stats_obj.min_imag)
134-
h5_ds.attrs.create('mean_imag_value', data=stats_obj.mean_imag)
135-
h5_ds.attrs.create('max_imag_value', data=stats_obj.max_imag)
136-
h5_ds.attrs.create('sample_stddev_imag',
137-
data=stats_obj.sample_stddev_imag)
138-
139-
140127
if __name__ == "__main__":
141128
yaml_parser = YamlArgparse()
142129
args = yaml_parser.parse()

python/packages/nisar/workflows/unwrap.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
from nisar.workflows import h5_prep
1818
from nisar.workflows.unwrap_runconfig import UnwrapRunConfig
1919
from nisar.workflows.yaml_argparse import YamlArgparse
20+
from nisar.workflows.compute_stats import compute_stats_real_data
21+
2022

2123
def run(cfg: dict, input_hdf5: str, output_hdf5: str):
2224
'''
@@ -143,7 +145,10 @@ def run(cfg: dict, input_hdf5: str, output_hdf5: str):
143145
unwrap_obj.unwrap(uigram_raster, conn_comp_raster, igram_raster,
144146
corr_raster)
145147

146-
# Copy coherence magnitude and culled offsets to RIFG
148+
# Compute stats for unwrapped phase
149+
compute_stats_real_data(uigram_raster, uigram_dataset)
150+
151+
# Copy coherence magnitude and culled offsets from RIFG
147152
dataset_names = ['coherenceMagnitude', 'alongTrackOffset',
148153
'slantRangeOffset']
149154
group_names = ['interferogram', 'pixelOffsets', 'pixelOffsets']
@@ -152,6 +157,13 @@ def run(cfg: dict, input_hdf5: str, output_hdf5: str):
152157
src_path = f'{src_freq_group_path}/{group_name}/{pol}/{dataset_name}'
153158
dst_h5[dst_path][:, :] = src_h5[src_path][()]
154159

160+
dst_dataset = dst_h5[dst_path]
161+
dst_raster = isce3.io.Raster(
162+
f"IH5:::ID={dst_dataset.id.id}".encode("utf-8"),
163+
update=True)
164+
165+
compute_stats_real_data(dst_raster, dst_dataset)
166+
155167
del uigram_raster
156168
del conn_comp_raster
157169

0 commit comments

Comments
 (0)