摔倒 姿态分类

来源

工业界行为识别:摔倒识别数据集收集技巧

  1. 使用手写AI的摔倒检测的关键点分类模型(gcn分类不如直接全连接)
  2. 修改了数据集的格式
  3. 关键点采用yolov11模型的关键点检测
  4. 数据集来源于手写AI
  5. 训练3个类别的精度不高,改成了只训练站立和摔倒两个类别
  6. 实验效果对于俯拍和非标准站立的误识别较高,猜测可以通过丰富数据集。
  7. 对于遮挡的识别率也很低
  8. 去掉yolov11识别的关键点分数作为训练数据,得到的结果和有关键点分数的差不多

数据集制作

  1. 使用yolov11模型检测画面中的人的关键点,yolov11使用的是coco的数据集,有17个关键点。
  2. 将关键点模型以及分数检测到的数据保存到csv文件中。
  3. 对于头部关键点只保留鼻子的关键点。
  4. 数据预处理(归一化):将关键点的坐标由全图映射到检测到人的框的相对坐标。x坐标除以人体框的宽度,y坐标除以人体框的宽度,再减去0.5,让坐标以人体为中心点。
import os
import csv
import random
from pydantic import BaseModel
from ultralytics import YOLO

header = [
    'label', 'width', 'height', 'path',
    # nose 
    'nose_x', 'nose_y', 'nose_score',
    # left shoulder
    'left_shoulder_x', 'left_shoulder_y', 'left_shoulder_score',
    # right sholder
    'right_shoulder_x', 'right_shoulder_y', 'right_shoulder_score',
    # left elbow
    'left_elbow_x', 'left_elbow_y', 'left_elbow_score',
    # rigth elbow
    'right_elbow_x', 'right_elbow_y', 'right_elbow_score',
    # left wrist
    'left_wrist_x', 'left_wrist_y', 'left_wrist_score',
    # right wrist
    'right_wrist_x', 'right_wrist_y', 'right_wrist_score',
    # left hip 
    'left_hip_x', 'left_hip_y', 'left_hip_score',
    # right hip
    'right_hip_x', 'right_hip_y', 'right_hip_score',
    # left knee
    'left_knee_x', 'left_knee_y', 'left_knee_score',
    # right knee
    'right_knee_x', 'right_knee_y', 'right_knee_score',
    # left ankle
    'left_ankle_x', 'left_ankle_y', 'left_ankle_score',
    # right ankle
    'right_ankle_x', 'right_ankle_y', 'right_ankle_score'
]

class GetKeypoint(BaseModel):
    NOSE:           int = 0
    LEFT_EYE:       int = 1
    RIGHT_EYE:      int = 2
    LEFT_EAR:       int = 3
    RIGHT_EAR:      int = 4
    LEFT_SHOULDER:  int = 5
    RIGHT_SHOULDER: int = 6
    LEFT_ELBOW:     int = 7
    RIGHT_ELBOW:    int = 8
    LEFT_WRIST:     int = 9
    RIGHT_WRIST:    int = 10
    LEFT_HIP:       int = 11
    RIGHT_HIP:      int = 12
    LEFT_KNEE:      int = 13
    RIGHT_KNEE:     int = 14
    LEFT_ANKLE:     int = 15
    RIGHT_ANKLE:    int = 16

get_keypoint = GetKeypoint()

