diff --git a/README.md b/README.md index a6decaa..e83fe4a 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ ## 3d-pose-baseline +[![](https://sourcerer.io/fame/ArashHosseini/ArashHosseini/3d-pose-baseline/images/0)](https://sourcerer.io/fame/ArashHosseini/ArashHosseini/3d-pose-baseline/links/0)[![](https://sourcerer.io/fame/ArashHosseini/ArashHosseini/3d-pose-baseline/images/1)](https://sourcerer.io/fame/ArashHosseini/ArashHosseini/3d-pose-baseline/links/1)[![](https://sourcerer.io/fame/ArashHosseini/ArashHosseini/3d-pose-baseline/images/2)](https://sourcerer.io/fame/ArashHosseini/ArashHosseini/3d-pose-baseline/links/2)[![](https://sourcerer.io/fame/ArashHosseini/ArashHosseini/3d-pose-baseline/images/3)](https://sourcerer.io/fame/ArashHosseini/ArashHosseini/3d-pose-baseline/links/3)[![](https://sourcerer.io/fame/ArashHosseini/ArashHosseini/3d-pose-baseline/images/4)](https://sourcerer.io/fame/ArashHosseini/ArashHosseini/3d-pose-baseline/links/4)[![](https://sourcerer.io/fame/ArashHosseini/ArashHosseini/3d-pose-baseline/images/5)](https://sourcerer.io/fame/ArashHosseini/ArashHosseini/3d-pose-baseline/links/5)[![](https://sourcerer.io/fame/ArashHosseini/ArashHosseini/3d-pose-baseline/images/6)](https://sourcerer.io/fame/ArashHosseini/ArashHosseini/3d-pose-baseline/links/6)[![](https://sourcerer.io/fame/ArashHosseini/ArashHosseini/3d-pose-baseline/images/7)](https://sourcerer.io/fame/ArashHosseini/ArashHosseini/3d-pose-baseline/links/7) + This is the code for the paper Julieta Martinez, Rayat Hossain, Javier Romero, James J. Little. @@ -51,6 +53,77 @@ This will produce a visualization similar to this: ![Visualization example](/imgs/viz_example.png?raw=1) + +### [openpose](https://github.com/CMU-Perceptual-Computing-Lab/openpose.git)/[tf-pose-estimation](https://github.com/ArashHosseini/tf-pose-estimation)/[keras_Realtime_Multi-Person_Pose_Estimation](https://github.com/ArashHosseini/keras_Realtime_Multi-Person_Pose_Estimation) to 3d-Pose-Baseline + + +### Caffe + +1. setup [openpose](https://github.com/CMU-Perceptual-Computing-Lab/openpose.git) and use `--write_json` flag to export Pose Keypoints. + +or + +### Tensorflow + +2. fork [tf-pose-estimation](https://github.com/ArashHosseini/tf-pose-estimation) and add `--output_json` flag to export Pose Keypoints like `python run_webcam.py --model=mobilenet_thin --resize=432x368 --camera=0 --output_json /path/to/directory`, check [diff](https://github.com/ArashHosseini/tf-pose-estimation/commit/eb25b197b3c0ed2d424513dbbe2565e910a736d1) + +or + +### Keras + +3. fork [keras_Realtime_Multi-Person_Pose_Estimation](https://github.com/ArashHosseini/keras_Realtime_Multi-Person_Pose_Estimation) and use `python demo_image.py --image sample_images/p1.jpg` for single image or `python demo_camera.py` for webcam feed. check [keypoints diff](https://github.com/ArashHosseini/keras_Realtime_Multi-Person_Pose_Estimation/commit/b5c76a35239aa7496010ff7f5e0b5fc0a9cf59a0) and [webcam diff](https://github.com/ArashHosseini/keras_Realtime_Multi-Person_Pose_Estimation/commit/3e414e68047fd7575bd8832ba776b0b5a93f2eea) for more info. + +4. Download Pre-trained model below + +5. simply run + +`python src/openpose_3dpose_sandbox.py --camera_frame --residual --batch_norm --dropout 0.5 --max_norm --evaluateActionWise --use_sh --epochs 200 --load 4874200 --pose_estimation_json /path/to/json_directory --write_gif --gif_fps 24 `, optional `--verbose 3` for debug and for interpolation add `--interpolation` and use `--multiplier`. + +6. or for 'Real Time' + +`python3.5 src/openpose_3dpose_sandbox_realtime.py --camera_frame --residual --batch_norm --dropout 0.5 --max_norm --evaluateActionWise --use_sh --epochs 200 --load 4874200 --pose_estimation_json /path/to/json_directory ` + + +### Export to DCC application and build skeleton + + + +1. use `--write_json` and `--write_images` flag to export keypoints and frame image from openpose, image will be used as imageplane inside maya. +2. run `python src/openpose_3dpose_sandbox.py --camera_frame --residual --batch_norm --dropout 0.5 --max_norm --evaluateActionWise --use_sh --epochs 200 --load 4874200 --pose_estimation_json /path/to/json_directory --write_gif --gif_fps 24 `. +3. for interpolation add `--interpolation` and use `--multiplier 0.5`. + +3d pose baseline now creates a json file `3d_data.json` with `x, y, z` coordinates inside maya folder + +4. change variables in `maya/maya_skeleton.py`. set `threed_pose_baseline` to main 3d-pose-baseline and `openpose_images` to same path as `--write_images` (step 1) +5. open maya and import `maya/maya_skeleton.py`. + +`maya_skeleton.py` will load the data(`3d_data.json` and `2d_data.json`) to build a skeleton, parenting joints and setting the predicted animation provided by 3d-pose-baseline. + +6. create a imageplane and use created images inside `maya/image_plane/` as sequence. + +

