【Python】使用YOLO8识别视频中的车与人物

文章目录

与我之前写的这篇文章呼应:【Web】使用Vue3开发3D游戏(九)纹理视觉效果

一、效果

1.1、原视频

路况

1.2、效果视频

路况识别效果

1.3、静图效果

二、简介

在行车记录仪路况分析、道路障碍物检测、智能驾驶仿真场景中,行人、车辆识别是最基础也是最核心的能力。本文基于 YOLOv8 实现本地 MP4 视频实时检测,精准区分行人、轿车、摩托车、货车、巴士等交通参与者,同时适配 Vue3 + PlayCanvas 3D 前端 数据协议,输出标准化障碍物类型,可直接对接 3D 场景做障碍物映射,非常适合做仿真项目、机器狗路况感知、Web3D 可视化开发。

三、环境准备

3.1、安装依赖

运行

bash 复制代码
pip install ultralytics opencv-python websockets
  • ultralytics:YOLOv8 官方库,一行加载模型、推理
  • opencv-python:视频读取、画面绘制、编码推流
  • websockets:向前端实时推送视频流 + 障碍物数据

3.2、YOLOv8 类别说明

YOLOv8 预训练模型自带 80 个类别,路况常用关键类别:

类别 ID 对应物体
0 行人 person
1 自行车
2 轿车car
3 摩托车
5 巴士公交车
7 卡车货车

业务规则:所有车辆统一归类为 car 类型 = 1,行人固定 type=0,完美适配你 Vue3 前端 mapTypeToModel 映射规则:

js 复制代码
const typeMap = {
  0: 'person',
  1: 'car'
};

四、核心解析

  • 只检测行人 + 全部车辆,过滤无关物体
  • 绘制矩形框标记目标
  • 适配前端协议:人type=0,所有车type=1
  • WebSocket 同时推送 视频画面 + 3D 障碍物坐标
  • 优化帧率、控制 CPU 占用,不卡顿服务器
  • 固定端口 9090,兼容你现有 Vue 前端连接

4.1、检测目标过滤

只保留路况有效目标:

py 复制代码
if cls_id not in [0, 1, 2, 3, 5, 7]:
    continue

排除猫、狗、椅子等无关物体,减少无效计算、降低 CPU。

4.2、类型映射(适配你 Vue 前端)

py 复制代码
if cls_id == 0:
    obj_type = 0   # 行人 → person
else:
    obj_type = 1   # 所有车辆 → car

完美对应前端:

js 复制代码
const typeMap = {0:'person', 1:'car'}

4.3、3D 坐标映射

根据画面中目标左右位置归一化,映射为 3D 场景左右偏移,前后固定,实现:

视频里人 / 车往左 → 3D 障碍物往左

视频里人 / 车往右 → 3D 障碍物往右

4.4、性能优化

  • 压缩视频分辨率推理,大幅降 CPU
  • 控制检测间隔,不无限死循环占用资源
  • 异常连接捕获,不抛崩溃错误
  • 视频播放完毕自动循环,无需手动重启

五、使用方法

把行车记录仪路况视频命名为 test.mp4,放在同目录

安装依赖后直接运行:

bash 复制代码
python3 mp4_websocket_stream.py

Vue3 前端直接连接 ws://ip:9090,自动接收:

实时摄像头画面

行人 / 车辆 3D 障碍物数据,自动渲染对应模型

适配场景

  • Web3D 智能驾驶仿真
  • 机器狗 / 无人车路况感知可视化
  • 行车记录仪视频 AI 分析
  • PlayCanvas + Vue3 前后端联合开发
  • 路口行人车辆检测统计

六、完整源码

py 复制代码
import asyncio
import websockets
import json
import base64
import cv2
import threading
import time
from ultralytics import YOLO

# ==================== 配置 ====================
VIDEO_PATH      = "./test.mp4"
WEBSOCKET_HOST  = "0.0.0.0"
WEBSOCKET_PORT  = 9090
JPEG_QUALITY    = 60
MOVE_SCALE      = 2.5
DETECT_INTERVAL = 0.05
# ==============================================

latest_frame_bytes = None
obstacle_list = []

# 加载YOLO模型
model = YOLO("yolov8n.pt")

