Spaces:
Build error
Build error
Update app.py
Browse files
app.py
CHANGED
|
@@ -5,6 +5,10 @@
|
|
| 5 |
# - (NEW) Per-employee live timers on video overlays: Working and Idle counters (mm:ss)
|
| 6 |
# - (NEW) Employees view shows the same live Working/Idle seconds
|
| 7 |
# - (Patch I–K) Real-time streaming pace: higher default FPS, up to 60 FPS slider, process every frame when possible, time-synced display (no fixed sleeps).
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
|
| 9 |
import os, json, math, tempfile, time
|
| 10 |
from datetime import datetime, date
|
|
@@ -683,9 +687,20 @@ with tab_live:
|
|
| 683 |
last_display_t = time.perf_counter()
|
| 684 |
target_dt = 1.0 / float(src_fps if src_fps and src_fps > 0 else 25.0)
|
| 685 |
|
|
|
|
|
|
|
|
|
|
| 686 |
while cap.isOpened() and frame_no < max_frames:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 687 |
ret, frame = cap.read()
|
| 688 |
-
if not ret:
|
|
|
|
| 689 |
pos = int(cap.get(cv2.CAP_PROP_POS_FRAMES))
|
| 690 |
if pos % step != 0:
|
| 691 |
frame_no += 1
|
|
@@ -696,6 +711,21 @@ with tab_live:
|
|
| 696 |
last_display_t = time.perf_counter()
|
| 697 |
continue
|
| 698 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 699 |
do_process = True
|
| 700 |
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
|
| 701 |
if prev_gray is not None and skip_near_identical:
|
|
@@ -712,15 +742,27 @@ with tab_live:
|
|
| 712 |
frame_no += 1
|
| 713 |
continue
|
| 714 |
|
| 715 |
-
|
| 716 |
-
|
| 717 |
-
|
|
|
|
| 718 |
|
| 719 |
vis = blur_faces_if_needed(frame, face_cascade, st.session_state.privacy_blur)
|
| 720 |
-
rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
| 721 |
|
| 722 |
-
|
| 723 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 724 |
|
| 725 |
for idx, p in enumerate(persons):
|
| 726 |
# motion / base activity
|
|
@@ -732,7 +774,7 @@ with tab_live:
|
|
| 732 |
# identify person
|
| 733 |
name = None
|
| 734 |
for (fbox, emb) in face_pairs:
|
| 735 |
-
fx1, fy1, fx2, fy2 = fbox
|
| 736 |
cx = (fx1+fx2)/2; cy = (fy1+fy2)/2
|
| 737 |
if (p.box[0] <= cx <= p.box[2]) and (p.box[1] <= cy <= p.box[3]):
|
| 738 |
name = recognize_name_from_embedding(emb, st.session_state.face_db)
|
|
@@ -856,7 +898,9 @@ with tab_live:
|
|
| 856 |
last_display_t = time.perf_counter()
|
| 857 |
|
| 858 |
frame_no += 1
|
| 859 |
-
|
|
|
|
|
|
|
| 860 |
|
| 861 |
cap.release(); os.remove(tmp_path); prog.progress(1.0)
|
| 862 |
st.success(f"Tile {tile_index+1}: processed {frame_no} frames")
|
|
|
|
| 5 |
# - (NEW) Per-employee live timers on video overlays: Working and Idle counters (mm:ss)
|
| 6 |
# - (NEW) Employees view shows the same live Working/Idle seconds
|
| 7 |
# - (Patch I–K) Real-time streaming pace: higher default FPS, up to 60 FPS slider, process every frame when possible, time-synced display (no fixed sleeps).
|
| 8 |
+
# - (Patch M) Real-time catch-up reader (clip plays in wall time; drop backlog if behind)
|
| 9 |
+
# - (Patch O) Downscale only for inference (rescale boxes for draw)
|
| 10 |
+
# - (Patch P) Run InsightFace every ~5 processed frames (reuse embeddings in between)
|
| 11 |
+
# - (Patch Q) Update progress bar every ~10 frames
|
| 12 |
|
| 13 |
import os, json, math, tempfile, time
|
| 14 |
from datetime import datetime, date
|
|
|
|
| 687 |
last_display_t = time.perf_counter()
|
| 688 |
target_dt = 1.0 / float(src_fps if src_fps and src_fps > 0 else 25.0)
|
| 689 |
|
| 690 |
+
# Patch M: wall-clock anchor (real-time catch-up)
|
| 691 |
+
wall_t0 = time.perf_counter()
|
| 692 |
+
|
| 693 |
while cap.isOpened() and frame_no < max_frames:
|
| 694 |
+
# ---- Patch M: real-time catch-up reader ----
|
| 695 |
+
elapsed = time.perf_counter() - wall_t0
|
| 696 |
+
target_idx = int(elapsed * (src_fps if src_fps and src_fps > 0 else 25.0))
|
| 697 |
+
cur_idx = int(cap.get(cv2.CAP_PROP_POS_FRAMES))
|
| 698 |
+
if target_idx > cur_idx + 1:
|
| 699 |
+
cap.set(cv2.CAP_PROP_POS_FRAMES, target_idx)
|
| 700 |
+
|
| 701 |
ret, frame = cap.read()
|
| 702 |
+
if not ret:
|
| 703 |
+
break
|
| 704 |
pos = int(cap.get(cv2.CAP_PROP_POS_FRAMES))
|
| 705 |
if pos % step != 0:
|
| 706 |
frame_no += 1
|
|
|
|
| 711 |
last_display_t = time.perf_counter()
|
| 712 |
continue
|
| 713 |
|
| 714 |
+
# ---- Patch O: downscale only for inference; rescale boxes for draw
|
| 715 |
+
H0, W0 = frame.shape[:2]
|
| 716 |
+
infer_scale = 1.0
|
| 717 |
+
if W0 > 1280:
|
| 718 |
+
infer_scale = 1280.0 / W0
|
| 719 |
+
infer_frame = cv2.resize(frame, (int(W0*infer_scale), int(H0*infer_scale)), interpolation=cv2.INTER_AREA)
|
| 720 |
+
else:
|
| 721 |
+
infer_frame = frame
|
| 722 |
+
|
| 723 |
+
def _inv(b):
|
| 724 |
+
if infer_scale == 1.0: return b
|
| 725 |
+
x1,y1,x2,y2 = b
|
| 726 |
+
inv = 1.0 / infer_scale
|
| 727 |
+
return (int(x1*inv), int(y1*inv), int(x2*inv), int(y2*inv))
|
| 728 |
+
|
| 729 |
do_process = True
|
| 730 |
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
|
| 731 |
if prev_gray is not None and skip_near_identical:
|
|
|
|
| 742 |
frame_no += 1
|
| 743 |
continue
|
| 744 |
|
| 745 |
+
# YOLO on infer_frame, rescale boxes back
|
| 746 |
+
dets = run_yolo_on_frame(model, infer_frame, conf_thres)
|
| 747 |
+
persons = [DetBox(d.cls, d.conf, _inv(d.box)) for d in dets if d.cls == "person"]
|
| 748 |
+
phones = [DetBox(d.cls, d.conf, _inv(d.box)) for d in dets if d.cls in ("cell phone","mobile phone","phone")]
|
| 749 |
|
| 750 |
vis = blur_faces_if_needed(frame, face_cascade, st.session_state.privacy_blur)
|
|
|
|
| 751 |
|
| 752 |
+
# ---- Patch P: run InsightFace every ~5 processed frames; reuse otherwise
|
| 753 |
+
if 'last_face_pairs' not in st.session_state: st.session_state.last_face_pairs = []
|
| 754 |
+
if 'last_face_update_pos' not in st.session_state: st.session_state.last_face_update_pos = -999
|
| 755 |
+
if (pos - st.session_state.last_face_update_pos) >= 5:
|
| 756 |
+
rgb_infer = cv2.cvtColor(infer_frame, cv2.COLOR_BGR2RGB)
|
| 757 |
+
face_pairs = get_face_pairs_insight(rgb_infer)
|
| 758 |
+
st.session_state.last_face_pairs = face_pairs
|
| 759 |
+
st.session_state.last_face_update_pos = pos
|
| 760 |
+
else:
|
| 761 |
+
face_pairs = st.session_state.last_face_pairs
|
| 762 |
+
face_boxes_only = [_inv(b) for (b, _) in face_pairs]
|
| 763 |
+
|
| 764 |
+
# (legacy var kept; harmless)
|
| 765 |
+
rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
| 766 |
|
| 767 |
for idx, p in enumerate(persons):
|
| 768 |
# motion / base activity
|
|
|
|
| 774 |
# identify person
|
| 775 |
name = None
|
| 776 |
for (fbox, emb) in face_pairs:
|
| 777 |
+
fx1, fy1, fx2, fy2 = _inv(fbox)
|
| 778 |
cx = (fx1+fx2)/2; cy = (fy1+fy2)/2
|
| 779 |
if (p.box[0] <= cx <= p.box[2]) and (p.box[1] <= cy <= p.box[3]):
|
| 780 |
name = recognize_name_from_embedding(emb, st.session_state.face_db)
|
|
|
|
| 898 |
last_display_t = time.perf_counter()
|
| 899 |
|
| 900 |
frame_no += 1
|
| 901 |
+
# Patch Q: throttle progress updates
|
| 902 |
+
if frame_no % 10 == 0:
|
| 903 |
+
prog.progress(min(1.0, frame_no/max_frames))
|
| 904 |
|
| 905 |
cap.release(); os.remove(tmp_path); prog.progress(1.0)
|
| 906 |
st.success(f"Tile {tile_index+1}: processed {frame_no} frames")
|