yolov8实现行人跌倒检测web
用flask编写网页,实现图片上传进行检测和调用电脑摄像头进行实时检测,但调用摄像头检测存在问题,会出现进程冲突的情况,导致视频采集不是很流畅。
(一)项目结构
yolo_flask/ # 项目根目录
├── static/ # Flask 静态资源
│ ├── css/ # 样式文件
│ │ └── style.css # 网页样式表
│ ├── images/ # 图片资源
│ │ ├── background.JPG
│ │ └── background_2.JPG
│ ├── results/ # 检测结果存储(处理后图片)
│ └── uploads/ # 用户上传文件存储
│
├── templates/ # Flask 网页模板
│ ├── base.html # 基础模板(主要是导航,其他页面继承此模板)
│ ├── camera.html # (废案)
│ ├── detect.html # 摄像头检测页面
│ ├── index.html # 主页
│ └── upload.html # 图片上传页面
│
├── app.py # Flask 主应用入口
├── app_demo.py # (废案)
├── best.pt # YOLO 预训练模型权重文件
├── demo.py # (废案)
├── local_detect.py # 本地检测脚本(非 Web 功能)
补充:
images文件夹下存放的是网站的背景,可以随意替换成你喜欢的照片。
best.pt是我自己训练得来的权重,不过数据集比较粗糙而且数量有点少,效果不是很好,看看后期有时间能不能再优化优化。
这一次模型用的是添加Seattentions的yolov8模型。
local_detect.py运行后是直接用opencv调用摄像头直接检测。
(二)开发环境
3050笔记本
pycharm社区版
python=3.8
Windows11操作系统
(三)实物展示
直接运行app.py

点击链接后进入主页

进入图片上传检测页面
从网上选了一张图片进行检测:

实时检测页面
打开相册能够较精确的检测,但稍微有些卡顿,后期还有待优化。

