Thumb:

<aside> <img src="/icons/list-indent_gray.svg" alt="/icons/list-indent_gray.svg" width="40px" />
目次:
</aside>
ffmpegのPythonモジュールではなく、Pythonでffmpegのコマンドを作成して ffmpeg.exe を直接実行するコード
ffmpegの実行ファイルは 事前に準備しておく必要がある。
VFXでは1001など、開始フレームが1frではない事も多いため、オプションで開始フレームの指定をデフォルトで行うようにする。
""" MDK メディアモジュール
* メディア関連のユーティリティ関数群
Info:
* Created : 2025-12-15 Tatsuya Yamagishi
* Coding : Python 3.11.9 & PySide6
* Author : Tatsuya Yamagishi [[email protected]]
Release Note:
* v0.0.1 2025-12-15 Tatsuya Yamagishi
* new
"""
NAME = 'Create MP4 from Sequence Folder'
VERSION = 'v0.0.1'
# ======================================= #
# Import libs
# ======================================= #
import os
import pathlib
import platform
import re
import subprocess
import typing
# ======================================= #
# Settings
# ======================================= #
MDK_VERSION_FFMPEG = 'ffmpeg-2025-12-10'
# ======================================= #
# functions
# ======================================= #
def create_mp4_from_sequence_dir(dirpath: str, fps: float = 24.0) -> str:
"""
画像シーケンスフォルダからMP4を作成
Args:
dirpath (str): 画像シーケンスフォルダパス
fps (float, optional): フレームレート. Defaults to 24.0.
Returns:
str: 作成したMP4ファイルパス
"""
_mdk_path = pathlib.Path(os.environ['MDK_PATH'])
if platform.system() == 'Windows':
_ffmpeg = _mdk_path / f'tools/ffmpeg/win/{MDK_VERSION_FFMPEG}/bin/ffmpeg.exe'
_seq_path, _start_frame = get_padded_sequence_path(dirpath)
_mp4 = pathlib.Path(dirpath).with_suffix('.mp4')
cmd = [
_ffmpeg.as_posix(),
"-framerate", str(fps), # フレームレートの設定
"-start_number", str(_start_frame), # 開始フレーム番号
"-i", _seq_path, # 連番画像の指定(%04dで4桁の数字に対応)
"-c:v", "libx264",
"-vf", f"drawtext=text='%{{n}}+{_start_frame-1}':x=W-tw-10:y=H-th-10:fontcolor=white:fontsize=24", # フレーム番号オーバーライド:開始フレーム番号を基準に
"-pix_fmt", "yuv420p",
_mp4.as_posix()
]
print('# --------------------------------------- #')
print('# MDK | Create MP4 from sequence directory')
print('# --------------------------------------- #')
print(f'MDK_PATH = {_mdk_path}')
print(f'FFMPEG = {_ffmpeg}')
print(f'cmd = {" ".join(cmd)}')
if not _ffmpeg.exists():
raise FileNotFoundError(f'FFMPEG not found: {_ffmpeg.as_posix()}')
# Create mp4
subprocess.run(cmd, check=True)
print('>>> Create mp4 complete <<<')
return _mp4.as_posix()
def get_padded_sequence_path(
folder: str,
extensions=('png', 'jpg', 'jpeg', 'tiff', 'tif', 'exr')
) -> typing.Optional[typing.Tuple[str, int]]:
"""
フォルダ内の連番画像から
- 区切り文字(_ / .)
- 開始フレーム
- paddingを自動判定し、ffmpeg 用 sequence パスを返す
- Coding by ChatGPT
Returns:
(sequence_path, start_frame)
Examples:
>>> get_padded_sequence_path(r'test/test.1012.exr')
>>> ('test/test.%04d.exr', 1012)
>>> get_padded_sequence_path(r'test/test.12345678.exr')
>>> ('test/test.%08d.exr', 12345678)
"""
entries = []
pattern = re.compile(
rf"(.+?)([_\\.])(\\d+)\\.({'|'.join(extensions)})$",
re.IGNORECASE
)
for name in os.listdir(folder):
match = pattern.match(name)
if match:
base = match.group(1)
sep = match.group(2)
frame_str = match.group(3)
ext = match.group(4).lower()
entries.append({
"base": base,
"sep": sep,
"frame": int(frame_str),
"padding": len(frame_str),
"ext": ext
})
if not entries:
return None
# 開始フレーム
start_frame = min(e["frame"] for e in entries)
# padding(最大桁数)
padding = max(e["padding"] for e in entries)
# 最も多く使われている separator を採用(安全)
sep = max(set(e["sep"] for e in entries), key=lambda s: sum(x["sep"] == s for x in entries))
# base / ext(最初のエントリを使用)
base = entries[0]["base"]
ext = entries[0]["ext"]
pattern_str = f"%0{padding}d"
sequence_path = os.path.join(folder, f"{base}{sep}{pattern_str}.{ext}")
return sequence_path, start_frame