跌倒行为检测算法(含源码文件)
1.国内外研究现状
老年人跌倒检测是目前国内外研究的热点领域之一。在国内外,已经有许多研究者利用计算机视觉、深度学习等技术,开展了跌倒检测技术的研究。目前主要的研究工作集中在三个方面:第一,基于智能穿戴设备的老年人跌倒检测,第二,利用环境传感器等硬件设备对老年人进行实时监测,对跌倒事件进行识别和处理;第三,利用计算机视觉和深度学习等先进技术,并安装摄像头用来实时捕捉老年人的运动状态,通过分析对比判断是否发生跌倒,如下图为跌倒检测类别图。
而本文将基于计算机视觉的跌倒行为检测算法进行展开阐述
2.ResNet模型
ResNet是一种卷积神经网络,它通过引入残差连接来解决深度网络的梯度消失问题。ResNet的核心思想是将输入和输出的层相连,以这样的方式,输入和输出的层之间的差值可以直接传播到输出层,从而避免梯度消失。
ResNet模型通过引入残差学习的概念,有效地解决了深度神经网络中的梯度消失和梯度爆炸问题。残差学习的思想是,网络的输入和输出之间的差异可以通过添加一个残差块来学习。残差的基本结构如下图所示:
3.LSTM模型
LSTM的核心思想是通过引入“门”结构来控制信息的流动。 具体来说,LSTM有三个门:输入门、遗忘门和输出门。 输入门决定了新信息的进入,遗忘门决定了旧信息的遗忘,而输出门则决定了最终输出的信息。通过这些门,LSTM能够选择性地记住或遗忘信息,从而在处理序列数据时具有更强的鲁棒性
4.实现步骤
4.1具体流程
该流程图展示了跌倒检测系统的整体架构,从开始到结束依次经过数据采集模块、预处理模块和特征提取模块,然后在模型训练模块分为LSTM模型和ResNet模型两个分支进行并行训练,最后两个训练好的模型都连接到预测模块进行跌倒行为的识别,形成一个完整的处理流程,系统整体架构如下图。
4.2环境搭建
深度学习框架:PyTorch 2.0.1
CUDA工具包:CUDA 11.7
视觉处理库:OpenCV 4.8.0
姿态估计:Mediapipe 0.10.3
还有部分其他库就不一一展示了。
4.3数据展示
我们使用的是提取好的特征数据
4.4数据预处理
(1)特征提取
这里可能会有人有疑惑,为什么我们使用的是提取好的特征数据,这里还会有数据预处理的流程图,在此我解释一下,是因为我们训练的时候对数据只做归一化,数据增强等操作,但是在模型预测,模型评估阶段我们使用的是视频数据,因此这里我们需要特征提取。
对于ResNet模型,我们对每个跌倒样本生成两个增强变体,通过添加噪声因子为0.05的高斯噪声来实现数据增强,这种方法可以在保持原始特征主要信息的同时增加数据的多样性。
对于LSTM模型,除了使用相同的高斯噪声增强方法外,还通过滑动窗口技术将特征组织成长度为20的序列,这种方式既增加了训练样本数量,又保持了时序特征的连续性。通过这些数据增强手段,我们有效缓解了数据集中正常样本和跌倒样本之间的类别不平衡问题,提高了模型的泛化能力
4.5 模型预测与评估
以上是模型预测与评估的主要步骤流程图展示
以下展示ResNet模型与LSTM模型的部分代码
ResNet模型预测部分代码:
import cv2
import torch
import numpy as np
import mediapipe as mp
from fall_detection_train import FallDetectionModel
import time
class FallDetector:
def __init__(self, model_path):
try:
self.device = torch.device('cpu')
checkpoint = torch.load(model_path, map_location=self.device)
self.model = FallDetectionModel(8)
self.model.load_state_dict(checkpoint['model_state_dict'])
self.model.to(self.device)
self.model.eval()
self.scaler = checkpoint['scaler']
# 初始化 MediaPipe
self.mp_pose = mp.solutions.pose
self.mp_draw = mp.solutions.drawing_utils
self.pose = self.mp_pose.Pose(
static_image_mode=False,
model_complexity=2,
min_detection_confidence=0.5,
min_tracking_confidence=0.5
)
except Exception as e:
print(f"初始化失败: {str(e)}")
raise e
def extract_features(self, frame):
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
results = self.pose.process(frame_rgb)
if results.pose_landmarks:
landmarks = results.pose_landmarks.landmark
# 计算边界框
h, w, _ = frame.shape
x_coords = [lm.x * w for lm in landmarks]
y_coords = [lm.y * h for lm in landmarks]
x_min, x_max = min(x_coords), max(x_coords)
y_min, y_max = min(y_coords), max(y_coords)
# 保存边界框坐标
self.bbox = (int(x_min), int(y_min), int(x_max), int(y_max))
width = x_max - x_min
height = y_max - y_min
# 计算特征
height_width_ratio = height / width if width > 0 else 0
bbox_area = width * height
frame_area = h * w
bbox_occupancy = bbox_area / frame_area
features = np.array([[
height_width_ratio,
height_width_ratio,
bbox_occupancy,
width / w,
height / h,
height,
h - y_max,
(h - y_max) / height if height > 0 else 0
]])
features = self.scaler.transform(features)
return features, results
return None, None
def draw_skeleton(self, frame, results):
if results.pose_landmarks:
# 绘制红色边界框
cv2.rectangle(frame,
(self.bbox[0], self.bbox[1]),
(self.bbox[2], self.bbox[3]),
(0, 0, 255), 2) # 红色BGR
# 绘制蓝色骨骼
connections = self.mp_pose.POSE_CONNECTIONS
landmarks = results.pose_landmarks.landmark
h, w, _ = frame.shape
LSTM模型预测部分代码
import os
import logging
import warnings
# 完全禁用所有警告和日志
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' # 禁用警告
warnings.filterwarnings('ignore')
logging.getLogger('mediapipe').setLevel(logging.ERROR)
# 禁用 absl 日志
import absl.logging
absl.logging.set_verbosity(absl.logging.ERROR)
logging.root.removeHandler(absl.logging._absl_handler)
absl.logging._warn_preinit_stderr = False
# 禁用所有日志输出
def disable_logging():
logging.getLogger().setLevel(logging.ERROR)
os.environ["PYTHONWARNINGS"] = "ignore"
disable_logging()
import cv2
import torch
import numpy as np
import mediapipe as mp
from fall_detection_train_lstm import FallDetectionLSTM
import time
from collections import deque
class FallDetectorLSTM:
def __init__(self, model_path):
try:
self.device = torch.device('cpu')
checkpoint = torch.load(model_path, map_location=self.device)
# 初始化LSTM模型
self.model = FallDetectionLSTM(input_size=8) # 8个特征
self.model.load_state_dict(checkpoint['model_state_dict'])
self.model.to(self.device)
self.model.eval()
self.scaler = checkpoint['scaler']
self.sequence_length = checkpoint['sequence_length']
# 初始化MediaPipe
self.mp_pose = mp.solutions.pose
self.mp_draw = mp.solutions.drawing_utils
self.pose = self.mp_pose.Pose(
static_image_mode=False,
model_complexity=2,
min_detection_confidence=0.5,
min_tracking_confidence=0.5
)
# 初始化特征序列缓冲区
self.feature_buffer = deque(maxlen=self.sequence_length)
except Exception as e:
print(f"初始化失败: {str(e)}")
raise e
def extract_features(self, frame):
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
results = self.pose.process(frame_rgb)
if results.pose_landmarks:
landmarks = results.pose_landmarks.landmark
h, w, _ = frame.shape
x_coords = [lm.x * w for lm in landmarks]
y_coords = [lm.y * h for lm in landmarks]
x_min, x_max = min(x_coords), max(x_coords)
y_min, y_max = min(y_coords), max(y_coords)
self.bbox = (int(x_min), int(y_min), int(x_max), int(y_max))
width = x_max - x_min
height = y_max - y_min
# 计算特征
height_width_ratio = height / width if width > 0 else 0
bbox_area = width * height
frame_area = h * w
bbox_occupancy = bbox_area / frame_area
features = np.array([[
height_width_ratio,
height_width_ratio,
bbox_occupancy,
width / w,
height / h,
height,
h - y_max,
(h - y_max) / height if height > 0 else 0
]])
features = self.scaler.transform(features)
return features, results
return None, None
def draw_skeleton(self, frame, results):
if results.pose_landmarks:
cv2.rectangle(frame,
(self.bbox[0], self.bbox[1]),
(self.bbox[2], self.bbox[3]),
(0, 0, 255), 2)
connections = self.mp_pose.POSE_CONNECTIONS
landmarks = results.pose_landmarks.landmark
h, w, _ = frame.shape
for connection in connections:
start_idx = connection[0]
end_idx = connection[1]
start_point = (int(landmarks[start_idx].x * w),
int(landmarks[start_idx].y * h))
end_point = (int(landmarks[end_idx].x * w),
int(landmarks[end_idx].y * h))
cv2.line(frame, start_point, end_point, (255, 0, 0), 4)
for landmark in landmarks:
point = (int(landmark.x * w), int(landmark.y * h))
cv2.circle(frame, point, 5, (255, 0, 0), -1)
5.结果展示
最终两个模型效果形如下面这个动图,但LSTM整体效果没有ResNet模型效果好
6.代码解释
LSM模型训练代码:fall_detection_train_lstm
LSTM模型预测代码:fall_detection_predict_lstm
ResNet模型训练代码: fall_detection_train
ResNet模型预测代码: fall_detection_predict
两个模型评估相关图像生成代码:generate_results