Spaces:
Running
Running
File size: 1,788 Bytes
8ce2c44 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | """Frame helpers shared by detectors and any app that touches the camera."""
from __future__ import annotations
import base64
from typing import Optional
import cv2
import numpy as np
def region_of_interest(frame: np.ndarray, left: float = 0.0, right: float = 1.0,
top: float = 0.0, bottom: float = 1.0) -> np.ndarray:
"""Crop a fractional ROI (0..1 of width/height). Defaults to the whole frame."""
h, w = frame.shape[:2]
x0, x1 = int(w * left), int(w * right)
y0, y1 = int(h * top), int(h * bottom)
return frame[y0:y1, x0:x1]
def to_blurred_gray(frame: np.ndarray, blur: int = 21) -> np.ndarray:
"""Grayscale + Gaussian blur — the standard prep for frame-diff motion."""
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
if blur and blur > 1:
k = blur if blur % 2 == 1 else blur + 1 # kernel must be odd
gray = cv2.GaussianBlur(gray, (k, k), 0)
return gray
def motion_fraction(prev_gray: np.ndarray, gray: np.ndarray, threshold: int = 30) -> float:
"""Fraction of pixels that changed between two prepped gray frames (0..1)."""
diff = cv2.absdiff(prev_gray, gray)
thresh = cv2.threshold(diff, threshold, 255, cv2.THRESH_BINARY)[1]
return float(np.count_nonzero(thresh)) / float(thresh.size)
def encode_jpeg_b64(frame: np.ndarray, max_width: int = 640, quality: int = 70) -> Optional[str]:
"""Downscale + JPEG-encode a frame to a base64 string (for VLM payloads)."""
h, w = frame.shape[:2]
if w > max_width:
scale = max_width / w
frame = cv2.resize(frame, (max_width, int(h * scale)))
ok, buf = cv2.imencode(".jpg", frame, [cv2.IMWRITE_JPEG_QUALITY, quality])
if not ok:
return None
return base64.b64encode(buf.tobytes()).decode("ascii")
|