74 lines
2.3 KiB
Python
74 lines
2.3 KiB
Python
"""Zone configuration — load/save counting zone presets from zones.json."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
from typing import Any
|
|
|
|
from src.truck_roi import TruckROI
|
|
|
|
|
|
def load_zone_config(path: str) -> dict[str, Any]:
|
|
"""Load zones.json and return full config dict."""
|
|
with open(path) as f:
|
|
return json.load(f)
|
|
|
|
|
|
def save_zone_config(config: dict[str, Any], path: str) -> None:
|
|
"""Save zone config dict to zones.json."""
|
|
with open(path, "w") as f:
|
|
json.dump(config, f, indent=2)
|
|
|
|
|
|
def build_static_roi(zone: dict[str, Any], video_w: int, video_h: int) -> TruckROI:
|
|
"""Build a TruckROI from a zone config dict.
|
|
|
|
The zone dict should have:
|
|
- truck_area: [[x1,y1], [x2,y2], [x3,y3], [x4,y4]] (4-point polygon)
|
|
- count_line: {"x_start": int, "x_end": int, "y": int}
|
|
Coordinates are in the video's native resolution (no scaling needed
|
|
if the zone was calibrated for this resolution).
|
|
"""
|
|
truck_pts = zone.get("truck_area", [])
|
|
count_line = zone.get("count_line", {})
|
|
|
|
# Compute bounding box of truck_area polygon
|
|
if truck_pts and len(truck_pts) >= 4:
|
|
xs = [p[0] for p in truck_pts]
|
|
ys = [p[1] for p in truck_pts]
|
|
x1 = max(0, min(xs))
|
|
y1 = max(0, min(ys))
|
|
x2 = min(video_w, max(xs))
|
|
y2 = min(video_h, max(ys))
|
|
else:
|
|
# Fallback: use 50% of frame
|
|
x1, y1 = int(video_w * 0.3), int(video_h * 0.1)
|
|
x2, y2 = int(video_w * 0.75), int(video_h * 0.95)
|
|
|
|
line_y = count_line.get("y", int((y1 + y2) / 2))
|
|
|
|
return TruckROI(
|
|
x1=x1, y1=y1, x2=x2, y2=y2,
|
|
line_y=line_y,
|
|
confidence=1.0,
|
|
)
|
|
|
|
|
|
def list_zone_presets(path: str) -> list[dict[str, Any]]:
|
|
"""Return list of preset summaries: [{"name": str, "description": str, ...}]."""
|
|
config = load_zone_config(path)
|
|
presets = config.get("presets", {})
|
|
return [
|
|
{"name": name, "description": vals.get("description", ""), **vals}
|
|
for name, vals in presets.items()
|
|
]
|
|
|
|
|
|
def get_active_zone(path: str, preset_name: str | None = None) -> dict[str, Any] | None:
|
|
"""Get the active preset dict. Falls back to active_preset field if name not given."""
|
|
config = load_zone_config(path)
|
|
presets = config.get("presets", {})
|
|
name = preset_name or config.get("active_preset")
|
|
return presets.get(name)
|