def blend_eye(fno: int, motion: VmdMotion): min_blink = min(motion.morphs["ウィンク右"][fno].ratio, motion.morphs["ウィンク"][fno].ratio) min_smile = min(motion.morphs["ウィンク2右"][fno].ratio, motion.morphs["ウィンク2"][fno].ratio) # 両方の同じ値はさっぴく motion.morphs["ウィンク右"][fno].ratio -= min_smile motion.morphs["ウィンク"][fno].ratio -= min_smile motion.morphs["ウィンク2右"][fno].ratio -= min_blink motion.morphs["ウィンク2"][fno].ratio -= min_blink mf = VmdMorphFrame(fno) mf.set_name("笑い") mf.ratio = max(0, min(1, min_smile)) motion.regist_mf(mf, mf.name, mf.fno) mf = VmdMorphFrame(fno) mf.set_name("まばたき") mf.ratio = max(0, min(1, min_blink)) motion.regist_mf(mf, mf.name, mf.fno)
def execute(cmd_args): folder_path = cmd_args.folder_path motion = VmdMotion() # 動画上の関節位置 for fno, joints_path in enumerate( glob.glob(osp.join(folder_path, 'faces_*.json'))): logger.info(f"■ fno: {fno} -----") frame_joints = {} with open(joints_path, 'r') as f: frame_joints = json.load(f) # まばたき calc_left_blink(fno, motion, frame_joints) calc_right_blink(fno, motion, frame_joints) blend_eye(fno, motion) # 口 calc_lip(fno, motion, frame_joints) # 眉 calc_eyebrow(fno, motion, frame_joints) model = PmxModel() model.name = "Morph Model" writer = VmdWriter( motion, model, osp.join( folder_path, "morph_{0}.vmd".format( datetime.datetime.now().strftime('%Y%m%d_%H%M%S')))) writer.write()
def calc_finger(bf: VmdBoneFrame, motion: VmdMotion, model: PmxModel, jname: str, default_joints: dict, frame_joints: dict, name_list: list, parent_list: list): rotation = calc_direction_qq(bf.fno, motion, frame_joints, *name_list) bone_initial = calc_bone_direction_qq(bf, motion, model, jname, *name_list) qq = MQuaternion() for parent_name in reversed(parent_list): qq *= motion.calc_bf(parent_name, bf.fno).rotation.inverted() qq = qq * rotation * bone_initial.inverted() _, _, z_qq, _ = separate_local_qq(bf.fno, bf.name, qq, model.get_local_x_axis(bf.name)) z_limited_qq = MQuaternion.fromAxisAndAngle( MVector3D(0, 0, -1 * (-1 if "right" in jname else 1)), min(90, z_qq.toDegree())) bf.rotation = z_limited_qq motion.regist_bf(bf, bf.name, bf.fno)
def calc_right_blink(fno: int, motion: VmdMotion, frame_joints: dict): right_eye1 = get_vec2(frame_joints["0"]["faces"], "right_eye1") right_eye2 = get_vec2(frame_joints["0"]["faces"], "right_eye2") right_eye3 = get_vec2(frame_joints["0"]["faces"], "right_eye3") right_eye4 = get_vec2(frame_joints["0"]["faces"], "right_eye4") right_eye5 = get_vec2(frame_joints["0"]["faces"], "right_eye5") right_eye6 = get_vec2(frame_joints["0"]["faces"], "right_eye6") # 右目のEAR(eyes aspect ratio) right_blink, right_smile = get_blink_ratio(right_eye1, right_eye2, right_eye3, right_eye4, right_eye5, right_eye6) mf = VmdMorphFrame(fno) mf.set_name("ウィンク") mf.ratio = max(0, min(1, right_smile)) motion.regist_mf(mf, mf.name, mf.fno) mf = VmdMorphFrame(fno) mf.set_name("ウィンク2") mf.ratio = max(0, min(1, right_blink)) motion.regist_mf(mf, mf.name, mf.fno)
def calc_left_blink(fno: int, motion: VmdMotion, frame_joints: dict): left_eye1 = get_vec2(frame_joints["0"]["faces"], "left_eye1") left_eye2 = get_vec2(frame_joints["0"]["faces"], "left_eye2") left_eye3 = get_vec2(frame_joints["0"]["faces"], "left_eye3") left_eye4 = get_vec2(frame_joints["0"]["faces"], "left_eye4") left_eye5 = get_vec2(frame_joints["0"]["faces"], "left_eye5") left_eye6 = get_vec2(frame_joints["0"]["faces"], "left_eye6") # 左目のEAR(eyes aspect ratio) left_blink, left_smile = get_blink_ratio(left_eye1, left_eye2, left_eye3, left_eye4, left_eye5, left_eye6) mf = VmdMorphFrame(fno) mf.set_name("ウィンク右") mf.ratio = max(0, min(1, left_smile)) motion.regist_mf(mf, mf.name, mf.fno) mf = VmdMorphFrame(fno) mf.set_name("ウィンク2右") mf.ratio = max(0, min(1, left_blink)) motion.regist_mf(mf, mf.name, mf.fno)
def calc_eyebrow(fno: int, motion: VmdMotion, frame_joints: dict): left_eye_brow1 = get_vec2(frame_joints["0"]["faces"], "left_eye_brow1") left_eye_brow2 = get_vec2(frame_joints["0"]["faces"], "left_eye_brow2") left_eye_brow3 = get_vec2(frame_joints["0"]["faces"], "left_eye_brow3") left_eye_brow4 = get_vec2(frame_joints["0"]["faces"], "left_eye_brow4") left_eye_brow5 = get_vec2(frame_joints["0"]["faces"], "left_eye_brow5") left_eye1 = get_vec2(frame_joints["0"]["faces"], "left_eye1") left_eye2 = get_vec2(frame_joints["0"]["faces"], "left_eye2") left_eye3 = get_vec2(frame_joints["0"]["faces"], "left_eye3") left_eye4 = get_vec2(frame_joints["0"]["faces"], "left_eye4") left_eye5 = get_vec2(frame_joints["0"]["faces"], "left_eye5") left_eye6 = get_vec2(frame_joints["0"]["faces"], "left_eye6") right_eye_brow1 = get_vec2(frame_joints["0"]["faces"], "right_eye_brow1") right_eye_brow2 = get_vec2(frame_joints["0"]["faces"], "right_eye_brow2") right_eye_brow3 = get_vec2(frame_joints["0"]["faces"], "right_eye_brow3") right_eye_brow4 = get_vec2(frame_joints["0"]["faces"], "right_eye_brow4") right_eye_brow5 = get_vec2(frame_joints["0"]["faces"], "right_eye_brow5") right_eye1 = get_vec2(frame_joints["0"]["faces"], "right_eye1") right_eye2 = get_vec2(frame_joints["0"]["faces"], "right_eye2") right_eye3 = get_vec2(frame_joints["0"]["faces"], "right_eye3") right_eye4 = get_vec2(frame_joints["0"]["faces"], "right_eye4") right_eye5 = get_vec2(frame_joints["0"]["faces"], "right_eye5") right_eye6 = get_vec2(frame_joints["0"]["faces"], "right_eye6") left_nose_1 = get_vec2(frame_joints["0"]["faces"], 'left_nose_1') right_nose_2 = get_vec2(frame_joints["0"]["faces"], 'right_nose_2') # 鼻の幅 nose_width = abs(left_nose_1.x() - right_nose_2.x()) # 眉のしかめ具合 frown_ratio = abs(left_eye_brow1.x() - right_eye_brow1.x()) / nose_width # 眉の幅 eye_brow_length = ( euclidean_distance(left_eye_brow1, left_eye_brow5) + euclidean_distance(right_eye_brow1, right_eye_brow5)) / 2 # 目の幅 eye_length = (euclidean_distance(left_eye1, left_eye4) + euclidean_distance(right_eye1, right_eye4)) / 2 # 目と眉の縦幅 left_vertical_length = ( euclidean_distance(left_eye1, left_eye_brow1) + euclidean_distance(right_eye1, right_eye_brow1)) / 2 center_vertical_length = ( euclidean_distance(left_eye2, left_eye_brow3) + euclidean_distance(right_eye2, right_eye_brow3)) / 2 right_vertical_length = ( euclidean_distance(left_eye4, left_eye_brow5) + euclidean_distance(right_eye4, right_eye_brow5)) / 2 left_ratio = left_vertical_length / eye_brow_length center_ratio = center_vertical_length / eye_brow_length right_ratio = right_vertical_length / eye_brow_length updown_ratio = center_ratio - 0.5 if updown_ratio >= 0.2: # 上 mf = VmdMorphFrame(fno) mf.set_name("上") mf.ratio = max(0, min(1, abs(updown_ratio) + 0.3)) motion.regist_mf(mf, mf.name, mf.fno) mf = VmdMorphFrame(fno) mf.set_name("下") mf.ratio = 0 motion.regist_mf(mf, mf.name, mf.fno) else: # 下 mf = VmdMorphFrame(fno) mf.set_name("下") mf.ratio = max(0, min(1, abs(updown_ratio) + 0.3)) motion.regist_mf(mf, mf.name, mf.fno) mf = VmdMorphFrame(fno) mf.set_name("上") mf.ratio = 0 motion.regist_mf(mf, mf.name, mf.fno) mf = VmdMorphFrame(fno) mf.set_name("困る") mf.ratio = max(0, min(1, (0.8 - frown_ratio))) motion.regist_mf(mf, mf.name, mf.fno) if left_ratio >= right_ratio: # 怒る系 mf = VmdMorphFrame(fno) mf.set_name("怒り") mf.ratio = max(0, min(1, abs(left_ratio - right_ratio))) motion.regist_mf(mf, mf.name, mf.fno) mf = VmdMorphFrame(fno) mf.set_name("にこり") mf.ratio = 0 motion.regist_mf(mf, mf.name, mf.fno) else: # 笑う系 mf = VmdMorphFrame(fno) mf.set_name("にこり") mf.ratio = max(0, min(1, abs(right_ratio - left_ratio))) motion.regist_mf(mf, mf.name, mf.fno) mf = VmdMorphFrame(fno) mf.set_name("怒り") mf.ratio = 0 motion.regist_mf(mf, mf.name, mf.fno)
def calc_lip(fno: int, motion: VmdMotion, frame_joints: dict): left_nose_1 = get_vec2(frame_joints["0"]["faces"], 'left_nose_1') right_nose_2 = get_vec2(frame_joints["0"]["faces"], 'right_nose_2') left_mouth_1 = get_vec2(frame_joints["0"]["faces"], 'left_mouth_1') left_mouth_2 = get_vec2(frame_joints["0"]["faces"], 'left_mouth_2') left_mouth_3 = get_vec2(frame_joints["0"]["faces"], 'left_mouth_3') mouth_top = get_vec2(frame_joints["0"]["faces"], 'mouth_top') right_mouth_3 = get_vec2(frame_joints["0"]["faces"], 'right_mouth_3') right_mouth_2 = get_vec2(frame_joints["0"]["faces"], 'right_mouth_2') right_mouth_1 = get_vec2(frame_joints["0"]["faces"], 'right_mouth_1') right_mouth_5 = get_vec2(frame_joints["0"]["faces"], 'right_mouth_5') right_mouth_4 = get_vec2(frame_joints["0"]["faces"], 'right_mouth_4') mouth_bottom = get_vec2(frame_joints["0"]["faces"], 'mouth_bottom') left_mouth_4 = get_vec2(frame_joints["0"]["faces"], 'left_mouth_4') left_mouth_5 = get_vec2(frame_joints["0"]["faces"], 'left_mouth_5') left_lip_1 = get_vec2(frame_joints["0"]["faces"], 'left_lip_1') left_lip_2 = get_vec2(frame_joints["0"]["faces"], 'left_lip_2') lip_top = get_vec2(frame_joints["0"]["faces"], 'lip_top') right_lip_2 = get_vec2(frame_joints["0"]["faces"], 'right_lip_2') right_lip_1 = get_vec2(frame_joints["0"]["faces"], 'right_lip_1') right_lip_3 = get_vec2(frame_joints["0"]["faces"], 'right_lip_3') lip_bottom = get_vec2(frame_joints["0"]["faces"], 'lip_bottom') left_lip_3 = get_vec2(frame_joints["0"]["faces"], 'left_lip_3') # 鼻の幅 nose_width = abs(left_nose_1.x() - right_nose_2.x()) # 口角の平均値 corner_center = (left_mouth_1 + right_mouth_1) / 2 # 口角の幅 mouse_width = abs(left_mouth_1.x() - right_mouth_1.x()) # 鼻基準の口の横幅比率 mouse_width_ratio = mouse_width / nose_width # 上唇の平均値 top_mouth_center = (right_mouth_3 + mouth_top + left_mouth_3) / 3 top_lip_center = (right_lip_2 + lip_top + left_lip_2) / 3 # 下唇の平均値 bottom_mouth_center = (right_mouth_4 + mouth_bottom + left_mouth_4) / 3 bottom_lip_center = (right_lip_3 + lip_bottom + left_lip_3) / 3 # 唇の外側の開き具合に対する内側の開き具合 open_ratio = (bottom_lip_center.y() - top_lip_center.y()) / ( bottom_mouth_center.y() - top_mouth_center.y()) # 笑いの比率 smile_ratio = (bottom_mouth_center.y() - corner_center.y()) / ( bottom_mouth_center.y() - top_mouth_center.y()) if smile_ratio >= 0: mf = VmdMorphFrame(fno) mf.set_name("∧") mf.ratio = 0 motion.regist_mf(mf, mf.name, mf.fno) mf = VmdMorphFrame(fno) mf.set_name("にやり") mf.ratio = max(0, min(1, abs(smile_ratio))) motion.regist_mf(mf, mf.name, mf.fno) else: mf = VmdMorphFrame(fno) mf.set_name("∧") mf.ratio = max(0, min(1, abs(smile_ratio))) motion.regist_mf(mf, mf.name, mf.fno) mf = VmdMorphFrame(fno) mf.set_name("にやり") mf.ratio = 0 motion.regist_mf(mf, mf.name, mf.fno) if mouse_width_ratio > 1.3: # 横幅広がってる場合は「い」 mf = VmdMorphFrame(fno) mf.set_name("い") mf.ratio = max(0, min(1, 1.5 / mouse_width_ratio) * open_ratio) motion.regist_mf(mf, mf.name, mf.fno) mf = VmdMorphFrame(fno) mf.set_name("う") mf.ratio = 0 motion.regist_mf(mf, mf.name, mf.fno) mf = VmdMorphFrame(fno) mf.set_name("あ") mf.ratio = max(0, min(1 - min(0.7, smile_ratio), open_ratio)) motion.regist_mf(mf, mf.name, mf.fno) mf = VmdMorphFrame(fno) mf.set_name("お") mf.ratio = 0 motion.regist_mf(mf, mf.name, mf.fno) else: # 狭まっている場合は「う」 mf = VmdMorphFrame(fno) mf.set_name("い") mf.ratio = 0 motion.regist_mf(mf, mf.name, mf.fno) mf = VmdMorphFrame(fno) mf.set_name("う") mf.ratio = max(0, min(1, (1.2 / mouse_width_ratio) * open_ratio)) motion.regist_mf(mf, mf.name, mf.fno) mf = VmdMorphFrame(fno) mf.set_name("あ") mf.ratio = 0 motion.regist_mf(mf, mf.name, mf.fno) mf = VmdMorphFrame(fno) mf.set_name("お") mf.ratio = max(0, min(1 - min(0.7, smile_ratio), open_ratio)) motion.regist_mf(mf, mf.name, mf.fno)
def execute(cmd_args): folder_path = cmd_args.folder_path bone_csv_path = cmd_args.bone_csv_path model = read_bone_csv(bone_csv_path) logger.info(model) motion = VmdMotion() # 動画上の関節位置 for fno, joints_path in enumerate( glob.glob(osp.join(folder_path, '**/*_joints.json'))): logger.info(f"■ fno: {fno} -----") frame_joints = {} with open(joints_path, 'r') as f: frame_joints = json.load(f) bf = VmdBoneFrame(fno) bf.set_name("センター") bf.position = calc_center(frame_joints) motion.regist_bf(bf, bf.name, bf.fno) for jname, (bone_name, calc_bone, name_list, parent_list, ranges) in VMD_CONNECTIONS.items(): if name_list is None: continue bf = VmdBoneFrame(fno) bf.set_name(bone_name) if calc_bone is None: if len(name_list) == 4: rotation = calc_direction_qq(bf.fno, motion, frame_joints, *name_list) initial = calc_bone_direction_qq(bf, motion, model, jname, *name_list) else: rotation = calc_direction_qq2(bf.fno, motion, frame_joints, *name_list) initial = calc_bone_direction_qq2(bf, motion, model, jname, *name_list) qq = MQuaternion() for parent_name in reversed(parent_list): qq *= motion.calc_bf(parent_name, bf.fno).rotation.inverted() qq = qq * rotation * initial.inverted() if ranges: # 可動域指定がある場合 x_qq, y_qq, z_qq, _ = separate_local_qq( bf.fno, bf.name, qq, model.get_local_x_axis(bf.name)) local_x_axis = model.get_local_x_axis(bf.name) local_z_axis = MVector3D( 0, 0, -1 * (-1 if "right" in jname else 1)) local_y_axis = MVector3D.crossProduct( local_x_axis, local_z_axis) x_limited_qq = MQuaternion.fromAxisAndAngle( local_x_axis, max( ranges["x"]["min"], min( ranges["x"]["max"], x_qq.toDegree() * MVector3D.dotProduct( local_x_axis, x_qq.vector())))) y_limited_qq = MQuaternion.fromAxisAndAngle( local_y_axis, max( ranges["y"]["min"], min( ranges["y"]["max"], y_qq.toDegree() * MVector3D.dotProduct( local_y_axis, y_qq.vector())))) z_limited_qq = MQuaternion.fromAxisAndAngle( local_z_axis, max( ranges["z"]["min"], min( ranges["z"]["max"], z_qq.toDegree() * MVector3D.dotProduct( local_z_axis, z_qq.vector())))) bf.rotation = y_limited_qq * x_limited_qq * z_limited_qq else: bf.rotation = qq motion.regist_bf(bf, bf.name, bf.fno) # 動画内の半分は地面に足が着いていると見なす center_values = np.zeros((1, 3)) for bf in motion.bones["センター"].values(): center_values = np.insert( center_values, 0, np.array([bf.position.x(), bf.position.y(), bf.position.z()]), axis=0) center_median = np.median(center_values, axis=0) for bf in motion.bones["センター"].values(): bf.position.setY(bf.position.y() - center_median[1]) writer = VmdWriter( motion, model, osp.join( folder_path, "output_{0}.vmd".format( datetime.datetime.now().strftime('%Y%m%d_%H%M%S')))) writer.write()
def convert_leg_fk2ik(direction: str, motion: VmdMotion, model: PmxModel): logger.info("足IK変換 【%s足IK】", direction, decoration=MLogger.DECORATION_LINE) leg_ik_bone_name = "{0}足IK".format(direction) toe_ik_bone_name = "{0}つま先IK".format(direction) leg_bone_name = "{0}足".format(direction) knee_bone_name = "{0}ひざ".format(direction) ankle_bone_name = "{0}足首".format(direction) # 足FK末端までのリンク fk_links = model.create_link_2_top_one(ankle_bone_name, is_defined=False) # 足IK末端までのリンク ik_links = model.create_link_2_top_one(leg_ik_bone_name, is_defined=False) # つま先IK末端までのリンク toe_ik_links = model.create_link_2_top_one(toe_ik_bone_name, is_defined=False) # つま先(足首の子ボーン)の名前 ankle_child_bone_name = "{0}つま先".format(direction) # つま先末端までのリンク toe_fk_links = model.create_link_2_top_one(ankle_child_bone_name, is_defined=False) fnos = motion.get_bone_fnos(leg_bone_name, knee_bone_name, ankle_bone_name) # まずキー登録 prev_sep_fno = 0 for fno in fnos: bf = motion.calc_bf(leg_ik_bone_name, fno) motion.regist_bf(bf, leg_ik_bone_name, fno) if fno // 2000 > prev_sep_fno and fnos[-1] > 0: logger.info("-- %sフレーム目:終了(%s%)【準備 - %s】", fno, round((fno / fnos[-1]) * 100, 3), leg_ik_bone_name) prev_sep_fno = fno // 2000 if len(fnos) > 0 and fnos[-1] > 0: logger.info("-- %sフレーム目:終了(%s%)【準備 - %s】", fnos[-1], round((fnos[-1] / fnos[-1]) * 100, 3), leg_ik_bone_name) logger.info("準備完了 【%s足IK】", direction, decoration=MLogger.DECORATION_LINE) ik_parent_name = ik_links.get(leg_ik_bone_name, offset=-1).name # 足IKの移植 prev_sep_fno = 0 # 移植 for fno in fnos: leg_fk_3ds_dic = calc_global_pos(model, fk_links, motion, fno) _, leg_ik_matrixs = calc_global_pos(model, ik_links, motion, fno, return_matrix=True) # IKの親から見た相対位置 leg_ik_parent_matrix = leg_ik_matrixs[ik_parent_name] bf = motion.calc_bf(leg_ik_bone_name, fno) # 足IKの位置は、足IKの親から見た足首のローカル位置(足首位置マイナス) bf.position = leg_ik_parent_matrix.inverted() * ( leg_fk_3ds_dic[ankle_bone_name] - (model.bones[ankle_bone_name].position - model.bones[ik_parent_name].position)) # 足首の角度がある状態での、つま先までのグローバル位置 leg_toe_fk_3ds_dic = calc_global_pos(model, toe_fk_links, motion, fno) # 一旦足IKの位置が決まった時点で登録 motion.regist_bf(bf, leg_ik_bone_name, fno) # 足IK回転なし状態でのつま先までのグローバル位置 leg_ik_3ds_dic, leg_ik_matrisxs = calc_global_pos(model, toe_ik_links, motion, fno, return_matrix=True) # つま先のローカル位置 ankle_child_initial_local_pos = leg_ik_matrisxs[ leg_ik_bone_name].inverted() * leg_ik_3ds_dic[toe_ik_bone_name] ankle_child_local_pos = leg_ik_matrisxs[leg_ik_bone_name].inverted( ) * leg_toe_fk_3ds_dic[ankle_child_bone_name] # 足IKの回転は、足首から見たつま先の方向 bf.rotation = MQuaternion.rotationTo(ankle_child_initial_local_pos, ankle_child_local_pos) motion.regist_bf(bf, leg_ik_bone_name, fno) if fno // 2000 > prev_sep_fno and fnos[-1] > 0: logger.info("-- %sフレーム目:終了(%s%)【足IK変換 - %s】", fno, round((fno / fnos[-1]) * 100, 3), leg_ik_bone_name) prev_sep_fno = fno // 2000 if len(fnos) > 0 and fnos[-1] > 0: logger.info("-- %sフレーム目:終了(%s%)【足IK変換 - %s】", fnos[-1], round((fnos[-1] / fnos[-1]) * 100, 3), leg_ik_bone_name) logger.info("変換完了 【%s足IK】", direction, decoration=MLogger.DECORATION_LINE)