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
|
# Hochgeladene Videos
|
||||||
uploads/
|
uploads/
|
||||||
|
|
||||||
|
# Persistierte Zaehllinie (maschinenspezifisch)
|
||||||
|
counting_line.json
|
||||||
|
|
||||||
# Python
|
# Python
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.py[cod]
|
*.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}
|
DEFAULT_LINE = {"x1": 0, "y1": 300, "x2": 1020, "y2": 300}
|
||||||
FRAME_SIZE = (1020, 600)
|
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
|
# Hilfsfunktionen
|
||||||
@@ -151,7 +190,7 @@ def ensure_upload_dir() -> None:
|
|||||||
|
|
||||||
def get_line_from_session():
|
def get_line_from_session():
|
||||||
if "counting_line" not in session:
|
if "counting_line" not in session:
|
||||||
session["counting_line"] = dict(DEFAULT_LINE)
|
session["counting_line"] = dict(SAVED_LINE)
|
||||||
return session["counting_line"]
|
return session["counting_line"]
|
||||||
|
|
||||||
|
|
||||||
@@ -314,7 +353,7 @@ class WebcamGrabber:
|
|||||||
self.frame_seq = 0
|
self.frame_seq = 0
|
||||||
self.viewers = 0
|
self.viewers = 0
|
||||||
self.reset_flag = Event()
|
self.reset_flag = Event()
|
||||||
self.line = dict(DEFAULT_LINE)
|
self.line = dict(SAVED_LINE)
|
||||||
|
|
||||||
# Eigenes Modell -> isolierter Tracker, getrennt vom Video-Pfad.
|
# Eigenes Modell -> isolierter Tracker, getrennt vom Video-Pfad.
|
||||||
# Lazy: wird erst beim ersten aktiven Stream geladen.
|
# Lazy: wird erst beim ersten aktiven Stream geladen.
|
||||||
@@ -612,18 +651,15 @@ def webcam_feed():
|
|||||||
def set_counting_line():
|
def set_counting_line():
|
||||||
"""Setzt die Zaehllinie (gilt fuer Video-Session UND Webcam-Grabber)."""
|
"""Setzt die Zaehllinie (gilt fuer Video-Session UND Webcam-Grabber)."""
|
||||||
data = request.get_json(silent=True) or {}
|
data = request.get_json(silent=True) or {}
|
||||||
try:
|
line = _valid_line(data)
|
||||||
line = {
|
if line is None:
|
||||||
"x1": int(data["x1"]),
|
|
||||||
"y1": int(data["y1"]),
|
|
||||||
"x2": int(data["x2"]),
|
|
||||||
"y2": int(data["y2"]),
|
|
||||||
}
|
|
||||||
except (KeyError, ValueError, TypeError):
|
|
||||||
abort(400, description="Ungueltige Linienkoordinaten")
|
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
|
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})
|
return jsonify({"status": "success", "line": line})
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user