Spaces:
Running
Running
Download scripts/analyze_regions.py from coderuday21/satdetect-dev: direct link, hf CLI and curl.
- Browser
- Download file 4.51 kB
-
https://huggingface.co/spaces/coderuday21/satdetect-dev/resolve/main/scripts/analyze_regions.py
- Command line
-
hf download hf://spaces/coderuday21/satdetect-dev/scripts/analyze_regions.py
-
curl -L -o analyze_regions.py https://huggingface.co/spaces/coderuday21/satdetect-dev/resolve/main/scripts/analyze_regions.py
4.51 kB
| """ | |
| Quantify the false-positive / hallucination pattern in a set of detected | |
| regions, WITHOUT re-running the model. Reads a saved region list and reports | |
| counts by type, a confidence-bucket histogram, per-type confidence, and a | |
| single "hallucination fraction" metric (low-confidence unclassified regions | |
| as a share of all regions). | |
| Purpose (DDA Grid_54 vs H43X2E1 reports, Jul 2026): give hard numbers behind | |
| "it's still hallucinating" so the fix can be measured, not guessed. Pairs with | |
| scripts/filter_regions.py — analyze first to pick thresholds, then filter. | |
| Input (pick one): | |
| --in regions.json JSON list of region dicts (or {"regions": [...]}) | |
| --run-id N read regions from the app DB (data/satellite_app.db) | |
| Options: | |
| --halluc-types "Unclassified Ground Change,Other" types treated as | |
| "junk-prone" for the hallucination metric (substring match) | |
| --halluc-conf 0.55 confidence below which a junk-prone region counts as a | |
| likely hallucination | |
| Example: | |
| python scripts/analyze_regions.py --in regions.json | |
| python scripts/analyze_regions.py --run-id 37 --halluc-conf 0.5 | |
| """ | |
| import argparse | |
| import json | |
| import sys | |
| from pathlib import Path | |
| ROOT = Path(__file__).resolve().parent.parent | |
| sys.path.insert(0, str(ROOT)) | |
| _BUCKETS = [(0.0, 0.25), (0.25, 0.40), (0.40, 0.50), (0.50, 0.75), (0.75, 1.01)] | |
| def _load(args) -> tuple: | |
| if args.run_id is not None: | |
| from app.database import SessionLocal | |
| from app.models import DetectionRun | |
| db = SessionLocal() | |
| run = db.query(DetectionRun).filter(DetectionRun.id == args.run_id).first() | |
| db.close() | |
| if run is None: | |
| raise SystemExit(f"No DetectionRun with id={args.run_id}") | |
| return json.loads(run.regions_json or "[]"), f"DB run #{args.run_id}" | |
| data = json.loads(Path(args.in_path).read_text(encoding="utf-8")) | |
| if isinstance(data, dict): | |
| data = data.get("regions", []) | |
| return data, args.in_path | |
| def _bucket_label(lo, hi): | |
| return f"{int(lo*100):>3d}-{int(min(hi,1.0)*100):>3d}%" | |
| def main(): | |
| parser = argparse.ArgumentParser( | |
| description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) | |
| src = parser.add_mutually_exclusive_group(required=True) | |
| src.add_argument("--in", dest="in_path", default="") | |
| src.add_argument("--run-id", type=int, default=None) | |
| parser.add_argument("--halluc-types", default="Unclassified Ground Change,Other") | |
| parser.add_argument("--halluc-conf", type=float, default=0.55) | |
| args = parser.parse_args() | |
| regions, source = _load(args) | |
| n = len(regions) | |
| if n == 0: | |
| raise SystemExit(f"No regions in {source}") | |
| halluc_subs = [s.strip().lower() for s in args.halluc_types.split(",") if s.strip()] | |
| print(f"Source: {source}") | |
| print(f"Total regions: {n}\n") | |
| # By type | |
| by_type = {} | |
| for r in regions: | |
| t = r.get("objectType", "unknown") | |
| by_type.setdefault(t, []).append(float(r.get("confidence", 0.0))) | |
| print("By type (count | mean confidence):") | |
| for t, confs in sorted(by_type.items(), key=lambda kv: -len(kv[1])): | |
| print(f" {t:32s} {len(confs):4d} | {sum(confs)/len(confs):.2f}") | |
| # Confidence histogram | |
| print("\nConfidence distribution:") | |
| for lo, hi in _BUCKETS: | |
| c = sum(1 for r in regions if lo <= float(r.get("confidence", 0.0)) < hi) | |
| bar = "#" * c | |
| print(f" {_bucket_label(lo, hi)} {c:4d} {bar}") | |
| # Area stats | |
| areas = sorted(float(r.get("area", 0)) for r in regions) | |
| if areas: | |
| med = areas[len(areas) // 2] | |
| print(f"\nArea (px): min={areas[0]:.0f} median={med:.0f} max={areas[-1]:.0f}") | |
| # Hallucination metric | |
| halluc = [ | |
| r for r in regions | |
| if any(s in str(r.get("objectType", "")).lower() for s in halluc_subs) | |
| and float(r.get("confidence", 0.0)) < args.halluc_conf | |
| ] | |
| frac = len(halluc) / n | |
| print(f"\nHallucination metric:") | |
| print(f" junk-prone types: {args.halluc_types}") | |
| print(f" low-confidence threshold: <{args.halluc_conf}") | |
| print(f" likely hallucinations: {len(halluc)}/{n} = {frac*100:.1f}% of all regions") | |
| print(f" -> filtering these leaves {n - len(halluc)} confident region(s).") | |
| if frac > 0.3: | |
| print(" ** Over 30% of regions are low-confidence junk — strong FP signal;" | |
| " apply scripts/filter_regions.py before reporting.") | |
| if __name__ == "__main__": | |
| main() | |