+ +

+ +7. "real-time" stream, openpose > 3d-pose-baseline > maya (soon) + +8. implemented unity stream, check work of Zhenyu Chen [openpose_3d-pose-baseline_unity3d](https://github.com/zhenyuczy/openpose_3d-pose-baseline_unity3d) + +### Mapping + +

+ +

+ +### Result + +

+ +

+ + +![Fps drops](/imgs/dirty_plot.png?raw=1)![holding](/imgs/smooth_plot.png?raw=2) ![interpolate](/imgs/interpolate_plot.png?raw=3) + ### Training To train a model with clean 2d detections, run: diff --git a/imgs/dirty_plot.png b/imgs/dirty_plot.png new file mode 100644 index 0000000..056e844 Binary files /dev/null and b/imgs/dirty_plot.png differ diff --git a/imgs/interpolate_plot.png b/imgs/interpolate_plot.png new file mode 100644 index 0000000..f173d86 Binary files /dev/null and b/imgs/interpolate_plot.png differ diff --git a/imgs/interpolation.gif b/imgs/interpolation.gif new file mode 100644 index 0000000..75ed9e6 Binary files /dev/null and b/imgs/interpolation.gif differ diff --git a/imgs/maya.png b/imgs/maya.png new file mode 100644 index 0000000..7b075e1 Binary files /dev/null and b/imgs/maya.png differ diff --git a/imgs/maya_skl.gif b/imgs/maya_skl.gif new file mode 100644 index 0000000..3dbdf50 Binary files /dev/null and b/imgs/maya_skl.gif differ diff --git a/imgs/open_pose_input.gif b/imgs/open_pose_input.gif new file mode 100644 index 0000000..ad834bd Binary files /dev/null and b/imgs/open_pose_input.gif differ diff --git a/imgs/output.gif b/imgs/output.gif new file mode 100644 index 0000000..9d00b1b Binary files /dev/null and b/imgs/output.gif differ diff --git a/imgs/smooth_plot.png b/imgs/smooth_plot.png new file mode 100644 index 0000000..2d47d3d Binary files /dev/null and b/imgs/smooth_plot.png differ diff --git a/maya/maya_skeleton.py b/maya/maya_skeleton.py new file mode 100644 index 0000000..eb2c7cc --- /dev/null +++ b/maya/maya_skeleton.py @@ -0,0 +1,170 @@ +import json +import maya.cmds as cmds +import pymel.core as pm +import maya.OpenMaya as om +import math +import re +import os + + +#path to imageplane content +openpose_images = "/home/flyn/git/3d-pose-baseline/test_images/" # replace it with abs path like "/path/to/bg_images" +#path to 3d-pose-baseline +threed_pose_baseline = "/home/flyn/git/3d-pose-baseline/" +#for 3d use 3d_data.json and set three_dim to True +input_json_path = [os.path.join(threed_pose_baseline, "maya/{0}.json".format(_data)) for _data in ["3d_data", "2d_data"]] # replace it with abs path like "/path/to/2d_data.json" + + + +def load_data(data, threed): + suffix = "threed" if threed else "twod" + # jnts to ignore + to_pass = [5,4,9,10,12] if threed else [] + # locator driver grp + if not cmds.objExists("drivers_{0}".format(suffix)): + cmds.group(n="drivers_{0}".format(suffix), em=True) + for frame, jnt in data.iteritems(): + if not cmds.objExists("anim_joint"): + cmds.group(n="anim_joint", em=True) + anim_grp_prj = cmds.group(n="anim_joint_2d", em=True) + cmds.parent(anim_grp_prj, "anim_joint") + for jnt_id, trans in jnt.iteritems(): + if not int(jnt_id) in to_pass: + if not cmds.objExists("anim_jnt_driver_{0}_{1}".format(jnt_id, suffix)): + cmds.select(clear=True) + jnt = cmds.joint(n="jnt_{0}_{1}".format(jnt_id, suffix), relative=True) + cmds.setAttr("{0}.radius".format(jnt), 10) + cmds.setAttr("{0}.displayLocalAxis".format(jnt), 1) + # match same pos for first frame + if threed: + cmds.move(trans["translate"][0],trans["translate"][1], trans["translate"][2], jnt) + else: + cmds.move(trans["translate"][0],trans["translate"][1], jnt) + anim_grp_child = cmds.listRelatives("anim_joint", children=True) or [] + if not jnt in anim_grp_child: + cmds.parent(jnt, "anim_joint") + + if threed: + #create 2d projection + jnt_proj = cmds.duplicate(jnt, n="jnt_prj_{0}".format(jnt_id)) + cmds.pointConstraint(jnt, jnt_proj, mo=False, skip="z") + cmds.setAttr("{0}.translateZ".format(jnt_proj[0]), 0) + cmds.parent(jnt_proj, "anim_joint_2d") + + # driver locator + driver = cmds.spaceLocator(n="anim_jnt_driver_{0}_{1}".format(jnt_id, suffix)) + # drive jnt with animated locator frim frame 0 + cmds.pointConstraint(driver, jnt) + #if not driver in cmds.listRelatives("drivers_{0}".format(suffix), children=True) or []: + cmds.parent(driver, "drivers_{0}".format(suffix)) + # add trans anim values to driver locator + cmds.setKeyframe("anim_jnt_driver_{0}_{1}".format(jnt_id, suffix), t=frame, v=trans["translate"][0], at='translateX') + cmds.setKeyframe("anim_jnt_driver_{0}_{1}".format(jnt_id, suffix), t=frame, v=trans["translate"][1], at='translateY') + if threed: + cmds.setKeyframe("anim_jnt_driver_{0}_{1}".format(jnt_id, suffix), t=frame, v=trans["translate"][2], at='translateZ') + # hacking 3d-pose-baseline coord. to maya + cmds.setAttr("drivers_{0}.rotateX".format(suffix), -110 if threed else -180) + +def parent_skeleton(jnt_mapping): + if not isinstance(jnt_mapping, dict): + raise Exception("expected dict, {0}".format(type(jnt_mapping))) + #parent jnts based on jnt_mapping + for body_part, jnt_map in jnt_mapping.iteritems(): + for map_dict in jnt_map: + for parent_jnt, child_jnt in map_dict.iteritems(): + if isinstance(child_jnt, list): + for child in child_jnt: + cmds.parent(child, parent_jnt) + else: + cmds.parent(child_jnt,parent_jnt) + +def get_rotate(p1, p2): + #calc rot for 3d json + punkt_a = om.MPoint(p1[0], p1[1], p1[2]) + punkt_b = om.MPoint(p2[0], p2[1], p2[2]) + rot_vector = punkt_a - punkt_b + world = om.MVector(0, 1, 0) + quat = om.MQuaternion(world, rot_vector, 1) + mat = om.MTransformationMatrix() + util = om.MScriptUtil() + util.createFromDouble(0, 0, 0) + rot_i = util.asDoublePtr() + mat.setRotation(rot_i, om.MTransformationMatrix.kXYZ) + mat = mat.asMatrix() * quat.asMatrix() + quat = om.MTransformationMatrix(mat).rotation() + m_rotation = om.MVector(math.degrees(quat.asEulerRotation().x), + math.degrees(quat.asEulerRotation().y), + math.degrees(quat.asEulerRotation().z) + ) + + return (m_rotation[0],m_rotation[1],m_rotation[2]) + +def set_orient(data, jnt_mapping): + #set orient + for frame, jnt in data.iteritems(): + cmds.currentTime(int(frame)) + for body_part, jnt_map in jnt_mapping.iteritems(): + for map_dict in jnt_map: + for parent_jnt, child_jnt in map_dict.iteritems(): + if not isinstance(child_jnt, list): + p1 = cmds.xform(parent_jnt, q=True, t=True, ws=True) + p2 = cmds.xform(child_jnt, q=True, t=True, ws=True) + rotation = get_rotate(p1,p2) + cmds.setKeyframe(parent_jnt, t=frame, v=rotation[0], at='rotateX') + cmds.setKeyframe(parent_jnt, t=frame, v=rotation[1], at='rotateY') + cmds.setKeyframe(parent_jnt, t=frame, v=rotation[2], at='rotateZ') + + +def main(): + threed_jnt_mapping = { 'root': [{'jnt_11_threed': ['jnt_1_threed','jnt_6_threed']}, + {'jnt_13_threed': ['jnt_17_threed', 'jnt_25_threed']}], + #left leg from jnt 6 to 8 + "left_leg":[{"jnt_{0}_threed".format(n):"jnt_{0}_threed".format(n+1)} for n in range(6,8)], + #right leg from jnt 1 to 3 + "right_leg":[{"jnt_{0}_threed".format(n):"jnt_{0}_threed".format(n+1)} for n in range(1,3)], + #left arm from jnt 17 to 19 + "left_arm":[{"jnt_{0}_threed".format(n):"jnt_{0}_threed".format(n+1)} for n in range(17,19)], + #right arm from jnt 25 to 27 + "right_arm":[{"jnt_{0}_threed".format(n):"jnt_{0}_threed".format(n+1)} for n in range(25,27)]} + + twod_jnt_mapping = { 'root': [{'jnt_1_twod': ['jnt_11_twod','jnt_8_twod','jnt_5_twod', 'jnt_2_twod']}], + #left leg from jnt 11 to 13 + "left_leg":[{"jnt_{0}_twod".format(n):"jnt_{0}_twod".format(n+1)} for n in range(11,13)], + #right leg from jnt 8 to 10 + "right_leg":[{"jnt_{0}_twod".format(n):"jnt_{0}_twod".format(n+1)} for n in range(8,10)], + #left arm from jnt 5 to 7 + "left_arm":[{"jnt_{0}_twod".format(n):"jnt_{0}_twod".format(n+1)} for n in range(5,7)], + #right arm from jnt 2 to 4 + "right_arm":[{"jnt_{0}_twod".format(n):"jnt_{0}_twod".format(n+1)} for n in range(2,4)]} + + #read 2 or 3d json payload + for _data in input_json_path: + with open(_data) as json_data: + # loaded data format: + # frames x jnts x (x,y,z) + # {frame:[jnt:"translate":[x,y,z], jnt:"translate":[x,y,z], ...], frame:...} + data = json.load(json_data) + #set orient on 3d + if "3d_data" in _data: + #load data and build locs and jnts + load_data(data, True) #true for 3d data + #parent jnts + parent_skeleton(threed_jnt_mapping) + #set orientation fo jnts + set_orient(data, threed_jnt_mapping) + else: + #load data and build locs and jnts + load_data(data, False) + #parent jnts + parent_skeleton(twod_jnt_mapping) + + #convert imageplane + convert_images = False + if convert_images: + #set imageplane + #convert openpose --write_images output to valid padding for maya + for image_file in os.listdir(openpose_images): + file_path = os.path.join(openpose_images, image_file) + frame_idx = int(re.findall("(\d+)", image_file)[-1]) + os.rename(file_path, os.path.join(threed_pose_baseline, "maya/image_plane/image.{0}.png".format(frame_idx))) + print "use", os.path.join(threed_pose_baseline, "maya/image_plane/"), " for imageplane." diff --git a/src/openpose_3dpose_sandbox.py b/src/openpose_3dpose_sandbox.py new file mode 100644 index 0000000..6dfe7d1 --- /dev/null +++ b/src/openpose_3dpose_sandbox.py @@ -0,0 +1,447 @@ +import numpy as np +import matplotlib.pyplot as plt +import matplotlib.gridspec as gridspec +import tensorflow as tf +import data_utils +import viz +import re +import cameras +import json +import os +import time +from predict_3dpose import create_model +import cv2 +import imageio +import logging +import scipy as sp +from pprint import pprint +from scipy.interpolate import interp1d +from scipy.interpolate import UnivariateSpline + +FLAGS = tf.app.flags.FLAGS + +order = [15, 12, 25, 26, 27, 17, 18, 19, 1, 2, 3, 6, 7, 8] + +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + +def show_anim_curves(anim_dict, _plt): + val = np.array(list(anim_dict.values())) + for o in range(0,36,2): + x = val[:,o] + y = val[:,o+1] + _plt.plot(x, 'r--', linewidth=0.2) + _plt.plot(y, 'g', linewidth=0.2) + return _plt + +def read_openpose_json(smooth=True, *args): + # openpose output format: + # [x1,y1,c1,x2,y2,c2,...] + # ignore confidence score, take x and y [x1,y1,x2,y2,...] + + logger.info("start reading json files") + #load json files + json_files = os.listdir(openpose_output_dir) + # check for other file types + json_files = sorted([filename for filename in json_files if filename.endswith(".json")]) + cache = {} + smoothed = {} + ### extract x,y and ignore confidence score + for file_name in json_files: + logger.debug("reading {0}".format(file_name)) + _file = os.path.join(openpose_output_dir, file_name) + if not os.path.isfile(_file): raise Exception("No file found!!, {0}".format(_file)) + data = json.load(open(_file)) + #take first person + _data = data["people"][0]["pose_keypoints_2d"] if "pose_keypoints_2d" in data["people"][0] else data["people"][0]["pose_keypoints"] + xy = [] + if len(_data)>=53: + #openpose incl. confidence score + #ignore confidence score + for o in range(0,len(_data),3): + xy.append(_data[o]) + xy.append(_data[o+1]) + else: + #tf-pose-estimation + xy = _data + + # get frame index from openpose 12 padding + frame_indx = re.findall("(\d+)", file_name) + logger.debug("found {0} for frame {1}".format(xy, str(int(frame_indx[-1])))) + + #body_25 support, convert body_25 output format to coco + if len(_data)>54: + _xy = xy[0:19*2] + for x in range(len(xy)): + #del jnt 8 + if x==8*2: + del _xy[x] + if x==8*2+1: + del _xy[x] + #map jnt 9 to 8 + if x==9*2: + _xy[16] = xy[x] + _xy[17] = xy[x+1] + #map jnt 10 to 9 + if x==10*2: + _xy[18] = xy[x] + _xy[19] = xy[x+1] + #map jnt 11 to 10 + if x==11*2: + _xy[20] = xy[x] + _xy[21] = xy[x+1] + #map jnt 12 to 11 + if x==12*2: + _xy[22] = xy[x] + _xy[23] = xy[x+1] + #map jnt 13 to 12 + if x==13*2: + _xy[24] = xy[x] + _xy[25] = xy[x+1] + #map jnt 14 to 13 + if x==14*2: + _xy[26] = xy[x] + _xy[27] = xy[x+1] + #map jnt 15 to 14 + if x==15*2: + _xy[28] = xy[x] + _xy[29] = xy[x+1] + #map jnt 16 to 15 + if x==16*2: + _xy[30] = xy[x] + _xy[31] = xy[x+1] + #map jnt 17 to 16 + if x==17*2: + _xy[32] = xy[x] + _xy[33] = xy[x+1] + #map jnt 18 to 17 + if x==18*2: + _xy[34] = xy[x] + _xy[35] = xy[x+1] + #coco + xy = _xy + + #add xy to frame + cache[int(frame_indx[-1])] = xy + + plt.figure(1) + drop_curves_plot = show_anim_curves(cache, plt) + pngName = 'gif_output/dirty_plot.png' + drop_curves_plot.savefig(pngName) + logger.info('writing gif_output/dirty_plot.png') + + # exit if no smoothing + if not smooth: + # return frames cache incl. 18 joints (x,y) + return cache + + if len(json_files) == 1: + logger.info("found single json file") + # return frames cache incl. 18 joints (x,y) on single image\json + return cache + + if len(json_files) <= 8: + raise Exception("need more frames, min 9 frames/json files for smoothing!!!") + + logger.info("start smoothing") + + # create frame blocks + head_frame_block = [int(re.findall("(\d+)", o)[-1]) for o in json_files[:4]] + tail_frame_block = [int(re.findall("(\d+)", o)[-1]) for o in json_files[-4:]] + + ### smooth by median value, n frames + for frame, xy in cache.items(): + # create neighbor array based on frame index + forward, back = ([] for _ in range(2)) + + # joints x,y array + _len = len(xy) # 36 + + # create array of parallel frames (-33) + for neighbor in range(1,4): + # first n frames, get value of xy in postive lookahead frames(current frame + 3) + if frame in head_frame_block: + forward += cache[frame+neighbor] + # last n frames, get value of xy in negative lookahead frames(current frame - 3) + elif frame in tail_frame_block: + back += cache[frame-neighbor] + else: + # between frames, get value of xy in bi-directional frames(current frame -+ 3) + forward += cache[frame+neighbor] + back += cache[frame-neighbor] + + # build frame range vector + frames_joint_median = [0 for i in range(_len)] + # more info about mapping in src/data_utils.py + # for each 18joints*x,y (x1,y1,x2,y2,...)~36 + for x in range(0,_len,2): + # set x and y + y = x+1 + if frame in head_frame_block: + # get vector of n frames forward for x and y, incl. current frame + x_v = [xy[x], forward[x], forward[x+_len], forward[x+_len*2]] + y_v = [xy[y], forward[y], forward[y+_len], forward[y+_len*2]] + elif frame in tail_frame_block: + # get vector of n frames back for x and y, incl. current frame + x_v =[xy[x], back[x], back[x+_len], back[x+_len*2]] + y_v =[xy[y], back[y], back[y+_len], back[y+_len*2]] + else: + # get vector of n frames forward/back for x and y, incl. current frame + # median value calc: find neighbor frames joint value and sorted them, use numpy median module + # frame[x1,y1,[x2,y2],..]frame[x1,y1,[x2,y2],...], frame[x1,y1,[x2,y2],..] + # ^---------------------|-------------------------^ + x_v =[xy[x], forward[x], forward[x+_len], forward[x+_len*2], + back[x], back[x+_len], back[x+_len*2]] + y_v =[xy[y], forward[y], forward[y+_len], forward[y+_len*2], + back[y], back[y+_len], back[y+_len*2]] + + # get median of vector + x_med = np.median(sorted(x_v)) + y_med = np.median(sorted(y_v)) + + # holding frame drops for joint + if not x_med: + # allow fix from first frame + if frame: + # get x from last frame + x_med = smoothed[frame-1][x] + # if joint is hidden y + if not y_med: + # allow fix from first frame + if frame: + # get y from last frame + y_med = smoothed[frame-1][y] + + logger.debug("old X {0} sorted neighbor {1} new X {2}".format(xy[x],sorted(x_v), x_med)) + logger.debug("old Y {0} sorted neighbor {1} new Y {2}".format(xy[y],sorted(y_v), y_med)) + + # build new array of joint x and y value + frames_joint_median[x] = x_med + frames_joint_median[x+1] = y_med + + + smoothed[frame] = frames_joint_median + + return smoothed + + +def main(_): + + smoothed = read_openpose_json() + plt.figure(2) + smooth_curves_plot = show_anim_curves(smoothed, plt) + #return + pngName = 'gif_output/smooth_plot.png' + smooth_curves_plot.savefig(pngName) + logger.info('writing gif_output/smooth_plot.png') + + if FLAGS.interpolation: + logger.info("start interpolation") + + framerange = len( smoothed.keys() ) + joint_rows = 36 + array = np.concatenate(list(smoothed.values())) + array_reshaped = np.reshape(array, (framerange, joint_rows) ) + + multiplier = FLAGS.multiplier + multiplier_inv = 1/multiplier + + out_array = np.array([]) + for row in range(joint_rows): + x = [] + for frame in range(framerange): + x.append( array_reshaped[frame, row] ) + + frame = range( framerange ) + frame_resampled = np.arange(0, framerange, multiplier) + spl = UnivariateSpline(frame, x, k=3) + #relative smooth factor based on jnt anim curve + min_x, max_x = min(x), max(x) + smooth_fac = max_x - min_x + smooth_resamp = 125 + smooth_fac = smooth_fac * smooth_resamp + spl.set_smoothing_factor( float(smooth_fac) ) + xnew = spl(frame_resampled) + + out_array = np.append(out_array, xnew) + + logger.info("done interpolating. reshaping {0} frames, please wait!!".format(framerange)) + + a = np.array([]) + for frame in range( int( framerange * multiplier_inv ) ): + jnt_array = [] + for jnt in range(joint_rows): + jnt_array.append( out_array[ jnt * int(framerange * multiplier_inv) + frame] ) + a = np.append(a, jnt_array) + + a = np.reshape(a, (int(framerange * multiplier_inv), joint_rows)) + out_array = a + + interpolate_smoothed = {} + for frame in range( int(framerange * multiplier_inv) ): + interpolate_smoothed[frame] = list( out_array[frame] ) + + plt.figure(3) + smoothed = interpolate_smoothed + interpolate_curves_plot = show_anim_curves(smoothed, plt) + pngName = 'gif_output/interpolate_{0}.png'.format(smooth_resamp) + interpolate_curves_plot.savefig(pngName) + logger.info('writing gif_output/interpolate_plot.png') + + enc_in = np.zeros((1, 64)) + enc_in[0] = [0 for i in range(64)] + + actions = data_utils.define_actions(FLAGS.action) + + SUBJECT_IDS = [1, 5, 6, 7, 8, 9, 11] + rcams = cameras.load_cameras(FLAGS.cameras_path, SUBJECT_IDS) + train_set_2d, test_set_2d, data_mean_2d, data_std_2d, dim_to_ignore_2d, dim_to_use_2d = data_utils.read_2d_predictions( + actions, FLAGS.data_dir) + train_set_3d, test_set_3d, data_mean_3d, data_std_3d, dim_to_ignore_3d, dim_to_use_3d, train_root_positions, test_root_positions = data_utils.read_3d_data( + actions, FLAGS.data_dir, FLAGS.camera_frame, rcams, FLAGS.predict_14) + + device_count = {"GPU": 1} + png_lib = [] + before_pose = None + with tf.Session(config=tf.ConfigProto( + device_count=device_count, + allow_soft_placement=True)) as sess: + #plt.figure(3) + batch_size = 128 + model = create_model(sess, actions, batch_size) + iter_range = len(smoothed.keys()) + export_units = {} + twod_export_units = {} + for n, (frame, xy) in enumerate(smoothed.items()): + logger.info("calc frame {0}/{1}".format(frame, iter_range)) + # map list into np array + joints_array = np.zeros((1, 36)) + joints_array[0] = [0 for i in range(36)] + for o in range(len(joints_array[0])): + #feed array with xy array + joints_array[0][o] = float(xy[o]) + + twod_export_units[frame]={} + for abs_b, __n in enumerate(range(0, len(xy),2)): + twod_export_units[frame][abs_b] = {"translate": [xy[__n],xy[__n+1]]} + + _data = joints_array[0] + # mapping all body parts or 3d-pose-baseline format + for i in range(len(order)): + for j in range(2): + # create encoder input + enc_in[0][order[i] * 2 + j] = _data[i * 2 + j] + for j in range(2): + # Hip + enc_in[0][0 * 2 + j] = (enc_in[0][1 * 2 + j] + enc_in[0][6 * 2 + j]) / 2 + # Neck/Nose + enc_in[0][14 * 2 + j] = (enc_in[0][15 * 2 + j] + enc_in[0][12 * 2 + j]) / 2 + # Thorax + enc_in[0][13 * 2 + j] = 2 * enc_in[0][12 * 2 + j] - enc_in[0][14 * 2 + j] + + # set spine + spine_x = enc_in[0][24] + spine_y = enc_in[0][25] + + enc_in = enc_in[:, dim_to_use_2d] + mu = data_mean_2d[dim_to_use_2d] + stddev = data_std_2d[dim_to_use_2d] + enc_in = np.divide((enc_in - mu), stddev) + + dp = 1.0 + dec_out = np.zeros((1, 48)) + dec_out[0] = [0 for i in range(48)] + _, _, poses3d = model.step(sess, enc_in, dec_out, dp, isTraining=False) + all_poses_3d = [] + enc_in = data_utils.unNormalizeData(enc_in, data_mean_2d, data_std_2d, dim_to_ignore_2d) + poses3d = data_utils.unNormalizeData(poses3d, data_mean_3d, data_std_3d, dim_to_ignore_3d) + gs1 = gridspec.GridSpec(1, 1) + gs1.update(wspace=-0.00, hspace=0.05) # set the spacing between axes. + plt.axis('off') + all_poses_3d.append( poses3d ) + enc_in, poses3d = map( np.vstack, [enc_in, all_poses_3d] ) + subplot_idx, exidx = 1, 1 + _max = 0 + _min = 10000 + + for i in range(poses3d.shape[0]): + for j in range(32): + tmp = poses3d[i][j * 3 + 2] + poses3d[i][j * 3 + 2] = poses3d[i][j * 3 + 1] + poses3d[i][j * 3 + 1] = tmp + if poses3d[i][j * 3 + 2] > _max: + _max = poses3d[i][j * 3 + 2] + if poses3d[i][j * 3 + 2] < _min: + _min = poses3d[i][j * 3 + 2] + + for i in range(poses3d.shape[0]): + for j in range(32): + poses3d[i][j * 3 + 2] = _max - poses3d[i][j * 3 + 2] + _min + poses3d[i][j * 3] += (spine_x - 630) + poses3d[i][j * 3 + 2] += (500 - spine_y) + + # Plot 3d predictions + ax = plt.subplot(gs1[subplot_idx - 1], projection='3d') + ax.view_init(18, -70) + + if FLAGS.cache_on_fail: + if np.min(poses3d) < -1000: + poses3d = before_pose + + p3d = poses3d + logger.info("frame score {0}".format(np.min(poses3d))) + x,y,z = [[] for _ in range(3)] + if not poses3d is None: + to_export = poses3d.tolist()[0] + else: + to_export = [0.0 for _ in range(96)] + logger.info("export {0}".format(to_export)) + for o in range(0, len(to_export), 3): + x.append(to_export[o]) + y.append(to_export[o+1]) + z.append(to_export[o+2]) + + export_units[frame]={} + for jnt_index, (_x, _y, _z) in enumerate(zip(x,y,z)): + export_units[frame][jnt_index] = {"translate": [_x, _y, _z]} + viz.show3Dpose(p3d, ax, lcolor="#9b59b6", rcolor="#2ecc71") + + pngName = 'png/pose_frame_{0}.png'.format(str(frame).zfill(12)) + plt.savefig(pngName) + if FLAGS.write_gif: + png_lib.append(imageio.imread(pngName)) + + if FLAGS.cache_on_fail: + before_pose = poses3d + + if FLAGS.write_gif: + if FLAGS.interpolation: + #take every frame on gif_fps * multiplier_inv + png_lib = np.array([png_lib[png_image] for png_image in range(0,len(png_lib), int(multiplier_inv)) ]) + logger.info("creating Gif gif_output/animation.gif, please Wait!") + imageio.mimsave('gif_output/animation.gif', png_lib, fps=FLAGS.gif_fps) + + _out_file = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'maya/3d_data.json') + twod_out_file = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'maya/2d_data.json') + with open(_out_file, 'w') as outfile: + logger.info("exported maya json to {0}".format(_out_file)) + json.dump(export_units, outfile) + with open(twod_out_file, 'w') as outfile: + logger.info("exported maya json to {0}".format(twod_out_file)) + json.dump(twod_export_units, outfile) + + logger.info("Done!".format(pngName)) + +if __name__ == "__main__": + + openpose_output_dir = FLAGS.pose_estimation_json + + level = {0:logging.ERROR, + 1:logging.WARNING, + 2:logging.INFO, + 3:logging.DEBUG} + + logger.setLevel(level[FLAGS.verbose]) + + + tf.app.run() diff --git a/src/openpose_3dpose_sandbox_realtime.py b/src/openpose_3dpose_sandbox_realtime.py new file mode 100644 index 0000000..48f870b --- /dev/null +++ b/src/openpose_3dpose_sandbox_realtime.py @@ -0,0 +1,237 @@ + +import numpy as np +import matplotlib.pyplot as plt +import matplotlib.gridspec as gridspec +import tensorflow as tf +import data_utils +import viz +import re +import cameras +import json +import os +from predict_3dpose import create_model +import cv2 +import imageio +import time +import logging +import glob +FLAGS = tf.app.flags.FLAGS + +order = [15, 12, 25, 26, 27, 17, 18, 19, 1, 2, 3, 6, 7, 8] + +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + + + +def main(_): + done = [] + + enc_in = np.zeros((1, 64)) + enc_in[0] = [0 for i in range(64)] + + actions = data_utils.define_actions(FLAGS.action) + + SUBJECT_IDS = [1, 5, 6, 7, 8, 9, 11] + rcams = cameras.load_cameras(FLAGS.cameras_path, SUBJECT_IDS) + train_set_2d, test_set_2d, data_mean_2d, data_std_2d, dim_to_ignore_2d, dim_to_use_2d = data_utils.read_2d_predictions( + actions, FLAGS.data_dir) + train_set_3d, test_set_3d, data_mean_3d, data_std_3d, dim_to_ignore_3d, dim_to_use_3d, train_root_positions, test_root_positions = data_utils.read_3d_data( + actions, FLAGS.data_dir, FLAGS.camera_frame, rcams, FLAGS.predict_14) + + device_count = {"GPU": 0} + png_lib = [] + with tf.Session(config=tf.ConfigProto( + device_count=device_count, + allow_soft_placement=True)) as sess: + #plt.figure(3) + batch_size = 128 + model = create_model(sess, actions, batch_size) + while True: + key = cv2.waitKey(1) & 0xFF + #logger.info("start reading data") + # check for other file types + list_of_files = glob.iglob("{0}/*".format(openpose_output_dir)) # You may use iglob in Python3 + latest_file = "" + try: + latest_file = max(list_of_files, key=os.path.getctime) + except ValueError: + #empthy dir + pass + if not latest_file: + continue + try: + _file = file_name = latest_file + print (latest_file) + if not os.path.isfile(_file): raise Exception("No file found!!, {0}".format(_file)) + data = json.load(open(_file)) + #take first person + _data = data["people"][0]["pose_keypoints_2d"] + xy = [] + if len(_data)>=53: + #openpose incl. confidence score + #ignore confidence score + for o in range(0,len(_data),3): + xy.append(_data[o]) + xy.append(_data[o+1]) + else: + #tf-pose-estimation + xy = _data + + frame_indx = re.findall("(\d+)", file_name) + frame = int(frame_indx[-1]) + logger.debug("found {0} for frame {1}".format(xy, str(frame))) + + #body_25 support, convert body_25 output format to coco + if len(xy)>54: + _xy = xy[0:19*2] + for x in range(len(xy)): + #del jnt 8 + if x==8*2: + del _xy[x] + if x==8*2+1: + del _xy[x] + #map jnt 9 to 8 + if x==9*2: + _xy[16] = xy[x] + _xy[17] = xy[x+1] + #map jnt 10 to 9 + if x==10*2: + _xy[18] = xy[x] + _xy[19] = xy[x+1] + #map jnt 11 to 10 + if x==11*2: + _xy[20] = xy[x] + _xy[21] = xy[x+1] + #map jnt 12 to 11 + if x==12*2: + _xy[22] = xy[x] + _xy[23] = xy[x+1] + #map jnt 13 to 12 + if x==13*2: + _xy[24] = xy[x] + _xy[25] = xy[x+1] + #map jnt 14 to 13 + if x==14*2: + _xy[26] = xy[x] + _xy[27] = xy[x+1] + #map jnt 15 to 14 + if x==15*2: + _xy[28] = xy[x] + _xy[29] = xy[x+1] + #map jnt 16 to 15 + if x==16*2: + _xy[30] = xy[x] + _xy[31] = xy[x+1] + #map jnt 17 to 16 + if x==17*2: + _xy[32] = xy[x] + _xy[33] = xy[x+1] + #map jnt 18 to 17 + if x==18*2: + _xy[34] = xy[x] + _xy[35] = xy[x+1] + #coco + xy = _xy + + joints_array = np.zeros((1, 36)) + joints_array[0] = [0 for i in range(36)] + for o in range(len(joints_array[0])): + #feed array with xy array + joints_array[0][o] = xy[o] + _data = joints_array[0] + # mapping all body parts or 3d-pose-baseline format + for i in range(len(order)): + for j in range(2): + # create encoder input + enc_in[0][order[i] * 2 + j] = _data[i * 2 + j] + for j in range(2): + # Hip + enc_in[0][0 * 2 + j] = (enc_in[0][1 * 2 + j] + enc_in[0][6 * 2 + j]) / 2 + # Neck/Nose + enc_in[0][14 * 2 + j] = (enc_in[0][15 * 2 + j] + enc_in[0][12 * 2 + j]) / 2 + # Thorax + enc_in[0][13 * 2 + j] = 2 * enc_in[0][12 * 2 + j] - enc_in[0][14 * 2 + j] + + # set spine + spine_x = enc_in[0][24] + spine_y = enc_in[0][25] + + enc_in = enc_in[:, dim_to_use_2d] + mu = data_mean_2d[dim_to_use_2d] + stddev = data_std_2d[dim_to_use_2d] + enc_in = np.divide((enc_in - mu), stddev) + + dp = 1.0 + dec_out = np.zeros((1, 48)) + dec_out[0] = [0 for i in range(48)] + _, _, poses3d = model.step(sess, enc_in, dec_out, dp, isTraining=False) + all_poses_3d = [] + enc_in = data_utils.unNormalizeData(enc_in, data_mean_2d, data_std_2d, dim_to_ignore_2d) + poses3d = data_utils.unNormalizeData(poses3d, data_mean_3d, data_std_3d, dim_to_ignore_3d) + gs1 = gridspec.GridSpec(1, 1) + gs1.update(wspace=-0.00, hspace=0.05) # set the spacing between axes. + plt.axis('off') + all_poses_3d.append( poses3d ) + enc_in, poses3d = map( np.vstack, [enc_in, all_poses_3d] ) + subplot_idx, exidx = 1, 1 + _max = 0 + _min = 10000 + + for i in range(poses3d.shape[0]): + for j in range(32): + tmp = poses3d[i][j * 3 + 2] + poses3d[i][j * 3 + 2] = poses3d[i][j * 3 + 1] + poses3d[i][j * 3 + 1] = tmp + if poses3d[i][j * 3 + 2] > _max: + _max = poses3d[i][j * 3 + 2] + if poses3d[i][j * 3 + 2] < _min: + _min = poses3d[i][j * 3 + 2] + + for i in range(poses3d.shape[0]): + for j in range(32): + poses3d[i][j * 3 + 2] = _max - poses3d[i][j * 3 + 2] + _min + poses3d[i][j * 3] += (spine_x - 630) + poses3d[i][j * 3 + 2] += (500 - spine_y) + + # Plot 3d predictions + ax = plt.subplot(gs1[subplot_idx - 1], projection='3d') + ax.view_init(18, -70) + logger.debug(np.min(poses3d)) + if np.min(poses3d) < -1000 and frame != 0: + poses3d = before_pose + + p3d = poses3d + + viz.show3Dpose(p3d, ax, lcolor="#9b59b6", rcolor="#2ecc71") + before_pose = poses3d + pngName = 'png/test_{0}.png'.format(str(frame)) + plt.savefig(pngName) + + #plt.show() + img = cv2.imread(pngName,0) + rect_cpy = img.copy() + cv2.imshow('3d-pose-baseline', rect_cpy) + done.append(file_name) + if key == ord('q'): + break + except Exception as e: + print (e) + + sess.close() + + + +if __name__ == "__main__": + + openpose_output_dir = FLAGS.pose_estimation_json + + level = {0:logging.ERROR, + 1:logging.WARNING, + 2:logging.INFO, + 3:logging.DEBUG} + + logger.setLevel(level[FLAGS.verbose]) + + + tf.app.run() diff --git a/src/predict_3dpose.py b/src/predict_3dpose.py index 7c1b23e..5e29144 100644 --- a/src/predict_3dpose.py +++ b/src/predict_3dpose.py @@ -51,6 +51,15 @@ tf.app.flags.DEFINE_string("data_dir", "data/h36m/", "Data directory") tf.app.flags.DEFINE_string("train_dir", "experiments", "Training directory.") +# openpose +tf.app.flags.DEFINE_string("pose_estimation_json", "/tmp/", "pose estimation json output directory, openpose or tf-pose-estimation") +tf.app.flags.DEFINE_boolean("interpolation", False, "interpolate openpose json") +tf.app.flags.DEFINE_float("multiplier", 0.1, "interpolation frame range") +tf.app.flags.DEFINE_boolean("write_gif", False, "write final anim gif") +tf.app.flags.DEFINE_integer("gif_fps", 30, "output gif framerate") +tf.app.flags.DEFINE_integer("verbose", 2, "0:Error, 1:Warning, 2:INFO*(default), 3:debug") +tf.app.flags.DEFINE_boolean("cache_on_fail", True, "caching last valid frame on invalid frame") + # Train or load tf.app.flags.DEFINE_boolean("sample", False, "Set to True for sampling.") tf.app.flags.DEFINE_boolean("use_cpu", False, "Whether to use the CPU") diff --git a/src/viz.py b/src/viz.py index ea43a31..c470c53 100644 --- a/src/viz.py +++ b/src/viz.py @@ -28,11 +28,10 @@ def show3Dpose(channels, ax, lcolor="#3498db", rcolor="#e74c3c", add_labels=Fals I = np.array([1,2,3,1,7,8,1, 13,14,15,14,18,19,14,26,27])-1 # start points J = np.array([2,3,4,7,8,9,13,14,15,16,18,19,20,26,27,28])-1 # end points LR = np.array([1,1,1,0,0,0,0, 0, 0, 0, 0, 0, 0, 1, 1, 1], dtype=bool) - # Make connection matrix for i in np.arange( len(I) ): x, y, z = [np.array( [vals[I[i], j], vals[J[i], j]] ) for j in range(3)] - ax.plot(x, y, z, lw=2, c=lcolor if LR[i] else rcolor) + ax.plot(x, y, z, marker='o', markersize=2, lw=1, c=lcolor if LR[i] else rcolor) RADIUS = 750 # space around the subject xroot, yroot, zroot = vals[0,0], vals[0,1], vals[0,2] @@ -56,7 +55,7 @@ def show3Dpose(channels, ax, lcolor="#3498db", rcolor="#e74c3c", add_labels=Fals ax.set_aspect('equal') # Get rid of the panes (actually, make them white) - white = (1.0, 1.0, 1.0, 0.0) + white = (1.0, 1.0, 0.1, 0.0) ax.w_xaxis.set_pane_color(white) ax.w_yaxis.set_pane_color(white) # Keep z pane