勉強しないとな~blog

ちゃんと勉強せねば…な電気設計エンジニアです。

OpenCVやってみる - 51. VideoCaptureタイムアウト設定

春のパン祭り点数集計GUIの調整をしています。

調整項目の一つとして、VideoCaptureの接続タイムアウト設定がありました。

iPhoneに入れたDroidCamアプリ経由で画像を取得する場合、割り当てられているIPアドレスが場合によって変わるので、IP設定を間違えて接続しようとしてしまうことがあり。

そうすると、前のコードでは、1分近く接続待ちしてしまうことがありました。
その間アプリを落とすこともできないと。

これを解決すべく、接続のタイムアウト設定のしかたを調べてみましたが、これで1記事にしておきます。

なかなかいい情報が見つからず、大変だったので…

結論

以下のコードで、VideoCaptureのオープン時にパラメータを設定すれば、接続タイムアウト時間を設定できるようでした。

ただし、なぜか最大5秒ぐらいまでしか設定できない…

import cv2
cap = cv2.VideoCapture()
timeout_ms = 2000
cap.open('http://192.168.1.13:4747/video', cv2.CAP_FFMPEG, (cv2.CAP_PROP_OPEN_TIMEOUT_MSEC, timeout_ms))
False

上記では、DroidCamを起動しない状態でVideoCaptureのオープンを試みています。
結果、設定した時間でタイムアウトしています。

説明

open()関数の引数は、

  • 第2引数: apiPreferenceで、どのバックエンドを使いたいかを指定します。
    今回はFFmpegとしていますが、デフォルトでFFmpegにはなるようでした。ただし、
    • タイムアウト設定のためには第3引数の指定が必要
    • いずれもオプション引数にはなっていない

    ということから、この引数を入れておく必要があるようです。

  • 第3引数: paramsで、プロパティ名と設定値のペアを与えます。複数プロパティの設定も可能。

となっています。

OpenCV: cv::VideoCapture Class Reference - open

プロパティは、以下にリストアップされています。

OpenCV: Flags for video I/O - VideoCaptureProperties

今回使ったのはCAP_PROP_OPEN_TIMEOUT_MSECで、以下のように説明があります。

  • video captureオープン時のタイムアウト時間(ms単位)
  • FFmpegとGstreamerバックエンドでのみ有効
  • open-only : open()関数かVideoCaptureのコンストラクタでのみ設定可能(他のプロパティはだいたいset()で随時設定可能)

下記は、VideoCaptureのコンストラクタで設定した例です。

cap = cv2.VideoCapture('http://192.168.1.13:4747/video', cv2.CAP_FFMPEG, (cv2.CAP_PROP_OPEN_TIMEOUT_MSEC, timeout_ms))
print(cap)
print(cap.isOpened())
< cv2.VideoCapture 000001C956F6A7B0>
False

時間測定

実際にどれぐらいのタイムアウト時間になっているかの確認です。

Jupyterでは%time%timeitのマジックコマンドで簡単に時間測定できます。

Jupyter Notebookでセルの実行時間をはかるなら%%timeを使おうって話 - EnsekiTT Blog

タイムアウト設定しない場合と、いくつかの設定値で設定した場合で確認しました。

%time cap.open('http://192.168.1.13:4747/video')
%time cap.open('http://192.168.1.13:4747/video', cv2.CAP_FFMPEG, (cv2.CAP_PROP_OPEN_TIMEOUT_MSEC, 1000))
%time cap.open('http://192.168.1.13:4747/video', cv2.CAP_FFMPEG, (cv2.CAP_PROP_OPEN_TIMEOUT_MSEC, 2000))
%time cap.open('http://192.168.1.13:4747/video', cv2.CAP_FFMPEG, (cv2.CAP_PROP_OPEN_TIMEOUT_MSEC, 3000))
%time cap.open('http://192.168.1.13:4747/video', cv2.CAP_FFMPEG, (cv2.CAP_PROP_OPEN_TIMEOUT_MSEC, 4000))
%time cap.open('http://192.168.1.13:4747/video', cv2.CAP_FFMPEG, (cv2.CAP_PROP_OPEN_TIMEOUT_MSEC, 5000))
%time cap.open('http://192.168.1.13:4747/video', cv2.CAP_FFMPEG, (cv2.CAP_PROP_OPEN_TIMEOUT_MSEC, 6000))
%time cap.open('http://192.168.1.13:4747/video', cv2.CAP_FFMPEG, (cv2.CAP_PROP_OPEN_TIMEOUT_MSEC, 7000))
%time cap.open('http://192.168.1.13:4747/video', cv2.CAP_FFMPEG, (cv2.CAP_PROP_OPEN_TIMEOUT_MSEC, 10000))
Wall time: 13.5 s
Wall time: 1.09 s
Wall time: 2.05 s
Wall time: 3.06 s
Wall time: 4.05 s
Wall time: 5.04 s
Wall time: 5.56 s
Wall time: 5.46 s
Wall time: 5.6 s





False

なぜかタイムアウト時間が5秒ちょっとで頭打ちになってしまう。

特に設定しないと、13秒になっています。
前回のGUIでやった感じだと、もっと時間がかかってた気がしますが…

読み出しのタイムアウト

同様に、CAP_PROP_READ_TIMEOUT_MSECプロパティで、read()タイムアウトも設定してみます。

一旦DroidCamを立ち上げた状態でVideoCaptureをオープンしてread()を繰り返し実行、その後にDroidCamを落とすことで、read()ができなくなる状況を作ります。

また、%timeだと、全部のread()の実行時間が表示されて分かりにくくなるので、timeitモジュールを使って時間計測し、最後のread()の時間だけ表示します。

timeitで実行時間計測 | Python Snippets

import timeit

params = (cv2.CAP_PROP_OPEN_TIMEOUT_MSEC, 2000, cv2.CAP_PROP_READ_TIMEOUT_MSEC, 2000)
cap.open('http://192.168.1.13:4747/video', cv2.CAP_FFMPEG, params)
ret = True
t = 0

def cap_read():
    global ret
    if cap.isOpened():
        ret, img = cap.read()
    else:
        print('VideoCapture is closed')

while ret:
    t = timeit.timeit('cap_read()', globals=globals(), number=1)

print(f'Last read() time: {t:.2f} sec')
Last read() time: 0.08 sec

2秒のつもりでタイムアウト設定しましたが、ずっと短い時間でread()が終わってしまいました…

タイムアウト設定なしだと、

cap.open('http://192.168.1.13:4747/video')

while ret:
    t = timeit.timeit('cap_read()', globals=globals(), number=1)

print(f'Last read() time: {t:.2f} sec')
Last read() time: 0.08 sec

特にタイムアウト設定した場合と変わりません。

これではタイムアウト発生条件にならないのか?

以上

今回はここまで。

次回は、今回のタイムアウト設定も含めて、春のパン祭り点数集計GUIの調整をやっていきます。

補足

VideoCaptureのプロパティは、OpenCVのバージョンによってときどき変わるようでした。

今回の環境では、4.70を使っています。

cv2.__version__
'4.7.0'

その他参考