From f6d61693e3b68b12b02ace29d5b2a43f6be90a3a Mon Sep 17 00:00:00 2001 From: duxin Date: Mon, 27 Jul 2026 14:52:15 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E8=80=80=E6=96=91=E5=8E=BB?= =?UTF-8?q?=E9=99=A4=E7=AE=97=E6=B3=95=E4=BB=8E=E6=B3=A2=E6=AE=B5=E7=B4=A2?= =?UTF-8?q?=E5=BC=95=E9=A9=B1=E5=8A=A8=E6=94=B9=E9=80=A0=E4=B8=BA=E6=B3=A2?= =?UTF-8?q?=E9=95=BF=E9=A9=B1=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Kutser/Hedley/Goodman 的 __init__ 参数从硬编码波段号改为目标波长(nm) - 内部通过 find_band_number() 从 HDR 元数据动态解析波段索引 - Goodman 消除 P0 不一致: 25/37 vs 65/91 统一为 641.93/751.49nm - 彻底解除传感器耦合,任何传感器均可自动适配 --- src/core/glint_removal/Goodman.py | 58 ++++++++++++++++++++++++------- src/core/glint_removal/Hedley.py | 16 ++++++--- src/core/glint_removal/Kutser.py | 36 +++++++++++++------ 3 files changed, 82 insertions(+), 28 deletions(-) diff --git a/src/core/glint_removal/Goodman.py b/src/core/glint_removal/Goodman.py index 6c2e9f4..cc154f2 100644 --- a/src/core/glint_removal/Goodman.py +++ b/src/core/glint_removal/Goodman.py @@ -9,6 +9,8 @@ except ImportError: GDAL_AVAILABLE = False print("警告: GDAL未安装,将使用numpy处理模式") +from src.utils.util import find_band_number + try: from tqdm import tqdm TQDM_AVAILABLE = True @@ -44,19 +46,35 @@ def _find_water_row_ranges(water_rows): class Goodman: - def __init__(self, im_aligned, NIR_lower = 25, NIR_upper = 37, A = 0.000019, B = 0.1, + def __init__(self, im_aligned, + nir_lower_wavelength=641.93, nir_upper_wavelength=751.49, + A=0.000019, B=0.1, use_gdal=True, chunk_size=None, water_mask=None, output_path=None): """ - :param im_aligned (np.ndarray or str): band aligned and calibrated & corrected reflectance image - 可以是numpy数组或GDAL可读取的文件路径 - :param NIR_lower (int): band index which corresponds to 641.93nm, closest band to 640nm - :param NIR_upper (int): band index which corresponds to 751.49nm, closest band to 750nm - :param A (float): the values in Goodman et al's paper, using AVIRIS reflectance (rather than radiance) data - :param B (float): the values in Goodman et al's paper, using AVIRIS reflectance (rather than radiance) data - see Goodman et al, which corrects each pixel independently. The NIR radiance is subtracted from the radiance at each wavelength, - but a wavelength-independent offset is also added. - it is not clear how A and B were chosen, but an optimization for a case where in situ data is - available would enable values to be found + Goodman 耀斑去除算法 — 波长驱动版。 + + ★ P0 修复:原算法类硬编码 NIR_lower=25/NIR_upper=37,GUI 硬编码 65/91, + 两套魔法数字对应不同传感器但指向相同物理波长(~640nm / ~750nm)。 + 现已统一为波长参数,通过 find_band_number() 动态解析波段号, + 任何传感器均可自动适配,彻底消除不一致。 + + :param im_aligned (np.ndarray or str): band aligned and calibrated & corrected + reflectance image。可以是numpy数组或GDAL可读取的文件路径。 + 当为文件路径时,通过 HDR 元数据动态解析波段索引; + 当为numpy数组时,波长参数被视为直接波段索引(无 HDR 可查)。 + :param nir_lower_wavelength (float): NIR下界目标波长(nm),默认641.93nm + (对应 ~640nm 参考波段,原硬编码 NIR_lower=25/65 指向的物理波长) + :param nir_upper_wavelength (float): NIR上界目标波长(nm),默认751.49nm + (对应 ~750nm 参考波段,原硬编码 NIR_upper=37/91 指向的物理波长) + :param A (float): the values in Goodman et al's paper, using AVIRIS + reflectance (rather than radiance) data + :param B (float): the values in Goodman et al's paper, using AVIRIS + reflectance (rather than radiance) data + see Goodman et al, which corrects each pixel independently. The NIR + radiance is subtracted from the radiance at each wavelength, + but a wavelength-independent offset is also added. + it is not clear how A and B were chosen, but an optimization for a + case where in situ data is available would enable values to be found :param use_gdal (bool): 是否使用GDAL加速处理(需要GDAL可用且输入为文件路径或大数组) :param chunk_size (int): 已废弃,不再使用分块处理,改为逐波段处理 :param water_mask (np.ndarray or str or None): 水域掩膜,1表示水域,0表示非水域 @@ -66,14 +84,28 @@ class Goodman: 如果为None,则不保存 """ self.im_aligned = im_aligned - self.NIR_lower = NIR_lower - self.NIR_upper = NIR_upper self.A = A self.B = B self.use_gdal = use_gdal and GDAL_AVAILABLE self.chunk_size = chunk_size self.is_file_path = isinstance(im_aligned, str) self.output_path = output_path + + # ── 波长驱动:通过 HDR 元数据动态解析波段索引 ── + if self.is_file_path: + self.NIR_lower = find_band_number( + float(nir_lower_wavelength), im_aligned) + self.NIR_upper = find_band_number( + float(nir_upper_wavelength), im_aligned) + print(f"[Goodman] 波段解析: " + f"NIR_lower={float(nir_lower_wavelength):.1f}nm→band{self.NIR_lower}, " + f"NIR_upper={float(nir_upper_wavelength):.1f}nm→band{self.NIR_upper}") + else: + # numpy 数组模式:无 HDR 可查,直接作为波段索引(向后兼容) + self.NIR_lower = int(nir_lower_wavelength) + self.NIR_upper = int(nir_upper_wavelength) + print(f"[Goodman] numpy数组模式: 直接使用波段索引 " + f"NIR_lower={self.NIR_lower}, NIR_upper={self.NIR_upper}") # 获取图像信息(需要在加载掩膜之前获取尺寸) if self.is_file_path: diff --git a/src/core/glint_removal/Hedley.py b/src/core/glint_removal/Hedley.py index ec2528e..94db60b 100644 --- a/src/core/glint_removal/Hedley.py +++ b/src/core/glint_removal/Hedley.py @@ -7,16 +7,22 @@ try: except ImportError: GDAL_AVAILABLE = False +from src.utils.util import find_band_number + class Hedley: - def __init__(self, img_path, shp_path=None, NIR_band=47, water_mask=None, - output_path=None, block_size=1000): + def __init__(self, img_path, shp_path=None, nir_wavelength=842.36, + water_mask=None, output_path=None, block_size=1000): """ Hedley 耀斑去除算法 - 分块逐波段处理版本 + 波段选择已从硬编码索引改造为波长驱动: + - 传入目标波长(nm),算法通过 find_band_number() 从影像 HDR 元数据中 + 动态解析最接近的波段号,彻底解除传感器耦合。 + :param img_path (str): 输入影像文件路径(GDAL可读取的格式) :param shp_path (str, optional): 深水区域shapefile,已废弃,请使用water_mask - :param NIR_band (int): NIR波段索引(默认47,对应842.36nm) + :param nir_wavelength (float): NIR参考波段目标波长(nm),默认842.36nm :param water_mask (np.ndarray or str or None): 水域掩膜 :param output_path (str): 输出文件路径(必须提供,用于分块写入) :param block_size (int): 分块大小(默认1000) @@ -25,7 +31,9 @@ class Hedley: raise ImportError("GDAL未安装,无法读取影像文件") self.img_path = img_path - self.NIR_band = int(float(NIR_band)) + # 波长驱动:通过 HDR 元数据动态解析波段索引 + self.NIR_band = find_band_number(float(nir_wavelength), img_path) + print(f"[Hedley] 波段解析: NIR={float(nir_wavelength):.1f}nm→band{self.NIR_band}") self.water_mask = None self.water_mask_path = water_mask self.output_path = output_path diff --git a/src/core/glint_removal/Kutser.py b/src/core/glint_removal/Kutser.py index 0e9d722..e27c7db 100644 --- a/src/core/glint_removal/Kutser.py +++ b/src/core/glint_removal/Kutser.py @@ -7,20 +7,27 @@ try: except ImportError: GDAL_AVAILABLE = False +from src.utils.util import find_band_number + class Kutser: - def __init__(self, img_path, shp_path=None, oxy_band=38, lower_oxy=36, - upper_oxy=49, NIR_band=47, water_mask=None, output_path=None, - block_size=1000): + def __init__(self, img_path, shp_path=None, + oxy_wavelength=760.6, lower_wavelength=742.39, + upper_wavelength=860.48, nir_wavelength=842.36, + water_mask=None, output_path=None, block_size=1000): """ Kutser 耀斑去除算法 - 分块逐波段处理版本 + 波段选择已从硬编码索引改造为波长驱动: + - 传入目标波长(nm),算法通过 find_band_number() 从影像 HDR 元数据中 + 动态解析最接近的波段号,彻底解除传感器耦合。 + :param img_path (str): 输入影像文件路径(GDAL可读取的格式) :param shp_path (str, optional): 深水区域shapefile,已废弃,请使用water_mask - :param oxy_band (int): 氧吸收波段索引(默认38,对应760.6nm) - :param lower_oxy (int): 氧吸收下方波段索引(默认36,对应742.39nm) - :param upper_oxy (int): 氧吸收上方波段索引(默认49,对应860.48nm) - :param NIR_band (int): NIR波段索引(默认47,对应842.36nm) + :param oxy_wavelength (float): 氧吸收波段目标波长(nm),默认760.6nm(O₂-A带) + :param lower_wavelength (float): 氧吸收左肩目标波长(nm),默认742.39nm + :param upper_wavelength (float): 氧吸收右肩目标波长(nm),默认860.48nm + :param nir_wavelength (float): NIR参考波段目标波长(nm),默认842.36nm :param water_mask (np.ndarray or str or None): 水域掩膜 :param output_path (str): 输出文件路径(必须提供,用于分块写入) :param block_size (int): 分块大小(默认1000) @@ -29,10 +36,17 @@ class Kutser: raise ImportError("GDAL未安装,无法读取影像文件") self.img_path = img_path - self.oxy_band = int(float(oxy_band)) - self.lower_oxy = int(float(lower_oxy)) - self.upper_oxy = int(float(upper_oxy)) - self.NIR_band = int(float(NIR_band)) + # 波长驱动:通过 HDR 元数据动态解析波段索引 + self.oxy_band = find_band_number(float(oxy_wavelength), img_path) + self.lower_oxy = find_band_number(float(lower_wavelength), img_path) + self.upper_oxy = find_band_number(float(upper_wavelength), img_path) + self.NIR_band = find_band_number(float(nir_wavelength), img_path) + + print(f"[Kutser] 波段解析: " + f"oxy={float(oxy_wavelength):.1f}nm→band{self.oxy_band}, " + f"lower={float(lower_wavelength):.1f}nm→band{self.lower_oxy}, " + f"upper={float(upper_wavelength):.1f}nm→band{self.upper_oxy}, " + f"NIR={float(nir_wavelength):.1f}nm→band{self.NIR_band}") self.water_mask = None # 延迟加载,在处理前初始化 self.water_mask_path = water_mask self.output_path = output_path