【机器视觉】人物安全距离监测

目标

实现的功能:

  1. 使用YOLOv8模型检测视频中的人物
  2. 计算人物之间的距离(基于检测框中心点的欧氏距离)
  3. 当人物之间距离小于设定的安全阈值时,标记为红色并绘制连接线
  4. 显示人物之间的实时距离值
  5. 显示当前的安全距离阈值

程序特点:

  • 从摄像头实时获取视频流进行处理
  • 使用绿色框标记安全距离内的人物,红色框标记距离过近的人物
  • 通过红色线条连接距离过近的人物,并显示距离数值
  • 按'q'键可退出程序
    测试结果: 程序运行正常,可以成功检测人物并计算距离,当人物距离过近时会进行适当标记。

看效果

实现思路

  1. 创建新的Python文件yoloCrowdDistance.py
  2. 复制yoloUltralytics.py的基本结构
  3. 添加距离计算功能
  4. 添加过近人物标记功能
  5. 测试程序

贴源码

python 复制代码
import cv2
from ultralytics import YOLO
import numpy as np
import os
import platform
from PIL import ImageFont, ImageDraw, Image

def draw_chinese_text(image, text, position, font_size, color):
    """
    使用PIL在图像上绘制中文文本
    """
    try:
        # 将OpenCV图像转换为PIL图像
        pil_image = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
        draw = ImageDraw.Draw(pil_image)
        
        # 选择字体
        font_path = None
        system = platform.system()
        if system == "Windows":
            # Windows系统默认字体路径(使用原始字符串)
            font_path = r"C:\Windows\Fonts\simhei.ttf"  # 黑体
            if not os.path.exists(font_path):
                font_path = r"C:\Windows\Fonts\simsun.ttc"  # 宋体
                if not os.path.exists(font_path):
                    font_path = r"C:\Windows\Fonts\msyh.ttc"  # 微软雅黑
        elif system == "Darwin":  # macOS
            font_path = "/Library/Fonts/PingFang.ttc"
        elif system == "Linux":
            font_path = "/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc"
        
        if font_path and os.path.exists(font_path):
            font = ImageFont.truetype(font_path, font_size)
            print(f"✅ 使用字体: {font_path}")
        else:
            font = ImageFont.load_default()
            print("⚠️ 使用默认字体,中文可能无法正常显示")
        
        # 绘制文本
        draw.text(position, text, font=font, fill=color)
        
        # 将PIL图像转换回OpenCV图像
        return cv2.cvtColor(np.array(pil_image), cv2.COLOR_RGB2BGR)
    except Exception as e:
        print(f"⚠️ 绘制中文文本时出错: {e}")
        # 如果绘制中文失败,使用OpenCV的putText(可能显示为方框)
        cv2.putText(image, text, position, cv2.FONT_HERSHEY_SIMPLEX, font_size/20, color, 2)
        return image

def calculate_distance(box1, box2):
    """
    计算两个检测框之间的距离(使用中心点之间的欧氏距离)
    box1, box2: 检测框坐标 (x1, y1, x2, y2)
    """
    # 计算两个检测框的中心点
    center1_x = (box1[0] + box1[2]) / 2
    center1_y = (box1[1] + box1[3]) / 2
    center2_x = (box2[0] + box2[2]) / 2
    center2_y = (box2[1] + box2[3]) / 2
    
    # 计算欧氏距离
    distance = np.sqrt((center1_x - center2_x) ** 2 + (center1_y - center2_y) ** 2)
    return distance

