修改像素精度

This commit is contained in:
2026-03-10 17:31:02 +08:00
parent 5e0984bf9c
commit c3bd750a66
4 changed files with 1804 additions and 47 deletions

6
.idea/vcs.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View File

@ -61,9 +61,9 @@ logger = logging.getLogger(__name__)
# ---------- 配置 ---------- # ---------- 配置 ----------
# 请根据实际情况修改这些路径 # 请根据实际情况修改这些路径
REF_TIF = r"E:\is2\guidingsahn\result.tif" # 参考 tif 文件路径 REF_TIF = r"E:\is2\dingshanhu\mask_water.tif" # 参考 tif 文件路径
BIP_DIR = Path(r"E:\is2\guidingsahn") # .bip 文件所在文件夹 BIP_DIR = Path(r"E:\is2\dingshanhu") # .bip 文件所在文件夹
OUT_DIR = Path(r"E:\is2\guidingsahn\output") # 输出文件夹 OUT_DIR = Path(r"E:\is2\dingshanhu\output") # 输出文件夹
# 匹配算法选择 # 匹配算法选择
MATCHER_NAME = "matchanything-roma" # 可选: xfeat-star, loftr, roma, superpoint-lightglue, sift-lightglue 等 MATCHER_NAME = "matchanything-roma" # 可选: xfeat-star, loftr, roma, superpoint-lightglue, sift-lightglue 等

View File