(四)源码展示(部分)
python:
app.py(主程序)
from flask import Flask, render_template, request, redirect, url_for, jsonify,Response
from ultralytics import YOLO
import cv2
import os
import uuid
import base64
import numpy as np
import time
import threading
app = Flask(__name__)
app.config.update({
'UPLOAD_FOLDER': 'static/uploads',
'RESULT_FOLDER': 'static/results',
'ALLOWED_EXTENSIONS': {'png', 'jpg', 'jpeg'},
'MAX_CONTENT_LENGTH': 5 * 1024 * 1024 # 限制上传文件为5MB
})
# 加载模型
model = YOLO('best.pt')
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in app.config['ALLOWED_EXTENSIONS']
# ================== 页面路由 ==================
@app.route('/')
def index():
return render_template('index.html', active_page='index')
@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
# 验证文件有效性
if 'file' not in request.files:
return redirect(url_for('upload_file'))
file = request.files['file']
if not valid_upload_file(file):
return redirect(url_for('upload_file'))
# 处理文件上传
result_filename = process_uploaded_file(file)
return render_template('upload.html',
active_page='upload',
result=result_filename)
return render_template('upload.html', active_page='upload')
@app.route('/detect')
def detect_page():
return render_template('detect.html', active_page='detect')
@app.route('/video_feed')
def video_feed():
return Response(gen_frames(),
mimetype='multipart/x-mixed-replace; boundary=frame')
@app.route('/alarm_status')
def get_alarm_status():
global alarm_status
return jsonify({'alarm': alarm_status})
@app.teardown_appcontext
def teardown(exception=None):
release_camera()
# ================== detect页面工具函数 ==================
cap = None
lock = threading.Lock()
ALARM_CONF = 0.7 # 报警置信度阈值
alarm_status = False # 报警状态
def init_camera():
global cap
with lock:
if cap is None or not cap.isOpened():
cap = cv2.VideoCapture(0)
if not cap.isOpened():
print("无法打开摄像头")
return False
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
return True
def release_camera():
global cap
with lock:
if cap is not None:
cap.release()
cap = None
def gen_frames():
global cap, alarm_status
while True:
try:
if not init_camera():
time.sleep(1)
continue
with lock:
success, frame = cap.read()
if not success:
print("摄像头读取失败,尝试重新初始化...")
release_camera()
continue
# 进行检测
results = model(frame, conf=0.5)
# 绘制检测结果
alarm_triggered = False
for result in results:
for box in result.boxes:
x1, y1, x2, y2 = map(int, box.xyxy[0].tolist())
conf = box.conf[0].item()
cls_id = int(box.cls[0])
# 绘制检测框
color = (0, 0, 255)
cv2.rectangle(frame, (x1, y1), (x2, y2), color, 2)
label = f"Fall {conf:.2f}"
cv2.putText(frame, label, (x1, y1 - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, color, 2)
if conf >= ALARM_CONF: #置信度超过报警阈值ALARM_CONF,设置alarm_triggered为True
alarm_triggered = True
# 更新报警状态
alarm_status = alarm_triggered
# 添加报警提示
if alarm_triggered:
cv2.putText(frame, "ALARM: FALL DETECTED!", (50, 50),
cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 3)
# 转换图像为JPEG格式
ret, buffer = cv2.imencode('.jpg', frame)
frame_bytes = buffer.tobytes()
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + frame_bytes + b'\r\n')
except Exception as e:
print(f"发生错误: {e}")
release_camera()
time.sleep(1)
# ================== upload工具函数 ==================
#upload文件检查
def valid_upload_file(file):
return file and file.filename != '' and allowed_file(file.filename)
#upload页面上传图片检测结果保存
def process_uploaded_file(file):
# 生成唯一文件名
ext = file.filename.rsplit('.', 1)[1].lower()
filename = f"{uuid.uuid4().hex}.{ext}"
upload_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
file.save(upload_path)
# 读取,然后调用模型执行检测并保存结果
img = cv2.imread(upload_path)
results = model(img)
draw_detections(img, results)
# 保存检测结果
result_filename = f"result_{filename}"
result_path = os.path.join(app.config['RESULT_FOLDER'], result_filename)
cv2.imwrite(result_path, img)
return result_filename
def draw_detections(img, results):
for result in results:
for box in result.boxes:
x1, y1, x2, y2 = map(int, box.xyxy[0].cpu().numpy())
conf = box.conf[0].item()
cv2.rectangle(img, (x1, y1), (x2, y2), (0, 0, 255), 2)
cv2.putText(img, f"Fall {conf:.2f}", (x1, y1 - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
if __name__ == '__main__':
# 创建图片检测的目录
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
os.makedirs(app.config['RESULT_FOLDER'], exist_ok=True)
#RUN
app.run(host='0.0.0.0', port=5000, debug=True)
html:
base.html源码(主要是写了个导航栏)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}{% endblock %}</title>
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.3.0/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.bootcdn.net/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
</head>
<body>
<!-- 固定导航栏 -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark fixed-top">
<div class="container">
<a class="navbar-brand" href="{{ url_for('index') }}">
<i class="fas fa-shield-alt me-2"></i>智能跌倒检测
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<li class="nav-item">
<a class="nav-link {{ 'active' if active_page == 'index' }}"
href="{{ url_for('index') }}">首页</a>
</li>
<li class="nav-item">
<a class="nav-link {{ 'active' if active_page == 'upload' }}"
href="{{ url_for('upload_file') }}">图片检测</a>
</li>
<li class="nav-item">
<a class="nav-link {{ 'active' if active_page == 'detect' }}"
href="{{ url_for('detect_page') }}">实时检测</a>
</li>
</ul>
</div>
</div>
</nav>
<!-- 内容区(留出导航栏高度) -->
<div class="container-fluid" style="margin-top: 80px;">
{% block content %}{% endblock %}
</div>
<!-- Bootstrap JS -->
<script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.3.0/js/bootstrap.bundle.min.js"></script>
{% block scripts %}{% endblock %}
</body>
</html>
upload.html(图片上传页面)
{% extends "base.html" %}
{% block title %}图片检测{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-6">
<div class="card shadow">
<div class="card-header bg-success text-white">
<h4><i class="fas fa-upload"></i> 上传图片</h4>
</div>
<div class="card-body">
<form method="post" enctype="multipart/form-data" action="/upload">
<div class="mb-3">
<input class="form-control" type="file" name="file" accept="image/*" required>
</div>
<button type="submit" class="btn btn-primary w-100">
<i class="fas fa-magic me-2"></i>开始检测
</button>
</form>
</div>
</div>
</div>
<div class="col-md-6">
{% if result %}
<div class="card shadow">
<div class="card-header bg-info text-white">
<h4><i class="fas fa-image"></i> 检测结果</h4>
</div>
<div class="card-body">
<img src="{{ url_for('static', filename='results/' + result) }}"
class="img-fluid rounded" alt="检测结果">
</div>
</div>
{% endif %}
</div>
</div>
{% endblock %}
index.html(网站主页)
{% extends "base.html" %}
{% block title %}首页{% endblock %}
{% block content %}
<div class="row justify-content-center">
<div class="col-md-8 text-center">
<div class="card shadow-lg">
<div class="card-header bg-primary text-white">
<h2><i class="fas fa-home"></i> 欢迎使用跌倒检测系统</h2>
</div>
<div class="card-body">
<h4 class="mb-4">请选择检测模式:</h4>
<div class="d-grid gap-3">
<a href="/upload" class="btn btn-lg btn-success">
<i class="fas fa-upload fa-2x me-2"></i>图片上传检测
</a>
<a href="/detect" class="btn btn-lg btn-info">
<i class="fas fa-video fa-2x me-2"></i>摄像头实时检测
</a>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
detect.html(实时检测)
{% extends "base.html" %}
{% block content %}
<head>
<title>实时跌倒检测</title>
<style>
body {
margin: 0;
padding: 20px;
background-color: #f0f0f0;
}
.container {
max-width: 1280px;
margin: 0 auto;
}
h1 {
text-align: center;
color: #333;
}
#video-feed {
width: 100%;
background-color: #000;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
.status-bar {
text-align: center;
margin: 20px 0;
font-size: 24px;
color: #d9534f;
font-weight: bold;
}
</style>
</head>
<body>
<div class="container">
<h1>实时跌倒检测系统</h1>
<div class="status-bar" id="status">监控中...</div>
<img id="video-feed" src="{{ url_for('video_feed') }}">
</div>
<script>
// 检测报警状态的简单实现
const statusElement = document.getElementById('status');
// 每3秒检查一次报警状态
setInterval(() => {
fetch('/alarm_status')
.then(response => response.json())
.then(data => {
if(data.alarm) {
statusElement.textContent = "警报:检测到跌倒!";
statusElement.style.color = "#d9534f";
} else {
statusElement.textContent = "状态正常";
statusElement.style.color = "#5cb85c";
}
})
.catch(error => {
console.error('获取报警状态失败:', error);
});
}, 3000);
</script>
</body>
{% endblock %}
CSS:
style.css
/* 背景样式 */
body {
background: url('../images/background_2.jpg') no-repeat center center fixed;
background-size: cover;
min-height: 100vh;
}
/* 导航栏间距调整 */
.navbar {
margin-bottom: 30px;
}
/* 卡片阴影效果 */
.card {
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
transition: transform 0.3s;
}
.card:hover {
transform: translateY(-5px);
}
/* 按钮间距 */
.btn-lg {
padding: 1.5rem 2rem;
font-size: 1.25rem;
}
(五)画饼
目前这个项目还是很粗糙的,断断续续的做,想到什么写什么。未来期望是解决摄像头的卡顿问题,同时修改当前的页面更加美观整洁,尽量风格统一且看起来清新一些。想要一整个项目打包文件的可以私信我,看到我会马上回。
更多推荐
所有评论(0)