| | |
| | | vector_path.write_text(json.dumps({"type": "FeatureCollection", "features": []}, indent=2), encoding="utf-8") |
| | | |
| | | |
| | | def run_change_detection(before_path: Path, after_path: Path, output_dir: Path, *, processed_dir: Path | None = None, model_name: str = MODEL_NAME, threshold: float = DEFAULT_THRESHOLD, tile_size: int = DEFAULT_TILE_SIZE, overlap: int = DEFAULT_OVERLAP, max_dimension: int = AUTO_MAX_DIMENSION, processing_mode: str = DEFAULT_PROCESSING_MODE) -> dict[str, Any]: |
| | | def run_change_detection(before_path: Path, after_path: Path, output_dir: Path, *, processed_dir: Path | None = None, model_name: str = MODEL_NAME, threshold: float = DEFAULT_THRESHOLD, tile_size: int = DEFAULT_TILE_SIZE, overlap: int = DEFAULT_OVERLAP, max_dimension: int = AUTO_MAX_DIMENSION, processing_mode: str = DEFAULT_PROCESSING_MODE, device: str = "auto") -> dict[str, Any]: |
| | | started = time.perf_counter() |
| | | if not math.isfinite(threshold) or not MIN_THRESHOLD <= threshold <= MAX_THRESHOLD: |
| | | raise ValueError(f"threshold must be between {MIN_THRESHOLD} and {MAX_THRESHOLD}") |
| | | if processing_mode not in PROCESSING_MODES: |
| | | raise ValueError(f"processing_mode must be one of {sorted(PROCESSING_MODES)}") |
| | | if device not in {"auto", "cpu", "cuda"}: |
| | | raise ValueError("device must be auto, cpu, or cuda") |
| | | if not isinstance(max_dimension, int) or max_dimension != AUTO_MAX_DIMENSION and not MIN_MAX_DIMENSION <= max_dimension <= MAX_MAX_DIMENSION: |
| | | raise ValueError(f"max_dimension must be 0 or between {MIN_MAX_DIMENSION} and {MAX_MAX_DIMENSION}") |
| | | before, before_info = _read_rgb(before_path, processing_mode) |
| | |
| | | Image.fromarray(after_small).save(output_dir / "after_registered_preview.jpg", quality=92) |
| | | |
| | | from geoai import ChangeStarDetection |
| | | import torch |
| | | |
| | | detector = ChangeStarDetection(model_name=model_name, device="cpu") |
| | | if device == "cuda" and not torch.cuda.is_available(): |
| | | raise RuntimeError("CUDA was requested but is unavailable.") |
| | | device_name = "cuda" if device == "cuda" or (device == "auto" and torch.cuda.is_available()) else "cpu" |
| | | detector = ChangeStarDetection(model_name=model_name, device=device_name) |
| | | result = detector.predict(str(work_dir / "before.tif"), str(work_dir / "after_registered.tif"), tile_size=tile_size, overlap=overlap, threshold=threshold) |
| | | probability = np.asarray(result["change_prob"], dtype=np.float32) |
| | | raw_mask = ((probability >= threshold) & valid_small).astype(np.uint8) * 255 |
| | |
| | | "geoai_version": "0.42.0", |
| | | "method": "geoai.ChangeStarDetection + rasterio.features.shapes", |
| | | "model": model_name, |
| | | "device": "cpu", |
| | | "requested_device": device, |
| | | "device": device_name, |
| | | "processing_mode": actual_mode, |
| | | "requested_processing_mode": processing_mode, |
| | | "thresholds": {"change_probability": threshold, "minimum_component_pixels": min_area}, |
| | |
| | | |
| | | |
| | | def build_parser() -> argparse.ArgumentParser: |
| | | parser = argparse.ArgumentParser(description="Run CPU ChangeStar change detection on a pair of images.") |
| | | parser = argparse.ArgumentParser(description="Run ChangeStar change detection on a pair of images.") |
| | | parser.add_argument("--before", type=Path, required=True) |
| | | parser.add_argument("--after", type=Path, required=True) |
| | | parser.add_argument("--output", type=Path, required=True) |
| | |
| | | parser.add_argument("--overlap", type=int, default=DEFAULT_OVERLAP) |
| | | parser.add_argument("--max-dimension", type=int, default=AUTO_MAX_DIMENSION, help="Long-edge cap in pixels; 0 keeps a valid GeoTIFF at native resolution and uses 1024 for ordinary images.") |
| | | parser.add_argument("--processing-mode", choices=sorted(PROCESSING_MODES), default=DEFAULT_PROCESSING_MODE) |
| | | parser.add_argument("--device", choices={"auto", "cpu", "cuda"}, default="auto") |
| | | return parser |
| | | |
| | | |
| | | if __name__ == "__main__": |
| | | args = build_parser().parse_args() |
| | | try: |
| | | print(json.dumps(run_change_detection(args.before, args.after, args.output, processed_dir=args.processed_output, model_name=args.model, threshold=args.threshold, tile_size=args.tile_size, overlap=args.overlap, max_dimension=args.max_dimension, processing_mode=args.processing_mode), ensure_ascii=False, indent=2)) |
| | | print(json.dumps(run_change_detection(args.before, args.after, args.output, processed_dir=args.processed_output, model_name=args.model, threshold=args.threshold, tile_size=args.tile_size, overlap=args.overlap, max_dimension=args.max_dimension, processing_mode=args.processing_mode, device=args.device), ensure_ascii=False, indent=2)) |
| | | except (FileNotFoundError, ValueError, RuntimeError) as exc: |
| | | raise SystemExit(f"变化检测失败: {exc}") from exc |