feat(edge): RingBuffer + face blur + JPEG snapshotter

This commit is contained in:
유창욱 2026-07-11 21:21:57 +09:00
parent e801c7d057
commit 5da838ef0b
2 changed files with 72 additions and 0 deletions

45
edge/snapshot.py Normal file
View file

@ -0,0 +1,45 @@
from collections import deque
import cv2
from edge.models import BBox
class RingBuffer:
def __init__(self, maxlen: int = 90):
self.frames: deque = deque(maxlen=maxlen)
def push(self, t: float, frame) -> None:
self.frames.append((t, frame))
def window(self, pre: int, post: int, index: int) -> list:
lo = max(0, index - pre)
hi = min(len(self.frames), index + post + 1)
return [f for (_t, f) in list(self.frames)[lo:hi]]
class FaceBlurrer:
def blur(self, frame, head_boxes: list[BBox]):
for b in head_boxes:
x1, y1 = int(max(0, b.x1)), int(max(0, b.y1))
x2, y2 = int(min(frame.shape[1], b.x2)), int(min(frame.shape[0], b.y2))
if x2 > x1 and y2 > y1:
roi = frame[y1:y2, x1:x2]
frame[y1:y2, x1:x2] = cv2.GaussianBlur(roi, (0, 0), sigmaX=12)
return frame
class Snapshotter:
def __init__(self, blurrer: FaceBlurrer, quality: int = 80):
self.blurrer = blurrer
self.quality = quality
def capture(self, frames: list, head_boxes_per_frame: list) -> list[bytes]:
out: list[bytes] = []
for frame, heads in list(zip(frames, head_boxes_per_frame))[:3]:
blurred = self.blurrer.blur(frame.copy(), heads)
ok, buf = cv2.imencode(".jpg", blurred,
[cv2.IMWRITE_JPEG_QUALITY, self.quality])
if ok:
out.append(buf.tobytes())
return out

27
tests/test_snapshot.py Normal file
View file

@ -0,0 +1,27 @@
import numpy as np
from edge.models import BBox
from edge.snapshot import RingBuffer, FaceBlurrer, Snapshotter
def _frame(val=128):
return np.full((64, 64, 3), val, dtype=np.uint8)
def test_ringbuffer_keeps_recent():
rb = RingBuffer(maxlen=3)
for i in range(5):
rb.push(float(i), _frame(i))
assert len(rb.frames) == 3
assert rb.frames[-1][0] == 4.0
def test_face_blur_changes_region():
fb = FaceBlurrer()
f = _frame(0)
f[0:10, 0:10] = 255
out = fb.blur(f.copy(), [BBox(0, 0, 10, 10)])
assert out is not None
assert out.shape == f.shape
def test_snapshotter_returns_jpeg_bytes():
snap = Snapshotter(FaceBlurrer())
imgs = snap.capture([_frame(100), _frame(120)], [[], []])
assert 1 <= len(imgs) <= 3
assert imgs[0][:2] == b"\xff\xd8" # JPEG magic