EasyMocap/easymocap/mytools/file_utils.py

255 lines
9.3 KiB
Python
Raw Normal View History

2021-04-14 15:22:51 +08:00
'''
@ Date: 2021-03-15 12:23:12
@ Author: Qing Shuai
@ LastEditors: Qing Shuai
2022-12-05 22:06:04 +08:00
@ LastEditTime: 2022-11-08 21:43:37
@ FilePath: /EasyMocapPublic/easymocap/mytools/file_utils.py
2021-04-14 15:22:51 +08:00
'''
import os
import json
import numpy as np
from os.path import join
mkdir = lambda x:os.makedirs(x, exist_ok=True)
# mkout = lambda x:mkdir(os.path.dirname(x)) if x is not None
def mkout(x):
if x is not None:
mkdir(os.path.dirname(x))
2021-04-14 15:22:51 +08:00
def read_json(path):
assert os.path.exists(path), path
with open(path) as f:
try:
data = json.load(f)
except:
print('Reading error {}'.format(path))
data = []
2021-04-14 15:22:51 +08:00
return data
def save_json(file, data):
if not os.path.exists(os.path.dirname(file)):
os.makedirs(os.path.dirname(file))
with open(file, 'w') as f:
json.dump(data, f, indent=4)
2022-12-05 22:06:04 +08:00
def save_numpy_dict(file, data):
if not os.path.exists(os.path.dirname(file)):
os.makedirs(os.path.dirname(file))
res = {}
for key, val in data.items():
res[key] = val.tolist()
with open(file, 'w') as f:
json.dump(res, f, indent=4)
def read_numpy_dict(path):
assert os.path.exists(path), path
with open(path) as f:
data = json.load(f)
for key, val in data.items():
data[key] = np.array(val, dtype=np.float32)
return data
def append_json(file, data):
if not os.path.exists(os.path.dirname(file)):
os.makedirs(os.path.dirname(file))
if os.path.exists(file):
res = read_json(file)
assert isinstance(res, list)
res.append(data)
data = res
with open(file, 'w') as f:
json.dump(data, f, indent=4)
2021-06-18 15:47:26 +08:00
save_annot = save_json
2021-04-14 15:22:51 +08:00
def getFileList(root, ext='.jpg'):
files = []
dirs = os.listdir(root)
while len(dirs) > 0:
path = dirs.pop()
fullname = join(root, path)
if os.path.isfile(fullname) and fullname.endswith(ext):
files.append(path)
elif os.path.isdir(fullname):
for s in os.listdir(fullname):
newDir = join(path, s)
dirs.append(newDir)
files = sorted(files)
return files
def read_annot(annotname, mode='body25'):
data = read_json(annotname)
if not isinstance(data, list):
data = data['annots']
for i in range(len(data)):
if 'id' not in data[i].keys():
data[i]['id'] = data[i].pop('personID')
if 'keypoints2d' in data[i].keys() and 'keypoints' not in data[i].keys():
data[i]['keypoints'] = data[i].pop('keypoints2d')
for key in ['bbox', 'keypoints',
'bbox_handl2d', 'handl2d',
'bbox_handr2d', 'handr2d',
'bbox_face2d', 'face2d']:
2021-04-14 15:22:51 +08:00
if key not in data[i].keys():continue
data[i][key] = np.array(data[i][key])
if key == 'face2d':
# TODO: Make parameters, 17 is the offset for the eye brows,
# etc. 51 is the total number of FLAME compatible landmarks
data[i][key] = data[i][key][17:17+51, :]
if 'bbox' in data[i].keys():
data[i]['bbox'] = data[i]['bbox'][:5]
if data[i]['bbox'][-1] < 0.001:
print('{}/{} bbox conf = 0, may be error'.format(annotname, i))
data[i]['bbox'][-1] = 0
# combine the basic results
2021-04-14 15:22:51 +08:00
if mode == 'body25':
data[i]['keypoints'] = data[i].get('keypoints', np.zeros((25, 3)))
2021-04-14 15:22:51 +08:00
elif mode == 'body15':
data[i]['keypoints'] = data[i]['keypoints'][:15, :]
2021-05-27 21:12:17 +08:00
elif mode in ['handl', 'handr']:
data[i]['keypoints'] = np.array(data[i][mode+'2d']).astype(np.float32)
key = 'bbox_'+mode+'2d'
if key not in data[i].keys():
data[i]['bbox'] = np.array(get_bbox_from_pose(data[i]['keypoints'])).astype(np.float32)
else:
data[i]['bbox'] = data[i]['bbox_'+mode+'2d'][:5]
2021-04-14 15:22:51 +08:00
elif mode == 'total':
data[i]['keypoints'] = np.vstack([data[i][key] for key in ['keypoints', 'handl2d', 'handr2d', 'face2d']])
elif mode == 'bodyhand':
data[i]['keypoints'] = np.vstack([data[i][key] for key in ['keypoints', 'handl2d', 'handr2d']])
elif mode == 'bodyhandface':
data[i]['keypoints'] = np.vstack([data[i][key] for key in ['keypoints', 'handl2d', 'handr2d', 'face2d']])
conf = data[i]['keypoints'][..., -1]
conf[conf<0] = 0
data.sort(key=lambda x:x['id'])
return data
2021-05-27 21:12:17 +08:00
def array2raw(array, separator=' ', fmt='%.3f'):
assert len(array.shape) == 2, 'Only support MxN matrix, {}'.format(array.shape)
res = []
for data in array:
res.append(separator.join([fmt%(d) for d in data]))
def myarray2string(array, separator=', ', fmt='%7.7f', indent=8):
2021-05-27 21:12:17 +08:00
assert len(array.shape) == 2, 'Only support MxN matrix, {}'.format(array.shape)
2021-06-18 15:47:26 +08:00
blank = ' ' * indent
2021-05-27 21:12:17 +08:00
res = ['[']
for i in range(array.shape[0]):
2021-06-18 15:47:26 +08:00
res.append(blank + ' ' + '[{}]'.format(separator.join([fmt%(d) for d in array[i]])))
2021-05-27 21:12:17 +08:00
if i != array.shape[0] -1:
res[-1] += ', '
2021-06-18 15:47:26 +08:00
res.append(blank + ']')
2021-05-27 21:12:17 +08:00
return '\r\n'.join(res)
def write_common_results(dumpname=None, results=[], keys=[], fmt='%2.3f'):
2021-04-14 15:22:51 +08:00
format_out = {'float_kind':lambda x: fmt % x}
2021-05-27 21:12:17 +08:00
out_text = []
out_text.append('[\n')
for idata, data in enumerate(results):
out_text.append(' {\n')
output = {}
output['id'] = data['id']
for k in ['type']:
if k in data.keys():output[k] = '\"{}\"'.format(data[k])
keys_current = [k for k in keys if k in data.keys()]
for key in keys_current:
2021-05-27 21:12:17 +08:00
# BUG: This function will failed if the rows of the data[key] is too large
# output[key] = np.array2string(data[key], max_line_width=1000, separator=', ', formatter=format_out)
output[key] = myarray2string(data[key], separator=', ', fmt=fmt)
for key in output.keys():
out_text.append(' \"{}\": {}'.format(key, output[key]))
if key != keys_current[-1]:
2021-05-27 21:12:17 +08:00
out_text.append(',\n')
2021-04-14 15:22:51 +08:00
else:
2021-05-27 21:12:17 +08:00
out_text.append('\n')
out_text.append(' }')
if idata != len(results) - 1:
out_text.append(',\n')
else:
out_text.append('\n')
out_text.append(']\n')
if dumpname is not None:
mkout(dumpname)
with open(dumpname, 'w') as f:
f.writelines(out_text)
else:
return ''.join(out_text)
2021-04-14 15:22:51 +08:00
def write_keypoints3d(dumpname, results, keys = ['keypoints3d']):
2021-04-14 15:22:51 +08:00
# TODO:rewrite it
write_common_results(dumpname, results, keys, fmt='%6.7f')
2021-05-27 21:12:17 +08:00
def write_vertices(dumpname, results):
keys = ['vertices']
write_common_results(dumpname, results, keys, fmt='%6.5f')
2021-04-14 15:22:51 +08:00
def write_smpl(dumpname, results):
keys = ['Rh', 'Th', 'poses', 'handl', 'handr', 'expression', 'shapes']
2021-04-14 15:22:51 +08:00
write_common_results(dumpname, results, keys)
2021-06-18 15:47:26 +08:00
def batch_bbox_from_pose(keypoints2d, height, width, rate=0.1):
# TODO:write this in batch
bboxes = np.zeros((keypoints2d.shape[0], 5), dtype=np.float32)
border = 20
for bn in range(keypoints2d.shape[0]):
valid = keypoints2d[bn, :, -1] > 0
if valid.sum() == 0:
continue
p2d = keypoints2d[bn, valid, :2]
x_min, y_min = p2d.min(axis=0)
x_max, y_max = p2d.max(axis=0)
x_mean, y_mean = p2d.mean(axis=0)
if x_mean < -border or y_mean < -border or x_mean > width + border or y_mean > height + border:
continue
dx = (x_max - x_min)*rate
dy = (y_max - y_min)*rate
bboxes[bn] = [x_min-dx, y_min-dy, x_max+dx, y_max+dy, 1]
return bboxes
2021-04-14 15:22:51 +08:00
2021-05-27 21:12:17 +08:00
def get_bbox_from_pose(pose_2d, img=None, rate = 0.1):
2021-04-14 15:22:51 +08:00
# this function returns bounding box from the 2D pose
# here use pose_2d[:, -1] instead of pose_2d[:, 2]
# because when vis reprojection, the result will be (x, y, depth, conf)
validIdx = pose_2d[:, -1] > 0
if validIdx.sum() == 0:
return [0, 0, 100, 100, 0]
y_min = int(min(pose_2d[validIdx, 1]))
y_max = int(max(pose_2d[validIdx, 1]))
x_min = int(min(pose_2d[validIdx, 0]))
x_max = int(max(pose_2d[validIdx, 0]))
dx = (x_max - x_min)*rate
dy = (y_max - y_min)*rate
# 后面加上类别这些
bbox = [x_min-dx, y_min-dy, x_max+dx, y_max+dy, 1]
2021-05-27 21:12:17 +08:00
if img is not None:
correct_bbox(img, bbox)
2021-04-14 15:22:51 +08:00
return bbox
def correct_bbox(img, bbox):
# this function corrects the bbox, which is out of image
w = img.shape[0]
h = img.shape[1]
if bbox[2] <= 0 or bbox[0] >= h or bbox[1] >= w or bbox[3] <= 0:
bbox[4] = 0
return bbox
def merge_params(param_list, share_shape=True):
output = {}
for key in ['poses', 'shapes', 'Rh', 'Th', 'expression']:
if key in param_list[0].keys():
output[key] = np.vstack([v[key] for v in param_list])
if share_shape:
output['shapes'] = output['shapes'].mean(axis=0, keepdims=True)
return output
def select_nf(params_all, nf):
output = {}
for key in ['poses', 'Rh', 'Th']:
output[key] = params_all[key][nf:nf+1, :]
if 'expression' in params_all.keys():
output['expression'] = params_all['expression'][nf:nf+1, :]
if params_all['shapes'].shape[0] == 1:
output['shapes'] = params_all['shapes']
else:
output['shapes'] = params_all['shapes'][nf:nf+1, :]
return output