@ -61,7 +61,7 @@ logger = logging.getLogger(__name__)
# ---------- 配置 ---------- # ---------- 配置 ----------
# 请根据实际情况修改这些路径 # 请根据实际情况修改这些路径
REF_TIF = r"E:\is2\guidingsahn\result.tif" # 参考 tif 文件路径 REF_TIF = r"E:\is2\guidingsahn\mask_water.tif" # 参考 tif 文件路径
BIP_DIR = Path(r"E:\is2\guidingsahn") # .bip 文件所在文件夹 BIP_DIR = Path(r"E:\is2\guidingsahn") # .bip 文件所在文件夹
OUT_DIR = Path(r"E:\is2\guidingsahn\output") # 输出文件夹 OUT_DIR = Path(r"E:\is2\guidingsahn\output") # 输出文件夹
@ -157,7 +157,30 @@ def _expand_window(win, pad, max_w, max_h):
row_off = int(max(0, win.row_off - pad)) row_off = int(max(0, win.row_off - pad))
col_end = int(min(max_w, win.col_off + win.width + pad)) col_end = int(min(max_w, win.col_off + win.width + pad))
row_end = int(min(max_h, win.row_off + win.height + pad)) row_end = int(min(max_h, win.row_off + win.height + pad))
return rasterio.windows.Window(col_off, row_off, col_end - col_off, row_end - row_off) return rasterio.windows.Window(col_off, row_off, col_end - col_off, col_end - col_off,)
def _pixel_size_xy(transform: Affine):
rx = float(np.hypot(transform.a, transform.d))
ry = float(np.hypot(transform.b, transform.e))
if not np.isfinite(rx) or rx <= 0:
rx = float(abs(transform.a)) if transform.a != 0 else 1.0
if not np.isfinite(ry) or ry <= 0:
ry = float(abs(transform.e)) if transform.e != 0 else 1.0
return rx, ry
def _grid_from_bounds(bounds, res_x: float, res_y: float):
left, bottom, right, top = [float(v) for v in bounds]
res_x = float(abs(res_x))
res_y = float(abs(res_y))
w = int(np.ceil((right - left) / max(1e-12, res_x)))
h = int(np.ceil((top - bottom) / max(1e-12, res_y)))
w = max(1, w)
h = max(1, h)
out_transform = Affine(res_x, 0.0, left, 0.0, -res_y, top)
return out_transform, w, h
def estimate_transform(method, k0, k1): def estimate_transform(method, k0, k1):
"""统一的变换估计函数,支持多种变换类型""" """统一的变换估计函数,支持多种变换类型"""
@ -689,41 +712,43 @@ def process_bip_to_tif(bip_path: Path, ref_dataset, matcher, out_dir: Path, stat
return False return False
bbox_window = rasterio.windows.Window(min_x, min_y, bbox_w, bbox_h) bbox_window = rasterio.windows.Window(min_x, min_y, bbox_w, bbox_h)
bbox_transform = ref_dataset.window_transform(bbox_window) bounds = rasterio.windows.bounds(bbox_window, transform=ref_dataset.transform)
res_x, res_y = _pixel_size_xy(src.transform)
out_transform, out_w, out_h = _grid_from_bounds(bounds, res_x, res_y)
out_path = out_dir / f"{bip_path.stem}_registered.bip" out_path = out_dir / f"{bip_path.stem}_registered.bip"
src_nodata = src.nodata src_nodata = src.nodata
dst_nodata = src_nodata if src_nodata is not None else 0 dst_nodata = src_nodata if src_nodata is not None else 0
out_profile = ref_dataset.profile.copy() out_profile = src.profile.copy()
out_profile.update( out_profile.update(
driver="ENVI", driver="ENVI",
dtype=src.dtypes[0], dtype=src.dtypes[0],
height=bbox_h, height=out_h,
width=bbox_w, width=out_w,
count=src.count, count=src.count,
transform=bbox_transform, transform=out_transform,
crs=ref_crs, crs=ref_crs,
interleave="bip", interleave="bip",
compress=None, compress=None,
nodata=dst_nodata nodata=dst_nodata
) )
# 重采样到最小外接矩形
with rasterio.open(out_path, "w", **out_profile) as out_ds: with rasterio.open(out_path, "w", **out_profile) as out_ds:
for b in range(1, src.count + 1): for b in range(1, src.count + 1):
src_band = src.read(b).astype(np.float32) src_band = src.read(b).astype(np.float32)
dst_band = np.zeros((bbox_h, bbox_w), dtype=np.float32) dst_band = np.zeros((out_h, out_w), dtype=np.float32)
reproject( reproject(
source=src_band, source=src_band,
destination=dst_band, destination=dst_band,
src_transform=corrected_affine, src_transform=corrected_affine,
src_crs=ref_crs, src_crs=ref_crs,
dst_transform=bbox_transform, dst_transform=out_transform,
dst_crs=ref_crs, dst_crs=ref_crs,
src_nodata=src_nodata, src_nodata=src_nodata,
dst_nodata=dst_nodata, dst_nodata=dst_nodata,
resampling=Resampling.bilinear, resampling=Resampling.nearest,
) )
if np.issubdtype(np.dtype(out_profile["dtype"]), np.integer): if np.issubdtype(np.dtype(out_profile["dtype"]), np.integer):
@ -772,46 +797,56 @@ def process_bip_to_tif(bip_path: Path, ref_dataset, matcher, out_dir: Path, stat
logger.warning(f"单应变换最小外接矩形无效: {bip_path.name}") logger.warning(f"单应变换最小外接矩形无效: {bip_path.name}")
return False return False
# 创建输出窗口
bbox_window = rasterio.windows.Window(min_x, min_y, bbox_w, bbox_h) bbox_window = rasterio.windows.Window(min_x, min_y, bbox_w, bbox_h)
bbox_transform = ref_dataset.window_transform(bbox_window) bounds = rasterio.windows.bounds(bbox_window, transform=ref_dataset.transform)
# 子窗口坐标的单应矩阵(输出坐标系是子窗口像素) res_x, res_y = _pixel_size_xy(src.transform)
T_off = np.array([[1,0,min_x],[0,1,min_y],[0,0,1]], dtype=np.float64) out_transform, out_w, out_h = _grid_from_bounds(bounds, res_x, res_y)
H_sub = np.linalg.inv(T_off) @ H_full
out_path = out_dir / f"{bip_path.stem}_registered.bip" out_path = out_dir / f"{bip_path.stem}_registered.bip"
src_nodata = src.nodata src_nodata = src.nodata
dst_nodata = src_nodata if src_nodata is not None else 0 dst_nodata = src_nodata if src_nodata is not None else 0
out_profile = ref_dataset.profile.copy() out_profile = src.profile.copy()
out_profile.update( out_profile.update(
driver="ENVI", driver="ENVI",
dtype=src.dtypes[0], dtype=src.dtypes[0],
height=bbox_h, height=out_h,
width=bbox_w, width=out_w,
count=src.count, count=src.count,
transform=bbox_transform, transform=out_transform,
crs=ref_crs, crs=ref_crs,
interleave="bip", interleave="bip",
compress=None, compress=None,
nodata=dst_nodata nodata=dst_nodata
) )
# 使用 OpenCV 进行单应变换重采样 ref_transform = ref_dataset.transform
Rt = np.array(
[[ref_transform.a, ref_transform.b, ref_transform.c],
[ref_transform.d, ref_transform.e, ref_transform.f],
[0.0, 0.0, 1.0]],
dtype=np.float64,
)
Out = np.array(
[[out_transform.a, out_transform.b, out_transform.c],
[out_transform.d, out_transform.e, out_transform.f],
[0.0, 0.0, 1.0]],
dtype=np.float64,
)
M = np.linalg.inv(Out) @ Rt @ H_full.astype(np.float64)
with rasterio.open(out_path, "w", **out_profile) as out_ds: with rasterio.open(out_path, "w", **out_profile) as out_ds:
for b in range(1, src.count + 1): for b in range(1, src.count + 1):
src_band = src.read(b).astype(np.float32) src_band = src.read(b).astype(np.float32)
dst_band = np.full((bbox_h, bbox_w), dst_nodata, dtype=np.float32)
# 使用 OpenCV warpPerspective子窗口坐标
dst_band = cv2.warpPerspective( dst_band = cv2.warpPerspective(
src_band, H_sub, src_band,
(bbox_w, bbox_h), M,
flags=cv2.INTER_LINEAR, (out_w, out_h),
flags=cv2.INTER_NEAREST,
borderMode=cv2.BORDER_CONSTANT, borderMode=cv2.BORDER_CONSTANT,
borderValue=dst_nodata borderValue=float(dst_nodata)
) ).astype(np.float32)
# 转回目标 dtype # 转回目标 dtype
if np.issubdtype(np.dtype(out_profile["dtype"]), np.integer): if np.issubdtype(np.dtype(out_profile["dtype"]), np.integer):
@ -957,7 +992,8 @@ def process_bip_to_tif(bip_path: Path, ref_dataset, matcher, out_dir: Path, stat
output_shape=(bbox_h, bbox_w), output_shape=(bbox_h, bbox_w),
mode='constant', mode='constant',
cval=dst_nodata, cval=dst_nodata,
preserve_range=True preserve_range=True,
order=0
).astype(np.float32) ).astype(np.float32)
# 转回目标 dtype # 转回目标 dtype
@ -1060,7 +1096,8 @@ def process_bip_to_tif(bip_path: Path, ref_dataset, matcher, out_dir: Path, stat
output_shape=(bbox_h, bbox_w), output_shape=(bbox_h, bbox_w),
mode='constant', mode='constant',
cval=dst_nodata, cval=dst_nodata,
preserve_range=True preserve_range=True,
order=0
).astype(np.float32) ).astype(np.float32)
if np.issubdtype(np.dtype(out_profile["dtype"]), np.integer): if np.issubdtype(np.dtype(out_profile["dtype"]), np.integer):
@ -1091,7 +1128,7 @@ def process_bip_to_tif(bip_path: Path, ref_dataset, matcher, out_dir: Path, stat
# 使用OpenCV的remap进行重采样 # 使用OpenCV的remap进行重采样
dst_band = cv2.remap( dst_band = cv2.remap(
src_band, map_x, map_y, src_band, map_x, map_y,
interpolation=cv2.INTER_LINEAR, interpolation=cv2.INTER_NEAREST,
borderMode=cv2.BORDER_CONSTANT, borderMode=cv2.BORDER_CONSTANT,
borderValue=dst_nodata borderValue=dst_nodata
) )
@ -1192,44 +1229,44 @@ def process_bip_to_tif(bip_path: Path, ref_dataset, matcher, out_dir: Path, stat
logger.warning(f"最小外接矩形无效: {bip_path.name}") logger.warning(f"最小外接矩形无效: {bip_path.name}")
return False return False
# 创建裁剪窗口和变换
bbox_window = rasterio.windows.Window(min_x, min_y, bbox_w, bbox_h) bbox_window = rasterio.windows.Window(min_x, min_y, bbox_w, bbox_h)
bbox_transform = ref_dataset.window_transform(bbox_window) bounds = rasterio.windows.bounds(bbox_window, transform=ref_dataset.transform)
res_x, res_y = _pixel_size_xy(src.transform)
out_transform, out_w, out_h = _grid_from_bounds(bounds, res_x, res_y)
out_path = out_dir / f"{bip_path.stem}_registered.bip" out_path = out_dir / f"{bip_path.stem}_registered.bip"
src_nodata = src.nodata src_nodata = src.nodata
dst_nodata = src_nodata if src_nodata is not None else 0 dst_nodata = src_nodata if src_nodata is not None else 0
# 更新输出 profile 使用最小外接矩形 out_profile = src.profile.copy()
out_profile = ref_dataset.profile.copy()
out_profile.update( out_profile.update(
driver="ENVI", driver="ENVI",
dtype=src.dtypes[0], dtype=src.dtypes[0],
height=bbox_h, height=out_h,
width=bbox_w, width=out_w,
count=src.count, count=src.count,
transform=bbox_transform, # 使用最小外接矩形的变换 transform=out_transform,
crs=ref_crs, crs=ref_crs,
interleave="bip", interleave="bip",
compress=None, compress=None,
nodata=dst_nodata nodata=dst_nodata
) )
# 重采样到最小外接矩形
with rasterio.open(out_path, "w", **out_profile) as out_ds: with rasterio.open(out_path, "w", **out_profile) as out_ds:
for b in range(1, src.count + 1): for b in range(1, src.count + 1):
src_band = src.read(b).astype(np.float32) src_band = src.read(b).astype(np.float32)
dst_band = np.zeros((bbox_h, bbox_w), dtype=np.float32) dst_band = np.zeros((out_h, out_w), dtype=np.float32)
reproject( reproject(
source=src_band, source=src_band,
destination=dst_band, destination=dst_band,
src_transform=corrected_affine, src_transform=corrected_affine,
src_crs=ref_crs, src_crs=ref_crs,
dst_transform=bbox_transform, dst_transform=out_transform,
dst_crs=ref_crs, dst_crs=ref_crs,
src_nodata=src_nodata, src_nodata=src_nodata,
dst_nodata=dst_nodata, dst_nodata=dst_nodata,
resampling=Resampling.bilinear, resampling=Resampling.nearest,
) )
# 转回目标 dtype # 转回目标 dtype
if np.issubdtype(np.dtype(out_profile["dtype"]), np.integer): if np.issubdtype(np.dtype(out_profile["dtype"]), np.integer):

1714
test V9GUI.py Normal file

File diff suppressed because it is too large Load Diff