3. 摄像头实时目标检测

任务目标

调用本机摄像头捕获实时视频流,使用YOLO模型对每一帧执行目标检测,在画面中绘制检测框与类别标签,并实时显示处理帧率(FPS)。

环境依赖

  • Python 3.8+
  • OpenCV:pip install opencv-python
  • NumPy:pip install numpy(OpenCV依赖,通常自动安装)
  • YOLO权重与配置:从Darknet官网下载 yolov3.weights(约248MB)、yolov3.cfg,以及 coco.names(80类目标名)。将三个文件置于与脚本同目录。

完整实现代码

import cv2
import numpy as np
import time

# ---------- 加载模型 ----------
net = cv2.dnn.readNet("yolov3.weights", "yolov3.cfg")
layer_names = net.getLayerNames()
output_layers = [layer_names[i - 1] for i in net.getUnconnectedOutLayers()]

with open("coco.names", "r") as f:
    classes = [line.strip() for line in f.readlines()]

# ---------- 摄像头初始化 ----------
cap = cv2.VideoCapture(0)  # 0 为默认摄像头
if not cap.isOpened():
    print("错误:无法打开摄像头")
    exit()

# ---------- 实时检测循环 ----------
prev_time = time.time()
frame_count = 0
fps = 0

while True:
    ret, frame = cap.read()
    if not ret:
        print("错误:无法读取视频帧")
        break

    height, width = frame.shape[:2]

    # YOLO 前向推理
    blob = cv2.dnn.blobFromImage(frame, 1/255.0, (416, 416), swapRB=True, crop=False)
    net.setInput(blob)
    outputs = net.forward(output_layers)

    # 解析检测结果
    boxes, confidences, class_ids = [], [], []
    for output in outputs:
        for detection in output:
            scores = detection[5:]
            class_id = np.argmax(scores)
            confidence = scores[class_id]
            if confidence > 0.5:  # 置信度阈值
                center_x = int(detection[0] * width)
                center_y = int(detection[1] * height)
                w = int(detection[2] * width)
                h = int(detection[3] * height)
                x = int(center_x - w / 2)
                y = int(center_y - h / 2)
                boxes.append([x, y, w, h])
                confidences.append(float(confidence))
                class_ids.append(class_id)

    # NMS 去除重叠框
    indexes = cv2.dnn.NMSBoxes(boxes, confidences, 0.5, 0.4)

    # 绘制检测框
    for i in range(len(boxes)):
        if i in indexes:
            x, y, w, h = boxes[i]
            label = classes[class_ids[i]]
            cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
            cv2.putText(frame, f"{label} {confidences[i]:.2f}", (x, y - 10),
                        cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)

    # 计算并显示 FPS
    frame_count += 1
    curr_time = time.time()
    if curr_time - prev_time >= 1.0:  # 每秒更新一次
        fps = frame_count / (curr_time - prev_time)
        frame_count = 0
        prev_time = curr_time

    cv2.putText(frame, f"FPS: {fps:.1f}", (10, 30),
                cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 0, 255), 2)

    # 显示画面
    cv2.imshow("YOLO Real-time Detection", frame)

    # 按 'q' 退出
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

# ---------- 释放资源 ----------
cap.release()
cv2.destroyAllWindows()

运行方式

  1. 将 yolov3.weights、yolov3.cfg、coco.names 与上述脚本置于同一目录。
  2. 运行命令:python realtime_detection.py
  3. 程序启动后,摄像头画面窗口弹出,检测框与FPS实时叠加显示。

验证方法与通过标准

  • 功能验证:将任意物体(如人、手机、杯子)置于摄像头前,画面中应出现绿色检测框,框上方显示类别名称与置信度(如 person 0.85)。
  • FPS验证:画面左上角显示红色FPS数值,正常应大于5(取决于CPU/GPU性能)。
  • 退出验证:按下键盘 q 键,窗口关闭,程序正常结束,终端无报错。

常见问题

  • 摄像头打不开:检查设备索引,若默认摄像头不是0,改为 cv2.VideoCapture(1) 尝试。
  • 检测速度极慢:可改用 yolov3-tiny.weights 与 yolov3-tiny.cfg(更小更快),或降低输入尺寸 (320, 320)。
  • 误检/漏检:调整置信度阈值(当前0.5)与NMS阈值(当前0.4),阈值越低检测越多但误检也增多。