def get_vel(fn, fill=True): ds = gdal.Open(fn) if fill: import dem_downsample_fill ds = dem_downsample_fill.gdalfill_ds(ds) u_b = ds.GetRasterBand(1) v_b = ds.GetRasterBand(2) u = iolib.b_getma(u_b) v = iolib.b_getma(v_b) m = np.ma.sqrt(u*u + v*v) return u, v, m
def apply_z_shift(ds, dz, createcopy=True): print("Z shift: ", dz) if createcopy: ds_shift = iolib.mem_drv.CreateCopy('', ds, 1) else: ds_shift = ds b = ds_shift.GetRasterBand(1) a = iolib.b_getma(b) a += dz b.WriteArray(a.filled()) return ds_shift
def apply_z_shift(ds, dz, createcopy=True): if isinstance(dz, np.ndarray): print("Z shift offset array mean: ", dz.mean()) else: print("Z shift offset: ", dz) if createcopy: ds_shift = iolib.mem_drv.CreateCopy('', ds, 0) else: ds_shift = ds b = ds_shift.GetRasterBand(1) a = iolib.b_getma(b) a += dz b.WriteArray(a.filled()) return ds_shift
def gdalfill_b(b, edgemask=True, prog_func=None): # Create mask of exterior nodata bma = iolib.b_getma(b) # Check to make sure there are actually holes # if bma.count_masked() > 0: if np.any(bma.mask): # Create 8-bit mask_ds, and add edgemask mask_ds = memdrv.Create('', b.XSize, b.YSize, 1, gdal.GDT_Byte) maskband = mask_ds.GetRasterBand(1) # The - here inverts the mask so that holes are set to 0 (invalid) and # filled by gdalFillNodata maskband.WriteArray((-bma.mask).astype(int)) # Now fill holes in the output print("Filling holes") max_distance = 40 smoothing_iterations = 0 fill_opt = [] gdal.FillNodata(b, maskband, max_distance, smoothing_iterations, fill_opt, callback=prog_func) # Apply the original edgemask # Note: need to implement convexhull option like geolib.get_outline if edgemask: print("Generating outer edgemask") edgemask = malib.get_edgemask(bma) out_ma = np.ma.array(b.ReadAsArray(), mask=edgemask) else: out_ma = np.ma.array(b.ReadAsArray()) # Set ndv to input ndv out_ma.set_fill_value(bma.fill_value) # Write the filled, masked array to our tmp dataset b.WriteArray(out_ma.filled()) # Free mask_ds mask_ds = None else: print("No holes found in input band") return b
def warp(src_ds, res=None, extent=None, t_srs=None, r='cubic', driver=mem_drv, dst_fn=None, dst_ndv=None, verbose=True): """Warp an input dataset with predetermined arguments specifying output res/extent/srs This is the function that actually calls gdal.ReprojectImage Parameters ---------- src_ds : gdal.Dataset object Dataset to be warped res : float Desired output resolution extent : list of float Desired output extent in t_srs coordinate system t_srs : osr.SpatialReference() Desired output spatial reference r : str Desired resampling algorithm driver : GDAL Driver to use for warp Either MEM or GTiff dst_fn : str Output filename (for disk warp) dst_ndv : float Desired output NoData Value Returns ------- dst_ds : gdal.Dataset object Warped dataset (either in memory or on disk) """ src_srs = geolib.get_ds_srs(src_ds) if t_srs is None: t_srs = geolib.get_ds_srs(src_ds) src_gt = src_ds.GetGeoTransform() #Note: get_res returns [x_res, y_res] #Could just use gt here and average x_res and y_res src_res = geolib.get_res(src_ds, t_srs=t_srs, square=True)[0] if res is None: res = src_res if extent is None: extent = geolib.ds_geom_extent(src_ds, t_srs=t_srs) #Note: GDAL Lanczos creates block artifacts #Wait for gdalwarp to support gaussian resampling #Want to use Lanczos for downsampling #if src_res < res: # gra = gdal.GRA_Lanczos #See http://blog.codinghorror.com/better-image-resizing/ # Suggests cubic for downsampling, bilinear for upsampling # gra = gdal.GRA_Cubic #Cubic for upsampling #elif src_res >= res: # gra = gdal.GRA_Bilinear gra = parse_rs_alg(r) #At this point, the resolution and extent values must be float #Extent must be list res = float(res) extent = [float(i) for i in extent] #Might want to move this to memwarp_multi, keep memwarp basic w/ gdal.GRA types #Create progress function prog_func = None if verbose: prog_func = gdal.TermProgress if dst_fn is None: #This is a dummy fn if only in mem, but can be accessed later via GetFileList() #Actually, no, doesn't look like the filename survivies dst_fn = '' #Compute output image dimensions dst_nl = int(round((extent[3] - extent[1])/res)) dst_ns = int(round((extent[2] - extent[0])/res)) #dst_nl = int(math.ceil((extent[3] - extent[1])/res)) #dst_ns = int(math.ceil((extent[2] - extent[0])/res)) #dst_nl = int(math.floor((extent[3] - extent[1])/res)) #dst_ns = int(math.floor((extent[2] - extent[0])/res)) if verbose: print('nl: %i ns: %i res: %0.3f' % (dst_nl, dst_ns, res)) #Create output dataset src_b = src_ds.GetRasterBand(1) src_dt = src_b.DataType src_nl = src_ds.RasterYSize src_ns = src_ds.RasterXSize dst_ds = driver.Create(dst_fn, dst_ns, dst_nl, src_ds.RasterCount, src_dt) dst_ds.SetProjection(t_srs.ExportToWkt()) #Might be an issue to use src_gt rotation terms here with arbitrary extent/res dst_gt = [extent[0], res, src_gt[2], extent[3], src_gt[4], -res] dst_ds.SetGeoTransform(dst_gt) #This will smooth the input before downsampling to prevent aliasing, fill gaps #Pretty inefficent, as we need to create another intermediate dataset gauss = False for n in range(1, src_ds.RasterCount+1): if dst_ndv is None: src_b = src_ds.GetRasterBand(n) src_ndv = iolib.get_ndv_b(src_b) dst_ndv = src_ndv b = dst_ds.GetRasterBand(n) b.SetNoDataValue(dst_ndv) b.Fill(dst_ndv) if gauss: from pygeotools.lib import filtlib #src_a = src_b.GetVirtualMemArray() #Compute resampling ratio to determine filter window size res_ratio = float(res)/src_res if verbose: print("Resampling factor: %0.3f" % res_ratio) #Might be more efficient to do iterative gauss filter with size 3, rather than larger windows f_size = math.floor(res_ratio/2.)*2+1 #This is conservative to avoid filling holes with noise #f_size = math.floor(res_ratio/2.)*2-1 if f_size <= 1: continue if verbose: print("Smoothing window size: %i" % f_size) #Create temp dataset to store filtered array - avoid overwriting original temp_ds = driver.Create('', src_ns, src_nl, src_ds.RasterCount, src_dt) temp_ds.SetProjection(src_srs.ExportToWkt()) temp_ds.SetGeoTransform(src_gt) temp_b = temp_ds.GetRasterBand(n) temp_b.SetNoDataValue(dst_ndv) temp_b.Fill(dst_ndv) src_a = iolib.b_getma(src_b) src_a = filtlib.gauss_fltr_astropy(src_a, size=f_size) #Want to run with maskfill, so only fills gaps, without expanding isolated points temp_b.WriteArray(src_a) src_ds = temp_ds #In theory, NN should be fine since we already smoothed. In practice, cubic still provides slightly better results #gra = gdal.GRA_NearestNeighbour """ if not verbose: #Suppress GDAL progress bar orig_stdout = sys.stdout sys.stdout = open(os.devnull, 'w') """ #Note: default maxerror=0.0, second 0.0 argument gdal.ReprojectImage(src_ds, dst_ds, src_srs.ExportToWkt(), t_srs.ExportToWkt(), gra, 0.0, 0.0, prog_func) """ if not verbose: sys.stdout.close() sys.stdout = orig_stdout """ #Note: this is now done in diskwarp #Write out to disk #if driver != mem_drv: # dst_ds.FlushCache() #Return GDAL dataset object in memory return dst_ds
def main(): parser = getparser() args = parser.parse_args() hs_overlay = args.hs_overlay kmz = args.kmz opacity = args.alpha cmap = args.cmap fn = args.fn print(fn) ds = gdal.Open(fn) b = ds.GetRasterBand(1) ndv = iolib.get_ndv_b(b) print("Loading input raster") a = iolib.b_getma(b) clim = args.clim if clim is None: clim = malib.calcperc(a, (2, 98)) print("Generating color ramp") cramp_fn = os.path.splitext(fn)[0]+'_ramp.txt' ncolors = 21 csteps = np.linspace(0, 1, ncolors) cm = plt.get_cmap(cmap) #Compute raster values between specified min/max vals = np.linspace(clim[0], clim[1], ncolors) #Compute rgba for these values on the given color ramp cvals = cm(csteps, bytes=True) #Combine into single array cramp = np.vstack((vals, cvals.T)).T #Set alpha to desired transparency cramp[:,-1] = opacity * 255 header = '#val r g b a' footer = 'nv %s %s %s 0' % (ndv, ndv, ndv) np.savetxt(cramp_fn, cramp, fmt='%f %i %i %i %i', header=header, footer=footer, comments='') print("Generating gdaldem color-relief tif") color_fn = os.path.splitext(fn)[0]+'_color.tif' if not os.path.exists(color_fn): #cmd = 'gdaldem color-relief -nearest_color_entry -alpha %s %s %s' % (fn, cramp_fn, color_fn) cmd = ['gdaldem', 'color-relief', '-alpha'] cmd.extend(iolib.gdal_opt_co) cmd.extend([fn, cramp_fn, color_fn]) print(' '.join(cmd)) subprocess.call(cmd, shell=False) if kmz: make_kmz(color_fn) if hs_overlay: print("Generating shaded relief") hs_fn = os.path.splitext(fn)[0]+'_hs_az315.tif' #Check to see if file exists, or if provided as input if not os.path.exists(hs_fn): cmd = ['gdaldem', 'hillshade'] #cmd.extend('-compute_edges') cmd.extend(iolib.gdal_opt_co) cmd.extend([fn, hs_fn]) print(' '.join(cmd)) subprocess.call(cmd, shell=False) print("Loading shaded relief and calculating percentile stretch") hs = iolib.fn_getma(hs_fn) hs_clim = malib.calcperc(hs, (1, 99)) #Since imagemagick was compiled with quantum depth 16, need to scale levels hs_clim = (hs_clim[0]*65535/255., hs_clim[1]*65535/255.) print("Generating color composite shaded relief") overlay_fn = os.path.splitext(color_fn)[0]+'_hs.tif' if not os.path.exists(overlay_fn): #Can also try hsvmerge.py #cmd = 'composite %s %s -dissolve "%i" %s' % (color_fn, hs_fn, opacity*100, overlay_fn) #This uses imagemagick composite function #For some reason, this level adjustment is not working #cmd = ['convert', hs_fn, color_fn, '-compose', 'dissolve', \ cmd = ['convert', hs_fn, '-level', '%i,%i' % hs_clim, color_fn, '-compose', 'dissolve', \ '-define', 'compose:args=%i' % int(opacity*100), '-composite', '-compress', 'LZW', overlay_fn] #cmd = ['composite', color_fn, hs_fn, '-dissolve', str(int(opacity*100)), '-compress', 'LZW', overlay_fn] print(' '.join(cmd)) subprocess.call(cmd, shell=False) print("Updating georeferencing metadata") out_ndv = 0 overlay_ds = gdal.Open(overlay_fn, gdal.GA_Update) overlay_ds.SetProjection(ds.GetProjection()) overlay_ds.SetGeoTransform(ds.GetGeoTransform()) for n in range(overlay_ds.RasterCount): overlay_ds.GetRasterBand(n+1).SetNoDataValue(out_ndv) overlay_ds = None #Rewrite with blocks and LZW-compression print("Creating tiled and compressed version") tmp_fn = '/tmp/temp_%s.tif' % os.getpid() cmd = ['gdal_translate',] cmd.extend(iolib.gdal_opt_co) cmd.extend((overlay_fn, tmp_fn)) print(' '.join(cmd)) subprocess.call(cmd, shell=False) shutil.move(tmp_fn, overlay_fn) if not os.path.exists(overlay_fn+'.ovr'): print("Generating overviews") cmd = ['gdaladdo', '-ro', '-r', 'average', '--config', \ 'COMPRESS_OVERVIEW', 'LZW', '--config', 'BIGTIFF_OVERVIEW', 'YES', \ overlay_fn, '2', '4', '8', '16', '32', '64'] print(' '.join(cmd)) subprocess.call(cmd, shell=False) if kmz: make_kmz(overlay_fn)
def main(): parser = getparser() args = parser.parse_args() if args.seedmode == 'existing_velocity': if args.vx_fn is None or args.vy_fn is None: parser.error('"-seedmode existing_velocity" requires "-vx_fn" and "-vy_fn"') print('\n%s' % datetime.now()) print('%s UTC\n' % datetime.utcnow()) align = args.align seedmode = args.seedmode spr = args.refinement erode = args.erode #Correlator tile timeout #With proper seeding, correlation should be very fast #timeout = 360 timeout = 1200 threads = args.threads kernel = (args.kernel, args.kernel) #SGM correlator if spr > 3: #kernel = (7,7) kernel = (11,11) erode = 0 #Smooth the output F.tif smoothF = args.filter res = args.tr #Resample input to something easier to work with #res = 4.0 #Open input files fn1 = args.fn1 fn2 = args.fn2 if not iolib.fn_check(fn1) or not iolib.fn_check(fn2): sys.exit("Unable to locate input files") if args.outdir is not None: outdir = args.outdir else: outdir = '%s__%s_vmap_%sm_%ipx_spm%i' % (os.path.splitext(os.path.split(fn1)[1])[0], \ os.path.splitext(os.path.split(fn2)[1])[0], res, kernel[0], spr) #Note, can encounter filename length issues in boost, just use vmap prefix outprefix = '%s/vmap' % (outdir) if not os.path.exists(outdir): os.makedirs(outdir) #Check to see if inputs have geolocation and projection information ds1 = iolib.fn_getds(fn1) ds2 = iolib.fn_getds(fn2) if geolib.srs_check(ds1) and geolib.srs_check(ds2): ds1_clip_fn = os.path.join(outdir, os.path.splitext(os.path.basename(fn1))[0]+'_warp.tif') ds2_clip_fn = os.path.join(outdir, os.path.splitext(os.path.basename(fn2))[0]+'_warp.tif') if not os.path.exists(ds1_clip_fn) or not os.path.exists(ds2_clip_fn): #This should write out files to new subdir ds1_clip, ds2_clip = warplib.diskwarp_multi_fn([fn1, fn2], extent='intersection', res=res, r='average', outdir=outdir) ds1_clip = None ds2_clip = None #However, if inputs have identical extent/res/proj, then link to original files if not os.path.exists(ds1_clip_fn): os.symlink(os.path.abspath(fn1), ds1_clip_fn) if not os.path.exists(ds2_clip_fn): os.symlink(os.path.abspath(fn2), ds2_clip_fn) align = 'None' #Mask support - limit correlation only to rock/ice surfaces, no water/veg #This masks input images - guarantee we won't waste time correlating over vegetation #TODO: Add support to load arbitrary raster or shp mask if args.mask_input: ds1_masked_fn = os.path.splitext(ds1_clip_fn)[0]+'_masked.tif' ds2_masked_fn = os.path.splitext(ds2_clip_fn)[0]+'_masked.tif' if not os.path.exists(ds1_masked_fn) or not os.path.exists(ds2_masked_fn): #Load NLCD or bareground mask from demcoreg.dem_mask import get_lulc_mask ds1_clip = iolib.fn_getds(ds1_clip_fn) lulc_mask_fn = os.path.join(outdir, 'lulc_mask.tif') #if not os.path.exists(nlcd_mask_fn): lulc_mask = get_lulc_mask(ds1_clip, mask_glaciers=False, filter='not_forest') iolib.writeGTiff(lulc_mask, lulc_mask_fn, ds1_clip) ds1_clip = None #Now apply to original images #This could be problematic for huge inputs, see apply_mask.py #lulc_mask = lulc_mask.astype(int) for fn in (ds1_clip_fn, ds2_clip_fn): ds = iolib.fn_getds(fn) a = iolib.ds_getma(ds) a = np.ma.array(a, mask=~(lulc_mask)) if a.count() > 0: out_fn = os.path.splitext(fn)[0]+'_masked.tif' iolib.writeGTiff(a,out_fn,ds) a = None else: sys.exit("No unmasked pixels over bare earth") ds1_clip_fn = ds1_masked_fn ds2_clip_fn = ds2_masked_fn else: ds1_clip_fn = fn1 ds2_clip_fn = fn2 #Now let user specify alignment methods as option - don't hardcode #align = 'Homography' #align = 'AffineEpipolar' ds1 = None ds2 = None #Should have extra kwargs option here stereo_opt = get_stereo_opt(threads=threads, kernel=kernel, timeout=timeout, \ erode=erode, spr=spr, align=align) #Stereo arguments #Latest version of ASP should accept tif without camera models #stereo_args = [ds1_clip_fn, ds2_clip_fn, outprefix] #Nope - still need to provide dummy camera models, and they must be unique files #Use the dummy.tsai file bundled in the vmap repo dummy_tsai = os.path.join(os.path.split(os.path.realpath(__file__))[0], 'dummy.tsai') dummy_tsai2 = os.path.splitext(dummy_tsai)[0]+'2.tsai' if not os.path.exists(dummy_tsai2): dummy_tsai2 = os.symlink(dummy_tsai, os.path.splitext(dummy_tsai)[0]+'2.tsai') stereo_args = [ds1_clip_fn, ds2_clip_fn, dummy_tsai, dummy_tsai2, outprefix] #Run stereo_pprc if not os.path.exists(outprefix+'-R_sub.tif'): run_cmd('stereo_pprc', stereo_opt+stereo_args, msg='0: Preprocessing') #Copy proj info to outputs, this should happen automatically now? for ext in ('L', 'R', 'L_sub', 'R_sub', 'lMask', 'rMask', 'lMask_sub', 'rMask_sub'): geolib.copyproj(ds1_clip_fn, '%s-%s.tif' % (outprefix,ext)) #Prepare seeding for stereo_corr #TODO: these are untested after refactoring if not os.path.exists(outprefix+'_D_sub.tif'): #Don't need to do anything for default seed-mode 1 if seedmode == 'sparse_disp': #Sparse correlation of full-res images stereo_opt.extend(['--corr-seed-mode', '3']) sparse_disp_opt = [] sparse_disp_opt.extend(['--Debug', '--coarse', '512', '--fine', '256', '--no_epipolar_fltr']) sparse_disp_opt.extend(['-P', str(threads)]) sparse_disp_args = [outprefix+'-L.tif', outprefix+'-R.tif', outprefix] run_cmd('sparse_disp', sparse_disp_opt+sparse_disp_args, msg='0.5: D_sub generation') elif seedmode == 'existing_velocity': #User-input low-res velocity maps for seeding #TODO: Add functions that fetch best available velocities for Ant/GrIS or user-defined low-res velocities #Automatically query GoLive velocities here vx_fn = args.vx_fn vy_fn = args.vy_fn #Check for existence #HMA seeding vdir = '/nobackup/deshean/rpcdem/hma/velocity_jpl_amaury_2013-2015' vx_fn = os.path.join(vdir, 'PKH_WRS2_B8_2013_2015_snr5_n1_r170_res12.x_vel.TIF') vy_fn = os.path.join(vdir, 'PKH_WRS2_B8_2013_2015_snr5_n1_r170_res12.y_vel.TIF') if os.path.exists(vx_fn) and os.path.exists(vy_fn): ds1_clip = iolib.fn_getds(ds1_clip_fn) ds1_res = geolib.get_res(ds1_clip, square=True)[0] #Compute L_sub res - use this for output dimensions L_sub_fn = outprefix+'-L_sub.tif' L_sub_ds = gdal.Open(L_sub_fn) L_sub_x_scale = float(ds1_clip.RasterXSize) / L_sub_ds.RasterXSize L_sub_y_scale = float(ds1_clip.RasterYSize) / L_sub_ds.RasterYSize L_sub_scale = np.max([L_sub_x_scale, L_sub_y_scale]) L_sub_res = ds1_res * L_sub_scale #Since we are likely upsampling here, use cubicspline vx_ds_clip, vy_ds_clip = warplib.memwarp_multi_fn([vx_fn, vy_fn], extent=ds1_clip, \ t_srs=ds1_clip, res=L_sub_res, r='cubicspline') ds1_clip = None #Get vx and vy arrays vx = iolib.ds_getma(vx_ds_clip) vy = iolib.ds_getma(vy_ds_clip) #Determine time interval between inputs #Use to scaling of known low-res velocities t_factor = get_t_factor_fn(ds1_clip_fn, ds2_clip_fn, ds=vx_ds_clip) if t_factor is not None: #Compute expected offset in scaled pixels dx = (vx*t_factor)/L_sub_res dy = (vy*t_factor)/L_sub_res #Note: Joughin and Rignot's values are positive y up! #ASP is positive y down, so need to multiply these values by -1 #dy = -(vy*t_factor)/L_sub_res #Should smooth/fill dx and dy #If absolute search window is only 30x30 #Don't seed, just use fixed search window #search_window_area_thresh = 900 search_window_area_thresh = 0 search_window = np.array([dx.min(), dy.min(), dx.max(), dy.max()]) dx_p = calcperc(dx, perc=(0.5, 99.5)) dy_p = calcperc(dy, perc=(0.5, 99.5)) search_window = np.array([dx_p[0], dy_p[0], dx_p[1], dy_p[1]]) search_window_area = (search_window[2]-search_window[0]) * (search_window[3]-search_window[1]) if search_window_area < search_window_area_thresh: stereo_opt.extend(['--corr-seed-mode', '0']) stereo_opt.append('--corr-search') stereo_opt.extend([str(x) for x in search_window]) #pad_perc=0.1 #stereo_opt.extend(['--corr-sub-seed-percent', str(pad_perc)] #Otherwise, generate a D_sub map from low-res velocity else: stereo_opt.extend(['--corr-seed-mode', '3']) #This is relative to the D_sub scaled disparities d_sub_fn = L_sub_fn.split('-L_sub')[0]+'-D_sub.tif' gen_d_sub(d_sub_fn, dx, dy) #If the above didn't generate a D_sub.tif for seeding, run stereo_corr to generate Low-res D_sub.tif if not os.path.exists(outprefix+'-D_sub.tif'): newopt = ['--compute-low-res-disparity-only',] run_cmd('stereo_corr', newopt+stereo_opt+stereo_args, msg='1.1: Low-res Correlation') #Copy projection info to D_sub geolib.copyproj(outprefix+'-L_sub.tif', outprefix+'-D_sub.tif') #Mask D_sub to limit correlation over bare earth surfaces #This _should_ be a better approach than masking input images, but stereo_corr doesn't honor D_sub #Still need to mask input images before stereo_pprc #Left this in here for reference, or if this changes in ASP if False: D_sub_ds = gdal.Open(outprefix+'-D_sub.tif', gdal.GA_Update) #Mask support - limit correlation only to rock/ice surfaces, no water/veg from demcoreg.dem_mask import get_nlcd, mask_nlcd nlcd_fn = get_nlcd() nlcd_ds = warplib.diskwarp_multi_fn([nlcd_fn,], extent=D_sub_ds, res=D_sub_ds, t_srs=D_sub_ds, r='near', outdir=outdir)[0] #validmask = mask_nlcd(nlcd_ds, valid='rock+ice') validmask = mask_nlcd(nlcd_ds, valid='not_forest', mask_glaciers=False) nlcd_mask_fn = os.path.join(outdir, 'nlcd_validmask.tif') iolib.writeGTiff(validmask, nlcd_mask_fn, nlcd_ds) #Now apply to D_sub (band 3 is valid mask) #validmask = validmask.astype(int) for b in (1,2,3): dsub = iolib.ds_getma(D_sub_ds, b) dsub = np.ma.array(dsub, mask=~(validmask)) D_sub_ds.GetRasterBand(b).WriteArray(dsub.filled()) D_sub_ds = None #OK, finally run stereo_corr full-res integer correlation with appropriate seeding if not os.path.exists(outprefix+'-D.tif'): run_cmd('stereo_corr', stereo_opt+stereo_args, msg='1: Correlation') geolib.copyproj(ds1_clip_fn, outprefix+'-D.tif') #Run stereo_rfne if spr > 0: if not os.path.exists(outprefix+'-RD.tif'): run_cmd('stereo_rfne', stereo_opt+stereo_args, msg='2: Refinement') geolib.copyproj(ds1_clip_fn, outprefix+'-RD.tif') d_fn = make_ln(outdir, outprefix, '-RD.tif') else: ln_fn = outprefix+'-RD.tif' if os.path.lexists(ln_fn): os.remove(ln_fn) os.symlink(os.path.split(outprefix)[1]+'-D.tif', ln_fn) #Run stereo_fltr if not os.path.exists(outprefix+'-F.tif'): run_cmd('stereo_fltr', stereo_opt+stereo_args, msg='3: Filtering') geolib.copyproj(ds1_clip_fn, outprefix+'-F.tif') d_fn = make_ln(outdir, outprefix, '-F.tif') if smoothF and not os.path.exists(outprefix+'-F_smooth.tif'): print('Smoothing F.tif') from pygeotools.lib import filtlib #Fill holes and smooth F F_fill_fn = outprefix+'-F_smooth.tif' F_ds = gdal.Open(outprefix+'-F.tif', gdal.GA_ReadOnly) #import dem_downsample_fill #F_fill_ds = dem_downsample_fill.gdalfill_ds(F_fill_ds) print('Creating F_smooth.tif') F_fill_ds = iolib.gtif_drv.CreateCopy(F_fill_fn, F_ds, 0, options=iolib.gdal_opt) F_ds = None for n in (1, 2): print('Smoothing band %i' % n) b = F_fill_ds.GetRasterBand(n) b_fill_bma = iolib.b_getma(b) #b_fill_bma = iolib.b_getma(dem_downsample_fill.gdalfill(b)) #Filter extreme values (careful, could lose areas of valid data with fastest v) #b_fill_bma = filtlib.perc_fltr(b_fill_bma, perc=(0.01, 99.99)) #These filters remove extreme values and fill data gaps #b_fill_bma = filtlib.median_fltr_skimage(b_fill_bma, radius=7, erode=0) #b_fill_bma = filtlib.median_fltr(b_fill_bma, fsize=7, origmask=True) #Gaussian filter b_fill_bma = filtlib.gauss_fltr_astropy(b_fill_bma, size=9) b.WriteArray(b_fill_bma) F_fill_ds = None d_fn = make_ln(outdir, outprefix, '-F_smooth.tif') print('\n%s' % datetime.now()) print('%s UTC\n' % datetime.utcnow()) #If time interval is specified, convert pixel displacements to rates if args.dt != 'none': #Check if vm.tif already exists #Should probably just overwrite by default #if os.path.exists(os.path.splitext(d_fn)[0]+'_vm.tif'): # print("\nFound existing velocity magnitude map!\n" #else: #Generate output velocity products and figure #Requires that vmap repo is in PATH cmd = ['disp2v.py', d_fn] #Note: this will attempt to automatically determine control surfaces #disp2v.py will accept arbitrary mask, could pass through here if args.remove_offsets: cmd.append('-remove_offsets') cmd.extend(['-dt', args.dt]) print("Converting disparities to velocities") print(cmd) subprocess.call(cmd)