cv2.VideoCapture(0)が遅い
最近GStreamer付きOpenCVをビルドして日常使いしているのだが、Webカメラの起動が妙に遅い(3秒ほどかかる)。この3秒の間に、「指定されたDLLが見つかりません」のようなエラーが出るんじゃないかと精神衛生に悪い。
あと、毎回毎回GStreamerのWARNがでてうるさいというのもある。今日、この対処方法を知ったのでここに書き残しておく。
cv2.VideoCapture(0,cv2.CAP_DSHOW)を使え
結論から言えば、
cv2.VideoCapture(0,cv2.CAP_DSHOW)
または
cv2.VideoCapture(0,700)
を使えばよいだけの話である。
ほとんどの人はVideoCapture
の第二引数を意識したことがないだろうが、GStreamerのパイプラインを記述するときにはcv2.CAP_GSTREAMERをよく使う。で、僕が知っていたのはcv2.CAP_GSTREAMER
(=1800)とcv2.CAP_FFMPEG
(=1900)だけだったので、てっきりwebカメラのキャプチャにもこのいずれかを使用しているものだと思い込んでいたのだが、何やらたくさんあるようだ。(数字としては、CAP_ANYが最小値0で、CAP_OBSENSORが最大値2600である。)
調査
第二引数を省略するとデフォルトでcv2.CAP_ANY(=0)が指定され、以下の中から使えるものを自動的に選ぶという仕様だそうだ。ここで、cv2.CAP_GSTREAMERやcv2.CAP_FFMPEGをWebカメラに対して使用したらどうなるのか?を確認しておく。
>>> cap=cv2.VideoCapture(0,cv2.CAP_FFMPEG)
>>> cap.read()
(False, None)
>>> cap=cv2.VideoCapture(0,cv2.CAP_GSTREAMER)
[ WARN:0@1278.266] global cap_gstreamer.cpp:1173 cv::GStreamerCapture::isPipelinePlaying OpenCV | GStreamer warning: GStreamer: pipeline have not been created
[ WARN:0@1278.266] global cap.cpp:344 cv::VideoCapture::open VIDEOIO(GSTREAMER): backend is generally available but can't be used to capture by index
>>> cap.read()
(False, None)
>>>
といった感じで、cap.read()の第一戻り値がFalseになってしまい、captureできていないことがわかる。では、実際には何が使われているのだろうか?VideoCaptureの第二引数に1から2600の数字を入れ、cap.read()の第一戻り値がTrueになるところを観測する。以下のコードを書いてみた。
import cv2
for i in range(1,2601): #0を含まず2600まで
print(i,end=",") #何が起こるかわからないので処理前に数字だけ出しておく
cap=cv2.VideoCapture(0,i) #cv2.CAP_XXXXの代わりに数字で
ret,frame=cap.read()
if ret: #ret==True
print(f"\n\n{i} {ret}\n") #表示
cap.release() #解放
結果は以下である(冗長なところは省略した)
1,2,3,...,700,
700 True
701,702,703,...,1400,
1400 True
1401,1402,...,1800,
[ WARN:0@6.563] global cap_gstreamer.cpp:1173 cv::GStreamerCapture::isPipelinePlaying OpenCV | GStreamer warning: GStreamer: pipeline have not been created
[ WARN:0@6.563] global cap.cpp:344 cv::VideoCapture::open VIDEOIO(GSTREAMER): backend is generally available but can't be used to capture by index
1801,1802,...,2000,
[ WARN:0@6.581] global cap.cpp:344 cv::VideoCapture::open VIDEOIO(CV_IMAGES): backend is generally available but can't be used to capture by index
2001,2002,...,2200,
[ WARN:0@6.596] global cap.cpp:344 cv::VideoCapture::open VIDEOIO(CV_MJPEG): backend is generally available but can't be used to capture by index
2201,2202,...,2600,
[ERROR:0@6.629] global obsensor_uvc_stream_channel.cpp:158 cv::obsensor::getStreamChannelGroup Camera index out of range
[ WARN:0@6.629] global cap.cpp:344 cv::VideoCapture::open VIDEOIO(OBSENSOR): backend is generally available but can't be used to capture by index
なるほど、700番(=cv2.CAP_DSHOW)と1400番(=cv2.CAP_MSMF)でキャプチャに成功していることが分かった。1800番のCAP_GSTREAMERでは当然の権利のようにWARNを吐き、CAP_IMAGES = 2000、CAP_OPENCV_MJPEG = 2200、CAP_OBSENSOR = 2600でそれぞれ違うWARNを吐いている。
700番(DSHOW)か1400番(MSMF)か
とにかく、Webカメラのキャプチャには700番と1400番が使えることが分かったので、それぞれのパフォーマンスを測ってみたい
import cv2
import time
def calcTakenTimeAvg(num): #10回平均
save=[]
save2=[]
for i in range(10):
start=time.time()
cap=cv2.VideoCapture(0,num)
take_time=time.time()-start #かかった時間
save.append(take_time)
start2=time.time()
ret,frame=cap.read()
take_time2=time.time()-start2 #かかった時間2
save2.append(take_time2)
cap.release()
print(f"{ret}",end=",")
print()
print(num)
print(f"VideoCapture所要 平均時間 {sum(save)/10}、最大 {max(save)}、最小 {min(save)}")
print(f"cap.read() 所要 平均時間 {sum(save2)/10}、最大 {max(save2)}、最小 {min(save2)}")
print()
calcTakenTimeAvg(700)
calcTakenTimeAvg(1400)
True,True,True,True,True,True,True,True,True,True,
700
VideoCapture所要 平均時間 0.5267948865890503、最大 0.569263219833374、最小 0.512610912322998
cap.read() 所要 平均時間 0.8471105575561524、最大 0.8586256504058838、最小 0.8365640640258789
True,True,True,True,True,True,True,True,True,True,
1400
VideoCapture所要 平均時間 2.6704511404037476、最大 3.006239175796509、最小 2.3922278881073
cap.read() 所要 平均時間 1.2015816688537597、最大 1.216357946395874、最小 1.1960184574127197
700番ではVideoCaptureが完了するのに0.5秒、cap.readに0.8秒(遅くね??)かかるのに対し、
1400番ではVideoCaptureに3秒ほど、cap.readに1.2秒もかかっているようだ。
この記事の最初でVideoCaptureに3秒かかると書いたが、この結果を見ると、デフォでは1400番が使われていると考えられる。どういう順番なのかは見当もつかないが、最初のスクショのWARNを見る限り、どうやらCAP_ANYでは
CAP_GSTREAMER(=1800番)を試してから、1400番(=MSMF)で再生しているようだ。