Пример #1
0
    def prepare_curve_morph(self, morph_name: str):
        try:
            logger.copy(self.options)

            logger.info("【スムージング1回目】%s 開始", morph_name)

            # 全キーフレを取得
            fnos = self.options.motion.get_morph_fnos(morph_name)
            logger.debug("get_morph_fnos morph_name: %s, fnos: %s", morph_name, fnos)

            m_values = []
            
            for fno in fnos:
                mf = self.options.motion.calc_mf(morph_name, fno)
                m_values.append(mf.ratio)
            
            m_all_values = MBezierUtils.calc_value_from_catmullrom(morph_name, fnos, m_values)
            
            logger.debug("get_morph_fnos morph_name: %s, m_all_values: %s", morph_name, m_all_values[:10])

            # カトマル曲線で生成した値を全打ち
            for fno, mr in enumerate(m_all_values):
                mf = VmdMorphFrame(fno)
                mf.set_name(morph_name)

                if math.isnan(mr) or math.isinf(mr):
                    logger.debug("** mr: (%s)%s", fno, mr)
                
                mf.ratio = mr
                self.options.motion.regist_mf(mf, morph_name, fno)
                
            logger.info("【スムージング1回目】%s 終了", morph_name)

            return True
        except SizingException as se:
            logger.error("スムージング処理が処理できないデータで終了しました。\n\n%s", se.message)
            return False
        except Exception as e:
            logger.error("スムージング処理が意図せぬエラーで終了しました。", e)
            return False
Пример #2
0
    def blend_morph(self):
        # モーションVMDディレクトリパス
        pmx_dir_path = MFileUtils.get_dir_path(self.options.model.path)
        # モーションVMDファイル名・拡張子
        pmx_file_name, pmx_ext = os.path.splitext(
            os.path.basename(self.options.model.path))

        dt_now = datetime.now()

        blend_fpath = "{0}\\{1}_blend_{2:%Y%m%d_%H%M%S}.vmd".format(
            pmx_dir_path, pmx_file_name, dt_now)

        bone_motion = VmdMotion()

        # 処理対象モーフ名(文字列)
        target_morphs = self.options.eye_list + self.options.eyebrow_list + self.options.lip_list + self.options.other_list

        # 処理対象モーフデータ
        all_morphs = []
        for mk, mv in self.options.model.morphs.items():
            if mv.display and mv.name in target_morphs:
                all_morphs.append(mv)
                bone_motion.morphs[mk] = {}

        # 変化量(少ない方のの割合を多くする)
        ratio_values = [self.options.inc_value * x for x in range(math.ceil(self.options.min_value / self.options.inc_value), \
                        math.ceil(self.options.max_value / self.options.inc_value) + 1) if self.options.min_value <= self.options.inc_value * x <= self.options.max_value]
        ratio_lower_values = [self.options.inc_value * x for x in range(math.ceil(self.options.min_value / self.options.inc_value), \
                              math.ceil(self.options.max_value / self.options.inc_value / 2) + 1) if self.options.min_value <= self.options.inc_value * x <= self.options.max_value]
        ratio_zero_values = [0 for x in range(len(all_morphs))]

        # 全体の比率増減量
        ratio_total_values = copy.deepcopy(ratio_values)
        ratio_total_values.extend(ratio_lower_values)
        ratio_total_values.extend(ratio_lower_values)
        ratio_total_values.extend(ratio_zero_values)

        # モーフの組合せ数
        morph_comb_cnt = 1
        # 変化量の組合せ数
        ratio_product_cnt = 1
        for n in range(len(all_morphs), 0, -1):
            morph_comb_cnt *= n
            ratio_product_cnt *= len(ratio_values)

        morph_total_cnt = 0

        if morph_comb_cnt * ratio_product_cnt <= 1000:
            # モーフの組合せ
            morph_comb = list(
                itertools.combinations(all_morphs, len(all_morphs)))
            logger.debug("morph_comb: %s", len(morph_comb))

            # 変化量の直積(同じ値を許容する)
            ratio_product = list(
                itertools.product(ratio_values, repeat=len(all_morphs)))
            logger.debug("ratio_product: %s", len(ratio_product))

            # 組合せが1000以下なら組合せをそのまま出力
            brend_mr_pairs_list = list(
                itertools.product(morph_comb, ratio_product))
            logger.debug("brend_mr_pairs_list: %s", len(brend_mr_pairs_list))

            for mframe, (morphs, ratios) in enumerate(brend_mr_pairs_list):
                # 上限までしか登録しない
                if morph_total_cnt > 19000:
                    break

                for morph, ratio in zip(morphs, ratios):
                    vmd_morph = VmdMorphFrame()
                    vmd_morph.fno = mframe
                    vmd_morph.set_name(morph.name)
                    vmd_morph.ratio = ratio
                    vmd_morph.key = True

                    bone_motion.morphs[morph.name][mframe] = vmd_morph
                    logger.test(vmd_morph)

                    morph_total_cnt += 1

        else:
            # 組合せが1000より多い場合、ランダム
            for mframe in range(1000):
                # 上限までしか登録しない
                if morph_total_cnt > 19000:
                    break

                for morph in all_morphs:
                    ratio = ratio_total_values[random.randint(
                        0,
                        len(ratio_values) - 1)]

                    vmd_morph = VmdMorphFrame()
                    vmd_morph.fno = mframe
                    vmd_morph.set_name(morph.name)
                    vmd_morph.ratio = ratio
                    vmd_morph.key = True

                    bone_motion.morphs[morph.name][mframe] = vmd_morph
                    logger.test(vmd_morph)

                    morph_total_cnt += 1

        data_set = MOptionsDataSet(bone_motion, self.options.model,
                                   self.options.model, blend_fpath, False,
                                   False, [], None, 0, [])

        VmdWriter(data_set).write()

        logger.info("モーフブレンドVMD: %s",
                    blend_fpath,
                    decoration=MLogger.DECORATION_BOX)

        return True
