def plot_num_triplet_with_nonzero_integer_ambiguity(fname, display=False, font_size=12, fig_size=[9,3]): """Plot the histogram for the number of triplets with non-zero integer ambiguity Fig. 3d-e in Yunjun et al. (2019, CAGEO). """ # read data data, atr = readfile.read(fname) vmax = int(np.nanmax(data)) # plot fig, axs = plt.subplots(nrows=1, ncols=2, figsize=fig_size) # subplot 1 - map ax = axs[0] im = ax.imshow(data, cmap='RdBu_r', interpolation='nearest') # reference point if all(key in atr.keys() for key in ['REF_Y','REF_X']): ax.plot(int(atr['REF_X']), int(atr['REF_Y']), 's', color='white', ms=3) # format pp.auto_flip_direction(atr, ax=ax, print_msg=False) fig.colorbar(im, ax=ax) ax.set_title(r'$T_{int}$', fontsize=font_size) # subplot 2 - histogram ax = axs[1] ax.hist(data[~np.isnan(data)].flatten(), range=(0, vmax), log=True, bins=vmax) # axis format ax.set_xlabel(r'# of triplets w non-zero int ambiguity $T_{int}$', fontsize=font_size) ax.set_ylabel('# of pixels', fontsize=font_size) ax.xaxis.set_minor_locator(ticker.AutoMinorLocator()) ax.yaxis.set_major_locator(ticker.LogLocator(base=10.0, numticks=15)) ax.yaxis.set_minor_locator(ticker.LogLocator(base=10.0, numticks=15, subs=(0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9))) ax.yaxis.set_minor_formatter(ticker.NullFormatter()) for ax in axs: ax.tick_params(which='both', direction='in', labelsize=font_size, bottom=True, top=True, left=True, right=True) fig.tight_layout() # output out_fig = '{}.png'.format(os.path.splitext(fname)[0]) print('plot and save figure to file', out_fig) fig.savefig(out_fig, bbox_inches='tight', transparent=True, dpi=300) if display: plt.show() else: plt.close(fig) return
def flip_map(): global inps, atr if inps.auto_flip: inps.flip_lr, inps.flip_ud = pp.auto_flip_direction(atr) else: inps.flip_ud = False inps.flip_lr = False
def get_common_region_int_ambiguity(ifgram_file, cc_mask_file, water_mask_file=None, num_sample=100, dsNameIn='unwrapPhase'): """Solve the phase unwrapping integer ambiguity for the common regions among all interferograms Parameters: ifgram_file : str, path of interferogram stack file cc_mask_file : str, path of common connected components file water_mask_file : str, path of water mask file num_sample : int, number of pixel sampled for each region dsNameIn : str, dataset name of the unwrap phase to be corrected Returns: common_regions : list of skimage.measure._regionprops._RegionProperties object modified by adding two more variables: sample_coords : 2D np.ndarray in size of (num_sample, 2) in int64 format int_ambiguity : 1D np.ndarray in size of (num_ifgram,) in int format """ print('-' * 50) print( 'calculating the integer ambiguity for the common regions defined in', cc_mask_file) # stack info stack_obj = ifgramStack(ifgram_file) stack_obj.open() date12_list = stack_obj.get_date12_list(dropIfgram=True) num_ifgram = len(date12_list) C = matrix( ifgramStack.get_design_matrix4triplet(date12_list).astype(float)) ref_phase = stack_obj.get_reference_phase(unwDatasetName=dsNameIn, dropIfgram=True).reshape( num_ifgram, -1) # prepare common label print('read common mask from', cc_mask_file) cc_mask = readfile.read(cc_mask_file)[0] if water_mask_file is not None and os.path.isfile(water_mask_file): water_mask = readfile.read(water_mask_file)[0] print('refine common mask based on water mask file', water_mask_file) cc_mask[water_mask == 0] = 0 label_img, num_label = connectComponent.get_large_label(cc_mask, min_area=2.5e3, print_msg=True) common_regions = measure.regionprops(label_img) print('number of common regions:', num_label) # add sample_coords / int_ambiguity print('number of samples per region:', num_sample) print('solving the phase-unwrapping integer ambiguity for {}'.format( dsNameIn)) print( '\tbased on the closure phase of interferograms triplets (Yunjun et al., 2019)' ) print( '\tusing the L1-norm regularzed least squares approximation (LASSO) ...' ) for i in range(num_label): common_reg = common_regions[i] # sample_coords idx = sorted( np.random.choice(common_reg.area, num_sample, replace=False)) common_reg.sample_coords = common_reg.coords[idx, :].astype(int) # solve for int_ambiguity U = np.zeros((num_ifgram, num_sample)) if common_reg.label == label_img[stack_obj.refY, stack_obj.refX]: print('{}/{} skip calculation for the reference region'.format( i + 1, num_label)) else: prog_bar = ptime.progressBar(maxValue=num_sample, prefix='{}/{}'.format( i + 1, num_label)) for j in range(num_sample): # read unwrap phase y, x = common_reg.sample_coords[j, :] unw = ifginv.read_unwrap_phase(stack_obj, box=(x, y, x + 1, y + 1), ref_phase=ref_phase, unwDatasetName=dsNameIn, dropIfgram=True, print_msg=False).reshape( num_ifgram, -1) # calculate closure_int closure_pha = np.dot(C, unw) closure_int = matrix( np.round( (closure_pha - ut.wrap(closure_pha)) / (2. * np.pi))) # solve for U U[:, j] = np.round( l1regls(-C, closure_int, alpha=1e-2, show_progress=0)).flatten() prog_bar.update(j + 1, every=5) prog_bar.close() # add int_ambiguity common_reg.int_ambiguity = np.median(U, axis=1) common_reg.date12_list = date12_list #sort regions by size to facilitate the region matching later common_regions.sort(key=lambda x: x.area, reverse=True) # plot sample result fig_size = pp.auto_figure_size(label_img.shape, disp_cbar=False) fig, ax = plt.subplots(figsize=fig_size) ax.imshow(label_img, cmap='jet') for common_reg in common_regions: ax.plot(common_reg.sample_coords[:, 1], common_reg.sample_coords[:, 0], 'k.', ms=2) pp.auto_flip_direction(stack_obj.metadata, ax, print_msg=False) out_img = 'common_region_sample.png' fig.savefig(out_img, bbox_inches='tight', transparent=True, dpi=300) print('saved common regions and sample pixels to file', out_img) return common_regions
def update_inps_with_file_metadata(inps, metadata): # Subset # Convert subset input into bounding box in radar / geo coordinate # geo_box = None if atr is not geocoded. coord = ut.coordinate(metadata) inps.pix_box, inps.geo_box = subset.subset_input_dict2box( vars(inps), metadata) inps.pix_box = coord.check_box_within_data_coverage(inps.pix_box) inps.geo_box = coord.box_pixel2geo(inps.pix_box) # Out message data_box = (0, 0, inps.width, inps.length) vprint('data coverage in y/x: ' + str(data_box)) vprint('subset coverage in y/x: ' + str(inps.pix_box)) vprint('data coverage in lat/lon: ' + str(coord.box_pixel2geo(data_box))) vprint('subset coverage in lat/lon: ' + str(inps.geo_box)) vprint( '------------------------------------------------------------------------' ) # DEM contour display if max(inps.pix_box[3] - inps.pix_box[1], inps.pix_box[2] - inps.pix_box[0]) > 2e3: inps.disp_dem_contour = False if inps.dem_file: vprint( 'area exceed 2000 pixels, turn off default DEM contour display' ) # Multilook, if too many subplots in one figure for less memory and faster speed if inps.multilook_num > 1: inps.multilook = True # Colormap inps.colormap = pp.check_colormap_input(metadata, inps.colormap, datasetName=inps.dset[0], cmap_lut=inps.cmap_lut, print_msg=inps.print_msg) # Reference Point # Convert ref_lalo if existed, to ref_yx, and use ref_yx for the following # ref_yx is referenced to input data coverage, not subseted area for display if inps.ref_lalo and inps.geo_box: inps.ref_yx = [ coord.lalo2yx(inps.ref_lalo[0], coord_type='lat'), coord.lalo2yx(inps.ref_lalo[1], coord_type='lon') ] vprint('input reference point in lat/lon: {}'.format(inps.ref_lalo)) vprint('input reference point in y /x : {}'.format(inps.ref_yx)) # ref_lalo if inps.ref_yx and inps.geo_box: inps.ref_lalo = [ coord.yx2lalo(inps.ref_yx[0], coord_type='y'), coord.yx2lalo(inps.ref_yx[1], coord_type='x') ] elif 'REF_LAT' in metadata.keys(): inps.ref_lalo = [ float(metadata['REF_LAT']), float(metadata['REF_LON']) ] else: inps.ref_lalo = None # Points of interest inps = pp.read_point2inps(inps, coord) # Unit and Wrap inps.disp_unit, inps.wrap = pp.check_disp_unit_and_wrap( metadata, disp_unit=inps.disp_unit, wrap=inps.wrap, wrap_range=inps.wrap_range, print_msg=inps.print_msg) # Min / Max - Display if not inps.vlim: if (any(i in inps.key.lower() for i in ['coherence', '.cor']) or (inps.key == 'ifgramStack' and inps.dset[0].split('-')[0] in ['coherence']) or inps.dset[0] == 'cmask'): inps.vlim = [0.0, 1.0] elif inps.key in ['.int'] or inps.wrap: inps.vlim = inps.wrap_range # Transparency - Alpha if not inps.transparency: # Auto adjust transparency value when showing shaded relief DEM if inps.dem_file and inps.disp_dem_shade: inps.transparency = 0.8 else: inps.transparency = 1.0 # Flip Left-Right / Up-Down if inps.auto_flip: inps.flip_lr, inps.flip_ud = pp.auto_flip_direction( metadata, print_msg=inps.print_msg) # Figure Title if not inps.fig_title: inps.fig_title = pp.auto_figure_title(metadata['FILE_PATH'], datasetNames=inps.dset, inps_dict=vars(inps)) vprint('figure title: ' + inps.fig_title) # Figure output file name if not inps.outfile: inps.outfile = ['{}{}'.format(inps.fig_title, inps.fig_ext)] inps = update_figure_setting(inps) return inps
def read_init_info(inps): # Time Series Info atr = readfile.read_attribute(inps.file[0]) inps.key = atr['FILE_TYPE'] if inps.key == 'timeseries': obj = timeseries(inps.file[0]) elif inps.key == 'giantTimeseries': obj = giantTimeseries(inps.file[0]) elif inps.key == 'HDFEOS': obj = HDFEOS(inps.file[0]) else: raise ValueError('input file is {}, not timeseries.'.format(inps.key)) obj.open(print_msg=inps.print_msg) inps.seconds = atr.get('CENTER_LINE_UTC', 0) if not inps.file_label: inps.file_label = [] for fname in inps.file: fbase = os.path.splitext(os.path.basename(fname))[0] fbase = fbase.replace('timeseries', '') inps.file_label.append(fbase) # default mask file if not inps.mask_file and 'msk' not in inps.file[0]: dir_name = os.path.dirname(inps.file[0]) if 'Y_FIRST' in atr.keys(): inps.mask_file = os.path.join(dir_name, 'geo_maskTempCoh.h5') else: inps.mask_file = os.path.join(dir_name, 'maskTempCoh.h5') if not os.path.isfile(inps.mask_file): inps.mask_file = None ## date info inps.date_list = obj.dateList inps.num_date = len(inps.date_list) if inps.start_date: inps.date_list = [i for i in inps.date_list if int(i) >= int(inps.start_date)] if inps.end_date: inps.date_list = [i for i in inps.date_list if int(i) <= int(inps.end_date)] inps.num_date = len(inps.date_list) inps.dates, inps.yearList = ptime.date_list2vector(inps.date_list) (inps.ex_date_list, inps.ex_dates, inps.ex_flag) = read_exclude_date(inps.ex_date_list, inps.date_list) # reference date/index if not inps.ref_date: inps.ref_date = atr.get('REF_DATE', None) if inps.ref_date: inps.ref_idx = inps.date_list.index(inps.ref_date) else: inps.ref_idx = None # date/index of interest for initial display if not inps.idx: if (not inps.ref_idx) or (inps.ref_idx < inps.num_date / 2.): inps.idx = inps.num_date - 2 else: inps.idx = 2 # Display Unit (inps.disp_unit, inps.unit_fac) = pp.scale_data2disp_unit(metadata=atr, disp_unit=inps.disp_unit)[1:3] # Map info - coordinate unit inps.coord_unit = atr.get('Y_UNIT', 'degrees').lower() # Read Error List inps.ts_plot_func = plot_ts_scatter inps.error_ts = None inps.ex_error_ts = None if inps.error_file: # assign plot function inps.ts_plot_func = plot_ts_errorbar # read error file error_fc = np.loadtxt(inps.error_file, dtype=bytes).astype(str) inps.error_ts = error_fc[:, 1].astype(np.float)*inps.unit_fac # update error file with exlcude date if inps.ex_date_list: e_ts = inps.error_ts[:] inps.ex_error_ts = e_ts[inps.ex_flag == 0] inps.error_ts = e_ts[inps.ex_flag == 1] # Zero displacement for 1st acquisition if inps.zero_first: inps.zero_idx = min(0, np.min(np.where(inps.ex_flag)[0])) # default lookup table file and coordinate object if not inps.lookup_file: inps.lookup_file = ut.get_lookup_file('./inputs/geometryRadar.h5') inps.coord = ut.coordinate(atr, inps.lookup_file) ## size and lalo info inps.pix_box, inps.geo_box = subset.subset_input_dict2box(vars(inps), atr) inps.pix_box = inps.coord.check_box_within_data_coverage(inps.pix_box) inps.geo_box = inps.coord.box_pixel2geo(inps.pix_box) data_box = (0, 0, int(atr['WIDTH']), int(atr['LENGTH'])) vprint('data coverage in y/x: '+str(data_box)) vprint('subset coverage in y/x: '+str(inps.pix_box)) vprint('data coverage in lat/lon: '+str(inps.coord.box_pixel2geo(data_box))) vprint('subset coverage in lat/lon: '+str(inps.geo_box)) vprint('------------------------------------------------------------------------') # calculate multilook_num # ONLY IF: # inps.multilook is True (no --nomultilook input) AND # inps.multilook_num ==1 (no --multilook-num input) # Note: inps.multilook is used for this check ONLY # Note: multilooking is only applied to the 3D data cubes and their related operations: # e.g. spatial indexing, referencing, etc. All the other variables are in the original grid # so that users get the same result as the non-multilooked version. if inps.multilook and inps.multilook_num == 1: inps.multilook_num = pp.auto_multilook_num(inps.pix_box, inps.num_date, max_memory=inps.maxMemory, print_msg=inps.print_msg) ## reference pixel if not inps.ref_lalo and 'REF_LAT' in atr.keys(): inps.ref_lalo = (float(atr['REF_LAT']), float(atr['REF_LON'])) if inps.ref_lalo: # set longitude to [-180, 180) if inps.coord_unit.lower().startswith('deg') and inps.ref_lalo[1] >= 180.: inps.ref_lalo[1] -= 360. # ref_lalo --> ref_yx if not set in cmd if not inps.ref_yx: inps.ref_yx = inps.coord.geo2radar(inps.ref_lalo[0], inps.ref_lalo[1], print_msg=False)[0:2] # use REF_Y/X if ref_yx not set in cmd if not inps.ref_yx and 'REF_Y' in atr.keys(): inps.ref_yx = (int(atr['REF_Y']), int(atr['REF_X'])) # ref_yx --> ref_lalo if in geo-coord # for plotting purpose only if inps.ref_yx and 'Y_FIRST' in atr.keys(): inps.ref_lalo = inps.coord.radar2geo(inps.ref_yx[0], inps.ref_yx[1], print_msg=False)[0:2] # do not plot native reference point if it's out of the coverage due to subset if (inps.ref_yx and 'Y_FIRST' in atr.keys() and inps.ref_yx == (int(atr.get('REF_Y',-999)), int(atr.get('REF_X',-999))) and not ( inps.pix_box[0] <= inps.ref_yx[1] < inps.pix_box[2] and inps.pix_box[1] <= inps.ref_yx[0] < inps.pix_box[3])): inps.disp_ref_pixel = False print('the native REF_Y/X is out of subset box, thus do not display') ## initial pixel coord if inps.lalo: inps.yx = inps.coord.geo2radar(inps.lalo[0], inps.lalo[1], print_msg=False)[0:2] try: inps.lalo = inps.coord.radar2geo(inps.yx[0], inps.yx[1], print_msg=False)[0:2] except: inps.lalo = None ## figure settings # Flip up-down / left-right if inps.auto_flip: inps.flip_lr, inps.flip_ud = pp.auto_flip_direction(atr, print_msg=inps.print_msg) # Transparency - Alpha if not inps.transparency: # Auto adjust transparency value when showing shaded relief DEM if inps.dem_file and inps.disp_dem_shade: inps.transparency = 0.7 else: inps.transparency = 1.0 ## display unit ans wrap # if wrap_step == 2*np.pi (default value), set disp_unit_img = radian; # otherwise set disp_unit_img = disp_unit inps.disp_unit_img = inps.disp_unit if inps.wrap: inps.range2phase = -4. * np.pi / float(atr['WAVELENGTH']) if 'cm' == inps.disp_unit.split('/')[0]: inps.range2phase /= 100. elif 'mm' == inps.disp_unit.split('/')[0]: inps.range2phase /= 1000. elif 'm' == inps.disp_unit.split('/')[0]: inps.range2phase /= 1. else: raise ValueError('un-recognized display unit: {}'.format(inps.disp_unit)) if (inps.wrap_range[1] - inps.wrap_range[0]) == 2*np.pi: inps.disp_unit_img = 'radian' inps.vlim = inps.wrap_range inps.cbar_label = 'Displacement [{}]'.format(inps.disp_unit_img) ## fit a suite of time func to the time series inps.model, inps.num_param = ts2vel.read_inps2model(inps, date_list=inps.date_list) # dense TS for plotting inps.date_list_fit = ptime.get_date_range(inps.date_list[0], inps.date_list[-1]) inps.dates_fit = ptime.date_list2vector(inps.date_list_fit)[0] inps.G_fit = time_func.get_design_matrix4time_func( date_list=inps.date_list_fit, model=inps.model, seconds=inps.seconds) return inps, atr
def read_init_info(inps): # Time Series Info ts_file0 = inps.file[0] atr = readfile.read_attribute(ts_file0) inps.key = atr['FILE_TYPE'] if inps.key == 'timeseries': obj = timeseries(ts_file0) elif inps.key == 'giantTimeseries': obj = giantTimeseries(ts_file0) elif inps.key == 'HDFEOS': obj = HDFEOS(ts_file0) else: raise ValueError('input file is {}, not timeseries.'.format(inps.key)) obj.open(print_msg=inps.print_msg) if not inps.file_label: inps.file_label = [str(i) for i in list(range(len(inps.file)))] # default mask file if not inps.mask_file and 'masked' not in ts_file0: dir_name = os.path.dirname(ts_file0) if 'Y_FIRST' in atr.keys(): inps.mask_file = os.path.join(dir_name, 'geo_maskTempCoh.h5') else: inps.mask_file = os.path.join(dir_name, 'maskTempCoh.h5') if not os.path.isfile(inps.mask_file): inps.mask_file = None # date info inps.date_list = obj.dateList inps.num_date = len(inps.date_list) if inps.start_date: inps.date_list = [ i for i in inps.date_list if int(i) >= int(inps.start_date) ] if inps.end_date: inps.date_list = [ i for i in inps.date_list if int(i) <= int(inps.end_date) ] inps.num_date = len(inps.date_list) inps.dates, inps.yearList = ptime.date_list2vector(inps.date_list) (inps.ex_date_list, inps.ex_dates, inps.ex_flag) = read_exclude_date(inps.ex_date_list, inps.date_list) # initial display index #if atr['REF_DATE'] in inps.date_list: # inps.ref_idx = inps.date_list.index(atr['REF_DATE']) #else: # inps.ref_idx = 0 if inps.ref_date: inps.ref_idx = inps.date_list.index(inps.ref_date) else: inps.ref_idx = 3 if not inps.idx: if inps.ref_idx < inps.num_date / 2.: inps.idx = inps.num_date - 3 else: inps.idx = 3 # Display Unit (inps.disp_unit, inps.unit_fac) = pp.scale_data2disp_unit(metadata=atr, disp_unit=inps.disp_unit)[1:3] # Map info - coordinate unit inps.coord_unit = atr.get('Y_UNIT', 'degrees').lower() # Read Error List inps.ts_plot_func = plot_ts_scatter inps.error_ts = None inps.ex_error_ts = None if inps.error_file: # assign plot function inps.ts_plot_func = plot_ts_errorbar # read error file error_fc = np.loadtxt(inps.error_file, dtype=bytes).astype(str) inps.error_ts = error_fc[:, 1].astype(np.float) * inps.unit_fac # update error file with exlcude date if inps.ex_date_list: e_ts = inps.error_ts[:] inps.ex_error_ts = e_ts[inps.ex_flag == 0] inps.error_ts = e_ts[inps.ex_flag == 1] # Zero displacement for 1st acquisition if inps.zero_first: inps.zero_idx = min(0, np.min(np.where(inps.ex_flag)[0])) # default lookup table file if not inps.lookup_file: inps.lookup_file = ut.get_lookup_file('./inputs/geometryRadar.h5') inps.coord = ut.coordinate(atr, inps.lookup_file) # size and lalo info inps.pix_box, inps.geo_box = subset.subset_input_dict2box(vars(inps), atr) inps.pix_box = inps.coord.check_box_within_data_coverage(inps.pix_box) inps.geo_box = inps.coord.box_pixel2geo(inps.pix_box) data_box = (0, 0, int(atr['WIDTH']), int(atr['LENGTH'])) vprint('data coverage in y/x: ' + str(data_box)) vprint('subset coverage in y/x: ' + str(inps.pix_box)) vprint('data coverage in lat/lon: ' + str(inps.coord.box_pixel2geo(data_box))) vprint('subset coverage in lat/lon: ' + str(inps.geo_box)) vprint( '------------------------------------------------------------------------' ) # reference pixel if not inps.ref_lalo and 'REF_LAT' in atr.keys(): inps.ref_lalo = (float(atr['REF_LAT']), float(atr['REF_LON'])) if inps.ref_lalo: if inps.ref_lalo[1] > 180.: inps.ref_lalo[1] -= 360. inps.ref_yx = inps.coord.geo2radar(inps.ref_lalo[0], inps.ref_lalo[1], print_msg=False)[0:2] if not inps.ref_yx and 'REF_Y' in atr.keys(): inps.ref_yx = [int(atr['REF_Y']), int(atr['REF_X'])] # Initial Pixel Coord if inps.lalo: inps.yx = inps.coord.geo2radar(inps.lalo[0], inps.lalo[1], print_msg=False)[0:2] try: inps.lalo = inps.coord.radar2geo(inps.yx[0], inps.yx[1], print_msg=False)[0:2] except: inps.lalo = None # Flip up-down / left-right if inps.auto_flip: inps.flip_lr, inps.flip_ud = pp.auto_flip_direction( atr, print_msg=inps.print_msg) # Transparency - Alpha if not inps.transparency: # Auto adjust transparency value when showing shaded relief DEM if inps.dem_file and inps.disp_dem_shade: inps.transparency = 0.7 else: inps.transparency = 1.0 # display unit ans wrap # if wrap_step == 2*np.pi (default value), set disp_unit_img = radian; # otherwise set disp_unit_img = disp_unit inps.disp_unit_img = inps.disp_unit if inps.wrap: inps.range2phase = -4. * np.pi / float(atr['WAVELENGTH']) if 'cm' == inps.disp_unit.split('/')[0]: inps.range2phase /= 100. elif 'mm' == inps.disp_unit.split('/')[0]: inps.range2phase /= 1000. elif 'm' == inps.disp_unit.split('/')[0]: inps.range2phase /= 1. else: raise ValueError('un-recognized display unit: {}'.format( inps.disp_unit)) if (inps.wrap_range[1] - inps.wrap_range[0]) == 2 * np.pi: inps.disp_unit_img = 'radian' inps.vlim = inps.wrap_range inps.cbar_label = 'Displacement [{}]'.format(inps.disp_unit_img) return inps, atr
def detect_unwrap_error(ifgram_file, mask_file, mask_cc_file='maskConnComp.h5', unwDatasetName='unwrapPhase', cutoff=1., min_num_pixel=1e4): """Detect unwrapping error based on phase closure and extract coherent conn comps based on its histogram distribution Check: https://en.wikipedia.org/wiki/Otsu%27s_method from skimage.filters import threshold_otsu Parameters: ifgram_file : string, path of ifgram stack file mask_file : string, path of mask file, e.g. waterMask.h5, maskConnComp.h5 mask_cc_file: string, path of mask file for coherent conn comps cutoff : float, cutoff value for the mean number of nonzero phase closure to be selected as coherent conn comps candidate min_num_pixel : float, min number of pixels left after morphology operation to be determined as coherent conn comps Returns: mask_cc_file : string, path of mask file for coherent conn comps """ print('-'*50) print('detect unwraping error based on phase closure') obj = ifgramStack(ifgram_file) obj.open(print_msg=False) C = obj.get_design_matrix4triplet(obj.get_date12_list(dropIfgram=False)) num_nonzero_closure = get_nonzero_phase_closure(ifgram_file, unwDatasetName=unwDatasetName) # get histogram of num_nonzero_phase_closure mask = readfile.read(mask_file)[0] mask *= num_nonzero_closure != 0. fig, ax = plt.subplots(nrows=1, ncols=2, figsize=[12, 4]) num4disp = np.array(num_nonzero_closure, dtype=np.float32) num4disp[mask == 0] = np.nan im = ax[0].imshow(num4disp) ax[0].set_xlabel('Range [pix.]') ax[0].set_ylabel('Azimuth [pix.]') ax[0] = pp.auto_flip_direction(obj.metadata, ax=ax[0], print_msg=False) cbar = fig.colorbar(im, ax=ax[0]) cbar.set_label('number of non-zero phase closure') print('2. extract coherent conn comps with unwrap error based on histogram distribution') max_nonzero_closure = int(np.max(num_nonzero_closure[mask])) bin_value, bin_edge = ax[1].hist(num_nonzero_closure[mask].flatten(), range=(0, max_nonzero_closure), log=True, bins=max_nonzero_closure)[0:2] ax[1].set_xlabel('number of non-zero phase closure') ax[1].set_ylabel('number of pixels') if 'Closure' not in unwDatasetName: print('eliminate pixels with number of nonzero phase closure < 5% of total phase closure number') print('\twhich can be corrected using phase closure alone.') bin_value[:int(C.shape[0]*0.05)] = 0. bin_value_thres = ut.median_abs_deviation_threshold(bin_value, cutoff=cutoff) print('median abs deviation cutoff value: {}'.format(cutoff)) plt.plot([0, max_nonzero_closure], [bin_value_thres, bin_value_thres]) out_img = 'numUnwErr_stat.png' fig.savefig(out_img, bbox_inches='tight', transparent=True, dpi=300) print('save unwrap error detection result to {}'.format(out_img)) # histogram --> candidates of coherence conn comps --> mask_cc # find pixel clusters sharing similar number of non-zero phase closure print('searching connected components with more than {} pixels'.format(min_num_pixel)) bin_label, n_bins = ndimage.label(bin_value > bin_value_thres) mask_cc = np.zeros(num_nonzero_closure.shape, dtype=np.int16) # first conn comp - reference conn comp with zero non-zero phase closure num_cc = 1 mask_cc1 = num_nonzero_closure == 0. mask_cc1s = ut.get_all_conn_components(mask_cc1, min_num_pixel=min_num_pixel) for mask_cc1 in mask_cc1s: mask_cc += mask_cc1 # other conn comps - target conn comps to be corrected for unwrap error for i in range(n_bins): idx = np.where(bin_label == i+1)[0] mask_cci0 = np.multiply(num_nonzero_closure >= bin_edge[idx[0]], num_nonzero_closure < bin_edge[idx[-1]+1]) mask_ccis = ut.get_all_conn_components(mask_cci0, min_num_pixel=min_num_pixel) if mask_ccis: for mask_cci in mask_ccis: num_cc += 1 mask_cc += mask_cci * num_cc fig, ax = plt.subplots(nrows=1, ncols=2, figsize=[8, 4]) im = ax[0].imshow(mask_cci0) im = ax[1].imshow(mask_cci) fig.savefig('mask_cc{}.png'.format(num_cc), bbox_inches='tight', transparent=True, dpi=300) # save to hdf5 file num_bridge = num_cc - 1 atr = dict(obj.metadata) atr['FILE_TYPE'] = 'mask' atr['UNIT'] = 1 writefile.write(mask_cc, out_file=mask_cc_file, metadata=atr) # plot and save figure to img file out_img = '{}.png'.format(os.path.splitext(mask_cc_file)[0]) fig, ax = plt.subplots(figsize=[6, 8]) im = ax.imshow(mask_cc) ax = pp.auto_flip_direction(atr, ax=ax, print_msg=False) divider = make_axes_locatable(ax) cax = divider.append_axes("right", "3%", pad="3%") cbar = plt.colorbar(im, cax=cax, ticks=np.arange(num_bridge+2)) fig.savefig(out_img, bbox_inches='tight', transparent=True, dpi=300) print('save to {}'.format(out_img)) return mask_cc_file
def get_common_region_int_ambiguity(ifgram_file, cc_mask_file, water_mask_file=None, num_sample=100, dsNameIn='unwrapPhase'): """Solve the phase unwrapping integer ambiguity for the common regions among all interferograms Parameters: ifgram_file : str, path of interferogram stack file cc_mask_file : str, path of common connected components file water_mask_file : str, path of water mask file num_sample : int, number of pixel sampled for each region dsNameIn : str, dataset name of the unwrap phase to be corrected Returns: common_regions : list of skimage.measure._regionprops._RegionProperties object modified by adding two more variables: sample_coords : 2D np.ndarray in size of (num_sample, 2) in int64 format int_ambiguity : 1D np.ndarray in size of (num_ifgram,) in int format """ print('-'*50) print('calculating the integer ambiguity for the common regions defined in', cc_mask_file) # stack info stack_obj = ifgramStack(ifgram_file) stack_obj.open() date12_list = stack_obj.get_date12_list(dropIfgram=True) num_ifgram = len(date12_list) C = matrix(ifgramStack.get_design_matrix4triplet(date12_list).astype(float)) ref_phase = stack_obj.get_reference_phase(unwDatasetName=dsNameIn, dropIfgram=True).reshape(num_ifgram, -1) # prepare common label print('read common mask from', cc_mask_file) cc_mask = readfile.read(cc_mask_file)[0] if water_mask_file is not None and os.path.isfile(water_mask_file): water_mask = readfile.read(water_mask_file)[0] print('refine common mask based on water mask file', water_mask_file) cc_mask[water_mask == 0] = 0 label_img, num_label = connectComponent.get_large_label(cc_mask, min_area=2.5e3, print_msg=True) common_regions = measure.regionprops(label_img) print('number of common regions:', num_label) # add sample_coords / int_ambiguity print('number of samples per region:', num_sample) print('solving the phase-unwrapping integer ambiguity for {}'.format(dsNameIn)) print('\tbased on the closure phase of interferograms triplets (Yunjun et al., 2019)') print('\tusing the L1-norm regularzed least squares approximation (LASSO) ...') for i in range(num_label): common_reg = common_regions[i] # sample_coords idx = sorted(np.random.choice(common_reg.area, num_sample, replace=False)) common_reg.sample_coords = common_reg.coords[idx, :].astype(int) # solve for int_ambiguity U = np.zeros((num_ifgram, num_sample)) if common_reg.label == label_img[stack_obj.refY, stack_obj.refX]: print('{}/{} skip calculation for the reference region'.format(i+1, num_label)) else: prog_bar = ptime.progressBar(maxValue=num_sample, prefix='{}/{}'.format(i+1, num_label)) for j in range(num_sample): # read unwrap phase y, x = common_reg.sample_coords[j, :] unw = ifginv.read_unwrap_phase(stack_obj, box=(x, y, x+1, y+1), ref_phase=ref_phase, unwDatasetName=dsNameIn, dropIfgram=True, print_msg=False).reshape(num_ifgram, -1) # calculate closure_int closure_pha = np.dot(C, unw) closure_int = matrix(np.round((closure_pha - ut.wrap(closure_pha)) / (2.*np.pi))) # solve for U U[:,j] = np.round(l1regls(-C, closure_int, alpha=1e-2, show_progress=0)).flatten() prog_bar.update(j+1, every=5) prog_bar.close() # add int_ambiguity common_reg.int_ambiguity = np.median(U, axis=1) common_reg.date12_list = date12_list #sort regions by size to facilitate the region matching later common_regions.sort(key=lambda x: x.area, reverse=True) # plot sample result fig_size = pp.auto_figure_size(label_img.shape, disp_cbar=False) fig, ax = plt.subplots(figsize=fig_size) ax.imshow(label_img, cmap='jet') for common_reg in common_regions: ax.plot(common_reg.sample_coords[:,1], common_reg.sample_coords[:,0], 'k.', ms=2) pp.auto_flip_direction(stack_obj.metadata, ax, print_msg=False) out_img = 'common_region_sample.png' fig.savefig(out_img, bbox_inches='tight', transparent=True, dpi=300) print('saved common regions and sample pixels to file', out_img) return common_regions
def detect_unwrap_error(ifgram_file, mask_file, mask_cc_file='maskConnComp.h5', unwDatasetName='unwrapPhase', cutoff=1., min_num_pixel=1e4): """Detect unwrapping error based on phase closure and extract coherent conn comps based on its histogram distribution Check: https://en.wikipedia.org/wiki/Otsu%27s_method from skimage.filters import threshold_otsu Parameters: ifgram_file : string, path of ifgram stack file mask_file : string, path of mask file, e.g. waterMask.h5, maskConnComp.h5 mask_cc_file: string, path of mask file for coherent conn comps cutoff : float, cutoff value for the mean number of nonzero phase closure to be selected as coherent conn comps candidate min_num_pixel : float, min number of pixels left after morphology operation to be determined as coherent conn comps Returns: mask_cc_file : string, path of mask file for coherent conn comps """ print('-' * 50) print('detect unwraping error based on phase closure') obj = ifgramStack(ifgram_file) obj.open(print_msg=False) C = obj.get_design_matrix4triplet(obj.get_date12_list(dropIfgram=False)) num_nonzero_closure = get_nonzero_phase_closure( ifgram_file, unwDatasetName=unwDatasetName) # get histogram of num_nonzero_phase_closure mask = readfile.read(mask_file)[0] mask *= num_nonzero_closure != 0. fig, ax = plt.subplots(nrows=1, ncols=2, figsize=[12, 4]) num4disp = np.array(num_nonzero_closure, dtype=np.float32) num4disp[mask == 0] = np.nan im = ax[0].imshow(num4disp) ax[0].set_xlabel('Range [pix.]') ax[0].set_ylabel('Azimuth [pix.]') ax[0] = pp.auto_flip_direction(obj.metadata, ax=ax[0], print_msg=False) cbar = fig.colorbar(im, ax=ax[0]) cbar.set_label('number of non-zero phase closure') print( '2. extract coherent conn comps with unwrap error based on histogram distribution' ) max_nonzero_closure = int(np.max(num_nonzero_closure[mask])) bin_value, bin_edge = ax[1].hist(num_nonzero_closure[mask].flatten(), range=(0, max_nonzero_closure), log=True, bins=max_nonzero_closure)[0:2] ax[1].set_xlabel('number of non-zero phase closure') ax[1].set_ylabel('number of pixels') if 'Closure' not in unwDatasetName: print( 'eliminate pixels with number of nonzero phase closure < 5% of total phase closure number' ) print('\twhich can be corrected using phase closure alone.') bin_value[:int(C.shape[0] * 0.05)] = 0. bin_value_thres = ut.median_abs_deviation_threshold(bin_value, cutoff=cutoff) print('median abs deviation cutoff value: {}'.format(cutoff)) plt.plot([0, max_nonzero_closure], [bin_value_thres, bin_value_thres]) out_img = 'numUnwErr_stat.png' fig.savefig(out_img, bbox_inches='tight', transparent=True, dpi=300) print('save unwrap error detection result to {}'.format(out_img)) # histogram --> candidates of coherence conn comps --> mask_cc # find pixel clusters sharing similar number of non-zero phase closure print('searching connected components with more than {} pixels'.format( min_num_pixel)) bin_label, n_bins = ndimage.label(bin_value > bin_value_thres) mask_cc = np.zeros(num_nonzero_closure.shape, dtype=np.int16) # first conn comp - reference conn comp with zero non-zero phase closure num_cc = 1 mask_cc1 = num_nonzero_closure == 0. mask_cc1s = ut.get_all_conn_components(mask_cc1, min_num_pixel=min_num_pixel) for mask_cc1 in mask_cc1s: mask_cc += mask_cc1 # other conn comps - target conn comps to be corrected for unwrap error for i in range(n_bins): idx = np.where(bin_label == i + 1)[0] mask_cci0 = np.multiply(num_nonzero_closure >= bin_edge[idx[0]], num_nonzero_closure < bin_edge[idx[-1] + 1]) mask_ccis = ut.get_all_conn_components(mask_cci0, min_num_pixel=min_num_pixel) if mask_ccis: for mask_cci in mask_ccis: num_cc += 1 mask_cc += mask_cci * num_cc fig, ax = plt.subplots(nrows=1, ncols=2, figsize=[8, 4]) im = ax[0].imshow(mask_cci0) im = ax[1].imshow(mask_cci) fig.savefig('mask_cc{}.png'.format(num_cc), bbox_inches='tight', transparent=True, dpi=300) # save to hdf5 file num_bridge = num_cc - 1 atr = dict(obj.metadata) atr['FILE_TYPE'] = 'mask' atr['UNIT'] = 1 writefile.write(mask_cc, out_file=mask_cc_file, metadata=atr) # plot and save figure to img file out_img = '{}.png'.format(os.path.splitext(mask_cc_file)[0]) fig, ax = plt.subplots(figsize=[6, 8]) im = ax.imshow(mask_cc) ax = pp.auto_flip_direction(atr, ax=ax, print_msg=False) divider = make_axes_locatable(ax) cax = divider.append_axes("right", "3%", pad="3%") cbar = plt.colorbar(im, cax=cax, ticks=np.arange(num_bridge + 2)) fig.savefig(out_img, bbox_inches='tight', transparent=True, dpi=300) print('save to {}'.format(out_img)) return mask_cc_file