updates
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
"""Measure a built-in OpenCV full-body detector on the Pi CSI camera.
|
||||
|
||||
This is an offline baseline: it logs detections but does not drive the eyes.
|
||||
It will miss people whose full bodies are outside the frame.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import statistics
|
||||
import time
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parents[2]
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--duration", type=float, default=30.0, help="seconds to sample")
|
||||
parser.add_argument("--rotation", type=int, choices=(0, 180), default=0)
|
||||
parser.add_argument("--width", type=int, default=320)
|
||||
parser.add_argument("--height", type=int, default=240)
|
||||
parser.add_argument("--log", type=Path, help="JSONL path (default: project diagnostics)")
|
||||
args = parser.parse_args()
|
||||
if args.duration <= 0 or args.width < 128 or args.height < 128:
|
||||
parser.error("duration must be positive and frame dimensions at least 128 pixels")
|
||||
|
||||
import cv2
|
||||
from libcamera import Transform
|
||||
from picamera2 import Picamera2
|
||||
|
||||
cv2.setNumThreads(2)
|
||||
hog = cv2.HOGDescriptor()
|
||||
hog.setSVMDetector(cv2.HOGDescriptor_getDefaultPeopleDetector())
|
||||
transform = Transform(hflip=1, vflip=1) if args.rotation == 180 else Transform()
|
||||
camera = Picamera2()
|
||||
config = camera.create_video_configuration(
|
||||
main={"size": (args.width, args.height), "format": "RGB888"},
|
||||
transform=transform,
|
||||
buffer_count=3,
|
||||
)
|
||||
camera.configure(config)
|
||||
|
||||
stamp = datetime.now().strftime("%Y%m%d-%H%M%S")
|
||||
log_path = args.log or PROJECT_ROOT / "diagnostics" / f"pi_hog_{stamp}.jsonl"
|
||||
log_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
detection_ms: list[float] = []
|
||||
detected_frames = 0
|
||||
frames = 0
|
||||
start = 0.0
|
||||
try:
|
||||
camera.start()
|
||||
time.sleep(1.0) # Let exposure settle before measuring.
|
||||
start = time.monotonic()
|
||||
with log_path.open("w", encoding="utf-8") as log:
|
||||
while time.monotonic() - start < args.duration:
|
||||
frame = camera.capture_array("main")
|
||||
detect_start = time.monotonic()
|
||||
boxes, weights = hog.detectMultiScale(
|
||||
frame,
|
||||
hitThreshold=0.0,
|
||||
winStride=(8, 8),
|
||||
padding=(8, 8),
|
||||
scale=1.05,
|
||||
groupThreshold=1,
|
||||
)
|
||||
elapsed_ms = (time.monotonic() - detect_start) * 1000
|
||||
detections = [
|
||||
{"x": int(x), "y": int(y), "width": int(w), "height": int(h),
|
||||
"score": float(score)}
|
||||
for (x, y, w, h), score in zip(boxes, weights)
|
||||
]
|
||||
frames += 1
|
||||
detected_frames += bool(detections)
|
||||
detection_ms.append(elapsed_ms)
|
||||
log.write(json.dumps({
|
||||
"time": datetime.now().astimezone().isoformat(),
|
||||
"frame": frames,
|
||||
"detect_ms": round(elapsed_ms, 1),
|
||||
"detections": detections,
|
||||
}) + "\n")
|
||||
if frames % 10 == 0:
|
||||
print(f"frames={frames} detected={detected_frames} "
|
||||
f"last_detect_ms={elapsed_ms:.0f}", flush=True)
|
||||
finally:
|
||||
camera.stop()
|
||||
camera.close()
|
||||
|
||||
total = time.monotonic() - start
|
||||
print(json.dumps({
|
||||
"frames": frames,
|
||||
"seconds": round(total, 2),
|
||||
"fps": round(frames / total, 2) if total else 0,
|
||||
"frames_with_person": detected_frames,
|
||||
"median_detect_ms": round(statistics.median(detection_ms), 1) if detection_ms else None,
|
||||
"log": str(log_path),
|
||||
}, indent=2))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user