OpenCV 获取摄像头数量

本文最后更新于:2020年11月19日 上午

简述

最近使用 opencv-python ,主要需要打开摄像头显示画面,但是就遇到个很尴尬得问题,当一台电脑有多个摄像头的时候,怎么选择打开对应的某一个摄像头呢?

一般我们回想既然 OpenCV 提供了打开某一个摄像头的接口,那应该也会有查看多有摄像头设备的接口函数可以直接调用吧~至少我是这么想的,但事实上,查了一圈,似乎并没有相关的接口可以调用。

网上也有一些解决方案,思路很简单。 OpenCV 打开摄像头的接口函数是这样的:

retval = cv.VideoCapture.open(index[, apiPreference])

使用起来很简单,传入一个摄像头序号 index0 为系统默认摄像头),打开对应序号的摄像头,打开成功的话返回 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

还是以我的两个摄像头为例,从 40 一个一个试探,显然后面三个序号都会打开失败,但耗时很少,下面某一次实测的 open() 函数的调用耗时:

0.0069806575775146484
0.000997781753540039
0.0019943714141845703
0.16755270957946777

首先可以看到, open() 函数仅调用 4 次,也就是说遇到第一个能打开的摄像头之后就跳出循环了,并不会尝试打开所有可用的摄像头。而且前面也说了,我两个摄像头,一个打开很快,另一个基本上要 8s 以上才能打开,所以从上面的测试结果也可以看到,运气比较好,索引为 1 的摄像头恰好是打开比较快的那个,只用了 0.16s 就打开成功了,第一次调用尽管耗时相对多一些也仅仅是 7ms ,另外两次打开失败都是 1~2ms ,相比起方案一中 8s 起步的耗时是不是就友好太多了!

最后的最后

最后的最后呢,可能会有这样的疑惑,因为我有,就是前面代码中通过 open() 打开的摄像头都要通过 release() 关闭,否则会占用摄像头导致其它应用无法使用这个摄像头,那么针对本文讨论的获取摄像头的数量,如果摄像头已经被别的应用打开了,那调用 open() 函数还能成功打开摄像头并返回 true 吗?

实验是检验真理的唯一标准,我测试过了,答案是可以的!

测试方法很简单嘛,虽然找个别的软件把摄像头打开,再执行上面的函数,完全没问题,会返回正确的结果。不过还是别认为这个摄像头就可用了,尝试使用 read() 函数获取帧的话,是获取不到的~

到现在,至少我们的目的达到了,成功获取到了摄像头的数量~


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!