def extract_keypoint(keypoint):
    # nose
    nose_x, nose_y, nose_score = keypoint[get_keypoint.NOSE]
    # eye
    left_eye_x, left_eye_y, left_eye_score = keypoint[get_keypoint.LEFT_EYE]
    right_eye_x, right_eye_y, right_eye_score = keypoint[get_keypoint.RIGHT_EYE]
    # ear
    left_ear_x, left_ear_y, left_ear_score = keypoint[get_keypoint.LEFT_EAR]
    right_ear_x, right_ear_y, right_ear_score = keypoint[get_keypoint.RIGHT_EAR]
    # shoulder
    left_shoulder_x, left_shoulder_y, left_shoulder_score = keypoint[get_keypoint.LEFT_SHOULDER]
    right_shoulder_x, right_shoulder_y, right_shoulder_score = keypoint[get_keypoint.RIGHT_SHOULDER]
    # elbow
    left_elbow_x, left_elbow_y, left_elbow_score = keypoint[get_keypoint.LEFT_ELBOW]
    right_elbow_x, right_elbow_y, right_elbow_score = keypoint[get_keypoint.RIGHT_ELBOW]
    # wrist
    left_wrist_x, left_wrist_y, left_wrist_score = keypoint[get_keypoint.LEFT_WRIST]
    right_wrist_x, right_wrist_y, right_wrist_score = keypoint[get_keypoint.RIGHT_WRIST]
    # hip
    left_hip_x, left_hip_y, left_hip_score = keypoint[get_keypoint.LEFT_HIP]
    right_hip_x, right_hip_y,right_hip_score = keypoint[get_keypoint.RIGHT_HIP]
    # knee
    left_knee_x, left_knee_y,left_knee_score = keypoint[get_keypoint.LEFT_KNEE]
    right_knee_x, right_knee_y, right_knee_score = keypoint[get_keypoint.RIGHT_KNEE]
    # ankle
    left_ankle_x, left_ankle_y, left_ankle_score = keypoint[get_keypoint.LEFT_ANKLE]
    right_ankle_x, right_ankle_y, right_ankle_score = keypoint[get_keypoint.RIGHT_ANKLE]
    
    return [
        nose_x, nose_y, nose_score,
        left_shoulder_x, left_shoulder_y, left_shoulder_score,
        right_shoulder_x, right_shoulder_y,right_shoulder_score,
        left_elbow_x, left_elbow_y, left_elbow_score,
        right_elbow_x, right_elbow_y, right_elbow_score,
        left_wrist_x, left_wrist_y,left_wrist_score,
        right_wrist_x, right_wrist_y,right_wrist_score,
        left_hip_x, left_hip_y, left_hip_score,
        right_hip_x, right_hip_y, right_hip_score,
        left_knee_x, left_knee_y, left_knee_score,
        right_knee_x, right_knee_y, right_knee_score,
        left_ankle_x, left_ankle_y, left_ankle_score,
        right_ankle_x, right_ankle_y, right_ankle_score
    ]

# 最好保证图片中只有一个人
def detect(root_path, poses = ["lying", "stand"]):
    model = YOLO("yolo11s-pose.pt")
    
    dataset_csv = []
    for pose in poses:
        image_path = os.path.join(root_path, pose)
        # Predict with the model
        image_list = os.listdir(image_path)
        for im in image_list:
            image = os.path.join(image_path, im)
            results = model(image)
            if len(results) == 0:continue
            results.sort(key=lambda res: res.boxes.cpu().numpy().xywh[0][2] * res.boxes.cpu().numpy().xywh[0][3])
            x1, y1, x2, y2 = results[-1].boxes.xyxy.cpu().numpy().tolist()[0]
            x, y, w, h = x1, y1, x2 - x1, y2 - y1
            mykeypoints = results[-1].keypoints.data.cpu().numpy().tolist()[0]
            n_keypoints = [[(kp[0] - x) / w - 0.5, (kp[1] - y) / h - 0.5, kp[2]] if kp[0] > 0 and kp[1] > 0 else kp[:3] for kp in mykeypoints]
            n_keypoints = extract_keypoint(n_keypoints)
            n_keypoints.insert(0, pose)
            n_keypoints.insert(1, w)
            n_keypoints.insert(2, h)
            n_keypoints.insert(3, image)
            dataset_csv.append(n_keypoints)
    return dataset_csv

