Safemotion Lib
Loading...
Searching...
No Matches
generate_clip_mtml.py
Go to the documentation of this file.
2import os
3from smutils.utils_os import search_file, create_directory, save_json
4from smdataset.AnnotationDataManager import AnnotationDataManager
6import copy
7
8def search_label_list(anno_list, cat_list):
9 """
10 어노테이션 데이터에 존재하는 카테고리별 클래스 리스트 탐색기능
11 args:
12 anno_list (list[dict]): 어노테이션 데이터 리스트, 데이터에는 카테고리별 클래스가 설정된 'action_id'키를 포함하여야함
13 cat_list (list[str]): 카테고리 리스트
14 return (dict): 어노테이션 데이터에 존재하는 각 카테고리별 클래스 리스트
15 """
16
17 #카테고리별 클래스 리스트 초기화
18 ret_dict = dict()
19 for cat in cat_list:
20 ret_dict[cat] = []
21
22 #탐색
23 for anno in anno_list:
24 for cat in cat_list:
25 label = anno['action_id'][cat]
26 if label not in ret_dict[cat]:
27 ret_dict[cat].append(label)
28
29 return ret_dict
30
31
32def search_clip_list_v22(anno_list, image_ids, frame_num, train_frame=60):
33 """
34 어노테이션 데이터(한명에 해당하는 데이터)에서 행동별로 클립을 생성하는 기능
35 args:
36 anno_list (list[dict]): 어노테이션 데이터 리스트(한명에 대한 정보), 데이터에는 카테고리별 클래스가 설정된 'action_id'키를 포함하여야함
37 image_ids (list(int)): 어노테이션 데이터의 이미지 아이디 리스트
38 frame_num (int): 어노테이션 데이터의 총 프레임 수
39 train_frame (int): 학습 데이터 생성에 사용되는 프레임 수
40 return (dict): 카테고리별 클래스의 클립 리스트, 클립은 클립의 시작 프레임 인덱스(start), 끝 프레임 인덱스(end), 카테고리의 클래스 번호(label), 중복방지를 위한 순번(seq)을 포함하고 있음
41 """
42
43 #클립 생성 파라미터 설정
44 cat_info = dict()
45 cat_list=['action_upper', 'action_lower', 'pose', 'hand', 'foot']
46 for cat in cat_list:
47 cat_info[cat] = dict()
48
49 cat_info['action_upper']['info'] = cvt_labelmap_v22_action_upper
50 cat_info['action_lower']['info'] = cvt_labelmap_v22_action_lower
51 cat_info['pose']['info'] = cvt_labelmap_v22_action_pose
52 cat_info['hand']['info'] = cvt_labelmap_v22_action_hand
53 cat_info['foot']['info'] = cvt_labelmap_v22_action_foot
54
55 #어노테이션 데이터에서 카테고리별 클래스 리스트를 탐색, 데이터에 존재하는 모든 클래스를 의미함
56 # label_info 예시
57 # {'action_upper': [0],
58 # 'action_lower': [0],
59 # 'pose': [1],
60 # 'hand': [0, 1],
61 # 'foot': [0]}
62 label_info = search_label_list(anno_list, cat_list)
63
64 #카테고리별 클래스에 해당하는 이미지 인덱스 리스트, 클립을 구성하는 프레임 인덱스
65 # clip_info init 예시
66 # {'action_upper': {0: [[]]},
67 # 'action_lower': {0: [[]]},
68 # 'pose': {1: [[]]},
69 # 'hand': {0: [[]], 1: [[]]},
70 # 'foot': {0: [[]]}}
71 clip_info = dict()
72 for cat in cat_list:
73 clip_info[cat] = dict()
74 label_list = label_info[cat]
75 for label in label_list:
76 clip_info[cat][label] = [[]]
77
78 #카테고리별 클래스에 해당하는 이미지 인덱스 리스트
79 clip_interval = 15 #프레임 간격의 임계치, clip_interval이상이면 다른 클립으로 분리함
80 for anno in anno_list:
81 image_idx = image_ids.index(anno['image_id']) #어노테이션 데이터의 이미지 인덱스
82
83 #카테고리별 클립 정보 설정
84 for cat in cat_list:
85 label = anno['action_id'][cat] #카테고리의 클래스
86
87 if len(clip_info[cat][label][-1]) == 0: #클립이 비어있으면 초기화
88 clip_info[cat][label][-1].append(image_idx)
89
90 if image_idx - clip_info[cat][label][-1][-1] < clip_interval: #프레임 간격이 임계치보다 작으면 같은 클립으로 취급함
91 clip_info[cat][label][-1].append(image_idx)
92 else:
93 clip_info[cat][label].append([image_idx]) #프레임 간격이 임계치보다 크면 새로운 클립으로 생성함
94
95 #카테고리별 클래스의 클립 설정
96 # clip_list init 예시
97 # {'action_upper': {0: []},
98 # 'action_lower': {0: []},
99 # 'pose': {1: []},
100 # 'hand': {0: [], 1: []},
101 # 'foot': {0: []}}
102 clip_list = dict()
103 for cat in cat_list:
104 clip_list[cat] = dict()
105 label_list = label_info[cat]
106 for label in label_list:
107 clip_list[cat][label] = []
108
109 #카테고리별 클래스의 클립 생성
110 for cat, cat_clip_info in clip_info.items():
111 for label, clip_frame_list in cat_clip_info.items():
112
113 #클래스의 유효성 체크
114 if label < 0 or label is None:
115 continue
116
117 #클립 정보 생성
118 seq = 0
119 for frame_list in clip_frame_list:
120
121 #클립의 시작, 끝 초기화
122 start, end = frame_list[0], frame_list[-1]
123
124 #클립 길이 유효성 체크(마진 없는 클립 길이)
125 clip_len = end - start +1
126 if clip_len < cat_info[cat]['info'][label][3]:
127 continue
128
129 #마진 추가
130 if cat_info[cat]['info'][label][4]: #사용자 설정 마진
131 #클립을 최소 90프레임으로 설정함
132 margin = max( (90 - clip_len)//2, 30) #마진값 설정
133 start = max(0, start - margin) #마진이 포함된 시작 프레임 설정
134 end = min(frame_num-1, max(start + train_frame, end + margin)) #마진이 포함된 끝 프레임 설정
135
136 #마진이 포함된 클립 길이 유효성 검사, 클립 길이 조건을 만족하지 못하면 시작 프레임을 조정함
137 if (end - start +1) < train_frame:
138 start = max(0, end - train_frame)
139
140 else: #기본 마진 설정
141 margin = 15
142 start = max(0, start - margin)
143 end = min(frame_num-1, end + margin)
144
145 #학습용 클립 길이기반 유효성 검사
146 if (end - start +1) < train_frame:
147 continue
148
149 #클립 생성
150 clip = dict(start=start, end=end, label=label, seq=seq)
151 clip_list[cat][label].append(clip)
152 seq += 1
153
154 return clip_list
155
156
157def save_clip_v22(clip_list, input_anno_list, image_ids, base_anno, save_folder, json_name):
158 """
159 한사람에 대해 행동별로 생성된 클립을 저장하는 기능
160 args:
161 clip_list (dict): 카테고리별 클래스의 클립정보(한명에 대한 정보)
162 구조
163 {
164 '카테고리 명': {클래스 번호: [ {클립정보(start, end, lable, seq)} ]}
165 }
166 구조 예시 :
167 {'action_upper': {0: [{'start': 20, 'end': 60, 'label':0, 'seq':0}]},
168 'action_lower': {0: []},
169 'pose': {1: []},
170 'hand': {0: [], 1: []},
171 'foot': {0: []}}
172 input_anno_list (list[dict]): 한사람에 대한 어노테이션 데이터
173 image_ids (list[str]): 이미지 아이디 리스트
174 base_anno : 어노테이션 데이터 원본, 파일을 읽었을때의 초기 상태
175 save_folder (str): 클립 데이터를 저장할 폴더
176 json_name (str): 원본 클립의 이름
177 """
178
179 #이미지 인덱스와 입력 어노테이션 데이터의 인덱스를 매칭시켜줌
180 idx_link = {}
181 for i, anno in enumerate(input_anno_list):
182 image_idx = image_ids.index(anno['image_id'])
183 idx_link[image_idx] = i
184
185 #클립 데이터 저장
186 for cat, label_clip_info in clip_list.items(): #cat(str):카테고리, label_clip_info(dict):클래스에 대한 클립 리스트
187 for label, clip_list in label_clip_info.items(): #label(int):클래스, clip_list(list[dict]):클립 리스트
188 for clip in clip_list:
189 start = clip['start']
190 end = clip['end']
191 label = clip['label']
192 seq = clip['seq']
193
194 #하나의 클립에 대한 정보만 저장
195 anno_list = []
196 for image_idx in range(start, end+1):
197 #이미지 인덱스에 해당하는 어노테이션 데이터를 가져오기, 어노테이션 데이터가 없을 경우 None으로 셋팅
198 anno = input_anno_list[idx_link[image_idx]] if image_idx in idx_link else None
199
200 #어노테이션 데이터가 존재할 경우만 클립 정보 저장
201 if anno is not None:
202 anno_list.append(anno)
203
204 #클립 데이터 생성, 원본어노테이션 파일에 클립 시작, 끝, 데이터 셋팅
205 base_anno['annotations'] = anno_list
206 base_anno['info']['start'] = start
207 base_anno['info']['end'] = end
208
209 #클립 데이터 저장
210 track_id = anno_list[0]['track_id'] #추적 아이디
211 new_name = json_name.replace('.json', f'T{track_id:02d}L{label:02d}S{seq:02d}.json') #클립 이름 설정
212 save_path = os.path.join(save_folder, cat, f'{label:02d}', new_name) #저장 경로
213 save_json(base_anno, save_path) #저장
214 print(' saved : ', save_path)
215
216def generate_clip(json_folder, save_folder):
217 """
218 어노테이션 데이터들을 개별 행동 클립으로 분할하는 기능
219 args:
220 json_folder (str): 어노테이션 파일이 저장된 폴더
221 save_folder (str): 클립 데이터를 저장할 폴더
222 """
223
224 #어노테이션 파일 리스트
225 json_name_list, json_path_list = search_file(json_folder, '.json')
226
227 #데이터 관리자 생성
228 data_manager = AnnotationDataManager()
229
230 #클립 생성 프로세스
231 N = len(json_name_list)
232 for i, (json_name, json_path) in enumerate(zip(json_name_list, json_path_list)):
233 print(f'{i}/{N} {json_path}')
234
235 #데이터 관리자 초기화
236 data_manager.load_annotation(json_path)
237 data_manager.init_annotation()
238
239 #원본 파일 복사, info와 images 키에 대한 정보가 필요함, 데이터를 클립의 데이터로 교체해서 저장하기 위함
240 base_anno = copy.deepcopy(data_manager.dataset)
241
242 frames = data_manager.get_number_of_image() #프레임 수
243
244 #추적 아이디 기준으로 구조 변경
245 track_data = data_manager.get_track_anno_data(filter=True)
246
247 #클립 생성
248 for track_id, anno_list in track_data.items():
249 simple_anno_list = cvt_labelmap_v22(anno_list) #학습을 위한 행동 라벨로 변경
250 clip_list = search_clip_list_v22(simple_anno_list, data_manager.image_ids, frames) #변경된 라벨 기준으로 클립 정보 추출
251 save_clip_v22(clip_list, anno_list, data_manager.image_ids, base_anno, save_folder, json_name) #클립 저장, 저장은 원본 행동 라벨 데이터로 저장함
save_clip_v22(clip_list, input_anno_list, image_ids, base_anno, save_folder, json_name)
search_clip_list_v22(anno_list, image_ids, frame_num, train_frame=60)
search_label_list(anno_list, cat_list)