def video_loop():
    global latest_frame_bytes, obstacle_list
    cap = cv2.VideoCapture(VIDEO_PATH)
    fps = cap.get(cv2.CAP_PROP_FPS) or 25

    while True:
        ret, frame = cap.read()
        if not ret:
            cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
            continue

        # 降低分辨率,省CPU
        small_frame = cv2.resize(frame, (640, 360))
        h, w = small_frame.shape[:2]
        obs = []

        # YOLO推理
        results = model(small_frame, conf=0.5, verbose=False, imgsz=320)
        
        for result in results:
            for box in result.boxes:
                cls_id = int(box.cls[0])

                # 只检测 人 + 车
                if cls_id not in [0,1,2,3,5,7]:
                    continue

                # 画框
                x1, y1, x2, y2 = map(int, box.xyxy[0])
                cv2.rectangle(small_frame, (x1, y1), (x2, y2), (0, 255, 0), 2)

                # 计算3D坐标
                cx = (x1 + x2) / 2
                norm_x = (cx - w/2) / (w/2)

                # ==========================
                # 前端要求:人=0,车=1
                # ==========================
                if cls_id == 0:
                    obj_type = 0  # 人
                else:
                    obj_type = 1  # 所有车辆统一为 car

                # 你的正确坐标,完全不动!
                obs.append({
                    "track_id": int(box.id[0]) if box.id is not None else 100,
                    "type": obj_type,
                    "x": 10,
                    "y": -norm_x * MOVE_SCALE,
                    "z": 5.0,
                    "width": 0.6,
                    "height": 1.7,
                    "length": 0.6,
                    "heading": 0.0
                })

        obstacle_list = obs

        # 修复这里!cv2.imencode
        ok, jpeg = cv2.imencode('.jpg', small_frame, [cv2.IMWRITE_JPEG_QUALITY, JPEG_QUALITY])
        if ok:
            latest_frame_bytes = jpeg.tobytes()

        time.sleep(DETECT_INTERVAL)

async def send_client(websocket):
    global latest_frame_bytes, obstacle_list
    try:
        while True:
            if latest_frame_bytes:
                b64 = base64.b64encode(latest_frame_bytes).decode()
                await websocket.send(json.dumps({"msg": {"data": b64}}))
                await websocket.send(json.dumps({"msg": {"objs": obstacle_list}}))
            await asyncio.sleep(0.05)
    except:
        pass

async def handle_conn(websocket):
    try:
        await send_client(websocket)
    except:
        pass

async def main():
    threading.Thread(target=video_loop, daemon=True).start()
    async with websockets.serve(handle_conn, WEBSOCKET_HOST, WEBSOCKET_PORT):
        print("✅ 启动成功:人=0,车=1,端口9090正常!")
        await asyncio.Future()

if __name__ == "__main__":
    asyncio.run(main())
相关推荐
DogDaoDao20 小时前
FFmpeg 9.0 “Lei“ 正式发布:十年之后,以你之名
ffmpeg·音视频·实时音视频·视频编解码·ffprobe·ffmpeg 9.0
开开心心_Every20 小时前
电脑文件搜索软件支持内容和拼音搜索
linux·服务器·人工智能·r语言·pdf·音视频·symfony
circuitsosk20 小时前
平台整体能力与功能特性设计:分群、联运、ABTest与运营位系统
python·机器学习·搜索引擎·ab测试·vllm·rag检索
Livia要学习21 小时前
Python三种常见高阶函数--map、filter、sorted
开发语言·python
冷咖啡离 我的笨笨21 小时前
用最简单的例子,从最简单的设计开始,重构着讲解设计原则与模式——从DIP中“倒置”的含义说接口的正确使用
python·重构·依赖倒置原则
weixin_4952484021 小时前
带硬字幕的老视频也能出海:短剧出海翻译服务商如何擦除重制?
人工智能·音视频
PC2005-cloud21 小时前
FinalShell 自定义背景图片教程
ide·python·pycharm
傻啦嘿哟1 天前
某短视频平台视频爬虫实战:抓取推荐流视频信息,绕过反爬的3种技巧
开发语言·爬虫·python
迷迭香yy1 天前
集合竞价数据挖掘实战:用Python构建开盘信号识别系统
人工智能·python·数据挖掘
硅谷秋水1 天前
EgoSteer:一种基于第一人称视角视频、实现可控灵巧操作的全栈系统
深度学习·机器学习·语言模型·机器人·音视频