Suivi des piétons et détection de chute
avant-propos
Utilisez les méthodes YOLO V3 Tiny, Deepsort, ST-GCN pour le suivi des piétons et la détection des chutes. Parmi eux, YOLO V3 Tiny est utilisé pour la détection des piétons, DeepSort est utilisé pour le suivi et ST-GCN est utilisé pour la détection des comportements.
Présentation du projet
Pour le projet de détection de chute, il existe une discrimination directe via le cadre de détection YOLO et une reconnaissance via la pose ouverte, mais les
exigences relatives à la direction de la caméra sont très élevées et l'effet de détection d'angle du côté est idéal, mais la détection d'angle de la caméra située en hauteur ou à l'arrière L'effet est médiocre. L'utilisation de ST-GCN dans ce projet a un meilleur effet sur la détection des chutes à différents angles.
Utilisez Tiny YOLO oneclass pour détecter chaque personne dans le cadre, et utilisez AlphaPose pour obtenir la pose squelettique, puis utilisez le modèle ST-GCN pour prédire l'action dans toutes les 30 images suivies par chaque personne.
Ce projet forme un nouveau modèle à classe unique TinyYOLO pour détecter uniquement les objets humains et réduit la taille du modèle. Formé à l'aide de l'ensemble de données de points clés de personne COCO augmenté par rotation pour une détection de personne plus robuste sous différentes poses d'angle.
Pour la reconnaissance d'action, utilisez les données du jeu de données de détection de chute Le2i (salle de café, maison) pour extraire les poses squelettiques via AlphaPose, et marquez manuellement chaque cadre d'action pour former le modèle ST-GCN.
Prend désormais en charge 7 actions : se tenir debout, marcher, s'asseoir, s'allonger, se lever, s'asseoir, tomber.
Présentation du ST-GCN
Étant donné les informations de séquence de squelette d'une vidéo d'action, construisez d'abord une structure de graphe représentant les informations de séquence de squelette, l'entrée de ST-GCN est le vecteur de coordonnées conjoint sur le nœud de graphe ; puis une série d'opérations de convolution de graphe spatio-temporelles pour extraire le haut- fonctionnalités de niveau ; enfin, utilisez le classificateur SofMax pour obtenir la classification d'action correspondante. L'ensemble du processus réalise une formation de bout en bout.
GCN nous aide à apprendre les caractéristiques locales des articulations adjacentes dans l'espace. Sur cette base, nous devons apprendre les caractéristiques locales des changements conjoints dans le temps. Comment superposer les caractéristiques de synchronisation pour Graph est l'un des problèmes rencontrés par les réseaux convolutifs de graphes. Il existe deux idées principales pour la recherche dans ce domaine : la convolution temporelle (TCN) et le modèle de séquence (LSTM).
ST-GCN utilise TCN. En raison de la forme fixe, l'opération de convolution temporelle peut être complétée à l'aide de couches convolutives traditionnelles. Pour faciliter la compréhension, l'opération de convolution d'une image peut être comparée. La forme des trois dernières dimensions de la carte des caractéristiques de ST-GCN est (C, V, T), qui correspond à la forme (C, W, H) de la carte des caractéristiques de l'image.
Le numéro de canal C de l'image correspond au numéro de caractéristique C de l'articulation.
La largeur W de l'image correspond au nombre V d'images clés.
La hauteur H de l'image correspond au nombre T de joints.
Dans la convolution d'image, la taille du noyau de convolution est "w" × "1", et la convolution de w lignes de pixels et 1 colonne de pixels est terminée à chaque fois. "stride" est s, puis déplace s pixels à chaque fois, puis effectue la convolution de la rangée de pixels suivante après avoir terminé une rangée.
Dans la convolution temporelle, la taille du noyau de convolution est "temporal_kernel_size" × "1", et la convolution de 1 nœud et des images clés temporal_kernel_size est terminée à chaque fois. Si "stride" est 1, il se déplace d'une image à la fois, complète 1 nœud et effectue la convolution du nœud suivant. Voici la formation :
Les données d'entrée sont d'abord normalisées par lots, puis passées à travers 9 unités ST-GCN, suivies d'un regroupement global pour obtenir le vecteur de caractéristiques à 256 dimensions de chaque séquence, et enfin classées avec la fonction SoftMax pour obtenir l'étiquette finale.
Chaque ST-GCN adopte la structure de Resnet. La sortie des trois premières couches a 64 canaux, les trois couches intermédiaires ont 128 canaux et les trois dernières couches ont 256 canaux. Après avoir traversé la structure ST-CGN à chaque fois, avec une probabilité de 0,5 Définissez aléatoirement l'abandon des caractéristiques, les foulées des 4e et 7e couches de convolution temporelle sur 2. Formé avec SGD, le taux d'apprentissage est de 0,01 et le taux d'apprentissage diminue de 0,1 toutes les 10 époques.
Lien papier ST-GCN : https://arxiv.org/abs/1801.07455
corps de code
import os
import cv2
import time
import torch
import argparse
import numpy as np
from Detection.Utils import ResizePadding
from CameraLoader import CamLoader, CamLoader_Q
from DetectorLoader import TinyYOLOv3_onecls
from PoseEstimateLoader import SPPE_FastPose
from fn import draw_single
from Track.Tracker import Detection, Tracker
from ActionsEstLoader import TSSTG
# source = '../Data/test_video/test7.mp4'
# source = '../Data/falldata/Home/Videos/video (2).avi' # hard detect ./output/test5.mp4
source = './output/test4.mp4'
# source = 2
def preproc(image):
"""preprocess function for CameraLoader.
"""
image = resize_fn(image)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
return image
def kpt2bbox(kpt, ex=20):
"""Get bbox that hold on all of the keypoints (x,y)获取保持所有关键点(x,y)的bbox
kpt: array of shape `(N, 2)`,
ex: (int) expand bounding box,
"""
return np.array((kpt[:, 0].min() - ex, kpt[:, 1].min() - ex,
kpt[:, 0].max() + ex, kpt[:, 1].max() + ex))
if __name__ == '__main__':
par = argparse.ArgumentParser(description='Human Fall Detection Demo.')
par.add_argument('-C', '--camera', default=source, # required=True, # default=2,
help='Source of camera or video file path.')
par.add_argument('--detection_input_size', type=int, default=384,
help='Size of input in detection model in square must be divisible by 32 (int).')
par.add_argument('--pose_input_size', type=str, default='224x160',
help='Size of input in pose model must be divisible by 32 (h, w)')
par.add_argument('--pose_backbone', type=str, default='resnet50', help='Backbone model for SPPE FastPose model.')
par.add_argument('--show_detected', default=False, action='store_true', help='Show all bounding box from detection.')
par.add_argument('--show_skeleton', default=True, action='store_true', help='Show skeleton pose.')
par.add_argument('--save_out', type=str, default='./output/output7.mp4', help='Save display to video file.')
par.add_argument('--device', type=str, default='cuda', help='Device to run model on cpu or cuda.')
args = par.parse_args()
device = args.device
# DETECTION MODEL.检测模型
inp_dets = args.detection_input_size
detect_model = TinyYOLOv3_onecls(inp_dets, device=device)
# POSE MODEL.姿势模型
inp_pose = args.pose_input_size.split('x')
inp_pose = (int(inp_pose[0]), int(inp_pose[1]))
pose_model = SPPE_FastPose(args.pose_backbone, inp_pose[0], inp_pose[1], device=device)
# Tracker.跟踪器
max_age = 30
tracker = Tracker(max_age=max_age, n_init=3)
# Actions Estimate.
action_model = TSSTG()
resize_fn = ResizePadding(inp_dets, inp_dets)
cam_source = args.camera
if type(cam_source) is str and os.path.isfile(cam_source):
# Use loader thread with Q for video file.
cam = CamLoader_Q(cam_source, queue_size=1000, preprocess=preproc).start()
else:
# Use normal thread loader for webcam.
cam = CamLoader(int(cam_source) if cam_source.isdigit() else cam_source,
preprocess=preproc).start()
# frame_size = cam.frame_size
# scf = torch.min(inp_size / torch.FloatTensor([frame_size]), 1)[0]
outvid = False
if args.save_out != '':
outvid = True
codec = cv2.VideoWriter_fourcc(*'mp4v')
print((inp_dets * 2, inp_dets * 2))
writer = cv2.VideoWriter(args.save_out, codec, 25, (inp_dets * 2, inp_dets * 2))
fps_time = 0
f = 0
while cam.grabbed():
f += 1
frame = cam.getitem()
image = frame.copy()
# Detect humans bbox in the frame with detector model.使用检测器模型检测帧中的人类bbox
detected = detect_model.detect(frame, need_resize=False, expand_bb=10)
# Predict each tracks bbox of current frame from previous frames information with Kalman filter.从先前帧信息预测当前帧的每个轨迹bbox
tracker.predict()
# Merge two source of predicted bbox together.将两个预测bbox源合并在一起
for track in tracker.tracks:
det = torch.tensor([track.to_tlbr().tolist() + [0.5, 1.0, 0.0]], dtype=torch.float32)
detected = torch.cat([detected, det], dim=0) if detected is not None else det
detections = [] # List of Detections object for tracking.用于跟踪的检测对象列表
if detected is not None:
# detected = non_max_suppression(detected[None, :], 0.45, 0.2)[0]
# Predict skeleton pose of each bboxs.预测每个BBOX的骨骼姿势。
poses = pose_model.predict(frame, detected[:, 0:4], detected[:, 4])
# Create Detections object.创建对象的检测。
detections = [Detection(kpt2bbox(ps['keypoints'].numpy()),
np.concatenate((ps['keypoints'].numpy(),
ps['kp_score'].numpy()), axis=1),
ps['kp_score'].mean().numpy()) for ps in poses]
# VISUALIZE.可视化
if args.show_detected:
for bb in detected[:, 0:5]:
frame = cv2.rectangle(frame, (bb[0], bb[1]), (bb[2], bb[3]), (0, 0, 255), 1)
# Update tracks by matching each track information of current and previous frame or通过匹配当前帧和前一帧的每个轨迹信息来更新轨迹
# create a new track if no matched.如果不匹配,则创建新轨迹。
tracker.update(detections)
# Predict Actions of each track.预测每个轨道的动作
for i, track in enumerate(tracker.tracks):
if not track.is_confirmed():
continue
track_id = track.track_id
bbox = track.to_tlbr().astype(int)
center = track.get_center().astype(int)
action = 'pending..'
clr = (0, 255, 0)
# Use 30 frames time-steps to prediction.使用30帧时间步长进行预测
if len(track.keypoints_list) == 30:
pts = np.array(track.keypoints_list, dtype=np.float32)
out = action_model.predict(pts, frame.shape[:2])
action_name = action_model.class_names[out[0].argmax()]
action = '{}: {:.2f}%'.format(action_name, out[0].max() * 100)
if action_name == 'Fall Down':
clr = (255, 0, 0)
elif action_name == 'Lying Down':
clr = (255, 200, 0)
# VISUALIZE.可视化
if track.time_since_update == 0:
if args.show_skeleton:
frame = draw_single(frame, track.keypoints_list[-1])
frame = cv2.rectangle(frame, (bbox[0], bbox[1]), (bbox[2], bbox[3]), (0, 255, 0), 1)
frame = cv2.putText(frame, str(track_id), (center[0], center[1]), cv2.FONT_HERSHEY_COMPLEX, 0.4, (255, 0, 0), 2)
frame = cv2.putText(frame, action, (bbox[0] + 5, bbox[1] + 15), cv2.FONT_HERSHEY_COMPLEX, 0.4, clr, 1)
# Show Frame.
frame = cv2.resize(frame, (0, 0), fx=2., fy=2.)
frame = cv2.putText(frame, '%d, FPS: %f' % (f, 1.0 / (time.time() - fps_time)), (10, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1)
frame = frame[:, :, ::-1]
fps_time = time.time()
if outvid:
writer.write(frame)
cv2.imshow('frame', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# Clear resource.
cam.stop()
if outvid:
writer.release()
cv2.destroyAllWindows()
Effet de détection
référence
AlphaPose : https://github.com/Amanbhandula/AlphaPose
ST-GCN : https://github.com/yysijie/st-gcn
Résumer
Comparé à d'autres algorithmes de détection de chute, cet algorithme a un meilleur effet de détection et peut détecter sept états. Cependant, lorsqu'il y a beaucoup de personnes dans l'écran de détection, la fréquence d'images diminue.Au lieu de cela, utilisez YOLO + mediapipe pour la détection, et la fréquence d'images n'augmente pas de manière significative. Le problème de la réduction de la fréquence d'images peut également être résolu en mettant à niveau la configuration matérielle.