Пример #3
0
    def replace_morph(self, data_set_idx: int, data_set: MOptionsDataSet):
        try:
            # 置換前のモーフリスト
            original_morphs = {}
            # 置換後のモーフリスト
            replaced_morphs = {}

            for (org_morph_name, rep_morph_name,
                 morph_ratio) in data_set.morph_list:
                if org_morph_name in data_set.motion.morphs:
                    # 置換元モーフがある場合、保持
                    original_morphs[org_morph_name] = copy.deepcopy(
                        data_set.motion.morphs[org_morph_name])

                if rep_morph_name in data_set.motion.morphs and org_morph_name != rep_morph_name:
                    # 置換先モーフがある場合、保持(元と先が同じ場合、スルー)
                    replaced_morphs[rep_morph_name] = copy.deepcopy(
                        data_set.motion.morphs[rep_morph_name])

            # 保持したので、モーフ削除
            for (org_morph_name, rep_morph_name,
                 morph_ratio) in data_set.morph_list:
                if org_morph_name in data_set.motion.morphs:
                    del data_set.motion.morphs[org_morph_name]

                if rep_morph_name in data_set.motion.morphs:
                    del data_set.motion.morphs[rep_morph_name]

            for (org_morph_name, rep_morph_name,
                 morph_ratio) in data_set.morph_list:
                if org_morph_name in original_morphs and rep_morph_name not in replaced_morphs:
                    # 元にあって、先にない場合、置換元を置換先の名前で登録
                    for fno, org_morph_data in original_morphs[
                            org_morph_name].items():
                        morph_data = VmdMorphFrame(fno)
                        # 名前を置き換える
                        morph_data.set_name(rep_morph_name)

                        # 大きさに比率をかける
                        morph_data.ratio = org_morph_data.ratio * morph_ratio

                        if rep_morph_name not in replaced_morphs:
                            replaced_morphs[rep_morph_name] = {}

                        # 置換後モーフデータを設定
                        replaced_morphs[rep_morph_name][fno] = morph_data

                elif org_morph_name in original_morphs and rep_morph_name in replaced_morphs:
                    # 元にあって、先にもある場合
                    for fno, org_morph_data in original_morphs[
                            org_morph_name].items():
                        morph_data = VmdMorphFrame(fno)

                        # 名前を置き換える
                        morph_data.set_name(rep_morph_name)

                        # 大きさに比率をかける
                        morph_data.ratio = org_morph_data.ratio * morph_ratio

                        if fno in replaced_morphs[rep_morph_name]:
                            # 既に対象fnoが登録されている場合、加算して登録
                            morph_data.ratio += replaced_morphs[
                                rep_morph_name][fno].ratio

                        # 置換後モーフデータを追加
                        replaced_morphs[rep_morph_name][fno] = morph_data

                logger.info("モーフ置換 %s → %s (%s)【No.%s】", org_morph_name,
                            rep_morph_name, morph_ratio, (data_set_idx + 1))

            # 置換が全部終わったら、再登録
            for new_rep_morph_name, new_rep_morph_data in replaced_morphs.items(
            ):
                data_set.motion.morphs[new_rep_morph_name] = new_rep_morph_data
        except MKilledException as ke:
            raise ke
        except SizingException as se:
            logger.error("サイジング処理が処理できないデータで終了しました。\n\n%s", se.message)
            return se
        except Exception as e:
            import traceback
            logger.error("サイジング処理が意図せぬエラーで終了しました。\n\n%s",
                         traceback.format_exc())
            raise e
