Help us understand the problem. What is going on with this article?

OpenCVのフレームを指定したreadの高速化

More than 1 year has passed since last update.

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秒ごとにフレームを取得する場合に
1. set + read
2. grab + read

の2パターンの処理時間を計測。

検証環境
Windows10 64bit
Python 3.6.9
OpenCV 4.1.1
numpy 1.16.5


1. set + read
結果 10.131 seconds

set+read
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()
set+read結果
         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

grab+read
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()
grab+read結果
         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秒程度の短縮というものであまり大きいとは感じませんが、

・長い動画を解析
・レンタルサーバーなどスペックが限られる場面

ではより効果を発揮できると思われます。

Neilus
OpenCVを使った動画解析アプリを作成してます。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away