Apply current workspace changes
This commit is contained in:
@@ -0,0 +1,394 @@
|
||||
#! /usr/bin/env python
|
||||
#################################################################
|
||||
### This program is part of PyINT v2.1 ###
|
||||
### Pixel Offset Tracking (POT) for a single pair ###
|
||||
### Based on GAMMA offset_pwr_tracking / offset_pwr_tracking2###
|
||||
### Author: ZYD / Cascade AI ###
|
||||
#################################################################
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import argparse
|
||||
import numpy as np
|
||||
|
||||
from pyint import _utils as ut
|
||||
|
||||
|
||||
INTRODUCTION = '''
|
||||
-------------------------------------------------------------------
|
||||
Pixel Offset Tracking (POT) for a single interferometric pair
|
||||
using GAMMA software.
|
||||
|
||||
Two-round estimation approach (Greenland tracking demo):
|
||||
Round 1: Large search window for initial offset field
|
||||
Post-processing: Quality check, outlier removal, gap filling
|
||||
Round 2: Smaller window with conditioned Round 1 as initial
|
||||
Final: Convert pixel offsets to displacement in meters + Geocode
|
||||
'''
|
||||
|
||||
EXAMPLE = '''
|
||||
Usage:
|
||||
POT_gamma.py projectName Mdate Sdate
|
||||
POT_gamma.py shanghaiT171F128S1A 20241105 20241117
|
||||
-------------------------------------------------------------------
|
||||
'''
|
||||
|
||||
|
||||
def run_cmd(cmd_str):
|
||||
"""执行 GAMMA 命令并打印"""
|
||||
print(f' >> {cmd_str}')
|
||||
return os.system(cmd_str)
|
||||
|
||||
|
||||
def sanitize_gamma_float(filepath, valid_max=1e6):
|
||||
"""
|
||||
清理 GAMMA 浮点数据文件中的无效值。
|
||||
GAMMA 对无效像素写入特殊标记值(~3.4e38 / NaN / Inf),
|
||||
这些值无法被 single_class_mapping 的范围过滤器正确捕获。
|
||||
将 NaN / Inf / |val| > valid_max 的像素替换为 0.0。
|
||||
"""
|
||||
data = np.fromfile(filepath, dtype=np.float32)
|
||||
bad_mask = ~np.isfinite(data) | (np.abs(data) > valid_max)
|
||||
n_bad = int(np.sum(bad_mask))
|
||||
if n_bad > 0:
|
||||
data[bad_mask] = 0.0
|
||||
data.tofile(filepath)
|
||||
print(f' [sanitize] {os.path.basename(filepath)}: '
|
||||
f'清理 {n_bad} 个无效像素 (NaN/Inf/|val|>{valid_max})')
|
||||
|
||||
|
||||
def postprocess_offsets(offs_cpx, ccp, mli, width,
|
||||
ccp_thresh, roff_min, roff_max,
|
||||
azoff_min, azoff_max,
|
||||
drange_thresh, dazimuth_thresh,
|
||||
median_win, median_nmin,
|
||||
prefix):
|
||||
"""
|
||||
偏移量场后处理流程(参考 GAMMA Greenland tracking demo):
|
||||
1) 提取距离向/方位向分量 + 清理 GAMMA no-data 标记值
|
||||
2) 初始掩膜: 互相关阈值 + 偏移量范围限制
|
||||
3) 中值滤波 + 偏差计算
|
||||
4) 偏差阈值精细掩膜
|
||||
5) 空洞填充
|
||||
6) 空间滤波 → 组合为 conditioned 复数偏移量
|
||||
|
||||
返回: (condi, real_interp, imag_interp)
|
||||
- condi: fspf 平滑后的复数偏移量(供 Round 2 初始值)
|
||||
- real_interp / imag_interp: 填充后的偏移量(供最终米制转换)
|
||||
"""
|
||||
real_file = prefix + '.real'
|
||||
imag_file = prefix + '.imag'
|
||||
|
||||
# --- 1) 提取距离向 (real) 和方位向 (imag) ---
|
||||
run_cmd(f'cpx_to_real {offs_cpx} {real_file} {width} 0')
|
||||
run_cmd(f'cpx_to_real {offs_cpx} {imag_file} {width} 1')
|
||||
|
||||
# 清理 GAMMA no-data 标记值(~3.4e38),避免污染后续掩膜和插值
|
||||
valid_max = max(abs(float(roff_max)), abs(float(roff_min)),
|
||||
abs(float(azoff_max)), abs(float(azoff_min))) * 10
|
||||
sanitize_gamma_float(real_file, valid_max)
|
||||
sanitize_gamma_float(imag_file, valid_max)
|
||||
sanitize_gamma_float(ccp, 1.0)
|
||||
|
||||
# --- 2) 初始掩膜: 互相关 + 偏移量范围 ---
|
||||
mask1 = prefix + '.mask1.bmp'
|
||||
real_m1 = prefix + '.real.masked1'
|
||||
imag_m1 = prefix + '.imag.masked1'
|
||||
|
||||
run_cmd(f'single_class_mapping 3 '
|
||||
f'{ccp} {ccp_thresh} 1.0 '
|
||||
f'{real_file} {roff_min} {roff_max} '
|
||||
f'{imag_file} {azoff_min} {azoff_max} '
|
||||
f'{mask1} {width} 1 0 1 1')
|
||||
run_cmd(f'mask_class {mask1} {real_file} {real_m1} 0 1 1 1 0 0.0')
|
||||
run_cmd(f'mask_class {mask1} {imag_file} {imag_m1} 0 1 1 1 0 0.0')
|
||||
|
||||
# 初始掩膜后 BMP
|
||||
run_cmd(f'rasdt_pwr {real_m1} {mli} {width} - - - - '
|
||||
f'{roff_min} {roff_max} 0 rmg.cm {real_m1}.bmp - - 24')
|
||||
run_cmd(f'rasdt_pwr {imag_m1} {mli} {width} - - - - '
|
||||
f'{azoff_min} {azoff_max} 0 rmg.cm {imag_m1}.bmp - - 24')
|
||||
|
||||
# --- 3) 中值滤波 + 偏差 ---
|
||||
real_med = prefix + '.real.median'
|
||||
imag_med = prefix + '.imag.median'
|
||||
dreal = prefix + '.dreal'
|
||||
dimag = prefix + '.dimag'
|
||||
|
||||
run_cmd(f'median_filter {real_m1} {real_med} {width} '
|
||||
f'{median_win} {median_win} {median_nmin}')
|
||||
run_cmd(f'lin_comb 2 {real_m1} {real_med} 0. 1. -1. '
|
||||
f'{dreal} {width} 1 0 1 1')
|
||||
run_cmd(f'median_filter {imag_m1} {imag_med} {width} '
|
||||
f'{median_win} {median_win} {median_nmin}')
|
||||
run_cmd(f'lin_comb 2 {imag_m1} {imag_med} 0. 1. -1. '
|
||||
f'{dimag} {width} 1 0 1 1')
|
||||
|
||||
# --- 4) 偏差阈值精细掩膜 ---
|
||||
mask2 = prefix + '.mask2.bmp'
|
||||
real_masked = prefix + '.real.masked'
|
||||
imag_masked = prefix + '.imag.masked'
|
||||
|
||||
run_cmd(f'single_class_mapping 5 '
|
||||
f'{dreal} -{drange_thresh} {drange_thresh} '
|
||||
f'{dimag} -{dazimuth_thresh} {dazimuth_thresh} '
|
||||
f'{ccp} {ccp_thresh} 1.0 '
|
||||
f'{real_file} {roff_min} {roff_max} '
|
||||
f'{imag_file} {azoff_min} {azoff_max} '
|
||||
f'{mask2} {width} 1 0 1 1 5')
|
||||
run_cmd(f'mask_class {mask2} {real_file} {real_masked} 0 1 1 1 0 0.0')
|
||||
run_cmd(f'mask_class {mask2} {imag_file} {imag_masked} 0 1 1 1 0 0.0')
|
||||
|
||||
# 精细掩膜后 BMP
|
||||
run_cmd(f'rasdt_pwr {real_masked} {mli} {width} - - - - '
|
||||
f'{roff_min} {roff_max} 0 rmg.cm {real_masked}.bmp - - 24')
|
||||
run_cmd(f'rasdt_pwr {imag_masked} {mli} {width} - - - - '
|
||||
f'{azoff_min} {azoff_max} 0 rmg.cm {imag_masked}.bmp - - 24')
|
||||
|
||||
# --- 5) 空洞填充 ---
|
||||
real_interp = prefix + '.real.interp'
|
||||
imag_interp = prefix + '.imag.interp'
|
||||
run_cmd(f'fill_gaps {real_masked} {width} {real_interp} 0 4 - 1')
|
||||
run_cmd(f'fill_gaps {imag_masked} {width} {imag_interp} 0 4 - 1')
|
||||
|
||||
# 清理 fill_gaps 插值可能引入的 NaN/Inf/极端值
|
||||
sanitize_gamma_float(real_interp, valid_max)
|
||||
sanitize_gamma_float(imag_interp, valid_max)
|
||||
|
||||
# 填充后 BMP
|
||||
run_cmd(f'rasdt_pwr {real_interp} {mli} {width} - - - - '
|
||||
f'{roff_min} {roff_max} 0 rmg.cm {real_interp}.bmp - - 24')
|
||||
run_cmd(f'rasdt_pwr {imag_interp} {mli} {width} - - - - '
|
||||
f'{azoff_min} {azoff_max} 0 rmg.cm {imag_interp}.bmp - - 24')
|
||||
|
||||
# --- 6) 空间滤波 + 组合 conditioned ---
|
||||
real_fspf = prefix + '.real.fspf'
|
||||
imag_fspf = prefix + '.imag.fspf'
|
||||
run_cmd(f'fspf {real_interp} {real_fspf} {width} 2 2 2')
|
||||
run_cmd(f'fspf {imag_interp} {imag_fspf} {width} 2 2 2')
|
||||
|
||||
condi = prefix + '.condi'
|
||||
run_cmd(f'real_to_cpx {real_fspf} {imag_fspf} {condi} {width} 0')
|
||||
|
||||
return condi, real_interp, imag_interp
|
||||
|
||||
|
||||
def cmdLineParse():
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Pixel Offset Tracking for a single pair using GAMMA.',
|
||||
formatter_class=argparse.RawTextHelpFormatter,
|
||||
epilog=INTRODUCTION + '\n' + EXAMPLE)
|
||||
|
||||
parser.add_argument('projectName', help='projectName for processing.')
|
||||
parser.add_argument('Mdate', help='Master date.')
|
||||
parser.add_argument('Sdate', help='Slave date.')
|
||||
|
||||
inps = parser.parse_args()
|
||||
return inps
|
||||
|
||||
|
||||
def main(argv):
|
||||
|
||||
start_time = time.time()
|
||||
inps = cmdLineParse()
|
||||
Mdate = inps.Mdate
|
||||
Sdate = inps.Sdate
|
||||
projectName = inps.projectName
|
||||
|
||||
scratchDir = os.getenv('SCRATCHDIR')
|
||||
templateDir = os.getenv('TEMPLATEDIR')
|
||||
templateFile = templateDir + '/' + projectName + '.template'
|
||||
templateDict = ut.update_template(templateFile)
|
||||
|
||||
rlks = templateDict['range_looks']
|
||||
azlks = templateDict['azimuth_looks']
|
||||
masterDate = templateDict['masterDate']
|
||||
|
||||
# ========== POT 参数 ==========
|
||||
pot_rstep = templateDict['pot_rstep']
|
||||
pot_azstep = templateDict['pot_azstep']
|
||||
pot_rwin = templateDict['pot_rwin']
|
||||
pot_azwin = templateDict['pot_azwin']
|
||||
pot_rwin2 = templateDict['pot_rwin2']
|
||||
pot_azwin2 = templateDict['pot_azwin2']
|
||||
pot_ovr = templateDict['pot_ovr']
|
||||
pot_snr_thresh = templateDict['pot_snr_thresh']
|
||||
pot_ccp_thresh = templateDict['pot_ccp_thresh']
|
||||
pot_roff_min = templateDict['pot_roff_min']
|
||||
pot_roff_max = templateDict['pot_roff_max']
|
||||
pot_azoff_min = templateDict['pot_azoff_min']
|
||||
pot_azoff_max = templateDict['pot_azoff_max']
|
||||
pot_drange_thresh = templateDict['pot_drange_thresh']
|
||||
pot_dazimuth_thresh = templateDict['pot_dazimuth_thresh']
|
||||
pot_median_win = templateDict['pot_median_win']
|
||||
pot_median_nmin = templateDict['pot_median_nmin']
|
||||
pot_two_rounds = templateDict['pot_two_rounds']
|
||||
pot_geocode = templateDict['pot_geocode']
|
||||
pot_disp_max = templateDict['pot_disp_max']
|
||||
|
||||
# ========== 目录 ==========
|
||||
projectDir = scratchDir + '/' + projectName
|
||||
rslcDir = projectDir + '/RSLC'
|
||||
demDir = projectDir + '/DEM'
|
||||
potDir = projectDir + '/offsets'
|
||||
if not os.path.isdir(potDir):
|
||||
os.mkdir(potDir)
|
||||
|
||||
Pair = Mdate + '-' + Sdate
|
||||
workDir = potDir + '/' + Pair
|
||||
if not os.path.isdir(workDir):
|
||||
os.mkdir(workDir)
|
||||
|
||||
# ========== 输入文件 ==========
|
||||
Mrslc = rslcDir + '/' + Mdate + '/' + Mdate + '.rslc'
|
||||
MrslcPar = rslcDir + '/' + Mdate + '/' + Mdate + '.rslc.par'
|
||||
Srslc = rslcDir + '/' + Sdate + '/' + Sdate + '.rslc'
|
||||
SrslcPar = rslcDir + '/' + Sdate + '/' + Sdate + '.rslc.par'
|
||||
|
||||
slc_width = ut.read_gamma_par(MrslcPar, 'read', 'range_samples')
|
||||
off_width = str(int(int(slc_width) // int(pot_rstep)))
|
||||
|
||||
print('=' * 60)
|
||||
print(f'Pixel Offset Tracking (POT): {Pair}')
|
||||
print(f' SLC width : {slc_width}')
|
||||
print(f' Offset width : {off_width}')
|
||||
print(f' Step (r x az) : {pot_rstep} x {pot_azstep}')
|
||||
print(f' R1 window : {pot_rwin} x {pot_azwin}')
|
||||
if pot_two_rounds == '1':
|
||||
print(f' R2 window : {pot_rwin2} x {pot_azwin2}')
|
||||
print(f' Offset range : [{pot_roff_min}, {pot_roff_max}] r '
|
||||
f'[{pot_azoff_min}, {pot_azoff_max}] az')
|
||||
print('=' * 60)
|
||||
|
||||
#######################################################################
|
||||
# Step 1: 生成偏移量几何下的 MLI(背景图 + 尺寸参考)
|
||||
#######################################################################
|
||||
print('\n[Step 1] 生成偏移量几何 MLI ...')
|
||||
MLI_pot = workDir + '/' + Mdate + '.mli_pot'
|
||||
MLI_pot_par = workDir + '/' + Mdate + '.mli_pot.par'
|
||||
|
||||
run_cmd(f'multi_look {Mrslc} {MrslcPar} {MLI_pot} {MLI_pot_par} '
|
||||
f'{pot_rstep} {pot_azstep}')
|
||||
run_cmd(f'raspwr {MLI_pot} {off_width} - - - - 1. .2 - {MLI_pot}.bmp')
|
||||
|
||||
#######################################################################
|
||||
# Step 2: 创建偏移量参数文件
|
||||
#######################################################################
|
||||
print('\n[Step 2] 创建偏移量参数文件 ...')
|
||||
OFF = workDir + '/' + Pair + '.off'
|
||||
run_cmd(f'create_offset {MrslcPar} {SrslcPar} {OFF} 1 {rlks} {azlks} 0')
|
||||
|
||||
#######################################################################
|
||||
# Step 3: Round 1 — 初始偏移量估计(大窗口)
|
||||
#######################################################################
|
||||
print(f'\n[Step 3] Round 1 偏移量估计 ({pot_rwin}x{pot_azwin}) ...')
|
||||
r1_tag = f'{pot_rwin}x{pot_azwin}'
|
||||
r1_prefix = workDir + '/' + Pair + '.offs' + r1_tag
|
||||
offs_r1 = r1_prefix
|
||||
ccp_r1 = workDir + '/' + Pair + '.ccp' + r1_tag
|
||||
|
||||
run_cmd(f'offset_pwr_tracking {Mrslc} {Srslc} {MrslcPar} {SrslcPar} '
|
||||
f'{OFF} {offs_r1} {ccp_r1} '
|
||||
f'{pot_rwin} {pot_azwin} - {pot_ovr} {pot_snr_thresh} '
|
||||
f'{pot_rstep} {pot_azstep}')
|
||||
|
||||
#######################################################################
|
||||
# Step 4: Round 1 后处理
|
||||
#######################################################################
|
||||
print(f'\n[Step 4] Round 1 后处理 ...')
|
||||
r1_condi, _, _ = postprocess_offsets(
|
||||
offs_cpx=offs_r1, ccp=ccp_r1, mli=MLI_pot, width=off_width,
|
||||
ccp_thresh=pot_ccp_thresh,
|
||||
roff_min=pot_roff_min, roff_max=pot_roff_max,
|
||||
azoff_min=pot_azoff_min, azoff_max=pot_azoff_max,
|
||||
drange_thresh=pot_drange_thresh, dazimuth_thresh=pot_dazimuth_thresh,
|
||||
median_win=pot_median_win, median_nmin=pot_median_nmin,
|
||||
prefix=r1_prefix)
|
||||
|
||||
# 默认使用 Round 1 结果
|
||||
final_prefix = r1_prefix
|
||||
final_ccp = ccp_r1
|
||||
final_off = OFF
|
||||
|
||||
#######################################################################
|
||||
# Step 5-6: Round 2 — 精细偏移量估计(小窗口,可选)
|
||||
#######################################################################
|
||||
if pot_two_rounds == '1':
|
||||
print(f'\n[Step 5] Round 2 偏移量估计 ({pot_rwin2}x{pot_azwin2}) ...')
|
||||
OFF2 = workDir + '/' + Pair + '.off2'
|
||||
run_cmd(f'create_offset {MrslcPar} {SrslcPar} {OFF2} 1 {rlks} {azlks} 0')
|
||||
|
||||
r2_tag = f'{pot_rwin2}x{pot_azwin2}'
|
||||
r2_prefix = workDir + '/' + Pair + '.offs' + r2_tag
|
||||
offs_r2 = r2_prefix
|
||||
ccp_r2 = workDir + '/' + Pair + '.ccp' + r2_tag
|
||||
|
||||
run_cmd(f'offset_pwr_tracking2 {Mrslc} {Srslc} {MrslcPar} {SrslcPar} '
|
||||
f'{OFF2} {offs_r2} {ccp_r2} {OFF} {r1_condi} '
|
||||
f'{pot_rwin2} {pot_azwin2} - {pot_ovr} {pot_snr_thresh} '
|
||||
f'{pot_rstep} {pot_azstep}')
|
||||
|
||||
print(f'\n[Step 6] Round 2 后处理 ...')
|
||||
_, _, _ = postprocess_offsets(
|
||||
offs_cpx=offs_r2, ccp=ccp_r2, mli=MLI_pot, width=off_width,
|
||||
ccp_thresh=pot_ccp_thresh,
|
||||
roff_min=pot_roff_min, roff_max=pot_roff_max,
|
||||
azoff_min=pot_azoff_min, azoff_max=pot_azoff_max,
|
||||
drange_thresh=pot_drange_thresh, dazimuth_thresh=pot_dazimuth_thresh,
|
||||
median_win=pot_median_win, median_nmin=pot_median_nmin,
|
||||
prefix=r2_prefix)
|
||||
|
||||
final_prefix = r2_prefix
|
||||
final_ccp = ccp_r2
|
||||
final_off = OFF2
|
||||
|
||||
#######################################################################
|
||||
# Step 7: 像素偏移量 → 米制位移量
|
||||
#######################################################################
|
||||
print('\n[Step 7] 像素偏移量转换为地面位移 (米) ...')
|
||||
|
||||
# 用填充后(非 fspf 平滑)的偏移量组合复数,供 offset_tracking 使用
|
||||
final_real_interp = final_prefix + '.real.interp'
|
||||
final_imag_interp = final_prefix + '.imag.interp'
|
||||
final_offs_combined = final_prefix + '.offs_combined'
|
||||
run_cmd(f'real_to_cpx {final_real_interp} {final_imag_interp} '
|
||||
f'{final_offs_combined} {off_width} 0')
|
||||
|
||||
disp_map = workDir + '/' + Pair + '.disp_map'
|
||||
run_cmd(f'offset_tracking {final_offs_combined} {final_ccp} '
|
||||
f'{MrslcPar} {final_off} {disp_map} - 2 {pot_ccp_thresh} 0')
|
||||
|
||||
# 提取位移分量
|
||||
disp_real = disp_map + '.real' # 地距向位移 (米)
|
||||
disp_imag = disp_map + '.imag' # 方位向位移 (米)
|
||||
disp_mag = disp_map + '.mag' # 位移幅值 (米)
|
||||
|
||||
run_cmd(f'cpx_to_real {disp_map} {disp_real} {off_width} 0')
|
||||
run_cmd(f'cpx_to_real {disp_map} {disp_imag} {off_width} 1')
|
||||
run_cmd(f'cpx_to_real {disp_map} {disp_mag} {off_width} 3')
|
||||
|
||||
# 清理 offset_tracking 输出中的 NaN/Inf/极端值
|
||||
disp_max_m = float(pot_disp_max) * 10
|
||||
sanitize_gamma_float(disp_real, disp_max_m)
|
||||
sanitize_gamma_float(disp_imag, disp_max_m)
|
||||
sanitize_gamma_float(disp_mag, disp_max_m)
|
||||
|
||||
# BMP 可视化
|
||||
run_cmd(f'rasdt_pwr {disp_real} {MLI_pot} {off_width} - - - - '
|
||||
f'-{pot_disp_max} {pot_disp_max} 1 rmg.cm {disp_real}.bmp - - 24')
|
||||
run_cmd(f'rasdt_pwr {disp_imag} {MLI_pot} {off_width} - - - - '
|
||||
f'-{pot_disp_max} {pot_disp_max} 1 rmg.cm {disp_imag}.bmp - - 24')
|
||||
run_cmd(f'rasdt_pwr {disp_mag} {MLI_pot} {off_width} - - - - '
|
||||
f'-{pot_disp_max} {pot_disp_max} 1 rmg.cm {disp_mag}.bmp - - 24')
|
||||
|
||||
# 注意: 地理编码已移至 geocode_gamma.py --type pot
|
||||
# 用法: geocode_gamma.py projectName Pair --type pot
|
||||
|
||||
print(f"\nPixel Offset Tracking for {Pair} is done!")
|
||||
ut.print_process_time(start_time, time.time())
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv[:])
|
||||
Reference in New Issue
Block a user