def main():
    print("开始加载YOLO模型进行人群距离监测...")

    try:
        # 加载YOLOv8模型
        model = YOLO('yolov8n.pt')
        print("✅ 模型加载成功!")

        # 打开摄像头
        cap = cv2.VideoCapture(0)
        if not cap.isOpened():
            print("❌ 无法打开摄像头")
            return

        print("🎥 摄像头已开启,按 'q' 键退出...")
        # 设定安全距离阈值(像素单位)
        safe_distance = 100

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

            # 使用模型进行预测
            results = model(frame)
            person_boxes = []

            # 遍历每一帧的检测结果
            for r in results:
                # 获取检测框的坐标、置信度和类别ID
                boxes = r.boxes
                if boxes is not None and len(boxes) > 0:
                    for box in boxes:
                        # 获取坐标
                        x1, y1, x2, y2 = box.xyxy[0].tolist()
                        conf = box.conf[0].item()  # 置信度
                        cls_id = int(box.cls[0].item())  # 类别ID
                        class_name = model.names[cls_id]  # 类别名称

                        # 只关注人物类别(COCO数据集中类别ID为0)
                        if cls_id == 0 and conf > 0.5:
                            # 保存人物检测框
                            person_boxes.append((x1, y1, x2, y2))
                            # 绘制人物边界框
                            cv2.rectangle(frame, (int(x1), int(y1)), (int(x2), int(y2)), (0, 255, 0), 2)
                            # 绘制标签
                            label = f"{class_name} {conf:.2f}"
                            cv2.putText(frame, label, (int(x1), int(y1) - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5,
                                        (0, 255, 0), 2)

            # 检测过近距离的人物对
            if len(person_boxes) > 1:
                for i in range(len(person_boxes)):
                    for j in range(i + 1, len(person_boxes)):
                        box1 = person_boxes[i]
                        box2 = person_boxes[j]
                        distance = calculate_distance(box1, box2)
                        
                        # 如果距离小于安全阈值,标记为危险
                        if distance < safe_distance:
                            # 绘制人物框为红色
                            cv2.rectangle(frame, (int(box1[0]), int(box1[1])), (int(box1[2]), int(box1[3])), (0, 0, 255), 2)
                            cv2.rectangle(frame, (int(box2[0]), int(box2[1])), (int(box2[2]), int(box2[3])), (0, 0, 255), 2)
                            
                            # 绘制连接线
                            center1_x = int((box1[0] + box1[2]) / 2)
                            center1_y = int((box1[1] + box1[3]) / 2)
                            center2_x = int((box2[0] + box2[2]) / 2)
                            center2_y = int((box2[1] + box2[3]) / 2)
                            cv2.line(frame, (center1_x, center1_y), (center2_x, center2_y), (0, 0, 255), 2)
                            
                            # 显示距离
                            mid_x = int((center1_x + center2_x) / 2)
                            mid_y = int((center1_y + center2_y) / 2)
                            cv2.putText(frame, f"{distance:.1f}px", (mid_x, mid_y), cv2.FONT_HERSHEY_SIMPLEX, 0.5,
                                        (0, 0, 255), 2)

            # 显示安全距离阈值信息
            frame = draw_chinese_text(frame, f"安全距离阈值: {safe_distance}px", (10, 30), 20, (255, 255, 255))
            
            # 显示视频帧
            cv2.imshow('YOLO 人群距离监测', frame)

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

        # 释放资源
        cap.release()
        cv2.destroyAllWindows()
        print("✅ 程序正常退出")

    except Exception as e:
        print(f"❌ 程序执行出错: {e}")


if __name__ == "__main__":
    main()
相关推荐
应用市场14 分钟前
用本机跑的开源音乐模型,给安卓小游戏做 20 秒无缝循环背景音乐
android·python·游戏开发·soundpool·音频处理
小lo想吃棒棒糖14 分钟前
把 启元机器人Q1 的训练搬到昇腾 910B:从 MuJoCo 到 PPO 的一次完整迁移
人工智能·python·机器人·昇腾·具身智能
weixin_3077791322 分钟前
RAG 级联失效分析——面试中的错误归因艺术
人工智能·python·面试·职场和发展
AI创界者24 分钟前
Python 进阶实战(十八):API 网关安全与异步鉴权中间件 —— 基于 AsyncIO 的 JWT 零拷贝验权与动态黑名单过滤
人工智能·python·aigc
zwc321043205532 分钟前
Day 5:threshold与inRange阈值分割
c++·图像处理·人工智能·opencv·算法·计算机视觉
前端精髓33 分钟前
学会调用Qwen3.7-通用文本向量
python
第九序言T9 小时前
Python 数据处理进阶(十八)批量处理同结构文件:一个文件夹一次跑完
开发语言·python·自动化·pandas·数据处理
Java后端的Ai之路9 小时前
CAS并发控制-从CPU指令到企业级乐观锁
vue.js·人工智能·python·cas·乐观锁
qq_426003969 小时前
UI自动化高频次登录login限流解决方案
python·ui·自动化
Sakura_j_h_10 小时前
EasyEDA PCB 快速设计Skills
c++·python·github