跌倒行为检测算法(含完整源码)

跌倒行为检测算法(含源码文件)

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值