春のパン祭り点数集計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'
その他参考
公式ドキュメントのVideo I/Oの解説
OpenCV: Video I/O with OpenCV OverviewVideoCapture
のコンストラクタでプロパティ設定するときの第3引数指定方法、StackOverflowでの質問
上記で書いたような指定方法で回答されているが、この質問者はそれではうまくいかなかった、と書いている
How to call OpenCV VideoCapture() with params (third) argument in Python - Stack OverflowOpenCV, tkinterで似たようなことをやっています。
RTSPで画像を取得する | i-PRO - Programming Items