ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于Python+OpenCV+FFmpeg的智慧养老系统跌倒检测与Web推流实战

基于Python+OpenCV+FFmpeg的智慧养老系统跌倒检测与Web推流实战 简介基于PythonOpenCVWebFFmpeg的智慧养老系统毕业设计/课程设计资源包主要面向计算机相关专业学生可用于课程设计或毕设参考。系统通过多组模拟摄像头画面利用计算机视觉完成人脸录入与识别、表情识别、摔倒检测、闯入告警、老人与义工互动分析以及陌生人追踪事件实时写入数据库并展示在Web报表中方便管理人员及时响应。资源包共1078个文件约316.77MB涵盖Python源码、OpenCV与OpenPose等依赖配置、FaceNet和Mini-Xception模型文件caffemodel、h5、prototxt、前端Vue/CSS/JavaScript页面以及PDF、DOCX、PPTX说明与答辩材料。已有237人学习下载适合需要整体架构参考和算法落地细节的读者可从中拆解系统设计、模型调用、数据库与前后端联动等具体实现。1. 为什么智慧养老系统要选 Python OpenCV Web FFmpeg 这个组合现在谈智慧养老很多方案一上来就推边缘盒子、带 NPU 的开发板但落到“毕业设计、课程设计”这类场景最实际的需求是人能跑通演示不翻车答辩有东西讲。基于 pythonopencvwebffmpeg 计算机视觉的智慧养老系统本质是把摄像头采集、人体检测、异常报警、录像回放四条链路用通用软件串起来。Python 负责快速写算法和 Web 服务OpenCV 处理每一帧图像和相机调用FFmpeg 解决浏览器不能直接播放 RTSP 摄像头的流媒体问题Web 页面承担值班看板和历史事件展示。这套组合不依赖特定硬件在一台普通电脑上就能完成原型老手用来验证算法效果也足够快。下面的内容按实际开发顺序展开从检测模块到推流再到报警闭环和答辩文档每一步给出能直接抄的参数和代码。2. 用 OpenCV 做智慧养老视频处理模块从相机取帧到人体框选2.1 OpenCV 安装与 FFmpeg 的版本搭配opencv 调相机前先确认这 3 个配置先解决环境。Python 版本建议用 3.10 或 3.11OpenCV 选 4.8 以上。安装时如果一个项目既要 DNN 推理又要完整的 contrib 模块直接装 opencv-contrib-python 即可不要同时安装 opencv-python 和 opencv-contrib-python两个包会发生文件冲突import cv2 后可能出现属性缺失这个是新手最常见的坑。pip install opencv-contrib-python numpy flask requestsFFmpeg 不属于 Python 包。Windows 下从官方构建版本下载解压后把 bin 目录加到系统 PATHUbuntu 下执行sudo apt install ffmpeg。装完在命令行输入ffmpeg -version能输出版本号才算成功。OpenCV 的 VideoCapture 本身虽然也能打开 RTSP但对网络抖动和 H.265 编码支持不稳定所以后面我们会让 FFmpeg 承担拉流和推流OpenCV 只负责处理已经到手的帧。# 检查 OpenCV 版本和 FFmpeg 是否存在 import cv2 import shutil print(cv2.__version__) print(shutil.which(ffmpeg))这段代码的意义是先确认两个独立组件的可用性。cv2 会打印 OpenCV 版本shutil.which 会返回 ffmpeg 的可执行文件路径如果是 None说明 ffmpeg 没加入 PATH。再强调一次OpenCV 不是 ffmpeg 的替代品它做的是图像计算不是流媒体协议转换。2.2 用 OpenCV DNN 加载人体检测模型YOLOv8 ONNX 推理代码人体检测是智慧养老系统的核心前置模块。常见做法是用 OpenCV 的 DNN 模块加载 YOLOv8 导出的 ONNX 模型替代耗时较重的 PyTorch 推理。导出方法很简单用 ultralytics 库加载模型后执行model.export(formatonnx, opset12)。得到yolov8n.onnx后OpenCV 直接加载。import cv2 import numpy as np net cv2.dnn.readNetFromONNX(yolov8n.onnx) src cv2.imread(frame.jpg) blob cv2.dnn.blobFromImage(src, 1/255.0, (640, 640), (0,0,0), swapRBTrue) net.setInput(blob) outs net.forward()blobFromImage 的参数需要解释清楚第一个1/255.0是把像素归一化到 0 到 1第二个(640, 640)是 YOLOv8 默认输入尺寸数值越大精度略高但推理越慢(0,0,0)是均值YOLOv8 不需要减均值所以填三个 0lastswapRBTrue表示把 OpenCV 的 BGR 转成 RGB 输入给模型。输出outs的 shape 是(1, 84, 8400)前 4 行是中心点坐标和宽高后 80 行是 COCO 类别置信度。这里我们要的类别一般是 0也就是 person。后处理做两件事过滤低置信度框再用 NMS 去掉重叠框。核心代码逻辑如下# 假设 outs shape 为 (1, 84, 8400) preds outs[0].transpose(1, 0) # 变成 (8400, 84) boxes, scores [], [] for pred in preds: cls_scores pred[4:] conf cls_scores[0] # person 类别 if conf 0.35: continue cx, cy, w, h pred[:4] x1 cx - w / 2 y1 cy - h / 2 boxes.append([x1, y1, w, h]) scores.append(float(conf)) idx cv2.dnn.NMSBoxes(boxes, scores, 0.35, 0.5) person_boxes [boxes[i] for i in idx]这里conf 0.35是置信度阈值下跌阈值会提高召回率但也会把椅子、柜子误判成人NMSBoxes的第二个参数 0.5 表示两个框的 IoU 超过 0.5 就保留得分更高的。对人看护场景我一般把置信度放在 0.3 到 0.4 之间因为老人会有弯腰、坐轮椅等非标准姿态阈值过高会漏检。画框时要把输入尺寸和原图尺寸做换算如果直接拿 640 尺寸框去原图画位置会偏到角落。2.3 检测结果结构化从 bounding box 到报警事件的数据约定OpenCV 推理得到的是坐标但 Web 端和告警模块需要的是结构化数据。我一般会在检测线程里维护一个事件列表每条记录包含event_id、timestamp、bbox、confidence、belly_down这类业务字段。这样做的好处是后期接 MQTT 或数据库时只改序列化层不用动检测代码。def build_event(bbox, conf, ratio): return { event_id: int(time.time() * 1000), ts: time.strftime(%Y-%m-%d %H:%M:%S), bbox: [float(x) for x in bbox], confidence: round(conf, 3), wh_ratio: round(ratio, 3), alarm: ratio 0.55 }wh_ratio是宽高比后面跌倒检测会用到。数据结构设计时不要把 OpenCV 的Rect对象或 ndarray 直接放进列表后面 Flask 做 jsonify 时会报 TypeError。统一转成 float 和 int这是一个很多人都忽略的细节。2.4 摄像头取帧是阻塞操作把 VideoCapture 放进独立线程OpenCV 的cap.read()在 RTSP 流里经常卡住原因包括网络重传、摄像头码率过大、DNS 解析慢。更麻烦的是如果检测逻辑写在主循环里cap.read()一卡整个 Web 服务和报警模块全被拖住。解决方式是把取帧放进独立线程用一个队列把最新帧交给检测线程。import threading, queue frame_queue queue.Queue(maxsize2) def capture_loop(rtsp_url): cap cv2.VideoCapture(rtsp_url) cap.set(cv2.CAP_PROP_BUFFERSIZE, 1) while True: ret, frame cap.read() if ret: if frame_queue.full(): frame_queue.get() frame_queue.put(frame)CAP_PROP_BUFFERSIZE设置为 1 很关键。OpenCV 默认会缓冲多帧导致实时性下降处理的是几秒前的画面跌倒检测就会失去意义。队列maxsize2是防止帧消费不过来时内存无限增长。检测线程里再调用frame_queue.get()如果队列为空就跳过这一轮。这个结构看起来简单但对智慧养老项目来说稳定性和实时性都能兼顾。3. 用 FFmpeg 做 Web 视频流HLS 推流命令与 OpenCV 帧管道3.1 为什么浏览器不认 RTSPHLS 协议选型与延迟预算IP 摄像头最常见的输出协议是 RTSP但浏览器不能直接播放。电玩城或智慧教室项目里有人用 WebRTC延迟确实低可以到 300 到 500 毫秒但需要额外部署信令服务。对于养老看护场景2 到 5 秒的延迟完全能接受HLS 是最稳妥的方案切分切片、天然支持 HTTP 缓存、CDN 分发也方便。部署时只需要一个静态目录nginx 可以直接托管。所以这里选 HLS 而不是 RTMP 或 WebRTC核心判断依据是复杂度可控、跨平台播放器多。3.2 FFmpeg 转推 HLS 的常用命令与参数解释FFmpeg 在这一环节的作用是从摄像头拉 RTSP 流重编码成 H.264再输出为 HLS 动态更新 m3u8 索引文件。常见命令如下ffmpeg -rtsp_transport tcp -i rtsp://admin:password192.168.1.10:554/stream1 \ -c:v libx264 -preset veryfast -tune zerolatency \ -c:a aac -b:a 32k \ -f hls -hls_time 2 -hls_list_size 5 -hls_flags delete_segments \ /var/www/html/live/index.m3u8逐项拆开说明。-rtsp_transport tcp把 RTSP 传输从默认的 UDP 改成 TCPUDP 在弱网环境下会花屏TCP 可靠但延迟略高养老场景更看重画面连续性。-preset veryfast牺牲一点压缩率换来编码速度避免旧电脑 CPU 跟不上。-tune zerolatency让编码器尽量少做帧间延时。-hls_time 2每个切片 2 秒-hls_list_size 5只保留最近 5 个切片-hls_flags delete_segments会自动删除旧切片否则磁盘会被堆满。hls_list_size 的设置可以直接影响延迟。列表越长越容易落后。5 个切片乘以 2 秒相当于播放器最多落后 10 秒这是一个相对舒服的中间值。如果觉得延迟还是高把 hls_time 改成 1但切片太短会增加文件句柄开销。3.3 OpenCV 帧如何交给 FFmpegrawvideo 管道最小实现如果摄像头输出的画面需要在进 Web 前先叠加检测框、时间戳或“跌倒报警”红色文字就不能直接让 FFmpeg 去拉摄像头原始流而是要让 OpenCV 先处理帧再把帧喂给 FFmpeg。做法是用 subprocess 启动一个 ffmpeg 进程从 stdin 读 rawvideo然后转 HLS。import subprocess import cv2 cmd [ ffmpeg, -y, -f, rawvideo, -pix_fmt, bgr24, -s, 640x480, -r, 15, -i, -, -c:v, libx264, -preset, veryfast, -tune, zerolatency, -f, hls, -hls_time, 2, -hls_list_size, 5, -hls_flags, delete_segments, live/index.m3u8 ] proc subprocess.Popen(cmd, stdinsubprocess.PIPE) while True: ret, frame cap.read() if not ret: break frame cv2.resize(frame, (640, 480)) proc.stdin.write(frame.tobytes())这里一个非常容易踩的坑是-s 640x480必须和 resize 后的尺寸严格一致。如果 OpenCV 的 frame 是 1920x1080而 ffmpeg 还是按 640x480 读取画面会变成绿色马赛克或直接报错。另外-r 15也建议保持和摄像头实际输出帧率一致不要写 30 但实际只有 15否则 ffmpeg 会重复或丢帧。3.4 FFmpeg 参数对照表实时性优先还是画质优先以下参数表可以放在项目文档的性能测试章节里答辩时列出实测数值比较有说服力参数组合CPU 占用画面清晰度HLS 延迟适用场景presetultrafast hls_time2低一般2-4 秒低配电脑演示presetveryfast hls_time2中较好2-5 秒日常看护presetslow hls_time4高清晰5-8 秒录像留档原画转封装 -c copy极低原始清晰度2 秒存储优先-c copy不重编码直接把 RTSP 里的 H.264 数据打包进 TSCPU 占用低但 Web 端播放兼容性依赖相机原始编码参数。如果相机的编码器输出带 B 帧浏览器播放 HLS 时可能出现花屏。这个表在实际项目中不是让你死记而是根据部署机器的 CPU 型号现场调。4. Web 后端与视觉算法整合跌倒检测告警闭合的工程实现4.1 跌倒检测判断宽高比、姿态关键点与连续帧确认跌倒检测不一定非要上姿态估计模型。最轻量的方案是用人体检测框的宽高比变化站立时高度大于宽度宽高比约等于 0.4摔倒后横向躺在地上宽度大于高度宽高比超过 0.8。这个规则对侧身跌倒有效对正面趴下同样有效。为了避免弯腰捡东西误报需要连续多帧确认。# bbox: [x, y, w, h] ratio bbox[2] / bbox[3] # width / height if ratio 0.85: fall_frames 1 else: fall_frames 0 if fall_frames 5: alarm True连续 5 帧按 15fps 计算大约是 0.33 秒既能滤掉瞬间抖动又不会延迟太多。如果检测到正面趴下后一直没有动静还要结合静止检测进一步上报“长时间倒地”。这个判断用中心点位置变化即可前后帧中心点偏移小于 5 个像素就认为静止。更精确的做法是叠加姿态关键点把肩膀中心高度和髋部中心高度做比较。肩部平均坐标低于髋部平均坐标时跌倒置信度提高。但姿态关键点模型推理速度比 YOLO 检测慢很多在项目文档里要写清楚阈值判断保证实时性姿态关键点作为二次确认两者是互补关系。4.2 Flask 提供事件与快照 API前端轮询告警记录Web 后端在智慧养老系统里扮演“数据出口”的角色。检测线程得出报警事件后放进一个带锁的内存列表Flask 提供接口给前端查询。from flask import Flask, jsonify, send_file import threading, io app Flask(__name__) events [] events_lock threading.Lock() app.get(/api/events) def get_events(): with events_lock: return jsonify(events[-50:]) app.get(/api/snapshot) def get_snapshot(): ret, frame cap.read() retval, buffer cv2.imencode(.jpg, frame) return send_file(io.BytesIO(buffer.tobytes()), mimetypeimage/jpeg)events[-50:]表示只返回最近 50 条防止前端渲染过多 DOM 节点性能下降。快照接口每次调用时取最新帧用于看护首页的实时画面缩略图。这里加锁是必要的因为检测线程在往 events 里 appendFlask 线程在读列表不加 lock 可能出现一边遍历一边写入的异常。4.3 前端用 HLS.js 播放推流地址并展示报警事件前端页面不复杂但要注意 video 标签必须设置 muted 和 playsinline移动端 webview 和部分浏览器禁止带声音自动播放。video idvideo muted autoplay playsinline/video script srchttps://cdn.jsdelivr.net/npm/hls.js1/script script if (Hls.isSupported()) { var hls new Hls(); hls.loadSource(/live/index.m3u8); hls.attachMedia(document.getElementById(video)); } async function loadEvents() { const res await fetch(/api/events); const data await res.json(); // data 渲染成列表按时间倒序 data.slice().reverse().forEach(evt { if (evt.alarm) { addAlarmItem(evt.ts, evt.bbox); } }); } setInterval(loadEvents, 2000);muted autoplay是自动播放的最小前提。如果不设置 mutedChrome 会直接阻止 autoplay页面出现黑屏但不报错。setInterval(loadEvents, 2000)是 2 秒轮询报警实时性可以接受。如果要改成 WebSocket 推送只需在后端定时 broadcast events前端的 fetch 部分可以整个替换。4.4 联调常见故障FFmpeg 子进程退出与 OpenCV 摄像头占用联调时最典型的报错是ffmpeg: command not found。出现这个错误时不要再去 pip install 任何包Python 的 imageio-ffmpeg 和系统 ffmpeg 是两回事先确认ffmpeg -version是否能正常执行。如果 FFmpeg 进程启动后马上退出常见原因是输出目录不存在。HLS 的 m3u8 文件不会被自动创建目录运行前要手动mkdir -p /var/www/html/live。另一个坑是 Windows 下摄像头被手机 app 或 OBS 占用后OpenCV 的 VideoCapture 打开成功但 read 永远返回 False。排查方式是在任务管理器中结束占用相机的进程或者换一个非摄像头的演示视频文件作为输入源。这类问题要写进项目文档的“常见错误”部分答辩时老师喜欢问。5. 答辩文档和演示脚本里值得写明的三个验收技巧5.1 用 FFmpeg 回放录屏做功能验收避免现场相机不可用答辩现场最容易翻车的不是算法不准而是摄像头驱动不兼容。建议准备两段素材一段正常行走的 mp4一段模拟跌倒的 mp4。用 FFmpeg 循环推流成 RTSP 或直接作为 OpenCV 输入源代码里只需要把cap cv2.VideoCapture(0)改成cap cv2.VideoCapture(demo.mp4)。这样即使现场没有 USB 摄像头也能完整演示检测和报警流程。录屏素材还能保证复现性同一段视频在调参前后跑出来的结果可以量化对比这一点在项目文档里是加分项。5.2 给检测日志加时间戳和置信度量化跌倒检测阈值很多人做视觉项目时只在前端弹报警后端不留日志答辩时被问“误报率多少”就答不上来。我一般会在检测循环里加一行结构化日志logger.info(detect person%s conf%.2f ratio%.2f alarm%s, len(boxes), max_conf, ratio, alarm)把时间、人员数量、最高置信度、宽高比和是否报警写进日志文件。答辩前运行 30 分钟用日志统计误报警次数在 PPT 文档里放一张简单的统计表比任何流程图都有说服力。FFmpeg 那边同样可以用-loglevel warning收集断流时间用于说明视频管道稳定性。5.3 用三组测试片段固定阈值参数写进项目文档跌倒检测的阈值不能拍脑袋定。我通常准备三组测试片段正常行走、弯腰拾物、快速跌倒。每组跑一遍记录准确率和响应时间。比如置信度阈值从 0.3 调到 0.5正常行走场景的误报率可能从 8% 降到 1%但跌倒场景召回率也可能从 98% 降到 82%。把这两组数据放在项目文档的“参数调优”表格里让后来者知道每个参数为什么是这个值。这个做法不单是写文档更是给答辩老师一个明确的工程验证路径。本文还有配套的精品资源点击获取
返回列表