Пример #4
0
    def convert_vmd(self):
        dt_now = datetime.now()

        bone_fpath = None
        bone_motion = VmdMotion()

        if self.options.bone_csv_path and os.path.exists(self.options.bone_csv_path):
            # ボーンモーションCSVディレクトリパス
            motion_csv_dir_path = MFileUtils.get_dir_path(self.options.bone_csv_path)
            # ボーンモーションCSVファイル名・拡張子
            motion_csv_file_name, _ = os.path.splitext(os.path.basename(self.options.bone_csv_path))

            bone_fpath = "{0}\\{1}_bone_{2:%Y%m%d_%H%M%S}.vmd".format(motion_csv_dir_path, motion_csv_file_name, dt_now)

            # ボーンCSV読み込み
            with open(self.options.bone_csv_path, encoding='cp932', mode='r') as f:
                reader = csv.reader(f)
                next(reader)  # ヘッダーを読み飛ばす

                cnt = 0
                for row in reader:
                    bf = VmdBoneFrame()

                    # ボーン名
                    bf.set_name(row[0])

                    # フレーム
                    bf.fno = int(float(row[1]))

                    # 位置
                    bf.position = MVector3D(float(row[2]), float(row[3]), float(row[4]))

                    # 回転
                    bf.rotation = MQuaternion.fromEulerAngles(float(row[5]), float(row[6]) * -1, float(row[7]) * -1)

                    # 補間曲線
                    # 補間曲線(一旦floatで読み込んで指数等も読み込んだ後、intに変換)
                    bf.interpolation = [int(float(row[8])), int(float(row[9])), int(float(row[10])), int(float(row[11])), int(float(row[12])), int(float(row[13])), \
                                        int(float(row[14])), int(float(row[15])), int(float(row[16])), int(float(row[17])), int(float(row[18])), int(float(row[19])), \
                                        int(float(row[20])), int(float(row[21])), int(float(row[22])), int(float(row[23])), int(float(row[24])), int(float(row[25])), \
                                        int(float(row[26])), int(float(row[27])), int(float(row[28])), int(float(row[29])), int(float(row[30])), int(float(row[31])), \
                                        int(float(row[32])), int(float(row[33])), int(float(row[34])), int(float(row[35])), int(float(row[36])), int(float(row[37])), \
                                        int(float(row[38])), int(float(row[39])), int(float(row[40])), int(float(row[41])), int(float(row[42])), int(float(row[43])), \
                                        int(float(row[44])), int(float(row[45])), int(float(row[46])), int(float(row[47])), int(float(row[48])), int(float(row[49])), \
                                        int(float(row[50])), int(float(row[51])), int(float(row[52])), int(float(row[53])), int(float(row[54])), int(float(row[55])), \
                                        int(float(row[56])), int(float(row[57])), int(float(row[58])), int(float(row[59])), int(float(row[60])), int(float(row[61])), \
                                        int(float(row[62])), int(float(row[63])), int(float(row[64])), int(float(row[65])), int(float(row[66])), int(float(row[67])), \
                                        int(float(row[68])), int(float(row[69])), int(float(row[70])), int(float(row[71]))]
                    
                    bf.read = True
                    bf.key = True

                    if bf.name not in bone_motion.bones:
                        bone_motion.bones[bf.name] = {}

                    bone_motion.bones[bf.name][bf.fno] = bf

                    cnt += 1

                    if cnt % 10000 == 0:
                        logger.info("[ボーン] %sキー目:終了", cnt)

        if self.options.morph_csv_path and os.path.exists(self.options.morph_csv_path):
            # モーフモーションCSVディレクトリパス
            motion_csv_dir_path = MFileUtils.get_dir_path(self.options.morph_csv_path)
            # モーフモーションCSVファイル名・拡張子
            motion_csv_file_name, _ = os.path.splitext(os.path.basename(self.options.morph_csv_path))

            if not bone_fpath:
                bone_fpath = "{0}\\{1}_morph_{2:%Y%m%d_%H%M%S}.vmd".format(motion_csv_dir_path, motion_csv_file_name, dt_now)

            # モーフCSV読み込み
            with open(self.options.morph_csv_path, encoding='cp932', mode='r') as f:
                reader = csv.reader(f)
                next(reader)  # ヘッダーを読み飛ばす

                cnt = 0
                for row in reader:
                    mf = VmdMorphFrame()

                    # ボーン名
                    mf.set_name(row[0])

                    # フレーム
                    mf.fno = int(float(row[1]))

                    # 位置
                    mf.ratio = float(row[2])

                    if mf.name not in bone_motion.morphs:
                        bone_motion.morphs[mf.name] = {}

                    bone_motion.morphs[mf.name][mf.fno] = mf

                    cnt += 1

                    if cnt % 1000 == 0:
                        logger.info("[モーフ] %sキー目:終了", cnt)

        if len(bone_motion.bones.keys()) > 0 or len(bone_motion.morphs.keys()) > 0:
            # ボーンかモーフのキーがある場合、まとめて出力

            model = PmxModel()
            model.name = "CSV Convert Model"
            data_set = MOptionsDataSet(bone_motion, None, model, bone_fpath, False, False, [], None, None, [])

            VmdWriter(data_set).write()

            logger.info("ボーン・モーフモーションVMD: %s", bone_fpath, decoration=MLogger.DECORATION_BOX)

        if self.options.camera_csv_path and os.path.exists(self.options.camera_csv_path):
            # カメラモーションCSVディレクトリパス
            motion_csv_dir_path = MFileUtils.get_dir_path(self.options.camera_csv_path)
            # カメラモーションCSVファイル名・拡張子
            motion_csv_file_name, _ = os.path.splitext(os.path.basename(self.options.camera_csv_path))

            camera_fpath = "{0}\\{1}_camera_{2:%Y%m%d_%H%M%S}.vmd".format(motion_csv_dir_path, motion_csv_file_name, dt_now)
            camera_motion = VmdMotion()

            # カメラCSV読み込み
            with open(self.options.camera_csv_path, encoding='cp932', mode='r') as f:
                reader = csv.reader(f)
                next(reader)  # ヘッダーを読み飛ばす

                cnt = 0
                for row in reader:
                    cf = VmdCameraFrame()

                    # フレーム
                    cf.fno = int(row[0])

                    # 位置
                    cf.position = MVector3D(float(row[1]), float(row[2]), float(row[3]))

                    # 回転(オイラー角)
                    cf.euler = MVector3D(float(row[4]), float(row[5]), float(row[6]))

                    # 距離
                    cf.length = -(float(row[7]))

                    # 視野角
                    cf.angle = int(row[8])

                    # パース
                    cf.perspective = int(row[9])

                    # 補間曲線
                    cf.interpolation = [int(float(row[10])), int(float(row[11])), int(float(row[12])), int(float(row[13])), int(float(row[14])), int(float(row[15])), \
                                        int(float(row[16])), int(float(row[17])), int(float(row[18])), int(float(row[19])), int(float(row[20])), int(float(row[21])), \
                                        int(float(row[22])), int(float(row[23])), int(float(row[24])), int(float(row[25])), int(float(row[26])), int(float(row[27])), \
                                        int(float(row[28])), int(float(row[29])), int(float(row[30])), int(float(row[31])), int(float(row[32])), int(float(row[33]))]

                    camera_motion.cameras[cf.fno] = cf

                    cnt += 1

                    if cnt % 500 == 0:
                        logger.info("[カメラ] %sキー目:終了", cnt)

            if len(camera_motion.cameras) > 0:
                # ボーンかモーフのキーがある場合、まとめて出力

                model = PmxModel()
                model.name = "カメラ・照明"
                data_set = MOptionsDataSet(camera_motion, None, model, camera_fpath, False, False, [], None, None, [])

                VmdWriter(data_set).write()

                logger.info("カメラモーションVMD: %s", camera_fpath, decoration=MLogger.DECORATION_BOX)

        return True