if __name__ == "__main__":
    root_path = "images/"
    dataset_csv = detect(root_path)
    random.shuffle(dataset_csv)
    
    percent = 0.8
    index = int(len(dataset_csv) * 0.8)
    
    with open('train.csv', 'w', encoding='UTF8', newline='') as f:
        writer = csv.writer(f)
        # write the header
        writer.writerow(header)
        # write multiple rows
        writer.writerows(dataset_csv[:index])

    
    with open('val.csv', 'w', encoding='UTF8', newline='') as f:
        writer = csv.writer(f)
        # write the header
        writer.writerow(header)
        # write multiple rows
        writer.writerows(dataset_csv[index:])

模型训练

import os
from tqdm import tqdm

import torch
import torch.nn as nn
import torch.optim
import torch.nn.functional as F
from torch.utils.data import DataLoader

import cv2
import numpy as np
import pandas as pd

class Model(nn.Module):
    def __init__(self, key_points_size, nfeature, nclass):
        super().__init__()
        self.fc1 = nn.Linear(key_points_size * nfeature, 512)
        self.fc2 = nn.Linear(512, nclass)

    def forward(self, x):
        x = x.view(-1, int(x.size(1) * x.size(2)))
        x = F.relu(self.fc1(x))
        x = F.dropout(x, 0.7, training=self.training)
        return self.fc2(x)


# categories : {"lying" : 0, "stand" : 1}
class Dataset:
    def __init__(self, csv_path, augment, categories):
        df = pd.read_csv(csv_path)
        self.augment = augment
        df = np.array(df)
        self.items = df
        self.categories = categories

    def __getitem__(self, index):
        x = self.items[index][4:]
        y = self.items[index][0]
        y = self.categories[y]
        x = np.array([x.copy()]).reshape(-1, 3).astype(float)

        if self.augment:
            if np.random.random() < 0.8:
                value = np.random.random((len(x), 2)) * 0.2 - 0.1
                x[:, :2] += value

            if np.random.random() < 0.8:
                mask = np.random.binomial(1, size=x.shape, p=0.2).astype(bool)
                x[mask] = 0

        return torch.FloatTensor(x), torch.tensor(y)

    def __len__(self):
        return len(self.items)

def test(model, test_loader):
    model.eval()
    with torch.no_grad():
        correct = 0
        for X, Y in test_loader:
            p = model(X)
            plabel = p.argmax(dim=1)
            correct += (plabel == Y).sum()
        accuracy = correct.item() / len(test_loader.dataset) * 100
    return accuracy

categories = {"lying" : 0, "stand" : 1}    

train_set    = Dataset("train.csv", True, categories)
train_loader = DataLoader(train_set, batch_size=256, pin_memory=True, shuffle=True, num_workers=0)

val_set    = Dataset("val.csv", True, categories)
val_loader = DataLoader(val_set, batch_size=256, pin_memory=True, shuffle=True, num_workers=0)

nepochs = 200

key_points_size = 13
future_size     = 3
classes_num     = 2
model   = Model(key_points_size, future_size, classes_num)
loss_fn = nn.CrossEntropyLoss(weight=torch.FloatTensor([0.5, 1]))
optim   = torch.optim.AdamW(model.parameters(), 1e-2)

best_accuracy = 0

for epoch in range(nepochs):
    model.train()
    for batch_index, (X, Y) in enumerate(train_loader):
        p = model(X)
        loss = loss_fn(p, Y)
        optim.zero_grad()
        loss.backward()
        optim.step()
    if epoch % 5 == 0:
        acc = test(model, val_loader)
        print(f"Accuracy : {acc },  loss = {loss.item()}")
        if acc > best_accuracy:
            torch.save(model.state_dict(), "best.pth") 
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,752评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,100评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,244评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,099评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,210评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,307评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,346评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,133评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,546评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,849评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,019评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,702评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,331评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,030评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,260评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,871评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,898评论 2 351

推荐阅读更多精彩内容