Safemotion Lib
Loading...
Searching...
No Matches
action_recognition_runner.py
Go to the documentation of this file.
1import torch
2import torch.nn as nn
3import numpy as np
4from smaction.builder.model_builder import build_action_model
5
6from smaction.datasets.transform.pose_transform import pose_sampling, pose_compact, pose_resize, make_pose_heatmap
8import smaction.utils.action_utils as utils
9import copy
10
11#skeleton, image, fusion 버전으로 분리 필요
12class ActionRecognitionRunner(nn.Module):
13 """
14 스켈레톤 및 이미지 피처를 입력으로 행동인식을 수행하는 클래스
15 TODO: 영상 특징을 사용할 수 있도록 내용 수정 필요, inference 구조를 변경해서 융합모듈을 따로 관리하는 부분 제거(백본쪽에 통합)
16 args:
17 backbone (dict) : 백본 모델을 구성하는 파라미터
18 head (dict) : 헤드 모델을 구성하는 파라미터
19 fusion (dict) : 융합 모델을 구성하는 파라미터
20 predict_keys (list[str]) : 스코어(head의 결과)의 키값, 키값에 대응하는 스코어에서 최대값을 가지는 클래스를 출력함
21 """
22 def __init__(self, backbone, head, fusion=None, predict_keys=None):
23 super().__init__()
24
25 self.backbone = nn.ModuleDict()
26 for key, cfg in backbone.items():
27 self.backbone[key] = build_action_model(cfg)
28
29 self.head = nn.ModuleDict()
30 for key, cfg in head.items():
31 self.head[key] = build_action_model(cfg)
32
33 self.fusion = None
34 if fusion is not None:
35 self.fusion = build_action_model(fusion)
36
37 self.predict_keys = {} if predict_keys is None else predict_keys
38
40 """
41 return (list[str])
42 모듈 구성의 종류를 반환함
43 모듈의 구성은 backbone, head, fusion이 있음
44 """
45 modules = ['backbone', 'head']
46 if self.fusion is not None:
47 modules.append('fusion')
48 return modules
49
50
51 def make_model_input(self, data_list, clip_len=20, device='cuda:0'):
52 """
53 args:
54 data_list (list[dict]) : 행동인식 모델의 입력으로 사용할 원천 데이터(dict type)를 담고있는 리스트
55 데이터 구성
56 keypoints (np.array) : 스켈레톤 좌표(x, y) 및 스코어, shape (17, 3)
57 TODO -> image_features (np.array or Tensor): 영상 특징
58 clip_len (int) : data_list에서 샘플링하는 수량
59 device (str) : inference에 사용할 디바이스
60 return (dict) :
61 모델의 입력에 사용될 데이터
62 pose_heatmap or pose_heatmap_for_action, pose_heatmap_for_pose (Tensor): 스켈레톤 좌표를 사용해서 생성한 이미지
63 shape (1, 17, clip_len, 64, 64) or (1, 17, clip_len_pose=6, 64, 64)
64 TODO -> image_features (Tensor) : 영상특징, shape (1, C, clip_len, 1)
65 """
66 #TODO : 백본을 여러개 사용하는 경우 입력 데이터 처리하는 부분 필요
67 #TODO : 영상 특징이 존재할 경우 처리 프로세스 코드 작성 필요, 하나로 붙이고 device로 넘기는 기능 필요
68 #TODO : 일반화 필요, 입력데이터를 생성하는 일반화 코드가 필요함, mmlab 참조해서 입력 전처리 파이프라인 코드 작성
69 sample = dict()
70
71 kps = []
72 for anno in data_list:
73 kps.append(anno['keypoints'])
74
75 kps = np.array([kps])
76 sample['keypoint'] = kps[..., :2]
77 sample['keypoint_score'] = kps[..., 2]
78
79 sample = pose_sampling(sample, clip_len=clip_len) #샘플링
80 sample = pose_compact(sample) #좌표의 범위 체크
81 sample = pose_resize(sample, scale=(64, 64)) #정규화
82
83 if len(self.backbone.keys()) == 1: #백본 하나만 사용할 경우
84 #키포인트 이미지 생성
85 sample = make_pose_heatmap(sample) #스켈레톤을 기반으로 이미지 생성
86 #사용하는 디바이스로 옮기기, 배치축 생성
87 sample['pose_heatmap'] = torch.from_numpy(sample['pose_heatmap']).to(device).unsqueeze(0)
88 else:
89 #포즈 예측 백본에 들어갈 입력을 추가로 생성
90
91 clip_len_pose = 6 #포즈인식에 사용할 프레임 수
92
93 #복사
94 sample_pose = copy.deepcopy(sample)
95
96 #포즈인식에 사용할 스켈레톤을 샘플링함, 중앙위치에서 선택함
97 frames = sample_pose['keypoint'].shape[1] #전체 프레임수
98 start = (frames - clip_len_pose)//2 #포즈인식에 사용할 시작 프레임 위치
99 end = start+clip_len_pose #포즈인식에 사용할 끝 프레임 위치
100 sample_pose['keypoint'] = sample_pose['keypoint'][:,start:end, :, : ] #키포인트 좌표 샘플링
101 sample_pose['keypoint_score'] = sample_pose['keypoint_score'][:,start:end, : ] #키포인트 스코어 샘플링
102 sample_pose = pose_compact(sample_pose) #좌표 범위 체크
103 sample_pose = pose_resize(sample_pose, scale=(64, 64)) #정규화
104
105 #키포인트 이미지 생성
106 sample = make_pose_heatmap(sample)
107 sample_pose = make_pose_heatmap(sample_pose)
108 sample['pose_heatmap_for_action'] = torch.from_numpy(sample['pose_heatmap']).to(device).unsqueeze(0)
109 sample['pose_heatmap_for_pose'] = torch.from_numpy(sample_pose['pose_heatmap']).to(device).unsqueeze(0)
110
111 return sample
112
113 def run_recognizer(self, data_list, k=60, clip_len=20, use_valid=True, device='cuda:0'):
114 """
115 args:
116 data_list (list[dict]) : 행동인식 모델의 입력으로 사용할 원천 데이터(dict type)를 담고있는 리스트
117 데이터 구성
118 keypoints (np.array) : 스켈레톤 좌표(x, y) 및 스코어, shape (17, 3)
119 frame_id (int) : 프레임 번호
120 TODO -> image_features (np.array or Tensor): 영상 특징
121 k (int) : 행동인식하는데 사용하는 프레임 구간
122 clip_len (int) : data_list에서 샘플링하는 수량
123 use_valid (bool) : 유효성 검사 사용 유무
124 device (str) : inference에 사용할 디바이스
125 return (dict):
126 스코어, 예측클래스를 출력
127
128 """
129 # TODO : 수정 필요 -> dual backbone 사용할 수 있도록 코드 수정 필요함
130
131 #유효성 체크
132 if use_valid:
133 if not utils.check_valid(data_list, k):
134 return None
135
136 #검출에 실패한 프레임에 더미 포즈를 삽입함, 더미포즈는 이전 데이터를 복사해서 사용
137 data_list = utils.insert_dummy_pose(data_list)
138
139 #모델의 입력데이터 생성
140 sample_dict = self.make_model_input(data_list, clip_len, device=device)
141
142 #모델 inference
143 with torch.no_grad():
144 result = self.inference(sample_dict)
145
146 return result
147
148 def inference(self, sample_dict):
149 """
150 args:
151 sample_dict (dict) : 행동인식 모델 백본의 입력으로 사용할 데이터
152 return (dict):
153 스코어 및 예측클래스
154 """
155 feats = {}
156 for key, net in self.backbone.items():
157 feats[key] = net(sample_dict)
158
159 if self.fusion is not None:
160 feats['fusion'] = self.fusion(feats)
161
162 scores = {}
163 for key, net in self.head.items():
164 scores[key] = net(feats)
165
166 preds = {}
167 for key, score_key in self.predict_keys.items():
168 preds[key] = scores[score_key].argmax(dim=1)
169
170 preds.update(scores)
171
172 return preds
173
174 def forward(self, sample_dict):
175 return self.inference(sample_dict)
make_model_input(self, data_list, clip_len=20, device='cuda:0')
run_recognizer(self, data_list, k=60, clip_len=20, use_valid=True, device='cuda:0')
__init__(self, backbone, head, fusion=None, predict_keys=None)