#1.はじめに
OpenCVでmp4を使う際にsetでフレーム位置を指定しreadするのが遅い現象がありました。
動画を一定間隔(0.5秒ごと)で取得する処理に時間がかかり過ぎていたため、その対策内容を記載します。
(他により良い手段があればコメント欄で教えて頂けると嬉しいです。)
#2.読者対象
・動画データを1フレームより大きい間隔で利用する方。(1フレームごとであればreadのみが適切)
#3.結論
不要なフレームはgrabで飛ばし、必要なフレームをreadで取得します。
36%の時間短縮が可能でした。
条件 | 処理時間(秒) |
---|---|
set + read | 10.131 |
grab + read | 6.491 |
#4.検証
検証対象:
Luis Fonsi - Despacito ft. Daddy Yankee
720p 30fps
動画時間 4分41秒
OpenCV::VideoCaptureを使い、1秒ごとにフレームを取得する場合に
- set + read
- grab + read
の2パターンの処理時間を計測。
検証環境
Windows10 64bit
Python 3.6.9
OpenCV 4.1.1
numpy 1.16.5
1. set + read
結果 10.131 seconds
import cv2
movie_path = "./movie/Luis Fonsi - Despacito ft. Daddy Yankee.mp4"
video = cv2.VideoCapture(movie_path)
# フレーム数を取得
frame_count = int(video.get(7))
# フレームレート(1フレームの時間単位はミリ秒)の取得
frame_rate = int(video.get(5))
# n秒ごと
n = 1
# 取得フレーム総数
frame_all = int((frame_count / frame_rate) / n)
for i in range(frame_all): # 取得フレーム総数分回す
frame_set = frame_rate * n * i
if frame_set > frame_count:
break
video.set(1, frame_set)
ret, work_frame = video.read()
if ret is False:
break
# ここにフレームへの処理が入る
video.release()
569 function calls in 10.131 seconds
Ordered by: internal time
ncalls tottime percall cumtime percall filename:lineno(function)
281 8.791 0.031 8.791 0.031 {method 'set' of 'cv2.VideoCapture' objects}
281 1.248 0.004 1.248 0.004 {method 'read' of 'cv2.VideoCapture' objects}
1 0.087 0.087 10.131 10.131 time_check.py:4(test)
1 0.006 0.006 0.006 0.006 {method 'release' of 'cv2.VideoCapture' objects}
1 0.000 0.000 10.131 10.131 <string>:1(<module>)
1 0.000 0.000 10.131 10.131 {built-in method builtins.exec}
2 0.000 0.000 0.000 0.000 {method 'get' of 'cv2.VideoCapture' objects}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
2. grab + read
結果 6.491 seconds
import cv2
movie_path = "./movie/Luis Fonsi - Despacito ft. Daddy Yankee.mp4"
video = cv2.VideoCapture(movie_path)
# フレーム数を取得
frame_count = int(video.get(7))
# フレームレート(1フレームの時間単位はミリ秒)の取得
frame_rate = int(video.get(5))
# n秒ごと
n = 1
# read間隔の設定
read_interval = int((frame_rate * n) - 1)
for i in range(frame_count): # フレーム数分回す
ret = video.grab()
if ret is False:
break
if i % read_interval is 0:
ret, work_frame = video.read()
if ret is False:
break
# ここにフレームへの処理が入る
video.release()
8437 function calls in 6.491 seconds
Ordered by: internal time
ncalls tottime percall cumtime percall filename:lineno(function)
8149 5.154 0.001 5.154 0.001 {method 'grab' of 'cv2.VideoCapture' objects}
281 1.232 0.004 1.232 0.004 {method 'read' of 'cv2.VideoCapture' objects}
1 0.098 0.098 6.490 6.490 time_check_set.py:4(test)
1 0.006 0.006 0.006 0.006 {method 'release' of 'cv2.VideoCapture' objects}
1 0.000 0.000 6.490 6.490 <string>:1(<module>)
1 0.000 0.000 6.491 6.491 {built-in method builtins.exec}
2 0.000 0.000 0.000 0.000 {method 'get' of 'cv2.VideoCapture' objects}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
#5.おわりに
最後まで見てくださりありがとうございました。
今回の処理は約4分の動画に対し3秒程度の短縮というものであまり大きいとは感じませんが、
・長い動画を解析
・レンタルサーバーなどスペックが限られる場面
ではより効果を発揮できると思われます。