OpenCV 获取摄像头数量
本文最后更新于:2023年2月8日 晚上
简述
最近使用 opencv-python ,主要需要打开摄像头显示画面,但是就遇到个很尴尬得问题,当一台电脑有多个摄像头的时候,怎么选择打开对应的某一个摄像头呢?
一般我们回想既然 OpenCV 提供了打开某一个摄像头的接口,那应该也会有查看多有摄像头设备的接口函数可以直接调用吧~至少我是这么想的,但事实上,查了一圈,似乎并没有相关的接口可以调用。
网上也有一些解决方案,思路很简单。 OpenCV 打开摄像头的接口函数是这样的:
retval = cv.VideoCapture.open(index[, apiPreference])
使用起来很简单,传入一个摄像头序号 index
( 0
为系统默认摄像头),打开对应序号的摄像头,打开成功的话返回 true
,否则 false
。
注意:通过 open()
或者直接调用构造函数打开的摄像头,一定要记得使用 release()
函数关闭!
方案一
所以很容易想到,从 0
开始一个一个试看能打开多少个摄像头,不久知道能使用的摄像头数量了嘛,确实是这样的,那上代码~
def get_camera_quantity() -> int:
"""获取可用摄像头数量"""
quantity = 0
cap = cv2.VideoCapture() # 视频流对象
index = 0
# 一般一台电脑连接的摄像头数量不会超过5个
# 普遍情况下最多两个
while index < 5:
ret = cap.open(index)
if ret:
quantity += 1 # 可用摄像头数量+1
cap.release() # 释放打开的摄像头
index += 1 # 索引+1
else:
break # 一旦遇到打开失败的序号,则没有更多的摄像头了
return quantity
是吧,很容易的~
但是,但是,但是,我在写这个代码之前就想到过一个问题,调用 open()
函数打开摄像头是会真正打开摄像头的,而这个过程相对来说是很耗时间的。实际上面代码我也测试过,我手上两个摄像头,一个就普通一米长的线,另一个奇葩的怕是有10米长的线……多次测试前者打开基本在 0.5s 内,而后者则是反人类的 8s 有余……换言之,上面的代码会尝试打开这两个摄像头,至少要耗时 8s 才能返回结果,我认为是无法接受的龟速!
事实上也是在上面测试的过程中,发现后续 3 个无法打开的索引,其实返回结果是相对很快的,基本就是 1ms 。
补一个测试时间:
8.240961074829102
0.05086374282836914
0.002992391586303711
调用三次,第一次成功打开线变态长的摄像头,耗时超 8s ,第二次耗时 50ms 打开了另一个摄像头,第三次打开失败仅耗时 3ms 。
方案二
那就对了,换个方向反着来岂不是好太多了,先尝试打开较大的序号,因为一般情况下摄像头数量不会超过 3 个,这里设置对 5 个索引进行试探已经算是考虑了比较特殊的情况了。
于是写出来下面的代码:
def get_camera_quantity() -> int:
"""获取可用摄像头数量"""
quantity = 0
cap = cv2.VideoCapture() # 视频流对象
index = 4
while index >= 0:
t0 = time.time()
ret = cap.open(index)
print(time.time() - t0)
if ret:
quantity = index + 1
cap.release()
break
index -= 1
return quantity
还是以我的两个摄像头为例,从 4
到 0
一个一个试探,显然后面三个序号都会打开失败,但耗时很少,下面某一次实测的 open()
函数的调用耗时:
0.0069806575775146484
0.000997781753540039
0.0019943714141845703
0.16755270957946777
首先可以看到, open()
函数仅调用 4 次,也就是说遇到第一个能打开的摄像头之后就跳出循环了,并不会尝试打开所有可用的摄像头。而且前面也说了,我两个摄像头,一个打开很快,另一个基本上要 8s 以上才能打开,所以从上面的测试结果也可以看到,运气比较好,索引为 1
的摄像头恰好是打开比较快的那个,只用了 0.16s 就打开成功了,第一次调用尽管耗时相对多一些也仅仅是 7ms ,另外两次打开失败都是 1~2ms ,相比起方案一中 8s 起步的耗时是不是就友好太多了!
最后的最后
最后的最后呢,可能会有这样的疑惑,因为我有,就是前面代码中通过 open()
打开的摄像头都要通过 release()
关闭,否则会占用摄像头导致其它应用无法使用这个摄像头,那么针对本文讨论的获取摄像头的数量,如果摄像头已经被别的应用打开了,那调用 open()
函数还能成功打开摄像头并返回 true
吗?
实验是检验真理的唯一标准,我测试过了,答案是可以的!
测试方法很简单嘛,虽然找个别的软件把摄像头打开,再执行上面的函数,完全没问题,会返回正确的结果。不过还是别认为这个摄像头就可用了,尝试使用 read()
函数获取帧的话,是获取不到的~
到现在,至少我们的目的达到了,成功获取到了摄像头的数量~