复制代码
# encoding=utf-8
from osgeo import gdal
import re
import numpy as np
import os
import xml.etree.ElementTree as ET
import warnings
from osgeo.gdalconst import GDT_Float32
warnings.filterwarnings('ignore')
def Get_File_Name(pathlog):
xmlfile = None
for f_name in os.listdir(pathlog):
if f_name.endswith('.meta.xml'):
print(f"找到元数据文件: {f_name}")
xmlfile = os.path.join(pathlog, f_name)
break
if xmlfile is None:
raise FileNotFoundError("未找到.meta.xml文件")
image_fn = []
for f_name in os.listdir(pathlog):
if f_name.endswith('.tiff') or f_name.endswith('.TIFF'):
print(f"找到图像文件: {f_name}")
image_fn.append(os.path.join(pathlog, f_name))
HH = []
HV = []
VH = []
VV = []
for image_path in image_fn:
image_name = os.path.basename(image_path).upper()
if '_HH_' in image_name or '_HH.' in image_name:
HH = image_path
elif '_HV_' in image_name or '_HV.' in image_name:
HV = image_path
elif '_VH_' in image_name or '_VH.' in image_name:
VH = image_path
elif '_VV_' in image_name or '_VV.' in image_name:
VV = image_path
return xmlfile, HH, HV, VH, VV
def Get_QualifyValue_And_Calibration(xmlfile):
tree = ET.parse(xmlfile)
root = tree.getroot()
HH_QualifyValue = root[17][13][0].text
HV_QualifyValue = root[17][13][1].text
VH_QualifyValue = root[17][13][2].text
VV_QualifyValue = root[17][13][3].text
QualifyValue = [HH_QualifyValue, HV_QualifyValue, VH_QualifyValue, VV_QualifyValue]
QualifyValue_new = []
for i in QualifyValue:
if i != 'NULL':
i = float(i)
QualifyValue_new.append(i)
else:
i = np.NAN
QualifyValue_new.append(i)
HH_CalibrationConst = root[18][3][0].text
HV_CalibrationConst = root[18][3][1].text
VH_CalibrationConst = root[18][3][2].text
VV_CalibrationConst = root[18][3][3].text
CalibrationConst = [HH_CalibrationConst, HV_CalibrationConst, VH_CalibrationConst, VV_CalibrationConst]
CalibrationConst_new = []
for i in CalibrationConst:
if i != 'NULL':
i = float(i)
CalibrationConst_new.append(i)
else:
i = np.NAN
CalibrationConst_new.append(i)
return QualifyValue_new, CalibrationConst_new
def Confirm_The_IMG_type(file, xmlfile):
QualifyValue_new, CalibrationConst_new = Get_QualifyValue_And_Calibration(xmlfile)
file_name = os.path.basename(file).upper()
if '_HH_' in file_name or '_HH.' in file_name:
QualifyValue_1A = QualifyValue_new[0]
Calibration = CalibrationConst_new[0]
elif '_HV_' in file_name or '_HV.' in file_name:
QualifyValue_1A = QualifyValue_new[1]
Calibration = CalibrationConst_new[1]
elif '_VH_' in file_name or '_VH.' in file_name:
QualifyValue_1A = QualifyValue_new[2]
Calibration = CalibrationConst_new[2]
elif '_VV_' in file_name or '_VV.' in file_name:
QualifyValue_1A = QualifyValue_new[3]
Calibration = CalibrationConst_new[3]
else:
raise ValueError(f"无法识别极化类型: {file}")
return QualifyValue_1A, Calibration
def save_to_tiff(ds, img_name, outpath):
base_name = os.path.basename(img_name)
out_filename = base_name.replace('L1A', 'L1B')
out_filename = os.path.join(outpath, out_filename)
print("L1B数据保存到:", out_filename)
os.makedirs(outpath, exist_ok=True)
driver = gdal.GetDriverByName('GTiff')
driver.Register()
if ds.dtype == np.uint8:
dtype = gdal.GDT_Byte
elif ds.dtype == np.uint16:
dtype = gdal.GDT_UInt16
else:
dtype = GDT_Float32
outDataset = driver.Create(out_filename, ds.shape[1], ds.shape[0], 1, dtype, ['COMPRESS=DEFLATE'])
outBand = outDataset.GetRasterBand(1)
for i in range(ds.shape[0]):
outBand.WriteArray(ds[i].reshape(1, -1), 0, i)
outBand.FlushCache()
outBand = None
outDataset = None
return out_filename
def Run_1Ato2(file, outpath, xmlfile, inputpath):
try:
print(f"开始处理文件: {file}")
img = gdal.Open(file)
if img is None:
raise ValueError(f"无法打开文件: {file}")
vh0 = img.ReadAsArray()
vh1 = np.array(vh0[0, :, :], dtype='float32')
vh2 = np.array(vh0[1, :, :], dtype='float32')
I = (vh1 ** 2 + vh2 ** 2)
A = np.sqrt(I)
QualifyValue_1A, Calibration = Confirm_The_IMG_type(file, xmlfile)
print(f'QualifyValue_1A={QualifyValue_1A}, Calibration={Calibration}')
del vh0, vh1, vh2
QualifyValue_1B = np.nanmax((A / 32767 * QualifyValue_1A))
print(f'QualifyValue_1B={QualifyValue_1B}')
DN = A / 32767 * QualifyValue_1A / QualifyValue_1B * 65535
k1 = DN * (QualifyValue_1B / 65535)
k2 = k1 ** 2
del A, DN, k1
dB_1B = 10 * np.log10(k2) - Calibration
del k2
tlstretch = truncated_linear_stretch(dB_1B, 2, max_out=65535, min_out=0)
_1b_file_path = save_to_tiff(tlstretch, file, outpath)
del dB_1B, tlstretch, I
print(f"完成L1A到L1B处理: {file}")
geometric_correction(_1b_file_path, outpath, inputpath)
except Exception as e:
print(f"处理文件 {file} 时出错: {e}")
return None
def truncated_linear_stretch(image, truncated_value, max_out=65535, min_out=0):
def gray_process(gray):
truncated_down = np.percentile(gray, truncated_value)
truncated_up = np.percentile(gray, 100 - truncated_value)
if truncated_up == truncated_down:
gray = np.zeros_like(gray)
else:
gray = (gray - truncated_down) / (truncated_up - truncated_down) * (max_out - min_out) + min_out
gray[gray < min_out] = min_out
gray[gray > max_out] = max_out
if max_out <= 255:
gray = np.uint8(gray)
elif max_out <= 65535:
gray = np.uint16(gray)
return gray
if len(image.shape) == 3:
image_stretch = []
for i in range(image.shape[0]):
gray = gray_process(image[i])
image_stretch.append(gray)
image_stretch = np.array(image_stretch)
else:
image_stretch = gray_process(image)
return image_stretch
def Repeat_Run_1Ato1B(image_name, outpath, xmlfile, inputpath):
num_i = 1
for file in image_name:
if file:
print(f'开始处理第{num_i}个文件: {os.path.basename(file)}')
Run_1Ato2(file, outpath, xmlfile, inputpath)
else:
print(f'第{num_i}个文件为空,跳过')
num_i += 1
def Read_Rpb(rpbfile):
"""修复版:正确处理GF-3 RPC文件格式"""
# 参数名映射:从文件中的驼峰式到GDAL需要的大写下划线式
PARAM_MAPPING = {
'errBias': 'ERR_BIAS',
'errRand': 'ERR_RAND',
'lineOffset': 'LINE_OFF',
'sampOffset': 'SAMP_OFF',
'latOffset': 'LAT_OFF',
'longOffset': 'LONG_OFF',
'heightOffset': 'HEIGHT_OFF',
'lineScale': 'LINE_SCALE',
'sampScale': 'SAMP_SCALE',
'latScale': 'LAT_SCALE',
'longScale': 'LONG_SCALE',
'heightScale': 'HEIGHT_SCALE',
'lineNumCoef': 'LINE_NUM_COEFF',
'lineDenCoef': 'LINE_DEN_COEFF',
'sampNumCoef': 'SAMP_NUM_COEFF',
'sampDenCoef': 'SAMP_DEN_COEFF'
}
rpc_dict = {}
try:
with open(rpbfile, 'r', encoding='utf-8') as f:
content = f.read()
# 方法1:简单但可靠的解析 - 直接查找每个参数
for file_param, gdal_param in PARAM_MAPPING.items():
# 查找格式:参数名 = 值;
pattern = rf'{file_param}\s*=\s*([^;]+);'
match = re.search(pattern, content, re.DOTALL)
if match:
value = match.group(1).strip()
# 处理括号内的多行系数
if value.startswith('('):
# 提取括号内的所有内容
value = re.search(r'\((.*?)\);', match.group(0), re.DOTALL)
if value:
value = value.group(1)
# 清理:移除换行、多余空格、逗号转换为空格
value = re.sub(r'\s+', ' ', value) # 多个空格换行变一个空格
value = re.sub(r',\s*', ' ', value) # 逗号+空格变空格
value = value.strip()
rpc_dict[gdal_param] = value
print(f" ✓ {gdal_param}: {value[:50]}..." if len(str(value)) > 50 else f" ✓ {gdal_param}: {value}")
else:
print(f" ✗ {gdal_param}: 未找到")
# 对于必需参数,设置默认值
if gdal_param in ['ERR_BIAS', 'ERR_RAND']:
rpc_dict[gdal_param] = '-1.0' # GDAL默认值
elif 'OFF' in gdal_param:
rpc_dict[gdal_param] = '0.0'
elif 'SCALE' in gdal_param:
rpc_dict[gdal_param] = '1.0'
# 转换为GDAL需要的列表格式
gdal_rpc_list = [f'{key}={value}' for key, value in rpc_dict.items()]
print(f"✅ RPC解析成功:共{len(gdal_rpc_list)}个参数")
return gdal_rpc_list
except Exception as e:
print(f"❌ RPC解析失败: {e}")
# 返回空列表让几何校正跳过
return []
def Get_Rpc_file(pathlog):
rpc_fn = []
for f_name in os.listdir(pathlog):
if f_name.endswith('.rpc') or f_name.endswith('.rpb'):
rpc_fn.append(os.path.join(pathlog, f_name))
HH_rpc = None
HV_rpc = None
VH_rpc = None
VV_rpc = None
for rpc_path in rpc_fn:
rpc_name = os.path.basename(rpc_path).upper()
# 精确匹配极化类型
if '_HH_' in rpc_name or '_HH.' in rpc_name:
HH_rpc = rpc_path
elif '_HV_' in rpc_name or '_HV.' in rpc_name:
HV_rpc = rpc_path
elif '_VH_' in rpc_name or '_VH.' in rpc_name:
VH_rpc = rpc_path
elif '_VV_' in rpc_name or '_VV.' in rpc_name:
VV_rpc = rpc_path
return [HH_rpc, HV_rpc, VH_rpc, VV_rpc]
def Confirm_The_rpc_type(file, inputpath):
file_name = os.path.basename(file).upper()
rpcfile_collection = Get_Rpc_file(inputpath)
if '_HH_' in file_name or '_HH.' in file_name:
rpcfile01 = rpcfile_collection[0]
elif '_HV_' in file_name or '_HV.' in file_name:
rpcfile01 = rpcfile_collection[1]
elif '_VH_' in file_name or '_VH.' in file_name:
rpcfile01 = rpcfile_collection[2]
elif '_VV_' in file_name or '_VV.' in file_name:
rpcfile01 = rpcfile_collection[3]
else:
raise ValueError(f"无法确定RPC文件类型: {file}")
return rpcfile01
def geometric_correction(file, outputpath, inputpath):
try:
print(f'正在进行几何校正: {os.path.basename(file)}')
rpcfile = Confirm_The_rpc_type(file, inputpath)
if rpcfile is None:
raise FileNotFoundError(f"未找到对应的RPC文件: {file}")
print(f"RPC文件: {os.path.basename(rpcfile)}")
rpc = Read_Rpb(rpcfile)
dataset = gdal.Open(file)
if dataset is None:
raise ValueError(f"无法打开文件: {file}")
for item in rpc:
key, value = item.split('=', 1)
dataset.SetMetadataItem(key, value, 'RPC')
base_name = os.path.basename(file)
out_filename = base_name.replace('L1B', 'L2')
out_filename = os.path.join(outputpath, out_filename)
print(f"几何校正输出: {out_filename}")
# 添加分辨率设置(0.0001度约11米)
gdal.Warp(out_filename, dataset,
dstSRS='EPSG:4326',
xRes=0.0001, yRes=0.0001,
rpc=True,
resampleAlg=gdal.GRA_Bilinear)
print(f'完成几何校正: {out_filename}')
dataset = None
except Exception as e:
print(f"几何校正失败: {e}")
return None
if __name__ == '__main__':
# 修改这里为您的路径
inputpath = r'K:\GF3\untreatedGF3\GF3_KSC_FSII_046450_E71.0_N41.4_20250606_L1A_HHHV_L10007136652'
outputpath = r'K:\GF3\output_gf3\\GF3_KSC_FSII_046450_E71.0_N41.4_20250606_L1A_HHHV_L10007136652'
# 创建输出目录
if not os.path.exists(outputpath):
os.makedirs(outputpath)
print(f"创建输出目录: {outputpath}")
# 获取文件列表
try:
xmlfile, HH, HV, VH, VV = Get_File_Name(inputpath)
print(f"元数据文件: {xmlfile}")
print(f"HH极化文件: {HH}")
print(f"HV极化文件: {HV}")
print(f"VH极化文件: {VH}")
print(f"VV极化文件: {VV}")
# 过滤掉空值
image_name = [f for f in [HH, HV, VH, VV] if f]
if not image_name:
print("警告: 未找到任何图像文件!")
else:
# 开始处理
Repeat_Run_1Ato1B(image_name, outputpath, xmlfile, inputpath)
print("所有处理完成!")
except Exception as e:
print(f"处理过程中出错: {e}")