"""Burning the counting overlay into the frame, as an MJPEG. Split out of `live_count.py` to keep it inside the 400-line limit. This is the fallback preview, used for archive files; a WebRTC session draws the same geometry on a canvas in the browser instead (REQ-177). """ import cv2 def render(session, frame, detections, ignored, counter): height, width = frame.shape[:2] # Shade what the region excludes. Without this the neighbouring truck's # sacks simply vanish from the overlay, and "are they being ignored?" # looks identical to "is the model missing them?". if session.line_x_start > 0 or session.line_x_end < width: shade = frame.copy() if session.line_x_start > 0: cv2.rectangle(shade, (0, 0), (session.line_x_start, height), (0, 0, 0), -1) if session.line_x_end < width: cv2.rectangle(shade, (session.line_x_end, 0), (width, height), (0, 0, 0), -1) cv2.addWeighted(shade, 0.55, frame, 0.45, 0, frame) # Ignored detections stay visible, in grey, so the region can be judged. for det in ignored: x1, y1, x2, y2 = (int(v) for v in det.bbox) cv2.rectangle(frame, (x1, y1), (x2, y2), (130, 130, 130), 1) for edge in (session.line_x_start, session.line_x_end): if 0 < edge < width: cv2.line(frame, (edge, 0), (edge, height), (255, 0, 255), 2) cv2.putText(frame, "IGNORED", (max(4, session.line_x_start - 92), height - 14), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 255), 1, cv2.LINE_AA) cv2.putText(frame, "IGNORED", (min(width - 88, session.line_x_end + 8), height - 14), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 255), 1, cv2.LINE_AA) for det in detections: x1, y1, x2, y2 = (int(v) for v in det.bbox) counted = bool(counter.counted_tracks.get(det.track_id)) colour = (74, 222, 128) if counted else (248, 191, 113) cv2.rectangle(frame, (x1, y1), (x2, y2), colour, 2) cv2.putText(frame, f"#{det.track_id} {det.confidence:.2f}", (x1, max(14, y1 - 6)), cv2.FONT_HERSHEY_SIMPLEX, 0.45, colour, 1, cv2.LINE_AA) cv2.line(frame, (session.line_x_start, session.line_y), (session.line_x_end, session.line_y), (0, 255, 255), 2) for edge in (session.line_y - session.margin, session.line_y + session.margin): cv2.line(frame, (session.line_x_start, edge), (session.line_x_end, edge), (0, 160, 160), 1) panel = f"IN {session.loading} OUT {session.unloading} NET {session.loading - session.unloading}" cv2.rectangle(frame, (12, 12), (12 + 9 * len(panel) + 20, 84), (0, 0, 0), -1) cv2.putText(frame, panel, (24, 46), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (74, 222, 128), 2, cv2.LINE_AA) cv2.putText(frame, f"{session.fps:.1f} fps {session.tracked} tracked {session.ignored} ignored", (24, 72), cv2.FONT_HERSHEY_SIMPLEX, 0.55, (200, 200, 200), 1, cv2.LINE_AA) ok, buffer = cv2.imencode(".jpg", frame, [cv2.IMWRITE_JPEG_QUALITY, 75]) return buffer.tobytes() if ok else None