From d35ef09f4339b0a5e5e52ffbb63d4ed3c9543ca5 Mon Sep 17 00:00:00 2001 From: Zhang Yunjun Date: Sun, 23 Jun 2024 20:32:03 +0800 Subject: [PATCH] `isce_utils`: add `crop_slc()` and `form_ifgram()` (#1221) + utils.isce_utils: add crop_slc() and form_ifgram() for more flexible interferogram manipulations. + tsview: add exclude date in the saved text file header, to be more informative. + circleci: use cimg/base docker image, to replace the ubuntu:bionic image --- .circleci/config.yml | 2 +- src/mintpy/tsview.py | 1 + src/mintpy/utils/isce_utils.py | 145 ++++++++++++++++++++++++++++----- src/mintpy/utils/writefile.py | 6 +- 4 files changed, 131 insertions(+), 23 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 4ba466776..c96a86b28 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2.1 jobs: unit-n-workflow-tests: docker: - - image: ubuntu:bionic + - image: cimg/base:current environment: CONDA_PREFIX: /root/tools/miniforge MINTPY_HOME: /root/tools/MintPy diff --git a/src/mintpy/tsview.py b/src/mintpy/tsview.py index f010a2621..365ae0a7d 100644 --- a/src/mintpy/tsview.py +++ b/src/mintpy/tsview.py @@ -609,6 +609,7 @@ def save_ts_data_and_plot(yx, d_ts, m_strs, inps): header += f'{get_point_coord_str(y, x, inps.coord, inps.lalo_digit)}\n' header += f'reference pixel: y={inps.ref_yx[0]}, x={inps.ref_yx[1]}\n' if inps.ref_yx else '' header += f'reference date: {inps.date_list[inps.ref_idx]}\n' if inps.ref_idx else '' + header += f'exclude date: {inps.ex_date_list}\n' if inps.ex_date_list else '' header += 'estimated time function parameters:\n' for m_str in m_strs: header += f' {m_str}\n' diff --git a/src/mintpy/utils/isce_utils.py b/src/mintpy/utils/isce_utils.py index 2338c941c..e89dfd2db 100644 --- a/src/mintpy/utils/isce_utils.py +++ b/src/mintpy/utils/isce_utils.py @@ -968,6 +968,111 @@ def convolve(data, kernel): return real + 1J * imag +def crop_slc(slc_file, sub_slc_file, x0=None, y0=None, x1=None, y1=None): + """Crop / subset the complex ISCE-2 SLC file in complex64. + + Parameters: slc_file - str, path to the SLC data file + sub_slc_file - str, path to the cropped SLC data file + x0/y0/x1/y1 - int, starting column / row, and ending column/row + Returns: sub_slc_file - str, path to the cropped SLC data file + """ + # default arg values + atr = readfile.read_attribute(slc_file) + length, width = int(atr['LENGTH']), int(atr['WIDTH']) + x0 = 0 if x0 is None else x0 + y0 = 0 if y0 is None else y0 + x1 = width if x1 is None else x1 + y1 = length if y1 is None else y1 + + # read + slc_data = readfile.read_binary( + slc_file, + shape=(length, width), + box=(x0, y0, x1, y1), + data_type='complex64', + cpx_band='complex', + ) + + # create output directory if not exist + if not os.path.isdir(os.path.dirname(sub_slc_file)): + os.makedirs(os.path.dirname(sub_slc_file), exist_ok=True) + print('create diretory:', os.path.dirname(sub_slc_file)) + + # write + writefile.write_isce_file( + data=slc_data, + out_file=sub_slc_file, + file_type='isce_slc', + print_msg=True, + ) + + return sub_slc_file + + +def form_ifgram(slc_file1, slc_file2, int_file, rg_look=1, az_look=1): + """Form interferogram from two SLC files via isce2. + + Modified from ISCE-2/stripmapStack/crossmul.py. + + Parameters: slc_file1 - str, path to the reference SLC data file + slc_file2 - str, path to the secondary SLC data file + int_file - str, path to the output interferogram file + rg_look - int, number of looks in the range direction + az_look - int, number of looks in the azimuth direction + Returns: int_file - str, path to the output interferogram file + """ + import isce + import isceobj + from components.stdproc.stdproc import crossmul + + # create output directory if not exist + if not os.path.isdir(os.path.dirname(int_file)): + os.makedirs(os.path.dirname(int_file), exist_ok=True) + print('create diretory:', os.path.dirname(int_file)) + + # open slc files + slc_img1 = isceobj.createSlcImage() + slc_img1.load(slc_file1 + '.xml') + slc_img1.setAccessMode('read') + slc_img1.createImage() + + slc_img2 = isceobj.createSlcImage() + slc_img2.load(slc_file2 + '.xml') + slc_img2.setAccessMode('read') + slc_img2.createImage() + + # prepare int files + slc_length = min(slc_img1.getLength(), slc_img2.getLength()) + slc_width = slc_img1.getWidth() + int_width = int(slc_width / rg_look) + + int_img = isceobj.createIntImage() + int_img.setFilename(int_file) + int_img.setWidth(int_width) + int_img.setAccessMode('write') + int_img.createImage() + + amp_file = int_file[:-4] + '.amp' + amp_img = isceobj.createAmpImage() + amp_img.setFilename(amp_file) + amp_img.setWidth(int_width) + amp_img.setAccessMode('write') + amp_img.createImage() + + # generate int files + crossmul_obj = crossmul.createcrossmul() + crossmul_obj.width = slc_width + crossmul_obj.length = slc_length + crossmul_obj.LooksDown = az_look + crossmul_obj.LooksAcross = rg_look + crossmul_obj.crossmul(slc_img1, slc_img2, int_img, amp_img) + + for img in [int_img, amp_img, slc_img1, slc_img2]: + img.finalizeImage() + + return int_file + + def filter_goldstein(int_file, filt_file, filt_strength=0.2): """Filter wrapped interferogram with the power-spectral filter via isce2. @@ -1024,34 +1129,34 @@ def estimate_coherence(intfile, corfile): from mroipac.icu.Icu import Icu # create filt interferogram file object - filtImage = isceobj.createIntImage() - filtImage.load(intfile + '.xml') - filtImage.setAccessMode('read') - filtImage.createImage() + filt_img = isceobj.createIntImage() + filt_img.load(intfile + '.xml') + filt_img.setAccessMode('read') + filt_img.createImage() # create phase sigma correlation file object - phsigImage = isceobj.createImage() - phsigImage.dataType='FLOAT' - phsigImage.bands = 1 - phsigImage.setWidth(filtImage.getWidth()) - phsigImage.setFilename(corfile) - phsigImage.setAccessMode('write') - phsigImage.createImage() + phsig_img = isceobj.createImage() + phsig_img.dataType='FLOAT' + phsig_img.bands = 1 + phsig_img.setWidth(filt_img.getWidth()) + phsig_img.setFilename(corfile) + phsig_img.setAccessMode('write') + phsig_img.createImage() # setup Icu() object - icuObj = Icu(name='sentinel_filter_icu') - icuObj.configure() - icuObj.unwrappingFlag = False - icuObj.useAmplitudeFlag = False - #icuObj.correlationType = 'NOSLOPE' + icu_obj = Icu(name='sentinel_filter_icu') + icu_obj.configure() + icu_obj.unwrappingFlag = False + icu_obj.useAmplitudeFlag = False + #icu_obj.correlationType = 'NOSLOPE' # run - icuObj.icu(intImage=filtImage, phsigImage=phsigImage) - phsigImage.renderHdr() + icu_obj.icu(intImage=filt_img, phsigImage=phsig_img) + phsig_img.renderHdr() # close - filtImage.finalizeImage() - phsigImage.finalizeImage() + filt_img.finalizeImage() + phsig_img.finalizeImage() return diff --git a/src/mintpy/utils/writefile.py b/src/mintpy/utils/writefile.py index d2db160b8..7bd249f00 100644 --- a/src/mintpy/utils/writefile.py +++ b/src/mintpy/utils/writefile.py @@ -661,7 +661,7 @@ def write_isce_xml(meta, fname, print_msg=True): return -def write_isce_file(data, out_file, file_type='isce_unw'): +def write_isce_file(data, out_file, file_type='isce_unw', print_msg=True): """write data to file in ISCE format Parameters: data - 2D np.ndarray, binary data matrix @@ -674,6 +674,8 @@ def write_isce_file(data, out_file, file_type='isce_unw'): # write data to binary file data.tofile(out_file) + if print_msg: + print(f'write file: {out_file}') # write isce xml metadata file length, width = data.shape @@ -710,7 +712,7 @@ def write_isce_file(data, out_file, file_type='isce_unw'): else: raise ValueError(f'un-recognized ISCE file type: {file_type}') - write_isce_xml(meta, out_file) + write_isce_xml(meta, out_file, print_msg=print_msg) return out_file