Add Snapshot

This commit is contained in:
proitlab committed 2026-07-10 00:17:17 +07:00
1 parent 3b8fb0c312
commit 210df4a038
5 files changed
+359 -4

No files matched your search

+76
View File
@@ -72,6 +72,20 @@ DAILY_CUTOFF_TIME = os.getenv("DAILY_CUTOFF_TIME", "20:00")
EXPORT_CSV = os.getenv("EXPORT_CSV", "true").lower() == "true"
CROSS_CSV = os.getenv("CROSS_CSV", f"{OUTPUT_DIR}/crossings.csv")
# Save an annotated frame snapshot each time an object crosses a line and the
# counter increases.
SAVE_CROSS_SNAPSHOT = os.getenv("SAVE_CROSS_SNAPSHOT", "false").lower() == "true"
# Also save one snapshot the first time each object is detected (before it crosses),
# named with the same track id so it can be correlated with the crossing snapshot.
SAVE_DETECT_SNAPSHOT = os.getenv("SAVE_DETECT_SNAPSHOT", "false").lower() == "true"
CROSS_SNAPSHOT_DIR = os.getenv("CROSS_SNAPSHOT_DIR", f"{OUTPUT_DIR}/snapshots")
CROSS_SNAPSHOT_QUALITY = int(os.getenv("CROSS_SNAPSHOT_QUALITY", "85"))
# Retention: delete oldest snapshots when either limit is exceeded (0 = disabled).
CROSS_SNAPSHOT_MAX_FILES = int(os.getenv("CROSS_SNAPSHOT_MAX_FILES", "1000"))
CROSS_SNAPSHOT_MAX_AGE_DAYS = float(os.getenv("CROSS_SNAPSHOT_MAX_AGE_DAYS", "7"))
# Run the cleanup sweep at most every N seconds to limit filesystem scans.
CROSS_SNAPSHOT_CLEANUP_SEC = int(os.getenv("CROSS_SNAPSHOT_CLEANUP_SEC", "60"))
RATE_WINDOW_SEC = int(os.getenv("RATE_WINDOW_SEC", "60"))
WARMUP_FRAMES = int(os.getenv("WARMUP_FRAMES", "30"))
RECONNECT_DELAY_SEC = int(os.getenv("RECONNECT_DELAY_SEC", "3"))
@@ -756,6 +770,23 @@ def prune_stale_tracks(tracked, now_mono):
del tracked[tid]
def cleanup_snapshots(snapshot_dir, max_files, max_age_days):
"""Delete oldest / expired crossing snapshots to bound disk usage."""
d = Path(snapshot_dir)
if not d.is_dir():
return
files = sorted(d.rglob("*.jpg"), key=lambda p: p.stat().st_mtime)
if max_age_days > 0:
cutoff = time.time() - max_age_days * 86400
for p in list(files):
if p.stat().st_mtime < cutoff:
p.unlink(missing_ok=True)
files.remove(p)
if max_files > 0 and len(files) > max_files:
for p in files[: len(files) - max_files]:
p.unlink(missing_ok=True)
def overlay_rect(img, x1, y1, x2, y2, color, alpha=0.65):
x1, y1 = max(0, x1), max(0, y1)
x2, y2 = min(img.shape[1], x2), min(img.shape[0], y2)
@@ -1036,6 +1067,8 @@ def run():
recent_cross_in = deque()
recent_cross_out = deque()
detect_snapshot_ids = set()
object_cross_flash1 = {}
object_cross_flash2 = {}
line_pulse = count_in_pulse = count_out_pulse = 0
@@ -1049,6 +1082,7 @@ def run():
crossing_times = deque()
counter_in = 0
counter_out = 0
last_snapshot_cleanup = 0.0
cap, w, h, fps = connect_stream(SOURCE)
if cap is None:
@@ -1096,6 +1130,8 @@ def run():
elapsed = now - session_start
mono = time.monotonic()
object_crossed_frame = False
cross_events_frame = []
detect_events_frame = []
skip_inference = False
if MOTION_DETECTION_ENABLED:
@@ -1179,6 +1215,10 @@ def run():
cx = object_cx_list[di]
cy = object_cy_list[di]
if tid not in detect_snapshot_ids:
detect_snapshot_ids.add(tid)
detect_events_frame.append(tid)
if tid not in object_tracked:
inherited = _inherit_prev(
object_tracked, tid, cx, cy, mono, INHERIT_SEC, INHERIT_PX
@@ -1243,6 +1283,7 @@ def run():
]
)
object_crossed_frame = True
cross_events_frame.append((tid, direction))
crossing_times.append(mono)
object_cross_flash1[tid] = CROSS_FLASH_FRAMES
object_cross_flash2[tid] = CROSS_FLASH_FRAMES
@@ -1334,6 +1375,41 @@ def run():
except Exception:
pass
if (SAVE_DETECT_SNAPSHOT and detect_events_frame) or (
SAVE_CROSS_SNAPSHOT and cross_events_frame
):
try:
ts = datetime.now().strftime("%Y%m%d_%H%M%S_%f")[:-3]
if SAVE_DETECT_SNAPSHOT and detect_events_frame:
detect_dir = Path(CROSS_SNAPSHOT_DIR) / "detect"
detect_dir.mkdir(parents=True, exist_ok=True)
for tid in detect_events_frame:
fname = f"{ts}_detect_id{tid}_f{frame_idx}.jpg"
cv2.imwrite(
str(detect_dir / fname),
frame,
[cv2.IMWRITE_JPEG_QUALITY, CROSS_SNAPSHOT_QUALITY],
)
if SAVE_CROSS_SNAPSHOT and cross_events_frame:
cross_dir = Path(CROSS_SNAPSHOT_DIR) / "cross"
cross_dir.mkdir(parents=True, exist_ok=True)
for tid, direction in cross_events_frame:
fname = f"{ts}_{direction}_id{tid}_f{frame_idx}.jpg"
cv2.imwrite(
str(cross_dir / fname),
frame,
[cv2.IMWRITE_JPEG_QUALITY, CROSS_SNAPSHOT_QUALITY],
)
if now - last_snapshot_cleanup >= CROSS_SNAPSHOT_CLEANUP_SEC:
cleanup_snapshots(
CROSS_SNAPSHOT_DIR,
CROSS_SNAPSHOT_MAX_FILES,
CROSS_SNAPSHOT_MAX_AGE_DAYS,
)
last_snapshot_cleanup = now
except Exception as exc:
print(f"[{now_str()}] Failed to save snapshot: {exc}")
frame_idx += 1
prune_stale_tracks(object_tracked, mono)