159 lines
5.3 KiB
Python
159 lines
5.3 KiB
Python
#!/usr/bin/env python3
|
|
"""Build one global orthophoto master from tiled orthophoto JPGs."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
from pathlib import Path
|
|
|
|
from osgeo import gdal
|
|
|
|
|
|
def parse_args() -> argparse.Namespace:
|
|
p = argparse.ArgumentParser(description="Create a global ortho master mosaic from ortho tile JPGs.")
|
|
p.add_argument(
|
|
"--input-dir",
|
|
action="append",
|
|
default=[],
|
|
help="Input directory containing ortho JPG tiles. Can be specified multiple times.",
|
|
)
|
|
p.add_argument("--pattern", default="*.jpg", help="Input filename pattern.")
|
|
p.add_argument("--out-dir", default="work/mask_master", help="Output directory.")
|
|
p.add_argument("--vrt-name", default="ortho_master.vrt", help="Output VRT name.")
|
|
p.add_argument("--tif-name", default="ortho_master.tif", help="Output GeoTIFF name.")
|
|
p.add_argument("--preview-name", default="ortho_master.jpg", help="Output preview JPG name.")
|
|
p.add_argument("--no-preview", action="store_true", help="Skip preview JPG generation.")
|
|
p.add_argument(
|
|
"--preview-max-size",
|
|
type=int,
|
|
default=8192,
|
|
help="Longest preview edge in pixels (aspect preserved).",
|
|
)
|
|
p.add_argument(
|
|
"--compress",
|
|
default="LZW",
|
|
help="GeoTIFF compression (e.g., LZW, DEFLATE, JPEG, NONE).",
|
|
)
|
|
p.add_argument("--resample", default="nearest", help="Resample algorithm for preview (nearest|bilinear|cubic...).")
|
|
return p.parse_args()
|
|
|
|
|
|
def collect_inputs(input_dirs: list[str], pattern: str) -> list[Path]:
|
|
dirs = [Path(d) for d in input_dirs] if input_dirs else [Path("export_unity/ortho_jpg")]
|
|
files: list[Path] = []
|
|
for d in dirs:
|
|
if not d.exists():
|
|
print(f"[ortho_build_master] Warning: input dir missing: {d}")
|
|
continue
|
|
for f in sorted(d.glob(pattern)):
|
|
if f.suffix.lower() != ".jpg":
|
|
continue
|
|
files.append(f)
|
|
return files
|
|
|
|
|
|
def preview_size(width: int, height: int, max_edge: int) -> tuple[int, int]:
|
|
if width <= 0 or height <= 0:
|
|
return width, height
|
|
edge = max(width, height)
|
|
if edge <= max_edge:
|
|
return width, height
|
|
scale = max_edge / float(edge)
|
|
return max(1, int(round(width * scale))), max(1, int(round(height * scale)))
|
|
|
|
|
|
def main() -> int:
|
|
args = parse_args()
|
|
gdal.UseExceptions()
|
|
|
|
inputs = collect_inputs(args.input_dir, args.pattern)
|
|
if not inputs:
|
|
raise SystemExit("[ortho_build_master] No input ortho tiles found.")
|
|
|
|
out_dir = Path(args.out_dir)
|
|
out_dir.mkdir(parents=True, exist_ok=True)
|
|
vrt_path = out_dir / args.vrt_name
|
|
tif_path = out_dir / args.tif_name
|
|
preview_path = out_dir / args.preview_name
|
|
meta_path = out_dir / "ortho_master_meta.json"
|
|
|
|
print(f"[ortho_build_master] Input tiles: {len(inputs)}")
|
|
print(f"[ortho_build_master] Building VRT: {vrt_path}")
|
|
vrt = gdal.BuildVRT(str(vrt_path), [str(p) for p in inputs])
|
|
if vrt is None:
|
|
raise SystemExit("[ortho_build_master] gdal.BuildVRT failed.")
|
|
|
|
width = vrt.RasterXSize
|
|
height = vrt.RasterYSize
|
|
gt = vrt.GetGeoTransform(can_return_null=True)
|
|
proj = vrt.GetProjectionRef()
|
|
|
|
print(f"[ortho_build_master] Translating GeoTIFF: {tif_path}")
|
|
tif_ds = gdal.Translate(
|
|
str(tif_path),
|
|
vrt,
|
|
options=gdal.TranslateOptions(
|
|
format="GTiff",
|
|
creationOptions=[
|
|
"TILED=YES",
|
|
f"COMPRESS={args.compress}",
|
|
"BIGTIFF=IF_SAFER",
|
|
],
|
|
),
|
|
)
|
|
if tif_ds is None:
|
|
raise SystemExit("[ortho_build_master] gdal.Translate to GeoTIFF failed.")
|
|
tif_ds = None
|
|
|
|
if not args.no_preview:
|
|
out_w, out_h = preview_size(width, height, args.preview_max_size)
|
|
print(f"[ortho_build_master] Writing preview JPG: {preview_path} ({out_w}x{out_h})")
|
|
jpg_ds = gdal.Translate(
|
|
str(preview_path),
|
|
vrt,
|
|
options=gdal.TranslateOptions(
|
|
format="JPEG",
|
|
width=out_w,
|
|
height=out_h,
|
|
resampleAlg=args.resample,
|
|
creationOptions=["QUALITY=92"],
|
|
),
|
|
)
|
|
if jpg_ds is None:
|
|
raise SystemExit("[ortho_build_master] gdal.Translate to JPEG preview failed.")
|
|
jpg_ds = None
|
|
|
|
vrt = None
|
|
|
|
meta = {
|
|
"schema_version": 1,
|
|
"inputs": [str(p) for p in inputs],
|
|
"outputs": {
|
|
"vrt": str(vrt_path),
|
|
"tif": str(tif_path),
|
|
"preview": None if args.no_preview else str(preview_path),
|
|
},
|
|
"raster": {
|
|
"width": width,
|
|
"height": height,
|
|
"geotransform": list(gt) if gt else None,
|
|
"projection": proj,
|
|
},
|
|
"settings": {
|
|
"compress": args.compress,
|
|
"preview_max_size": args.preview_max_size,
|
|
"resample": args.resample,
|
|
"pattern": args.pattern,
|
|
"input_dirs": args.input_dir if args.input_dir else ["export_unity/ortho_jpg"],
|
|
},
|
|
}
|
|
meta_path.write_text(json.dumps(meta, indent=2), encoding="utf-8")
|
|
print(f"[ortho_build_master] Wrote metadata: {meta_path}")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|
|
|