YOLOv8 安防系统实战:实时人员预警与智能监控
YOLOv8安防系统实战摘要 本文详细介绍基于YOLOv8的智能安防系统实现方案,主要包含以下核心内容: 系统架构:采用模块化设计,包括输入源处理、YOLOv8目标检测、区域预警、语音告警和数据存储五大模块。 关键技术: 使用YOLOv8实现高精度人员检测 通过OpenCV处理视频流 SQLite数据库存储检测记录 pyttsx3实现语音告警功能 核心功能: 支持图片/视频/RTSP流多种输入源
YOLOv8 安防系统实战:实时人员预警与智能监控
目录
引言
在安防领域,实时人员检测与预警一直是核心需求。传统的监控系统往往依赖人工值守,不仅效率低下,而且容易出现漏检。随着深度学习技术的发展,基于目标检测的智能安防系统逐渐成为主流。
本文将为小白读者详细介绍如何使用 YOLOv8 构建一个完整的安防系统,实现对人员的实时检测、预警告警、标记,并将数据记录到 SQLite 数据库。系统还支持当人员进入指定区域时播放语音告警,提高安防的实时性和有效性。
系统架构总览
为了让小白读者更好地理解系统结构,我们先看一下整体架构:
- 输入层:接收来自图片、视频或摄像头的输入
- 处理层:包括目标检测、区域判断、告警触发等核心逻辑
- 输出层:显示检测结果、播放告警、存储数据
整个系统采用模块化设计,各个模块之间职责明确,便于理解和维护。即使你是小白,也可以按照本文的步骤逐步构建和理解整个系统。
技术栈选择
- 目标检测模型:YOLOv8 (Ultralytics)
- 编程语言:Python 3.8+
- Web框架:FastAPI
- 数据库:SQLite
- 视频处理:OpenCV
- 语音播放:pyttsx3
- 依赖库:ultralytics, opencv-python, sqlite3, pyttsx3, fastapi, uvicorn, jinja2, python-multipart
系统架构
我们的安防系统主要由以下几个模块组成:
- 输入源模块:支持图片、MP4视频、RTSP视频流
- 检测模块:使用YOLOv8进行人员检测
- 预警模块:基于检测结果生成告警
- 标记模块:在画面上标记检测到的人员
- 存储模块:将检测结果记录到SQLite数据库
- 语音告警模块:当人员进入指定区域时播放语音告警
- 输出模块:显示实时检测结果
环境搭建
首先,我们需要安装必要的依赖库:
pip install ultralytics opencv-python pyttsx3 fastapi uvicorn jinja2 python-multipart
核心模块详解
1. 数据库初始化
import sqlite3
def init_db():
conn = sqlite3.connect('security.db')
cursor = conn.cursor()
# 创建检测记录表
cursor.execute('''
CREATE TABLE IF NOT EXISTS detections (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp TEXT,
source TEXT,
person_count INTEGER,
image_path TEXT,
alarm BOOLEAN
)
''')
conn.commit()
conn.close()
# 初始化数据库
init_db()
2. YOLOv8 检测器
from ultralytics import YOLO
import cv2
class YoloDetector:
def __init__(self, model_path='yolov8n.pt'):
self.model = YOLO(model_path)
def detect(self, frame):
# 检测结果
results = self.model(frame)
return results
def draw_boxes(self, frame, results):
# 在画面上绘制检测框
for result in results:
for box in result.boxes:
if box.cls == 0: # 0 表示 person
x1, y1, x2, y2 = map(int, box.xyxy[0])
confidence = box.conf[0]
# 绘制矩形框
cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)
# 添加标签
label = f'Person: {confidence:.2f}'
cv2.putText(frame, label, (x1, y1 - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
return frame
3. 区域设置模块
class RegionManager:
def __init__(self):
self.regions = [] # 存储告警区域
self.drawing = False
self.current_region = []
def add_region(self, region):
"""添加告警区域
Args:
region: 区域坐标 [x1, y1, x2, y2]
"""
self.regions.append(region)
def clear_regions(self):
"""清除所有告警区域"""
self.regions = []
def is_in_region(self, box):
"""检查目标是否在告警区域内
Args:
box: 目标坐标 [x1, y1, x2, y2]
Returns:
bool: 是否在告警区域内
"""
box_center = ((box[0] + box[2]) // 2, (box[1] + box[3]) // 2)
for region in self.regions:
rx1, ry1, rx2, ry2 = region
if rx1 <= box_center[0] <= rx2 and ry1 <= box_center[1] <= ry2:
return True
return False
def draw_regions(self, frame):
"""在画面上绘制告警区域
Args:
frame: 图像帧
Returns:
绘制了区域的图像帧
"""
for region in self.regions:
x1, y1, x2, y2 = region
# 绘制区域框
cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 0, 255), 2)
# 添加标签
cv2.putText(frame, 'Alarm Region', (x1, y1 - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)
return frame
def select_region(self, frame):
"""交互式选择告警区域
Args:
frame: 图像帧
Returns:
选择的区域坐标 [x1, y1, x2, y2]
"""
def mouse_callback(event, x, y, flags, param):
nonlocal self, frame, temp_frame
if event == cv2.EVENT_LBUTTONDOWN:
self.drawing = True
self.current_region = [x, y]
temp_frame = frame.copy()
elif event == cv2.EVENT_MOUSEMOVE:
if self.drawing:
temp_frame = frame.copy()
cv2.rectangle(temp_frame,
tuple(self.current_region),
(x, y),
(0, 255, 0), 2)
elif event == cv2.EVENT_LBUTTONUP:
self.drawing = False
self.current_region.extend([x, y])
# 确保坐标顺序正确
if self.current_region[0] > self.current_region[2]:
self.current_region[0], self.current_region[2] = self.current_region[2], self.current_region[0]
if self.current_region[1] > self.current_region[3]:
self.current_region[1], self.current_region[3] = self.current_region[3], self.current_region[1]
temp_frame = frame.copy()
cv2.namedWindow('Select Alarm Region')
cv2.setMouseCallback('Select Alarm Region', mouse_callback)
print("请在画面上框选告警区域,完成后按 'Enter' 键确认")
while True:
cv2.imshow('Select Alarm Region', temp_frame)
key = cv2.waitKey(1) & 0xFF
if key == 13: # Enter 键
break
cv2.destroyAllWindows()
if len(self.current_region) == 4:
self.add_region(self.current_region)
return self.current_region
return None
4. 语音告警模块
import pyttsx3
class VoiceAlarm:
def __init__(self):
self.engine = pyttsx3.init()
# 设置语音属性
self.engine.setProperty('rate', 150) # 语速
self.engine.setProperty('volume', 1.0) # 音量
def play_alarm(self, message="警告,有人员进入 restricted 区域"):
"""播放语音告警
Args:
message: 告警信息
"""
print(f"播放告警: {message}")
self.engine.say(message)
self.engine.runAndWait()
def stop(self):
"""停止语音引擎"""
self.engine.stop()
5. 预警系统
class AlarmSystem:
def __init__(self, threshold=3):
self.threshold = threshold # 人员数量阈值
self.voice_alarm = VoiceAlarm()
def check_alarm(self, person_count):
"""检查是否需要告警
Args:
person_count: 人员数量
Returns:
bool: 是否需要告警
"""
return person_count > self.threshold
def check_region_alarm(self, person_in_region):
"""检查区域告警
Args:
person_in_region: 是否有人员在告警区域内
Returns:
bool: 是否需要告警
"""
return person_in_region
def trigger_alarm(self, message="警告,有人员进入 restricted 区域"):
"""触发告警
Args:
message: 告警信息
"""
self.voice_alarm.play_alarm(message)
6. 数据存储
import datetime
import os
class DataStorage:
def __init__(self, db_path='security.db'):
self.db_path = db_path
# 创建保存图片的目录
os.makedirs('detections', exist_ok=True)
def save_detection(self, source, person_count, frame, alarm):
# 生成时间戳
timestamp = datetime.datetime.now().isoformat()
# 保存图片
image_path = f'detections/{timestamp.replace(":", "-")}.jpg'
cv2.imwrite(image_path, frame)
# 保存到数据库
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
cursor.execute('''
INSERT INTO detections (timestamp, source, person_count, image_path, alarm)
VALUES (?, ?, ?, ?, ?)
''', (timestamp, source, person_count, image_path, alarm))
conn.commit()
conn.close()
7. 主系统
class SecuritySystem:
def __init__(self, model_path='yolov8n.pt', threshold=3):
self.detector = YoloDetector(model_path)
self.alarm_system = AlarmSystem(threshold)
self.storage = DataStorage()
self.region_manager = RegionManager()
self.alarm_triggered = False # 避免重复告警
def set_alarm_region(self, frame):
"""设置告警区域
Args:
frame: 用于选择区域的图像帧
"""
self.region_manager.select_region(frame)
def process_image(self, image_path):
# 处理图片
frame = cv2.imread(image_path)
# 如果没有设置告警区域,先设置
if not self.region_manager.regions:
self.set_alarm_region(frame)
results = self.detector.detect(frame)
# 统计人员数量和区域内人员
person_count = 0
person_in_region = False
for result in results:
for box in result.boxes:
if box.cls == 0: # 0 表示 person
person_count += 1
# 检查是否在告警区域内
box_coords = list(map(int, box.xyxy[0]))
if self.region_manager.is_in_region(box_coords):
person_in_region = True
# 检查告警
alarm = self.alarm_system.check_alarm(person_count) or self.alarm_system.check_region_alarm(person_in_region)
# 触发语音告警
if alarm and not self.alarm_triggered:
if person_in_region:
self.alarm_system.trigger_alarm("警告,有人员进入 restricted 区域")
else:
self.alarm_system.trigger_alarm("警告,人员数量超过限制")
self.alarm_triggered = True
elif not alarm:
self.alarm_triggered = False
# 绘制检测框和告警区域
frame = self.detector.draw_boxes(frame, results)
frame = self.region_manager.draw_regions(frame)
# 添加告警信息
if alarm:
if person_in_region:
cv2.putText(frame, 'ALARM: Person in restricted area!', (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
else:
cv2.putText(frame, 'ALARM: Too many people!', (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
# 保存结果
self.storage.save_detection('image', person_count, frame, alarm)
# 显示结果
cv2.imshow('Detection', frame)
cv2.waitKey(0)
cv2.destroyAllWindows()
def process_video(self, video_path):
# 处理视频
cap = cv2.VideoCapture(video_path)
# 读取第一帧用于设置告警区域
ret, first_frame = cap.read()
if ret and not self.region_manager.regions:
self.set_alarm_region(first_frame)
# 重置视频捕获
cap.release()
cap = cv2.VideoCapture(video_path)
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
results = self.detector.detect(frame)
# 统计人员数量和区域内人员
person_count = 0
person_in_region = False
for result in results:
for box in result.boxes:
if box.cls == 0: # 0 表示 person
person_count += 1
# 检查是否在告警区域内
box_coords = list(map(int, box.xyxy[0]))
if self.region_manager.is_in_region(box_coords):
person_in_region = True
# 检查告警
alarm = self.alarm_system.check_alarm(person_count) or self.alarm_system.check_region_alarm(person_in_region)
# 触发语音告警
if alarm and not self.alarm_triggered:
if person_in_region:
self.alarm_system.trigger_alarm("警告,有人员进入 restricted 区域")
else:
self.alarm_system.trigger_alarm("警告,人员数量超过限制")
self.alarm_triggered = True
elif not alarm:
self.alarm_triggered = False
# 绘制检测框和告警区域
frame = self.detector.draw_boxes(frame, results)
frame = self.region_manager.draw_regions(frame)
# 添加告警信息
if alarm:
if person_in_region:
cv2.putText(frame, 'ALARM: Person in restricted area!', (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
else:
cv2.putText(frame, 'ALARM: Too many people!', (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
# 保存结果
self.storage.save_detection('video', person_count, frame, alarm)
# 显示结果
cv2.imshow('Detection', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
def process_rtsp(self, rtsp_url):
# 处理RTSP视频流
cap = cv2.VideoCapture(rtsp_url)
# 读取第一帧用于设置告警区域
ret, first_frame = cap.read()
if ret and not self.region_manager.regions:
self.set_alarm_region(first_frame)
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
results = self.detector.detect(frame)
# 统计人员数量和区域内人员
person_count = 0
person_in_region = False
for result in results:
for box in result.boxes:
if box.cls == 0: # 0 表示 person
person_count += 1
# 检查是否在告警区域内
box_coords = list(map(int, box.xyxy[0]))
if self.region_manager.is_in_region(box_coords):
person_in_region = True
# 检查告警
alarm = self.alarm_system.check_alarm(person_count) or self.alarm_system.check_region_alarm(person_in_region)
# 触发语音告警
if alarm and not self.alarm_triggered:
if person_in_region:
self.alarm_system.trigger_alarm("警告,有人员进入 restricted 区域")
else:
self.alarm_system.trigger_alarm("警告,人员数量超过限制")
self.alarm_triggered = True
elif not alarm:
self.alarm_triggered = False
# 绘制检测框和告警区域
frame = self.detector.draw_boxes(frame, results)
frame = self.region_manager.draw_regions(frame)
# 添加告警信息
if alarm:
if person_in_region:
cv2.putText(frame, 'ALARM: Person in restricted area!', (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
else:
cv2.putText(frame, 'ALARM: Too many people!', (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
# 保存结果
self.storage.save_detection('rtsp', person_count, frame, alarm)
# 显示结果
cv2.imshow('Detection', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
8. Web界面实现
为了提供更友好的用户界面,我们使用FastAPI构建了一个Web应用,支持通过网页设置告警区域和查看检测结果。
from fastapi import FastAPI, Request, Response
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
import cv2
import json
import os
import numpy as np
from ultralytics import YOLO
import pyttsx3
import sqlite3
import datetime
app = FastAPI()
# 静态文件和模板
app.mount("/static", StaticFiles(directory="static"), name="static")
templates = Jinja2Templates(directory="templates")
# 区域配置文件
REGIONS_FILE = "regions.json"
# 数据库文件
DB_FILE = "security.db"
# 检测结果保存目录
DETECTIONS_DIR = "detections"
os.makedirs(DETECTIONS_DIR, exist_ok=True)
# 初始化数据库
def init_db():
conn = sqlite3.connect(DB_FILE)
cursor = conn.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS detections (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp TEXT,
source TEXT,
person_count INTEGER,
image_path TEXT,
alarm BOOLEAN
)
''')
conn.commit()
conn.close()
init_db()
# 初始化组件
detector = YoloDetector()
region_manager = RegionManager()
alarm_system = AlarmSystem()
data_storage = DataStorage()
# 视频捕获
cap = None
# 主页
@app.get("/")
def index(request: Request):
return templates.TemplateResponse("index.html", {"request": request})
# 获取区域设置
@app.get("/api/regions")
def get_regions():
return {"regions": region_manager.regions}
# 保存区域设置
@app.post("/api/regions")
def save_regions(request: Request):
data = request.json()
regions = data.get("regions", [])
region_manager.regions = regions
region_manager.save_regions()
return {"success": True}
# 清除区域设置
@app.delete("/api/regions")
def clear_regions():
region_manager.clear_regions()
return {"success": True}
# 视频流
@app.get("/api/video_feed")
def video_feed():
def generate():
global cap
if cap is None:
cap = cv2.VideoCapture(0) # 使用默认摄像头
while True:
success, frame = cap.read()
if not success:
break
# 绘制告警区域
frame = region_manager.draw_regions(frame)
# 编码为JPEG
ret, buffer = cv2.imencode('.jpg', frame)
frame = buffer.tobytes()
yield (b'--frame\r\n' b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
return Response(generate(), media_type="multipart/x-mixed-replace; boundary=frame")
# 检测图片
@app.post("/api/detect/image")
async def detect_image(request: Request):
from fastapi import UploadFile, File
import io
file = await request.form()
image_file = file.get("image")
if not image_file:
return {"error": "No image provided"}
# 读取图片
contents = await image_file.read()
nparr = np.frombuffer(contents, np.uint8)
frame = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
# 检测
results = detector.detect(frame)
# 统计人员数量和区域内人员
person_count = 0
person_in_region = False
for result in results:
for box in result.boxes:
if box.cls == 0:
person_count += 1
box_coords = list(map(int, box.xyxy[0]))
if region_manager.is_in_region(box_coords):
person_in_region = True
# 检查告警
alarm = alarm_system.check_alarm(person_count) or alarm_system.check_region_alarm(person_in_region)
# 触发告警
if alarm:
if person_in_region:
alarm_system.trigger_alarm("警告,有人员进入 restricted 区域")
else:
alarm_system.trigger_alarm("警告,人员数量超过限制")
# 绘制检测框和区域
frame = detector.draw_boxes(frame, results)
frame = region_manager.draw_regions(frame)
# 添加告警信息
if alarm:
if person_in_region:
cv2.putText(frame, 'ALARM: Person in restricted area!', (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
else:
cv2.putText(frame, 'ALARM: Too many people!', (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
# 保存结果
data_storage.save_detection('image', person_count, frame, alarm)
# 编码为JPEG
ret, buffer = cv2.imencode('.jpg', frame)
frame_bytes = buffer.tobytes()
return Response(content=frame_bytes, media_type="image/jpeg")
# 启动视频检测
@app.post("/api/detect/video/start")
def start_video_detection():
global cap
if cap is None:
cap = cv2.VideoCapture(0) # 使用默认摄像头
return {"success": True}
# 停止视频检测
@app.post("/api/detect/video/stop")
def stop_video_detection():
global cap
if cap is not None:
cap.release()
cap = None
return {"success": True}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8002)
9. 前端界面
前端使用HTML、CSS和JavaScript实现,提供友好的用户界面:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>YOLOv8 安防系统</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
background-color: #f5f5f5;
}
.container {
max-width: 1200px;
margin: 0 auto;
background-color: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
}
h1 {
color: #333;
text-align: center;
}
.section {
margin: 20px 0;
padding: 15px;
border: 1px solid #ddd;
border-radius: 5px;
}
.section h2 {
color: #555;
margin-top: 0;
}
#video-container {
position: relative;
display: inline-block;
margin: 10px 0;
}
#video {
max-width: 100%;
border: 1px solid #ccc;
}
#canvas {
position: absolute;
top: 0;
left: 0;
cursor: crosshair;
}
.controls {
margin-top: 10px;
}
button {
padding: 8px 16px;
margin-right: 10px;
cursor: pointer;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
}
button:hover {
background-color: #45a049;
}
.form-group {
margin: 10px 0;
}
input[type="file"] {
margin: 10px 0;
}
#region-info {
margin-top: 10px;
padding: 10px;
background: #f0f0f0;
border-radius: 5px;
}
.alert {
padding: 10px;
margin: 10px 0;
border-radius: 4px;
}
.alert-success {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.alert-danger {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
</style>
</head>
<body>
<div class="container">
<h1>YOLOv8 安防系统</h1>
<!-- 告警区域设置 -->
<div class="section">
<h2>1. 告警区域设置</h2>
<div id="video-container">
<img id="video" src="/api/video_feed" alt="视频流">
<canvas id="canvas"></canvas>
</div>
<div class="controls">
<button id="start-btn">开始选择区域</button>
<button id="clear-btn">清除所有区域</button>
<button id="save-btn">保存区域设置</button>
</div>
<div id="region-info">
<h3>区域信息</h3>
<p id="region-coords">未选择区域</p>
</div>
</div>
<!-- 图片检测 -->
<div class="section">
<h2>2. 图片检测</h2>
<form id="image-form" enctype="multipart/form-data">
<div class="form-group">
<label for="image">选择图片:</label>
<input type="file" id="image" name="image" accept="image/*">
</div>
<button type="submit">开始检测</button>
</form>
<div id="image-result" style="margin-top: 20px;"></div>
</div>
<!-- 视频流检测 -->
<div class="section">
<h2>3. 视频流检测</h2>
<div class="controls">
<button id="start-video-btn">开始视频检测</button>
<button id="stop-video-btn">停止视频检测</button>
</div>
<div id="video-status" class="alert alert-info" style="display: none;"></div>
</div>
</div>
<script>
// 获取DOM元素
const video = document.getElementById('video');
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const startBtn = document.getElementById('start-btn');
const clearBtn = document.getElementById('clear-btn');
const saveBtn = document.getElementById('save-btn');
const regionCoords = document.getElementById('region-coords');
const imageForm = document.getElementById('image-form');
const imageResult = document.getElementById('image-result');
const startVideoBtn = document.getElementById('start-video-btn');
const stopVideoBtn = document.getElementById('stop-video-btn');
const videoStatus = document.getElementById('video-status');
// 变量
let isDrawing = false;
let startX, startY;
let regions = [];
// 设置canvas大小
function resizeCanvas() {
canvas.width = video.clientWidth;
canvas.height = video.clientHeight;
}
// 初始化
function init() {
resizeCanvas();
window.addEventListener('resize', resizeCanvas);
loadRegions();
}
// 加载区域
function loadRegions() {
fetch('/api/regions')
.then(response => response.json())
.then(data => {
regions = data.regions;
updateRegionInfo();
drawRegions();
});
}
// 开始选择区域
startBtn.addEventListener('click', () => {
isDrawing = true;
startBtn.textContent = '正在选择...';
});
// 鼠标事件
canvas.addEventListener('mousedown', (e) => {
if (!isDrawing) return;
const rect = canvas.getBoundingClientRect();
startX = e.clientX - rect.left;
startY = e.clientY - rect.top;
});
canvas.addEventListener('mousemove', (e) => {
if (!isDrawing) return;
const rect = canvas.getBoundingClientRect();
const currentX = e.clientX - rect.left;
const currentY = e.clientY - rect.top;
// 清除画布
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 绘制已保存的区域
drawRegions();
// 绘制当前正在选择的区域
ctx.strokeStyle = 'red';
ctx.lineWidth = 2;
ctx.strokeRect(startX, startY, currentX - startX, currentY - startY);
});
canvas.addEventListener('mouseup', (e) => {
if (!isDrawing) return;
const rect = canvas.getBoundingClientRect();
const endX = e.clientX - rect.left;
const endY = e.clientY - rect.top;
// 确保坐标顺序正确
const x1 = Math.min(startX, endX);
const y1 = Math.min(startY, endY);
const x2 = Math.max(startX, endX);
const y2 = Math.max(startY, endY);
// 添加新区域
regions.push([x1, y1, x2, y2]);
isDrawing = false;
startBtn.textContent = '开始选择区域';
// 更新区域信息
updateRegionInfo();
// 绘制所有区域
drawRegions();
});
// 清除区域
clearBtn.addEventListener('click', () => {
regions = [];
ctx.clearRect(0, 0, canvas.width, canvas.height);
updateRegionInfo();
// 清除服务器上的区域
fetch('/api/regions', {
method: 'DELETE'
}).then(response => response.json());
});
// 保存区域
saveBtn.addEventListener('click', () => {
if (regions.length === 0) {
alert('请先选择区域');
return;
}
fetch('/api/regions', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ regions })
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('区域保存成功');
} else {
alert('保存失败');
}
});
});
// 绘制所有区域
function drawRegions() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
regions.forEach(region => {
const [x1, y1, x2, y2] = region;
ctx.strokeStyle = 'red';
ctx.lineWidth = 2;
ctx.strokeRect(x1, y1, x2 - x1, y2 - y1);
ctx.fillStyle = 'rgba(255, 0, 0, 0.2)';
ctx.fillRect(x1, y1, x2 - x1, y2 - y1);
});
}
// 更新区域信息
function updateRegionInfo() {
if (regions.length === 0) {
regionCoords.textContent = '未选择区域';
} else {
regionCoords.textContent = `已选择 ${regions.length} 个区域`;
}
}
// 图片检测
imageForm.addEventListener('submit', (e) => {
e.preventDefault();
const formData = new FormData(imageForm);
fetch('/api/detect/image', {
method: 'POST',
body: formData
})
.then(response => response.blob())
.then(blob => {
const url = URL.createObjectURL(blob);
imageResult.innerHTML = `<img src="${url}" alt="检测结果" style="max-width: 100%;">`;
})
.catch(error => {
imageResult.innerHTML = '<div class="alert alert-danger">检测失败</div>';
});
});
// 视频检测
startVideoBtn.addEventListener('click', () => {
fetch('/api/detect/video/start')
.then(response => response.json())
.then(data => {
if (data.success) {
videoStatus.textContent = '视频检测已启动';
videoStatus.className = 'alert alert-success';
videoStatus.style.display = 'block';
}
});
});
stopVideoBtn.addEventListener('click', () => {
fetch('/api/detect/video/stop')
.then(response => response.json())
.then(data => {
if (data.success) {
videoStatus.textContent = '视频检测已停止';
videoStatus.className = 'alert alert-info';
videoStatus.style.display = 'block';
}
});
});
// 定期刷新视频流
setInterval(() => {
video.src = '/api/video_feed?' + new Date().getTime();
}, 100);
// 初始化
init();
</script>
</body>
</html>
使用示例
1. 使用Web界面
-
启动服务器:
python main.py -
访问网页:
打开浏览器,访问http://localhost:8002 -
设置告警区域:
- 在"告警区域设置"部分,点击"开始选择区域"按钮
- 在视频画面上拖拽鼠标框选告警区域
- 可以设置多个告警区域
- 点击"保存区域设置"按钮保存设置
-
图片检测:
- 在"图片检测"部分,点击"选择图片"按钮上传图片
- 点击"开始检测"按钮进行检测
- 查看检测结果,包括人员标记和告警信息
-
视频流检测:
- 在"视频流检测"部分,点击"开始视频检测"按钮
- 系统会实时检测摄像头视频流
- 当人员进入告警区域时,会触发语音告警
2. 使用命令行
2.1 处理图片
system = SecuritySystem()
system.process_image('test.jpg')
2.2 处理MP4视频
system = SecuritySystem()
system.process_video('test.mp4')
2.3 处理RTSP视频流
system = SecuritySystem()
rtsp_url = 'rtsp://username:password@ip:port/stream'
system.process_rtsp(rtsp_url)
系统优化建议
- 模型选择:根据硬件性能选择合适的YOLOv8模型(nano、small、medium、large、xlarge)
- 帧率优化:对于实时视频流,可以降低检测频率或使用更轻量级的模型
- 告警策略:可以根据不同区域设置不同的人员数量阈值
- 数据管理:定期清理数据库和图片文件,避免存储空间不足
- 远程监控:可以添加网络接口,实现远程查看和控制
- Web性能优化:
- 使用更小的视频分辨率减少网络带宽使用
- 采用WebSocket代替HTTP轮询提高实时性
- 添加缓存机制减少重复计算
- 安全性:
- 添加用户认证机制
- 限制API访问频率
- 使用HTTPS加密传输
实际应用场景
- 商场/超市:监测人流密度,避免拥挤
- 工厂/仓库:确保安全区域不超过规定人数
- 学校/医院:监控重点区域,防止意外发生
- 小区/园区:实时监测进出人员,提高安全性
- 交通枢纽:监测客流情况,优化人员疏导
总结
本文介绍了如何使用 YOLOv8 构建一个完整的安防系统,实现对人员的实时检测、预警告警、标记,并将数据记录到 SQLite 数据库。该系统支持多种输入源,包括图片、MP4视频和RTSP视频流,可广泛应用于各种安防场景。
特别地,我们还使用FastAPI构建了一个Web界面,提供了以下功能:
- 通过网页直观地设置告警区域
- 实时查看视频流和检测结果
- 上传图片进行检测
- 启动/停止视频检测
通过本文的代码示例,你可以快速搭建一个功能完整的智能安防系统,提高监控效率,减少人工成本,为安全保障提供更可靠的技术支持。系统采用模块化设计,易于扩展和维护,可以根据实际需求进行定制。
扩展阅读
声明:本文代码仅作为示例,实际应用中需要根据具体场景进行调整和优化。
最后给大家推荐一个封装的库,能够方便地调用
VisionForgeSDK 中的模型服务
介绍
VisionForge SDK 为用户提供新一代人工智能解决方案,释放数据的真正潜力;
1、火灾监测识别系统:可用于森林、厂区等防火区域;
2、垃圾监测识别系统:支持常见垃圾监测;
3、人脸轨迹提取系统:根据视频画面提取人员的时间活动轨迹,追踪目标;
4、智慧工地监测系统:实时监控施工场景,保障工人安全,提高管理效率;
5、头盔监测识别系统:头盔佩戴等;
6、打电话识别,应用于加油站等场景;
7、人员打架,应用于安全校园,街道安全等;
8、吸烟识别,应用于加油站、仓库等场景;
9、行人识别,应用于夜间安防区域告警;
10、人员跌倒识别,应用于老人看护等智能应用;
11、翻越围栏识别,应用于安全校园,厂区安防;
12、街头流动商贩识别,应用于城市管理监管;
【2026产品开发计划,内测中】
1、支持告警消息,邮件推送;
2、开启钉钉群聊 机器人推送,单人消息推送,需要到钉钉官方开通调用授权;
3、支持告警消息,微信公众号推送;
4、短信发送告警消息【需联系申请短信模板,定制】;
【预览效果图】
1、安全帽检测
2、垃圾检测
更新日志
2025.10.30 新增《006helmet安全帽检测API调用说明文档.md》,兼容 类型006safeHat等同于006helmet,建议使用安全帽 006helmet模型,请尽快升级使用到模型006helmet;
2025.10.27 新增《005fight检测API调用说明文档.md》
2024.08.30 新增《004callphone检测API调用说明文档.md》
2024.08.01 新增《003facedetect检测API调用说明文档.md》
2023.05.27 新增《002garbage检测API调用说明文档.md》
2023.05.01 新增《001fire检测API调用说明文档.md》
软件架构
软件架构说明:支持python sdk/C# SDK/java SDK,根据需要选择合适的调用示例,下面提供了部分枚举值。平台会不定期升级,以最新的文档为准。
SDK目录介绍
| SDK类型 | 文件名 | 推荐开发工具 |
|---|---|---|
| pythonSDK | VisionForge SDK_python.py | Pycharm |
| csharpSDK | C# SDK | vs2022 |
| javaSDK | java SDK | IdeaJ |
【下载地址】
通过网盘分享的文件:P0130VisionSDK机器视觉识别
链接: https://pan.baidu.com/s/1GENsMAOhbHDu3TNO7Q3ybQ?pwd=v7zh 提取码: v7zh
项目地址:https://gitee.com/51diysoft/VisionForgeSDK
YOLOv8官方文档:https://docs.ultralytics.com/
OpenCV官方文档:https://docs.opencv.org/
参考文章
VisionForgeSDK:基于YOLOv8的新一代人工智能视觉检测解决方案
感谢关注,我们将持续优化和升级,为用户提供更多的调用示例。后续分享更多的yolo相关教程,人脸识别,姿态识别等场景的应用!
更多推荐
所有评论(0)