feat(edge): main-loop wiring (run_once) + pipeline factory
This commit is contained in:
parent
e74cc7f898
commit
b621627863
2 changed files with 103 additions and 0 deletions
55
edge/app.py
Normal file
55
edge/app.py
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
from edge.config import Config
|
||||||
|
from edge.tracking import IouTracker
|
||||||
|
from edge.smoothing import KeypointSmoother
|
||||||
|
from edge.detectors.fall import FallDetector
|
||||||
|
from edge.detectors.immobility import ImmobilityWatchdog
|
||||||
|
from edge.detectors.ghost import GhostIncidentDetector
|
||||||
|
from edge.detectors.zone import ZoneIntrusionDetector
|
||||||
|
from edge.engine import EventEngine
|
||||||
|
from edge.snapshot import RingBuffer, FaceBlurrer, Snapshotter
|
||||||
|
from edge.models import Person, BBox
|
||||||
|
|
||||||
|
|
||||||
|
def _head_boxes(persons: list) -> list:
|
||||||
|
# 배경인물 머리영역 근사: 코 주변 작은 박스 (MVP; 실제는 얼굴 검출 포트로 교체)
|
||||||
|
boxes = []
|
||||||
|
for p in persons:
|
||||||
|
nose = p.keypoints[0]
|
||||||
|
boxes.append(BBox(nose.x - 20, nose.y - 25, nose.x + 20, nose.y + 15))
|
||||||
|
return boxes
|
||||||
|
|
||||||
|
|
||||||
|
def run_once(source, pose, tracker, smoother, engine, ring, snapshotter, sink, now_fn):
|
||||||
|
ok, frame = source.read()
|
||||||
|
if not ok:
|
||||||
|
return []
|
||||||
|
now = now_fn()
|
||||||
|
persons = pose.infer(frame)
|
||||||
|
smoothed = [Person(p.bbox, smoother.smooth(p.keypoints, now)) for p in persons]
|
||||||
|
tracks = tracker.update(smoothed, now)
|
||||||
|
lost = tracker.lost()
|
||||||
|
ring.push(now, frame)
|
||||||
|
events = engine.process(tracks, lost, now)
|
||||||
|
if events:
|
||||||
|
snaps = snapshotter.capture([frame], [_head_boxes(persons)])
|
||||||
|
for e in events:
|
||||||
|
sink.try_send(e, snaps)
|
||||||
|
return events
|
||||||
|
|
||||||
|
|
||||||
|
def build_pipeline(config: Config, source, pose) -> dict:
|
||||||
|
return {
|
||||||
|
"source": source,
|
||||||
|
"pose": pose,
|
||||||
|
"tracker": IouTracker(),
|
||||||
|
"smoother": KeypointSmoother(),
|
||||||
|
"engine": EventEngine(
|
||||||
|
FallDetector(config.fall),
|
||||||
|
ImmobilityWatchdog(config.immobility),
|
||||||
|
GhostIncidentDetector(config.ghost),
|
||||||
|
ZoneIntrusionDetector([], config.zone),
|
||||||
|
config.engine,
|
||||||
|
),
|
||||||
|
"ring": RingBuffer(),
|
||||||
|
"snapshotter": Snapshotter(FaceBlurrer()),
|
||||||
|
}
|
||||||
48
tests/test_app.py
Normal file
48
tests/test_app.py
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
import numpy as np
|
||||||
|
from edge.models import Person, BBox, Keypoint
|
||||||
|
from edge.tracking import IouTracker
|
||||||
|
from edge.smoothing import KeypointSmoother
|
||||||
|
from edge.detectors.fall import FallDetector, FallConfig
|
||||||
|
from edge.detectors.immobility import ImmobilityWatchdog, ImmobilityConfig
|
||||||
|
from edge.detectors.ghost import GhostIncidentDetector, GhostConfig
|
||||||
|
from edge.detectors.zone import ZoneIntrusionDetector, ZoneConfig
|
||||||
|
from edge.engine import EventEngine, EngineConfig
|
||||||
|
from edge.snapshot import RingBuffer, FaceBlurrer, Snapshotter
|
||||||
|
from edge.events import EventType
|
||||||
|
from edge.app import run_once
|
||||||
|
|
||||||
|
def _lying_person():
|
||||||
|
kps = [Keypoint(150, 300, 0.9) for _ in range(17)]
|
||||||
|
kps[5] = Keypoint(100, 300, 0.9); kps[6] = Keypoint(100, 310, 0.9)
|
||||||
|
kps[11] = Keypoint(200, 300, 0.9); kps[12] = Keypoint(200, 310, 0.9)
|
||||||
|
kps[0] = Keypoint(90, 320, 0.9)
|
||||||
|
return Person(BBox(90, 290, 210, 320), kps)
|
||||||
|
|
||||||
|
class _Source:
|
||||||
|
def read(self): return True, np.full((360, 640, 3), 128, np.uint8)
|
||||||
|
|
||||||
|
class _Pose:
|
||||||
|
def infer(self, frame): return [_lying_person()]
|
||||||
|
|
||||||
|
class _Sink:
|
||||||
|
def __init__(self): self.events = []
|
||||||
|
def try_send(self, event, snapshots):
|
||||||
|
self.events.append(event); return True
|
||||||
|
|
||||||
|
def test_run_once_pipeline_emits_fall_after_prone():
|
||||||
|
src, pose = _Source(), _Pose()
|
||||||
|
tracker = IouTracker()
|
||||||
|
smoother = KeypointSmoother()
|
||||||
|
engine = EventEngine(
|
||||||
|
FallDetector(FallConfig(prone_confirm_s=0.5)),
|
||||||
|
ImmobilityWatchdog(ImmobilityConfig(immobile_s=999)),
|
||||||
|
GhostIncidentDetector(GhostConfig()),
|
||||||
|
ZoneIntrusionDetector([], ZoneConfig()),
|
||||||
|
EngineConfig(camera_id="cam-01"),
|
||||||
|
)
|
||||||
|
ring = RingBuffer(); snap = Snapshotter(FaceBlurrer()); sink = _Sink()
|
||||||
|
times = iter([0.0, 0.6])
|
||||||
|
def now_fn(): return next(times)
|
||||||
|
run_once(src, pose, tracker, smoother, engine, ring, snap, sink, now_fn)
|
||||||
|
run_once(src, pose, tracker, smoother, engine, ring, snap, sink, now_fn)
|
||||||
|
assert any(e.event_type == EventType.FALL for e in sink.events)
|
||||||
Loading…
Reference in a new issue