###はじめに
動画からフレーム単位で連番ファイルを抽出するPythonプログラムを作った。
https://note.nkmk.me/python-opencv-video-to-still-image/
Python, OpenCVで動画ファイルからフレームを切り出して保存
を参考にした。と言うか、上記サイトにある、フレーム毎に出力するのと、時間を表記するのを合わせただけ。
細かい追加としては、時間を秒だけでなく、時間_分_秒表記にしたのと、全フレームでなく、指定した単位で飛ばして出力するようにした。
ただ、いくつかの動画で実行してみると、正しい時間が表示されないものがあり、調べて見ると、cap.get(cv2.CAP_PROP_FRAME_COUNT)で得られるフレーム数と、実際の動画のフレーム数が異なっているのが原因だった。
実際に数えてみる、という雑な手法で、解決したものを、後半に掲示する。
#動画からフレーム単位で連番ファイルを抽出してフレーム数と時間を付けるスクリプト
import cv2
import os
def save_all_frames(video_path, dir_path, basename, step_num, ext='jpg'):
cap = cv2.VideoCapture(video_path)
if not cap.isOpened():
return
os.makedirs(dir_path, exist_ok=True)
base_path = os.path.join(dir_path, basename)
digit = len(str(int(cap.get(cv2.CAP_PROP_FRAME_COUNT))))
fps = cap.get(cv2.CAP_PROP_FPS)
fps_inv = 1 / fps
n = 0
while True:
ret, frame = cap.read()
#read()メソッドで動画を1フレームずつ読み込む。retにはbool値が入る。動画の最後まで来るとwhile処理が終了
if ret:
if n % step_num ==0:
cv2.imwrite('{}_{}frm_{:02.0f}h_{:02.0f}min_{:02.0f}sec.{}'.format(base_path, str(n).zfill(digit), (n * fps_inv) // 3600, ((n * fps_inv) // 60) %60, (n * fps_inv) % 60, ext), frame)
n += 1
else:
return
save_all_frames('data/temp/sample_video.mp4', 'data/temp/result', 'sample_video_img', 10)
#引数は、元の動画の場所、出力画像の保存フォルダ、出力画像の頭に付ける文字列、フレームを飛ばす数(全フレーム出力なら1を設定)
#cap.get(cv2.CAP_PROP_FRAME_COUNT)で得られるフレーム数と、実際の動画のフレーム数が異なっている場合に、定義したreal_frame_num_count関数で、実際のフレーム数を数えて、それを使って、ファイル名に時間を表記するプログラム
import cv2
import os
def save_all_frames(video_path, dir_path, basename, step_num, ext='jpg'):
cap = cv2.VideoCapture(video_path)
if not cap.isOpened():
return
real_frame_num = real_frame_num_count(video_path)
os.makedirs(dir_path, exist_ok=True)
base_path = os.path.join(dir_path, basename)
digit = len(str(int(cap.get(cv2.CAP_PROP_FRAME_COUNT))))
total_sec = cap.get(cv2.CAP_PROP_FRAME_COUNT) / cap.get(cv2.CAP_PROP_FPS)
fps_inv = total_sec / real_frame_num
n = 0
while True:
ret, frame = cap.read()
#read()メソッドで動画を1フレームずつ読み込む。retにはbool値が入る。動画の最後まで来るとwhile処理が終了
if ret:
if n % step_num ==0:
cv2.imwrite('{}_{}frm_{:02.0f}h_{:02.0f}min_{:02.0f}sec.{}'.format(base_path, str(n).zfill(digit), (n * fps_inv) // 3600, ((n * fps_inv) // 60) %60, (n * fps_inv) % 60, ext), frame)
n += 1
else:
return
def real_frame_num_count(video_path):
real_frame_num = 0
cap = cv2.VideoCapture(video_path)
if not cap.isOpened():
return
while True:
ret, frame = cap.read()
if ret:
real_frame_num += 1
else:
return real_frame_num
save_all_frames('data/temp/sample_video.mp4', 'data/temp/result', 'sample_video_img', 10)
#引数は、元の動画の場所、出力画像の保存フォルダ、出力画像の頭に付ける文字列、フレームを飛ばす数(全フレーム出力なら1を設定)
real_frame_num_count関数の実行だけで、結構な時間が掛かってしまい、実行してもすぐにはファイルは作成されていかないのが問題。