Add offline mosaic EXIF tagging (stitch --write-exif, tag_mosaic_exif CLI)

- spruce.exif: tag_mosaic_jpeg_for_scan_dir, resolve_machine_label_for_scan_dir; ProcessingSoftware for tile-stitched mosaics
- spruce.settings: load_config(require_credentials=False) for config without login
- scripts/tag_mosaic_exif.py and tests; stitch script --write-exif path
This commit is contained in:
2026-04-26 20:47:23 -04:00
parent 314b68322c
commit 08a29d124a
6 changed files with 424 additions and 5 deletions
+31
View File
@@ -26,6 +26,9 @@ if str(_REPO_ROOT) not in sys.path:
from PIL import Image, ImageChops
from spruce.exif import tag_mosaic_jpeg_for_scan_dir
from spruce.settings import DEFAULT_CONFIG, load_config
TILE_FILENAME_RE = re.compile(r"tile_r(\d+)_c(\d+)\.jpg$", re.IGNORECASE)
@@ -281,6 +284,20 @@ def main() -> None:
default=95,
help="JPEG quality for output (default: 95).",
)
parser.add_argument(
"--write-exif",
action="store_true",
help=(
"After saving, write mosaic EXIF using metadata.json and config "
"(implies override if write_exif is false in config). JPEG output only."
),
)
parser.add_argument(
"--config",
default=DEFAULT_CONFIG,
metavar="FILE",
help=f"YAML config for EXIF machine_metadata (default: {DEFAULT_CONFIG})",
)
parser.add_argument(
"--tile-gap",
type=int,
@@ -339,6 +356,20 @@ def main() -> None:
f"grid {nx}x{ny}, tile_gap={args.tile_gap})"
)
if args.write_exif:
if out.suffix.lower() not in (".jpg", ".jpeg"):
raise SystemExit("--write-exif requires a .jpg or .jpeg output path.")
cfg_path = Path(args.config).expanduser()
if not cfg_path.is_file():
raise SystemExit(f"Config not found: {cfg_path}")
config = load_config(str(cfg_path), require_credentials=False)
try:
ok = tag_mosaic_jpeg_for_scan_dir(scan_dir, out, config, force=True)
except ValueError as exc:
raise SystemExit(str(exc)) from exc
if not ok:
raise SystemExit("EXIF tagging failed (see log).")
if args.compare_mosaic:
compare_mosaics(canvas, scan_dir / "mosaic.jpg", fit=args.fit)