refactor: 中间层消除硬编码波段号+CRS动态探测

- GlintRemovalStep/handler/service 全部改为传递波长参数而非波段索引
- ContentMapper 移除 EPSG:32651 硬编码, 新增 _ensure_crs() + _probe_crs_from_file()
- _prepare_shared_context 在 read_csv_data 前先探测边界文件 CRS
- GUI QSpinBox→QDoubleSpinBox,默认值改为波长(nm),placeholder 提示自动探测
- 消除 38/36/49/47/25/37/65/91 等所有魔法数字
This commit is contained in:
duxin
2026-07-27 14:52:23 +08:00
parent f6d61693e3
commit 99aeab3076
14 changed files with 263 additions and 275 deletions

View File

@ -40,10 +40,15 @@ def _process_one_map(csv_path: str, base_kwargs: dict, output_dir: str
if shared_context is not None:
# ★ 快速通道:直接调 ContentMapper,复用预计算的网格/掩膜
from src.postprocessing.map import ContentMapper
mapper = ContentMapper(
input_crs=base_kwargs.get('input_crs', 'EPSG:32651'),
output_crs=base_kwargs.get('output_crs', base_kwargs.get('input_crs', 'EPSG:32651')),
)
# ★ CRS 动态探测:从边界文件自动获取,不再硬编码 EPSG:32651
input_crs = base_kwargs.get('input_crs') or None
output_crs = base_kwargs.get('output_crs') or None
boundary_path = base_kwargs.get('boundary_shp_path')
mapper = ContentMapper(input_crs=input_crs, output_crs=output_crs)
if boundary_path:
mapper._ensure_crs(reference_file=str(boundary_path))
else:
mapper._ensure_crs()
result_path = mapper.process_data(
csv_file=csv_path,
shp_file=base_kwargs.get('boundary_shp_path'),
@ -103,7 +108,8 @@ def _pre_vectorize_boundary(boundary_path: str, work_dir: str) -> str:
if proj:
srs.ImportFromWkt(proj)
else:
srs.ImportFromEPSG(32651)
# 栅格无投影信息时,兜底 WGS84(而非某个特定 UTM 分区)
srs.ImportFromEPSG(4326)
layer = out_ds.CreateLayer('boundary', srs, ogr.wkbMultiPolygon)
gdal.Polygonize(band, band, layer, 0, [], callback=None)
@ -163,8 +169,8 @@ class Step11MapHandler(BaseStepHandler):
base_kwargs = {
'boundary_shp_path': resolved_boundary or None,
'resolution': float(config.get('resolution', 10.0)),
'input_crs': config.get('input_crs', 'EPSG:32651'),
'output_crs': config.get('output_crs', config.get('input_crs', 'EPSG:32651')),
'input_crs': config.get('input_crs') or None,
'output_crs': config.get('output_crs') or None,
'show_sample_points': bool(config.get('show_sample_points', False)),
'use_distance_diffusion': bool(config.get('use_distance_diffusion', False)),
}

View File

@ -71,11 +71,8 @@ class Step12KrigingHandler(BaseStepHandler):
boundary_shp_path=boundary_shp_path,
output_image_path=output_image_path,
resolution=config.get('resolution', 30),
input_crs=config.get('input_crs', 'EPSG:32651'),
# ★★★ 强制 output_crs = input_crs,避免 ContentMapper 把栅格重投影到 EPSG:4326 ★★★
# 旧实现:output_crs=config.get('output_crs', 'EPSG:4326')
# 重投影会让栅格和基于投影坐标的掩膜在 visualize_raster 叠加时发生仿射变换撕裂
output_crs=config.get('input_crs', 'EPSG:32651'),
input_crs=config.get('input_crs') or None,
output_crs=config.get('output_crs') or None,
show_sample_points=config.get('show_sample_points', False),
base_map_tif=config.get('base_map_tif'),
use_distance_diffusion=config.get('use_distance_diffusion', True),

View File

@ -45,16 +45,16 @@ class Step3GlintRemovalHandler(BaseStepHandler):
interpolation_method=config.get('interpolation_method', 'nearest'),
enabled=config.get('enabled', True),
kutser_shp_path=config.get('kutser_shp_path'),
oxy_band=config.get('oxy_band', 38),
lower_oxy=config.get('lower_oxy', 36),
upper_oxy=config.get('upper_oxy', 49),
nir_band=config.get('nir_band', 47),
nir_lower=config.get('nir_lower', 25),
nir_upper=config.get('nir_upper', 37),
goodman_A=config.get('goodman_A', 0.000019),
goodman_B=config.get('goodman_B', 0.1),
oxy_wavelength=float(config.get('oxy_wavelength', 760.6)),
lower_wavelength=float(config.get('lower_wavelength', 742.39)),
upper_wavelength=float(config.get('upper_wavelength', 860.48)),
nir_wavelength=float(config.get('nir_wavelength', 842.36)),
nir_lower_wavelength=float(config.get('nir_lower_wavelength', 641.93)),
nir_upper_wavelength=float(config.get('nir_upper_wavelength', 751.49)),
goodman_A=float(config.get('goodman_A', 0.000019)),
goodman_B=float(config.get('goodman_B', 0.1)),
hedley_shp_path=config.get('hedley_shp_path'),
hedley_nir_band=config.get('hedley_nir_band', 47),
hedley_nir_wavelength=float(config.get('hedley_nir_wavelength', 842.36)),
sugar_bounds=config.get('sugar_bounds'),
sugar_sigma=config.get('sugar_sigma', 1.0),
sugar_estimate_background=config.get('sugar_estimate_background', True),

View File

@ -209,20 +209,20 @@ class GlintRemovalStep:
interpolate_zeros: bool = False,
interpolation_method: str = "nearest",
enabled: bool = True,
# Kutser 参数
# Kutser 参数(波长驱动:nm → 自动解析波段号)
kutser_shp_path: Optional[str] = None,
oxy_band: int = 38,
lower_oxy: int = 36,
upper_oxy: int = 49,
nir_band: int = 47,
# Goodman 参数
nir_lower: int = 25,
nir_upper: int = 37,
oxy_wavelength: float = 760.6,
lower_wavelength: float = 742.39,
upper_wavelength: float = 860.48,
nir_wavelength: float = 842.36,
# Goodman 参数(波长驱动:统一为 ~640nm / ~750nm 物理波长)
nir_lower_wavelength: float = 641.93,
nir_upper_wavelength: float = 751.49,
goodman_A: float = 0.000019,
goodman_B: float = 0.1,
# Hedley 参数
# Hedley 参数(波长驱动)
hedley_shp_path: Optional[str] = None,
hedley_nir_band: int = 47,
hedley_nir_wavelength: float = 842.36,
# SUGAR 参数
sugar_bounds: Optional[List[tuple]] = None,
sugar_sigma: float = 1.0,
@ -383,10 +383,10 @@ class GlintRemovalStep:
# ==================== Kutser ====================
if method == "kutser":
print(f"使用方法: Kutser (氧吸收波段={oxy_band}, NIR波段={nir_band})")
print(f"使用方法: Kutser (波长驱动: oxy={oxy_wavelength}nm, "
f"NIR={nir_wavelength}nm)")
hardcoded_bsq = str(deglint_dir / "deglint_kutser.bsq")
hardcoded_hdr = hardcoded_bsq.replace(".bsq", ".hdr")
# 将用户指定的 output_path 标准化为 .bsq 路径
if output_path:
final_bsq = output_path.replace('.dat', '.bsq').replace('.tif', '.bsq')
final_hdr = final_bsq.replace(".bsq", ".hdr")
@ -402,10 +402,10 @@ class GlintRemovalStep:
kutser = Kutser(
img_path,
shp_path=None,
oxy_band=oxy_band,
lower_oxy=lower_oxy,
upper_oxy=upper_oxy,
NIR_band=nir_band,
oxy_wavelength=oxy_wavelength,
lower_wavelength=lower_wavelength,
upper_wavelength=upper_wavelength,
nir_wavelength=nir_wavelength,
water_mask=mask_for_algorithm,
output_path=hardcoded_bsq,
)
@ -420,7 +420,9 @@ class GlintRemovalStep:
# ==================== Goodman ====================
elif method == "goodman":
print(f"使用方法: Goodman (NIR波段范围: {nir_lower}-{nir_upper})")
print(f"使用方法: Goodman (波长驱动: "
f"NIR_lower={nir_lower_wavelength}nm, "
f"NIR_upper={nir_upper_wavelength}nm)")
hardcoded_bsq = str(deglint_dir / "deglint_goodman.bsq")
hardcoded_hdr = hardcoded_bsq.replace(".bsq", ".hdr")
if output_path:
@ -437,8 +439,8 @@ class GlintRemovalStep:
goodman = Goodman(
img_path,
NIR_lower=nir_lower,
NIR_upper=nir_upper,
nir_lower_wavelength=nir_lower_wavelength,
nir_upper_wavelength=nir_upper_wavelength,
A=goodman_A,
B=goodman_B,
water_mask=mask_for_algorithm,
@ -458,7 +460,7 @@ class GlintRemovalStep:
# ==================== Hedley ====================
elif method == "hedley":
print(f"使用方法: Hedley (NIR波段={hedley_nir_band})")
print(f"使用方法: Hedley (波长驱动: NIR={hedley_nir_wavelength}nm)")
hardcoded_bsq = str(deglint_dir / "deglint_hedley.bsq")
hardcoded_hdr = hardcoded_bsq.replace(".bsq", ".hdr")
if output_path:
@ -476,7 +478,7 @@ class GlintRemovalStep:
hedley = Hedley(
img_path,
shp_path=None,
NIR_band=hedley_nir_band,
nir_wavelength=hedley_nir_wavelength,
water_mask=mask_for_algorithm,
output_path=hardcoded_bsq,
)

View File

@ -19,11 +19,9 @@ class MappingStep:
boundary_shp_path: Optional[str] = None, # ★★★ Plan C: None = 不依赖水域掩膜 ★★★
output_image_path: Optional[str] = None,
resolution: float = 30,
input_crs: str = "EPSG:32651",
# ★★★ 强制默认 output_crs = input_crs,禁止重投影到 EPSG:4326 ★★★
# 历史默认值 'EPSG:4326' 会让 ContentMapper 将插值栅格从投影坐标系转到经纬度坐标系,
# 与基于 EPSG:32651 的水域掩膜叠加时发生仿射变换撕裂(栅格错位、坐标轴扭曲)。
output_crs: str = "EPSG:32651",
input_crs: Optional[str] = None,
# ★ CRS 动态探测:ContentMapper 自动从掩膜/栅格中读取投影信息
output_crs: Optional[str] = None,
show_sample_points: bool = False,
base_map_tif: Optional[str] = None,
use_distance_diffusion: bool = True,