Wire detection pipeline: throttle YOLO, save crops, insert sightings, route person/vehicle
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
c77f732ac7
commit
6771923585
@ -21,6 +21,7 @@ from vigilar.camera.recorder import AdaptiveRecorder
|
|||||||
from vigilar.camera.ring_buffer import RingBuffer
|
from vigilar.camera.ring_buffer import RingBuffer
|
||||||
from vigilar.config import CameraConfig, MQTTConfig, PetsConfig, RemoteConfig
|
from vigilar.config import CameraConfig, MQTTConfig, PetsConfig, RemoteConfig
|
||||||
from vigilar.constants import Topics
|
from vigilar.constants import Topics
|
||||||
|
from vigilar.detection.crop_manager import CropManager
|
||||||
from vigilar.detection.pet_id import PetIDClassifier
|
from vigilar.detection.pet_id import PetIDClassifier
|
||||||
from vigilar.detection.wildlife import classify_wildlife_threat
|
from vigilar.detection.wildlife import classify_wildlife_threat
|
||||||
from vigilar.detection.yolo import YOLODetector
|
from vigilar.detection.yolo import YOLODetector
|
||||||
@ -113,6 +114,7 @@ def run_camera_worker(
|
|||||||
# Object detection (YOLOv8 unified detector)
|
# Object detection (YOLOv8 unified detector)
|
||||||
yolo_detector = None
|
yolo_detector = None
|
||||||
pet_classifier = None
|
pet_classifier = None
|
||||||
|
crop_manager = None
|
||||||
if pets_cfg and pets_cfg.enabled:
|
if pets_cfg and pets_cfg.enabled:
|
||||||
yolo_detector = YOLODetector(
|
yolo_detector = YOLODetector(
|
||||||
model_path=pets_cfg.model_path,
|
model_path=pets_cfg.model_path,
|
||||||
@ -124,9 +126,14 @@ def run_camera_worker(
|
|||||||
high_threshold=pets_cfg.pet_id_threshold,
|
high_threshold=pets_cfg.pet_id_threshold,
|
||||||
low_threshold=pets_cfg.pet_id_low_confidence,
|
low_threshold=pets_cfg.pet_id_low_confidence,
|
||||||
)
|
)
|
||||||
|
crop_manager = CropManager(
|
||||||
|
staging_dir=pets_cfg.crop_staging_dir,
|
||||||
|
training_dir=pets_cfg.training_dir,
|
||||||
|
)
|
||||||
|
|
||||||
state = CameraState()
|
state = CameraState()
|
||||||
shutdown = False
|
shutdown = False
|
||||||
|
last_detection_time: float = 0
|
||||||
|
|
||||||
def handle_signal(signum, frame):
|
def handle_signal(signum, frame):
|
||||||
nonlocal shutdown
|
nonlocal shutdown
|
||||||
@ -261,15 +268,24 @@ def run_camera_worker(
|
|||||||
if state.frame_count % idle_skip_factor == 0:
|
if state.frame_count % idle_skip_factor == 0:
|
||||||
recorder.write_frame(frame)
|
recorder.write_frame(frame)
|
||||||
|
|
||||||
# Run object detection on motion frames
|
# Run object detection on motion frames — throttled to 1 inference/second
|
||||||
if state.motion_active and yolo_detector and yolo_detector.is_loaded:
|
if (state.motion_active and yolo_detector and yolo_detector.is_loaded
|
||||||
|
and now - last_detection_time >= 1.0):
|
||||||
|
last_detection_time = now
|
||||||
detections = yolo_detector.detect(frame)
|
detections = yolo_detector.detect(frame)
|
||||||
for det in detections:
|
for det in detections:
|
||||||
category = YOLODetector.classify(det)
|
category = YOLODetector.classify(det)
|
||||||
if category == "domestic_animal":
|
if category == "domestic_animal":
|
||||||
# Crop for pet ID
|
# Crop for pet ID and staging
|
||||||
x, y, w, h = det.bbox
|
x, y, w, h = det.bbox
|
||||||
crop = frame[max(0, y):y + h, max(0, x):x + w]
|
crop = frame[max(0, y):y + h, max(0, x):x + w]
|
||||||
|
|
||||||
|
crop_path = None
|
||||||
|
if crop_manager and crop.size > 0:
|
||||||
|
crop_path = crop_manager.save_staging_crop(
|
||||||
|
crop, species=det.class_name, camera_id=camera_id
|
||||||
|
)
|
||||||
|
|
||||||
pet_result = None
|
pet_result = None
|
||||||
if pet_classifier and pet_classifier.is_loaded and crop.size > 0:
|
if pet_classifier and pet_classifier.is_loaded and crop.size > 0:
|
||||||
pet_result = pet_classifier.identify(crop, species=det.class_name)
|
pet_result = pet_classifier.identify(crop, species=det.class_name)
|
||||||
@ -278,6 +294,7 @@ def run_camera_worker(
|
|||||||
"species": det.class_name,
|
"species": det.class_name,
|
||||||
"confidence": round(det.confidence, 3),
|
"confidence": round(det.confidence, 3),
|
||||||
"camera_location": camera_cfg.location,
|
"camera_location": camera_cfg.location,
|
||||||
|
"crop_path": crop_path,
|
||||||
}
|
}
|
||||||
if pet_result and pet_result.is_identified:
|
if pet_result and pet_result.is_identified:
|
||||||
payload["pet_id"] = pet_result.pet_id
|
payload["pet_id"] = pet_result.pet_id
|
||||||
@ -302,6 +319,21 @@ def run_camera_worker(
|
|||||||
threat_level=threat_level,
|
threat_level=threat_level,
|
||||||
confidence=round(det.confidence, 3),
|
confidence=round(det.confidence, 3),
|
||||||
camera_location=camera_cfg.location,
|
camera_location=camera_cfg.location,
|
||||||
|
crop_path=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
elif category == "person":
|
||||||
|
bus.publish_event(
|
||||||
|
Topics.camera_motion_start(camera_id),
|
||||||
|
detection="person",
|
||||||
|
confidence=round(det.confidence, 3),
|
||||||
|
)
|
||||||
|
|
||||||
|
elif category == "vehicle":
|
||||||
|
bus.publish_event(
|
||||||
|
Topics.camera_motion_start(camera_id),
|
||||||
|
detection="vehicle",
|
||||||
|
confidence=round(det.confidence, 3),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Heartbeat every 10 seconds
|
# Heartbeat every 10 seconds
|
||||||
|
|||||||
@ -101,6 +101,34 @@ class EventProcessor:
|
|||||||
payload=payload,
|
payload=payload,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Insert pet/wildlife sightings
|
||||||
|
if event_type in (
|
||||||
|
EventType.PET_DETECTED, EventType.PET_ESCAPE, EventType.UNKNOWN_ANIMAL
|
||||||
|
):
|
||||||
|
from vigilar.storage.queries import insert_pet_sighting
|
||||||
|
insert_pet_sighting(
|
||||||
|
engine,
|
||||||
|
pet_id=payload.get("pet_id"),
|
||||||
|
species=payload.get("species", "unknown"),
|
||||||
|
camera_id=source_id or "",
|
||||||
|
confidence=payload.get("confidence", 0.0),
|
||||||
|
crop_path=payload.get("crop_path"),
|
||||||
|
event_id=event_id,
|
||||||
|
)
|
||||||
|
elif event_type in (
|
||||||
|
EventType.WILDLIFE_PREDATOR, EventType.WILDLIFE_NUISANCE, EventType.WILDLIFE_PASSIVE
|
||||||
|
):
|
||||||
|
from vigilar.storage.queries import insert_wildlife_sighting
|
||||||
|
insert_wildlife_sighting(
|
||||||
|
engine,
|
||||||
|
species=payload.get("species", "unknown"),
|
||||||
|
threat_level=payload.get("threat_level", "PASSIVE"),
|
||||||
|
camera_id=source_id or "",
|
||||||
|
confidence=payload.get("confidence", 0.0),
|
||||||
|
crop_path=payload.get("crop_path"),
|
||||||
|
event_id=event_id,
|
||||||
|
)
|
||||||
|
|
||||||
# Evaluate rules
|
# Evaluate rules
|
||||||
actions = rule_engine.evaluate(topic, payload, fsm.state)
|
actions = rule_engine.evaluate(topic, payload, fsm.state)
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user