大学で研究する過程で、一般的に役立ちそうだと思ったことをつらつら書きます。
こちらの記事を参考にしています。
上記の記事には動画から全フレームを抽出する、$t_1$秒から$t_2$秒間のフレームを抽出するなどの方法が載っておりますが、本記事では1秒ごとに切り出すという方法を紹介しています。
目的
「動画の分析してみたいけど、動画そのまま扱うの難しくね?」
「じゃあフレームで切り出せば、画像のタスクに帰着できんじゃね?」
という私の個人的な経験から、動画から1秒ずつフレームを切り出すことが目的です。
前提
以下のディレクトリ構成を想定しています。
|── sample.ipynb
|
|── video
| └── video_sample.mp4
|
└── frame
└── video_sample
|── image_0000.jpg
︙
└── image_00030.jpg
動画(video_sample.mp4
)が30秒の場合は、0秒時点, 1秒時点, ..., 30秒時点の31枚の画像が frame/video_sample
下に保存される想定です。
実行環境
- Windows 10
- Python 3.7.5
- OpenCV 4.1.2
OpenCVのインストールはopencv-pythonが参考になります。
実装
引数の説明
- video_path: フレームを切り抜きたい動画のパス
- frame_dir: フレームの保存先
コード
sample.ipynb
import cv2
from os import makedirs
from os.path import splitext, dirname, basename, join
def save_frames(video_path: str, frame_dir: str,
name="image", ext="jpg"):
cap = cv2.VideoCapture(video_path)
if not cap.isOpened():
return
v_name = splitext(basename(video_path))[0]
if frame_dir[-1:] == "\\" or frame_dir[-1:] == "/":
frame_dir = dirname(frame_dir)
frame_dir_ = join(frame_dir, v_name)
makedirs(frame_dir_, exist_ok=True)
base_path = join(frame_dir_, name)
idx = 0
while cap.isOpened():
idx += 1
ret, frame = cap.read()
if ret:
if cap.get(cv2.CAP_PROP_POS_FRAMES) == 1: # 0秒のフレームを保存
cv2.imwrite("{}_{}.{}".format(base_path, "0000", ext),
frame)
elif idx < cap.get(cv2.CAP_PROP_FPS):
continue
else: # 1秒ずつフレームを保存
second = int(cap.get(cv2.CAP_PROP_POS_FRAMES)/idx)
filled_second = str(second).zfill(4)
cv2.imwrite("{}_{}.{}".format(base_path, filled_second, ext),
frame)
idx = 0
else:
break
save_frames(".\\video\\video_sample.mp4", ".\\frame")
結論
動画の扱いって難しい。