Files
2026-05-03 21:58:47 +03:00

179 lines
6.4 KiB
Python

from __future__ import annotations
import argparse
import logging
from pathlib import Path
from .coordinates import extract_coordinate_crops, score_coordinates_for_sheet
from .detector_cv import detect_sheet, draw_overlay
from .export_yolo import export_candidates_to_yolo
from .inventory import discover_original_assets, download_assets, read_manifest_csv, write_manifest_csv
from .report import build_report
from .train_yolo import train_yolo
from .utils import load_yaml, setup_logging
LOG = logging.getLogger(__name__)
def cmd_inventory(args):
cfg = load_yaml(args.config)
base_url = args.base_url or cfg["source"]["base_url"]
assets = discover_original_assets(base_url=base_url, include_100k=args.include_100k)
if args.limit:
assets = assets[: args.limit]
write_manifest_csv(assets, args.out)
def cmd_download(args):
assets = read_manifest_csv(args.manifest)
selected = download_assets(assets, args.out_dir, limit=args.limit, overwrite=args.overwrite)
write_manifest_csv(selected, args.out_manifest)
def cmd_detect(args):
cfg = load_yaml(args.config)
detect_sheet(args.map, args.tif, args.sheet_id, cfg, args.out_dir)
def cmd_overlay(args):
draw_overlay(args.tif, args.candidates, args.out)
def cmd_score_coords(args):
cfg = load_yaml(args.config)
score_coordinates_for_sheet(
coord_csv=args.coordinates,
candidates_csv=args.candidates,
map_path=args.map,
tif_path=args.tif,
sheet_id=args.sheet_id,
cfg=cfg,
out_dir=args.out_dir,
coord_crs=args.coord_crs,
)
def cmd_crops(args):
extract_coordinate_crops(
coord_scores_csv=args.scores,
map_path=args.map,
tif_path=args.tif,
out_dir=args.out_dir,
crop_size=args.crop_size,
)
def cmd_export_yolo(args):
cfg = load_yaml(args.config)
export_candidates_to_yolo(
tif_path=args.tif,
candidates_csv=args.candidates,
out_dir=args.out_dir,
cfg=cfg,
sheet_id=args.sheet_id,
tile_size=args.tile_size,
overlap=args.overlap,
val_fraction=args.val_fraction,
)
def cmd_train_yolo(args):
train_yolo(args.data_yaml, model=args.model, imgsz=args.imgsz, epochs=args.epochs, batch=args.batch, device=args.device)
def cmd_report(args):
build_report([Path(p) for p in args.candidates], [Path(p) for p in args.overlays], args.out)
def build_parser():
p = argparse.ArgumentParser(prog="bgtopo-bluebox", description="BGtopoVJ blue rectangle/square PoC pipeline")
p.add_argument("--verbose", action="store_true")
sub = p.add_subparsers(dest="cmd", required=True)
s = sub.add_parser("inventory", help="Crawl original raster directory and create manifest")
s.add_argument("--config", default="configs/blue_detector.yaml")
s.add_argument("--base-url", default=None)
s.add_argument("--out", default="data/manifest.csv")
s.add_argument("--limit", type=int, default=None)
s.add_argument("--include-100k", action="store_true")
s.set_defaults(func=cmd_inventory)
s = sub.add_parser("download", help="Download .map/.tif pairs from manifest")
s.add_argument("--manifest", default="data/manifest.csv")
s.add_argument("--out-dir", default="data/raw")
s.add_argument("--out-manifest", default="data/manifest_downloaded.csv")
s.add_argument("--limit", type=int, default=2)
s.add_argument("--overwrite", action="store_true")
s.set_defaults(func=cmd_download)
s = sub.add_parser("detect", help="Detect blue rectangle/square candidates on one sheet")
s.add_argument("--config", default="configs/blue_detector.yaml")
s.add_argument("--sheet-id", required=True)
s.add_argument("--map", default=None)
s.add_argument("--tif", required=True)
s.add_argument("--out-dir", default="data/interim/candidates")
s.set_defaults(func=cmd_detect)
s = sub.add_parser("overlay", help="Draw candidate overlay for manual QA")
s.add_argument("--tif", required=True)
s.add_argument("--candidates", required=True)
s.add_argument("--out", required=True)
s.set_defaults(func=cmd_overlay)
s = sub.add_parser("score-coords", help="Score coordinate CSV against candidates from one sheet")
s.add_argument("--config", default="configs/blue_detector.yaml")
s.add_argument("--sheet-id", required=True)
s.add_argument("--coordinates", required=True)
s.add_argument("--candidates", required=True)
s.add_argument("--map", default=None)
s.add_argument("--tif", required=True)
s.add_argument("--out-dir", default="data/interim/coordinate_scores")
s.add_argument("--coord-crs", default="EPSG:4326")
s.set_defaults(func=cmd_score_coords)
s = sub.add_parser("crops", help="Extract review crops around scored coordinates")
s.add_argument("--scores", required=True)
s.add_argument("--map", default=None)
s.add_argument("--tif", required=True)
s.add_argument("--out-dir", default="data/interim/crops")
s.add_argument("--crop-size", type=int, default=256)
s.set_defaults(func=cmd_crops)
s = sub.add_parser("export-yolo", help="Export weak candidates to YOLO dataset format")
s.add_argument("--config", default="configs/blue_detector.yaml")
s.add_argument("--sheet-id", required=True)
s.add_argument("--tif", required=True)
s.add_argument("--candidates", required=True)
s.add_argument("--out-dir", default="data/yolo/bluebox")
s.add_argument("--tile-size", type=int, default=1024)
s.add_argument("--overlap", type=int, default=128)
s.add_argument("--val-fraction", type=float, default=0.20)
s.set_defaults(func=cmd_export_yolo)
s = sub.add_parser("train-yolo", help="Train YOLO on exported dataset")
s.add_argument("--data-yaml", required=True)
s.add_argument("--model", default="yolov8s.pt")
s.add_argument("--imgsz", type=int, default=1024)
s.add_argument("--epochs", type=int, default=80)
s.add_argument("--batch", type=int, default=4)
s.add_argument("--device", default="0")
s.set_defaults(func=cmd_train_yolo)
s = sub.add_parser("report", help="Build HTML QA report")
s.add_argument("--candidates", nargs="+", required=True)
s.add_argument("--overlays", nargs="*", default=[])
s.add_argument("--out", default="reports/poc_report.html")
s.set_defaults(func=cmd_report)
return p
def main():
parser = build_parser()
args = parser.parse_args()
setup_logging(args.verbose)
args.func(args)
if __name__ == "__main__":
main()