Пример #5
0
    def convert_vmd(self):
        dt_now = datetime.now()

        bone_fpath = None
        bone_motion = VmdMotion()

        if self.options.bone_csv_path and os.path.exists(
                self.options.bone_csv_path):
            # ボーンモーションCSVディレクトリパス
            motion_csv_dir_path = MFileUtils.get_dir_path(
                self.options.bone_csv_path)
            # ボーンモーションCSVファイル名・拡張子
            motion_csv_file_name, _ = os.path.splitext(
                os.path.basename(self.options.bone_csv_path))

            bone_fpath = "{0}\\{1}_bone_{2:%Y%m%d_%H%M%S}.vmd".format(
                motion_csv_dir_path, motion_csv_file_name, dt_now)

            # ボーンCSV読み込み
            with open(self.options.bone_csv_path, encoding='cp932',
                      mode='r') as f:
                reader = csv.reader(f)
                next(reader)  # ヘッダーを読み飛ばす

                cnt = 0
                for ridx, row in enumerate(reader):
                    bf = VmdBoneFrame()
                    rno = ridx + 1

                    try:
                        if len(row) < 0 or not row[0]:
                            logger.error("[ボーン] %s行目のボーン名(1列目)が設定されていません",
                                         rno,
                                         decoration=MLogger.DECORATION_BOX)
                            return False

                        # ボーン名
                        bf.set_name(row[0])
                    except Exception as e:
                        logger.error("[ボーン] %s行目のボーン名の読み取りに失敗しました\n%s",
                                     rno,
                                     e,
                                     decoration=MLogger.DECORATION_BOX)
                        return False

                    try:
                        if len(row) < 1 or not row[1]:
                            logger.error("[ボーン] %s行目のフレーム番号(2列目)が設定されていません",
                                         rno,
                                         decoration=MLogger.DECORATION_BOX)
                            return False

                        # フレーム
                        bf.fno = int(float(row[1]))

                        if bf.fno < 0:
                            logger.error("[ボーン] %s行目のフレーム番号(2列目)に負数が設定されています",
                                         rno,
                                         decoration=MLogger.DECORATION_BOX)
                            return False

                    except Exception as e:
                        logger.error(
                            "[ボーン] %s行目のフレーム番号の読み取りに失敗しました\nフレーム番号は半角数字のみ入力可能です。\n%s",
                            rno,
                            e,
                            decoration=MLogger.DECORATION_BOX)
                        return False

                    try:
                        if len(row
                               ) < 4 or not row[2] or not row[3] or not row[4]:
                            logger.error("[ボーン] %s行目の位置(3-5列目)のいずれかが設定されていません",
                                         rno,
                                         decoration=MLogger.DECORATION_BOX)
                            return False

                        # 位置
                        bf.position = MVector3D(float(row[2]), float(row[3]),
                                                float(row[4]))
                    except Exception as e:
                        logger.error(
                            "[ボーン] %s行目の位置の読み取りに失敗しました\n位置は半角数字・符号・小数点のみ入力可能です。\n%s",
                            rno,
                            e,
                            decoration=MLogger.DECORATION_BOX)
                        return False

                    try:
                        if len(row
                               ) < 7 or not row[5] or not row[6] or not row[7]:
                            logger.error("[ボーン] %s行目の回転(6-8列目)のいずれかが設定されていません",
                                         rno,
                                         decoration=MLogger.DECORATION_BOX)
                            return False

                        # 回転
                        bf.rotation = MQuaternion.fromEulerAngles(
                            float(row[5]),
                            float(row[6]) * -1,
                            float(row[7]) * -1)
                    except Exception as e:
                        logger.error(
                            "[ボーン] %s行目の回転の読み取りに失敗しました\n位置は半角数字・符号・小数点のみ入力可能です。\n%s",
                            rno,
                            e,
                            decoration=MLogger.DECORATION_BOX)
                        return False

                    try:
                        if len(row) < 71:
                            logger.error(
                                "[ボーン] %s行目の補間曲線(9-72列目)のいずれかが設定されていません",
                                rno,
                                decoration=MLogger.DECORATION_BOX)
                            return False

                        for cidx in range(8, 72):
                            if not row[cidx]:
                                logger.error("[ボーン] %s行目の補間曲線の%s番目が設定されていません",
                                             rno,
                                             cidx - 7,
                                             decoration=MLogger.DECORATION_BOX)
                                return False

                        # 補間曲線(一旦floatで読み込んで指数等も読み込んだ後、intに変換)
                        bf.interpolation = [int(float(row[8])), int(float(row[9])), int(float(row[10])), int(float(row[11])), int(float(row[12])), int(float(row[13])), \
                                            int(float(row[14])), int(float(row[15])), int(float(row[16])), int(float(row[17])), int(float(row[18])), int(float(row[19])), \
                                            int(float(row[20])), int(float(row[21])), int(float(row[22])), int(float(row[23])), int(float(row[24])), int(float(row[25])), \
                                            int(float(row[26])), int(float(row[27])), int(float(row[28])), int(float(row[29])), int(float(row[30])), int(float(row[31])), \
                                            int(float(row[32])), int(float(row[33])), int(float(row[34])), int(float(row[35])), int(float(row[36])), int(float(row[37])), \
                                            int(float(row[38])), int(float(row[39])), int(float(row[40])), int(float(row[41])), int(float(row[42])), int(float(row[43])), \
                                            int(float(row[44])), int(float(row[45])), int(float(row[46])), int(float(row[47])), int(float(row[48])), int(float(row[49])), \
                                            int(float(row[50])), int(float(row[51])), int(float(row[52])), int(float(row[53])), int(float(row[54])), int(float(row[55])), \
                                            int(float(row[56])), int(float(row[57])), int(float(row[58])), int(float(row[59])), int(float(row[60])), int(float(row[61])), \
                                            int(float(row[62])), int(float(row[63])), int(float(row[64])), int(float(row[65])), int(float(row[66])), int(float(row[67])), \
                                            int(float(row[68])), int(float(row[69])), int(float(row[70])), int(float(row[71]))]

                        for bidx, bi in enumerate(bf.interpolation):
                            if 0 > bi:
                                logger.error(
                                    "[ボーン] %s行目の補間曲線(%s列目)に負数が設定されています",
                                    rno,
                                    bidx + 9,
                                    decoration=MLogger.DECORATION_BOX)
                                return False

                    except Exception as e:
                        logger.error(
                            "[ボーン] %s行目の補間曲線の読み取りに失敗しました\n位置は半角数字のみ入力可能です。\n%s",
                            rno,
                            e,
                            decoration=MLogger.DECORATION_BOX)
                        return False

                    bf.read = True
                    bf.key = True

                    if bf.name not in bone_motion.bones:
                        bone_motion.bones[bf.name] = {}

                    bone_motion.bones[bf.name][bf.fno] = bf

                    cnt += 1

                    if cnt % 10000 == 0:
                        logger.info("[ボーン] %sキー目:終了", cnt)

        if self.options.morph_csv_path and os.path.exists(
                self.options.morph_csv_path):
            # モーフモーションCSVディレクトリパス
            motion_csv_dir_path = MFileUtils.get_dir_path(
                self.options.morph_csv_path)
            # モーフモーションCSVファイル名・拡張子
            motion_csv_file_name, _ = os.path.splitext(
                os.path.basename(self.options.morph_csv_path))

            if not bone_fpath:
                bone_fpath = "{0}\\{1}_morph_{2:%Y%m%d_%H%M%S}.vmd".format(
                    motion_csv_dir_path, motion_csv_file_name, dt_now)

            # モーフCSV読み込み
            with open(self.options.morph_csv_path, encoding='cp932',
                      mode='r') as f:
                reader = csv.reader(f)
                next(reader)  # ヘッダーを読み飛ばす

                cnt = 0
                for ridx, row in enumerate(reader):
                    mf = VmdMorphFrame()
                    rno = ridx + 1

                    try:
                        if len(row) < 0 or not row[0]:
                            logger.error("[モーフ] %s行目のモーフ名(1列目)が設定されていません",
                                         rno,
                                         decoration=MLogger.DECORATION_BOX)
                            return False

                        # ボーン名
                        mf.set_name(row[0])
                    except Exception as e:
                        logger.error("[モーフ] %s行目のモーフ名の読み取りに失敗しました\n%s",
                                     rno,
                                     e,
                                     decoration=MLogger.DECORATION_BOX)
                        return False

                    try:
                        if len(row) < 1 or not row[1]:
                            logger.error("[モーフ] %s行目のフレーム番号(2列目)が設定されていません",
                                         rno,
                                         decoration=MLogger.DECORATION_BOX)
                            return False

                        # フレーム
                        mf.fno = int(float(row[1]))

                        if mf.fno < 0:
                            logger.error("[モーフ] %s行目のフレーム番号(2列目)に負数が設定されています",
                                         rno,
                                         decoration=MLogger.DECORATION_BOX)
                            return False
                    except Exception as e:
                        logger.error(
                            "[モーフ] %s行目のフレーム番号の読み取りに失敗しました\nフレーム番号は半角数字のみ入力可能です。\n%s",
                            rno,
                            e,
                            decoration=MLogger.DECORATION_BOX)
                        return False

                    try:
                        if len(row) < 2 or not row[2]:
                            logger.error("[モーフ] %s行目の大きさ(3列目)が設定されていません",
                                         rno,
                                         decoration=MLogger.DECORATION_BOX)
                            return False

                        # 値
                        mf.ratio = float(row[2])
                    except Exception as e:
                        logger.error(
                            "[モーフ] %s行目の大きさの読み取りに失敗しました\n大きさは半角数字・符号・小数点のみ入力可能です。\n%s",
                            rno,
                            e,
                            decoration=MLogger.DECORATION_BOX)
                        return False

                    if mf.name not in bone_motion.morphs:
                        bone_motion.morphs[mf.name] = {}

                    bone_motion.morphs[mf.name][mf.fno] = mf

                    cnt += 1

                    if cnt % 1000 == 0:
                        logger.info("[モーフ] %sキー目:終了", cnt)

        if len(bone_motion.bones.keys()) > 0 or len(
                bone_motion.morphs.keys()) > 0:
            # ボーンかモーフのキーがある場合、まとめて出力

            model = PmxModel()
            model.name = "CSV Convert Model"
            data_set = MOptionsDataSet(bone_motion, model, model, bone_fpath,
                                       False, False, [], None, 0, [])

            VmdWriter(data_set).write()

            logger.info("ボーン・モーフモーションVMD: %s",
                        bone_fpath,
                        decoration=MLogger.DECORATION_BOX)

        if self.options.camera_csv_path and os.path.exists(
                self.options.camera_csv_path):
            # カメラモーションCSVディレクトリパス
            motion_csv_dir_path = MFileUtils.get_dir_path(
                self.options.camera_csv_path)
            # カメラモーションCSVファイル名・拡張子
            motion_csv_file_name, _ = os.path.splitext(
                os.path.basename(self.options.camera_csv_path))

            camera_fpath = "{0}\\{1}_camera_{2:%Y%m%d_%H%M%S}.vmd".format(
                motion_csv_dir_path, motion_csv_file_name, dt_now)
            camera_motion = VmdMotion()

            # カメラCSV読み込み
            with open(self.options.camera_csv_path, encoding='cp932',
                      mode='r') as f:
                reader = csv.reader(f)
                next(reader)  # ヘッダーを読み飛ばす

                cnt = 0
                for ridx, row in enumerate(reader):
                    cf = VmdCameraFrame()
                    rno = ridx + 1

                    try:
                        if len(row) < 1 or not row[0]:
                            logger.error("[カメラ] %s行目のフレーム番号(1列目)が設定されていません",
                                         rno,
                                         decoration=MLogger.DECORATION_BOX)
                            return False

                        # フレーム
                        cf.fno = int(row[0])

                        if cf.fno < 0:
                            logger.error("[カメラ] %s行目のフレーム番号(1列目)に負数が設定されています",
                                         rno,
                                         decoration=MLogger.DECORATION_BOX)
                            return False
                    except Exception as e:
                        logger.error(
                            "[カメラ] %s行目のフレーム番号の読み取りに失敗しました\nフレーム番号は半角数字のみ入力可能です。\n%s",
                            rno,
                            e,
                            decoration=MLogger.DECORATION_BOX)
                        return False

                    try:
                        if len(row
                               ) < 3 or not row[1] or not row[2] or not row[3]:
                            logger.error("[カメラ] %s行目の位置(2-4列目)のいずれかが設定されていません",
                                         rno,
                                         decoration=MLogger.DECORATION_BOX)
                            return False

                        # 位置
                        cf.position = MVector3D(float(row[1]), float(row[2]),
                                                float(row[3]))
                    except Exception as e:
                        logger.error(
                            "[カメラ] %s行目の位置の読み取りに失敗しました\n位置は半角数字・符号・小数点のみ入力可能です。\n%s",
                            rno,
                            e,
                            decoration=MLogger.DECORATION_BOX)
                        return False

                    try:
                        if len(row
                               ) < 6 or not row[4] or not row[5] or not row[6]:
                            logger.error("[カメラ] %s行目の回転(5-7列目)のいずれかが設定されていません",
                                         rno,
                                         decoration=MLogger.DECORATION_BOX)
                            return False

                        # 回転(オイラー角)
                        cf.euler = MVector3D(float(row[4]), float(row[5]),
                                             float(row[6]))
                    except Exception as e:
                        logger.error(
                            "[カメラ] %s行目の回転の読み取りに失敗しました\n回転は半角数字・符号・小数点のみ入力可能です。\n%s",
                            rno,
                            e,
                            decoration=MLogger.DECORATION_BOX)
                        return False

                    try:
                        if len(row) < 7 or not row[7]:
                            logger.error("[カメラ] %s行目の距離(8列目)が設定されていません",
                                         rno,
                                         decoration=MLogger.DECORATION_BOX)
                            return False

                        # 距離
                        cf.length = -(float(row[7]))
                    except Exception as e:
                        logger.error(
                            "[カメラ] %s行目の距離の読み取りに失敗しました\n距離は半角数字・符号・小数点のみ入力可能です。\n%s",
                            rno,
                            e,
                            decoration=MLogger.DECORATION_BOX)
                        return False

                    try:
                        if len(row) < 8 or not row[8]:
                            logger.error("[カメラ] %s行目の視野角(9列目)が設定されていません",
                                         rno,
                                         decoration=MLogger.DECORATION_BOX)
                            return False

                        # 視野角
                        cf.angle = int(row[8])

                        if cf.angle < 0:
                            logger.error("[カメラ] %s行目の視野角(9列目)に負数が設定されています",
                                         rno,
                                         decoration=MLogger.DECORATION_BOX)
                            return False

                    except Exception as e:
                        logger.error(
                            "[カメラ] %s行目の視野角の読み取りに失敗しました\n視野角は半角数字のみ入力可能です。\n%s",
                            rno,
                            e,
                            decoration=MLogger.DECORATION_BOX)
                        return False

                    try:
                        if len(row) < 8 or not row[9]:
                            logger.error("[カメラ] %s行目のパース(10列目)が設定されていません",
                                         rno,
                                         decoration=MLogger.DECORATION_BOX)
                            return False

                        # パース
                        cf.perspective = int(row[9])

                        if cf.perspective not in [0, 1]:
                            logger.error(
                                "[カメラ] %s行目のパース(10列目)に0, 1以外の値が設定されています",
                                rno,
                                decoration=MLogger.DECORATION_BOX)
                            return False
                    except Exception as e:
                        logger.error(
                            "[カメラ] %s行目のパースの読み取りに失敗しました\nパースは0, 1のみ入力可能です。\n%s",
                            rno,
                            e,
                            decoration=MLogger.DECORATION_BOX)
                        return False

                    try:
                        if len(row) < 33:
                            logger.error(
                                "[カメラ] %s行目の補間曲線(11-34列目)のいずれかが設定されていません",
                                rno,
                                decoration=MLogger.DECORATION_BOX)
                            return False

                        for cidx in range(10, 34):
                            if not row[cidx]:
                                logger.error("[カメラ] %s行目の補間曲線の%s番目が設定されていません",
                                             rno,
                                             cidx - 9,
                                             decoration=MLogger.DECORATION_BOX)
                                return False

                        # 補間曲線(一旦floatで読み込んで指数等も読み込んだ後、intに変換)
                        cf.interpolation = [int(float(row[10])), int(float(row[11])), int(float(row[12])), int(float(row[13])), int(float(row[14])), int(float(row[15])), \
                                            int(float(row[16])), int(float(row[17])), int(float(row[18])), int(float(row[19])), int(float(row[20])), int(float(row[21])), \
                                            int(float(row[22])), int(float(row[23])), int(float(row[24])), int(float(row[25])), int(float(row[26])), int(float(row[27])), \
                                            int(float(row[28])), int(float(row[29])), int(float(row[30])), int(float(row[31])), int(float(row[32])), int(float(row[33]))]

                        for cidx, ci in enumerate(cf.interpolation):
                            if 0 > ci:
                                logger.error(
                                    "[カメラ] %s行目の補間曲線(%s列目)に負数が設定されています",
                                    rno,
                                    cidx + 11,
                                    decoration=MLogger.DECORATION_BOX)
                                return False

                    except Exception as e:
                        logger.error(
                            "[カメラ] %s行目の補間曲線の読み取りに失敗しました\n位置は半角数字のみ入力可能です。\n%s",
                            rno,
                            e,
                            decoration=MLogger.DECORATION_BOX)
                        return False

                    camera_motion.cameras[cf.fno] = cf

                    cnt += 1

                    if cnt % 500 == 0:
                        logger.info("[カメラ] %sキー目:終了", cnt)

            if len(camera_motion.cameras) > 0:
                # ボーンかモーフのキーがある場合、まとめて出力

                model = PmxModel()
                model.name = "カメラ・照明"
                data_set = MOptionsDataSet(camera_motion, model, model,
                                           camera_fpath, False, False, [],
                                           None, 0, [])

                VmdWriter(data_set).write()

                logger.info("カメラモーションVMD: %s",
                            camera_fpath,
                            decoration=MLogger.DECORATION_BOX)

        return True