Zaehllinie persistent speichern (ueberlebt Neustart)
- Linie wird beim Setzen atomar in counting_line.json gespeichert - beim Start geladen -> Default fuer Session UND Webcam-Grabber - Pfad per LINE_FILE-Env ueberschreibbar, Datei via .gitignore ausgeschlossen - set_line-Route nutzt jetzt _valid_line (weniger Doppelcode) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -4,6 +4,9 @@
|
||||
# Hochgeladene Videos
|
||||
uploads/
|
||||
|
||||
# Persistierte Zaehllinie (maschinenspezifisch)
|
||||
counting_line.json
|
||||
|
||||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
|
||||
58
app.py
58
app.py
@@ -136,6 +136,45 @@ UPLOAD_DIR = "uploads"
|
||||
DEFAULT_LINE = {"x1": 0, "y1": 300, "x2": 1020, "y2": 300}
|
||||
FRAME_SIZE = (1020, 600)
|
||||
|
||||
# Persistente Zaehllinie: wird als JSON gespeichert und beim Start geladen,
|
||||
# damit sie einen Neustart ueberlebt (Pfad per Env ueberschreibbar).
|
||||
LINE_FILE = os.environ.get("LINE_FILE", "counting_line.json")
|
||||
|
||||
|
||||
def _valid_line(d):
|
||||
"""Validiert/normalisiert ein Linien-Dict zu int-Koordinaten oder None."""
|
||||
try:
|
||||
return {k: int(d[k]) for k in ("x1", "y1", "x2", "y2")}
|
||||
except (KeyError, ValueError, TypeError):
|
||||
return None
|
||||
|
||||
|
||||
def load_saved_line() -> dict:
|
||||
"""Laedt die gespeicherte Linie oder faellt auf DEFAULT_LINE zurueck."""
|
||||
try:
|
||||
with open(LINE_FILE) as fh:
|
||||
line = _valid_line(json.load(fh))
|
||||
if line:
|
||||
return line
|
||||
except (OSError, json.JSONDecodeError):
|
||||
pass
|
||||
return dict(DEFAULT_LINE)
|
||||
|
||||
|
||||
def save_line(line: dict) -> None:
|
||||
"""Speichert die Linie atomar als JSON (ueberlebt App-Neustart)."""
|
||||
try:
|
||||
tmp = f"{LINE_FILE}.tmp"
|
||||
with open(tmp, "w") as fh:
|
||||
json.dump(line, fh)
|
||||
os.replace(tmp, LINE_FILE)
|
||||
except OSError as exc:
|
||||
print(f"[line] save failed: {exc}", flush=True)
|
||||
|
||||
|
||||
# Beim Start einmal laden -> Default fuer Session UND Webcam-Grabber.
|
||||
SAVED_LINE = load_saved_line()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Hilfsfunktionen
|
||||
@@ -151,7 +190,7 @@ def ensure_upload_dir() -> None:
|
||||
|
||||
def get_line_from_session():
|
||||
if "counting_line" not in session:
|
||||
session["counting_line"] = dict(DEFAULT_LINE)
|
||||
session["counting_line"] = dict(SAVED_LINE)
|
||||
return session["counting_line"]
|
||||
|
||||
|
||||
@@ -314,7 +353,7 @@ class WebcamGrabber:
|
||||
self.frame_seq = 0
|
||||
self.viewers = 0
|
||||
self.reset_flag = Event()
|
||||
self.line = dict(DEFAULT_LINE)
|
||||
self.line = dict(SAVED_LINE)
|
||||
|
||||
# Eigenes Modell -> isolierter Tracker, getrennt vom Video-Pfad.
|
||||
# Lazy: wird erst beim ersten aktiven Stream geladen.
|
||||
@@ -612,18 +651,15 @@ def webcam_feed():
|
||||
def set_counting_line():
|
||||
"""Setzt die Zaehllinie (gilt fuer Video-Session UND Webcam-Grabber)."""
|
||||
data = request.get_json(silent=True) or {}
|
||||
try:
|
||||
line = {
|
||||
"x1": int(data["x1"]),
|
||||
"y1": int(data["y1"]),
|
||||
"x2": int(data["x2"]),
|
||||
"y2": int(data["y2"]),
|
||||
}
|
||||
except (KeyError, ValueError, TypeError):
|
||||
line = _valid_line(data)
|
||||
if line is None:
|
||||
abort(400, description="Ungueltige Linienkoordinaten")
|
||||
|
||||
global SAVED_LINE
|
||||
SAVED_LINE = line # Default fuer kuenftige Sessions
|
||||
save_line(line) # persistent -> ueberlebt Neustart
|
||||
session["counting_line"] = line
|
||||
webcam.set_line(line) # Webcam nutzt eine globale Linie (eine Kamera)
|
||||
webcam.set_line(line) # Webcam nutzt eine globale Linie (eine Kamera)
|
||||
return jsonify({"status": "success", "line": line})
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user