summaryrefslogtreecommitdiff
path: root/Biz/Kidcam/Detector.py
diff options
context:
space:
mode:
Diffstat (limited to 'Biz/Kidcam/Detector.py')
-rwxr-xr-xBiz/Kidcam/Detector.py135
1 files changed, 135 insertions, 0 deletions
diff --git a/Biz/Kidcam/Detector.py b/Biz/Kidcam/Detector.py
new file mode 100755
index 0000000..f2af9ad
--- /dev/null
+++ b/Biz/Kidcam/Detector.py
@@ -0,0 +1,135 @@
+#!/usr/bin/env run.sh
+"""Person detection module for Kidcam using YOLOv8-nano."""
+
+# : out kidcam-detector
+# : dep opencv-python
+# : dep ultralytics
+# : dep numpy
+# : dep pytest
+import cv2 as cv
+import numpy as np
+import sys
+
+
+class PersonDetector:
+ """Detect persons in video frames using YOLOv8-nano."""
+
+ def __init__(
+ self,
+ model_path: str,
+ confidence_threshold: float = 0.5,
+ ) -> None:
+ """Initialize person detector.
+
+ Args:
+ model_path: Path to YOLOv8 model file
+ confidence_threshold: Minimum confidence for detection (0.0-1.0)
+ """
+ try:
+ import ultralytics as ul
+
+ self.model = ul.YOLO(model_path)
+ self.confidence_threshold = confidence_threshold
+ self.last_bbox: tuple[int, int, int, int] | None = None
+ except ImportError as e:
+ msg = "ultralytics library required for YOLOv8"
+ raise ImportError(msg) from e
+
+ def detect_person(self, frame: np.ndarray) -> bool:
+ """Detect if person is present in frame.
+
+ Args:
+ frame: BGR image from OpenCV
+
+ Returns:
+ True if person detected above confidence threshold
+ """
+ results = self.model(frame, verbose=False)
+ self.last_bbox = None
+
+ for result in results:
+ boxes = result.boxes
+ for box in boxes:
+ class_id = int(box.cls[0])
+ confidence = float(box.conf[0])
+
+ if class_id == 0 and confidence >= self.confidence_threshold:
+ x1, y1, x2, y2 = box.xyxy[0].cpu().numpy()
+ self.last_bbox = (int(x1), int(y1), int(x2), int(y2))
+ return True
+
+ return False
+
+ def get_last_detection_bbox(
+ self,
+ ) -> tuple[int, int, int, int] | None:
+ """Get bounding box from last detection.
+
+ Returns:
+ Tuple of (x1, y1, x2, y2) or None if no recent detection
+ """
+ return self.last_bbox
+
+
+def main() -> None:
+ """Test person detection with webcam."""
+ if "test" in sys.argv:
+ test()
+ return
+
+ try:
+ cap = cv.VideoCapture("/dev/video0")
+ if not cap.isOpened():
+ sys.exit(1)
+
+ detector = PersonDetector("yolov8n.pt", confidence_threshold=0.5)
+
+ while True:
+ ret, frame = cap.read()
+ if not ret:
+ break
+
+ if detector.detect_person(frame):
+ bbox = detector.get_last_detection_bbox()
+ if bbox:
+ x1, y1, x2, y2 = bbox
+ cv.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)
+
+ cv.imshow("Kidcam - Person Detection", frame)
+
+ if cv.waitKey(1) & 0xFF == ord("q"):
+ break
+
+ except FileNotFoundError:
+ sys.exit(1)
+ except Exception:
+ sys.exit(1)
+ finally:
+ if "cap" in locals():
+ cap.release()
+ cv.destroyAllWindows()
+
+
+def test() -> None:
+ """Basic unit tests for PersonDetector."""
+ test_frame = np.zeros((480, 640, 3), dtype=np.uint8)
+
+ try:
+ detector = PersonDetector("yolov8n.pt", confidence_threshold=0.5)
+ assert detector.confidence_threshold == 0.5
+ assert detector.get_last_detection_bbox() is None
+
+ result = detector.detect_person(test_frame)
+ assert isinstance(result, bool)
+
+ bbox = detector.get_last_detection_bbox()
+ assert bbox is None or (isinstance(bbox, tuple) and len(bbox) == 4)
+
+ except ImportError:
+ pass
+ except Exception:
+ sys.exit(1)
+
+
+if __name__ == "__main__":
+ main()