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

目标

实现的功能:

  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()
相关推荐
何以解忧,唯有..1 小时前
Pydantic 介绍与使用:Python 数据校验的现代方案
数据库·python·microsoft
又幸福了哥2 小时前
Python入门到高级(知识点七)
python
又幸福了哥2 小时前
Python入门到高级(知识点六)
python
维克兜率天2 小时前
【维克】动量指标家族:RSI、ROC、CCI、Momentum全面解析
python·算法
XZ-0700013 小时前
week2-1-可视化
开发语言·python
Java后端的Ai之路3 小时前
14、Python - 责任链模式
服务器·开发语言·人工智能·python·责任链模式
张人玉4 小时前
基于 Python 机器学习 与 PyQt5 桌面框架开发的可视化分析系统——基于大数据的网上购物消费行为分析可视化大屏
python·qt·机器学习·echarts·three.js
一位正在转型AI全栈的前端工程师4 小时前
AI 全栈学习之旅 -Week 5:RAG 知识库问答系统:从零到生产级部署的全栈实践总结
前端·python
2601_962381864 小时前
Python办公联动:Excel数据一键自动生成PPT图表
python·数据分析·自动化·excel·ppt
东莞市永盛电气4 小时前
三相208V变380V变压器,出海工业设备供电适配方案
python·单片机·嵌入式硬件