示例#1
1
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)
示例#2
0
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()
示例#3
0
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)
示例#4
0
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)
示例#5
0
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)
示例#6
0
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)
示例#7
0
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)
示例#8
0
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()
示例#9
0
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)