带有多个网络摄像头的OpenCV-如何分辨代码中的哪个摄像头?

时间:2019-11-20 20:21:27

标签: opencv camera webcam

以前,我曾使用具有以太网连接和不同IP地址的工业相机进行多种相机设置。现在,我正在尝试使用OpenCV设置多摄像机,但不确定如何将OpenCV VideoCapture ID与特定摄像机匹配。

我可能应该以当前情况为例,以使我的问题更加清楚。我目前已连接3台摄像机。如果那很重要,我正在使用Ubuntu 18.04。这是我从lsusb的输出(省略了我已连接的3个Logitech网络摄像头以外的所有内容):

$ lsusb
Bus 001 Device 013: ID 046d:0843 Logitech, Inc. Webcam C930e
Bus 001 Device 003: ID 046d:0843 Logitech, Inc. Webcam C930e
Bus 001 Device 006: ID 046d:0892 Logitech, Inc. OrbiCam

如您所见,我连接了2个C930e和一个OrbiCam。基于这篇非常有帮助的帖子:

https://superuser.com/questions/902012/how-to-identify-usb-webcam-by-serial-number-from-the-linux-command-line

我发现我可以像这样获得凸轮的序列号:

$ sudo lsusb -v -d 046d:0843 | grep -i serial
  iSerial                 1 D2DF1D2E
  iSerial                 1 99A8F15E
$ sudo lsusb -v -d 046d:0892 | grep -i serial
  iSerial                 1 C83E952F

太好了,所以我现在有一种方法可以根据存储在凸轮内存中的序列号(D2DF1D2E99A8F15EC83E952F)来唯一地标识每个摄像机。

问题是,在OpenCV中打开网络摄像头连接的步骤如下:

vidCapForCamX = cv2.VideoCapture(OPEN_CV_VID_CAP_ID_FOR_CAM_X)
vidCapForCamY = cv2.VideoCapture(OPEN_CV_VID_CAP_ID_FOR_CAM_Y)
vidCapForCamZ = cv2.VideoCapture(OPEN_CV_VID_CAP_ID_FOR_CAM_Z)

其中X,Y和Z摄像机是我需要使用的3个摄像机,每个摄像机用于不同的确定目的,而OPEN_CV_VID_CAP_ID_FOR_CAM_XYZ是OpenCV { {1}}个ID。现在,我通过以下手动过程将摄像机与OpenCV VideoCapture ID关联起来:

1)编写如下测试脚本:

VideoCapture

2)尝试为# cam_test.py import numpy as np import cv2 cap = cv2.VideoCapture(4) cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1920) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 1080) while True: # Capture frame-by-frame ret, frame = cap.read() # Display the resulting frame cv2.imshow('frame', frame) keyPress = cv2.waitKey(10) if keyPress == ord('q'): break # end if # end while # When everything done, release the capture cap.release() cv2.destroyAllWindows() 参数使用数字0-99,直到找到3个连接的摄像机的3个幻数。在我当前的示例中,它们是0、2和4。

3)每次我找到有效的VideoCapture ID时,请在每台摄像机前挥动我的手,直到确定VideoCapture ID适用于哪个摄像机,然后在项目中写下该摄像机需要对应,例如我的情况:

VideoCapture

4)编辑我的代码(或存储的配置文件或数据库字段),以便cam X使用0 => serial D2DF1D2E => cam X 2 => serial 99A8F15E => cam Y 4 => serial C83E952F => cam Z ID 2,cam Y使用VideoCapture ID 2,依此类推。

我应该澄清一下,摄像机X,Y和Z处于不同的位置,并且具有不同的用途,即,如果我对凸轮X使用ID VideoCapture ID 4,则该应用程序将无法工作(必须将它们映射为如上所述)。

显然,对于生产应用程序,此例程是不可接受的。

我意识到我可以做这样的事情:

VideoCapture

要获取有效的OpenCV import cv2 openCvVidCapIds = [] for i in range(100): try: cap = cv2.VideoCapture(i) if cap is not None and cap.isOpened(): openCvVidCapIds.append(i) # end if except: pass # end try # end for print(str(openCvVidCapIds)) ID的列表,但是我仍然必须手动操作,以确定每个摄像机对应的OpenCV VideoCapture ID。

更糟糕的是,交换将哪个摄像头连接到设备上的哪个物理端口会扰乱OpenCV VideoCapture ID,因此,如果更改了任何摄像头连接,或者添加或删除了凸轮,则必须执行手动操作对所有摄像机重复此操作。

所以我的问题是 ,是否存在某种天才方法(以代码方式而非手动方式)来关联每个摄像机的序列号或存储的其他唯一ID在凸轮的内存中找到OpenCV似乎为VideoCapture ID提供的幻数?

以另一种方式提出我的问题 ,我需要编写一个可以像这样使用的函数VideoCapture

camSerialNumToOpenCvVidCapId

这可能吗,怎么办?

P.S。我对OpenCV C ++或Python感到很满意,无论使用哪种方法,任何有用的答案都将不胜感激。

---编辑---

这个问题:

OpenCV VideoCapture device index / device number

有一个与使用Windows API调用有关的响应(不被接受),但我在使用Ubuntu。

---编辑2 ---

@ Micka,这是我在/ dev /中的相机的东西:

vidCapForCamX = cv2.VideoCapture(camSerialNumToOpenCvVidCapId(D2DF1D2E))
vidCapForCamY = cv2.VideoCapture(camSerialNumToOpenCvVidCapId(99A8F15E))
vidCapForCamZ = cv2.VideoCapture(camSerialNumToOpenCvVidCapId(C83E952F))

我不确定这是否有帮助

---编辑3 ---

在进一步考虑了这一点之后,我真正需要的是OpenCV中的cam属性,以唯一地标识每个摄像机。如上所述获得可用的$ ls -l /dev/video* crw-rw----+ 1 root video 81, 0 Nov 20 12:26 /dev/video0 crw-rw----+ 1 root video 81, 1 Nov 20 12:26 /dev/video1 crw-rw----+ 1 root video 81, 2 Nov 20 12:26 /dev/video2 crw-rw----+ 1 root video 81, 3 Nov 20 12:26 /dev/video3 crw-rw----+ 1 root video 81, 4 Nov 20 12:26 /dev/video4 crw-rw----+ 1 root video 81, 5 Nov 20 12:26 /dev/video5 ID列表之后,是否存在类似以下属性:

VideoCapture

然后这很容易,但是似乎没有这样的属性或任何类似的东西(在检查serialNum = cv2.get(cv2.CAP_PROP_SERIAL_NUM) 的PyCharm自动完成并阅读cv2.CAP_PROP_*的OpenCV文档之后)。

1 个答案:

答案 0 :(得分:1)

对于找到的解决方案,您需要root特权。在我使用Ubuntu20的安装程序中,这不是必需的:

udevadm info --name=/dev/video0

这将输出检测到的第一个摄像机的属性。将其通过“ grep”进行传递,以过滤出与所有摄像机(例如“ ID_SERIAL =“)不同的特定属性。然后,您可以使用“ cut”删除字符串“ ID_SERIAL =”的开头,并仅保留以下值:

udevadm info --name=/dev/video0 | grep ID_SERIAL= | cut -d "=" -f 2

在Python中,您可以运行外部命令来获取此信息,例如:

def get_cam_serial(cam_id):
    # Prepare the external command to extract serial number. 
    p = subprocess.Popen('udevadm info --name=/dev/video{} | grep ID_SERIAL= | cut -d "=" -f 2'.format(cam_id),
                         stdout=subprocess.PIPE, shell=True)

    # Run the command
    (output, err) = p.communicate()

    # Wait for it to finish
    p.status = p.wait()

    # Decode the output
    response = output.decode('utf-8')

    # The response ends with a new line so remove it
    return response.replace('\n', '')

要获取所有摄像机的序列号,只需循环几个摄像机ID。在我的设置中,尝试将摄像机ID 0和1定位到同一台摄像机。另外2和4瞄准第二台摄像机,因此循环中可以有2个步进。提取所有ID后,将它们放在字典中,以便将凸轮ID与序列号相关联。完整的代码可能是:

serials = {}
FILTER = "ID_SERIAL="


def get_cam_serial(cam_id):
    p = subprocess.Popen('udevadm info --name=/dev/video{} | grep {} | cut -d "=" -f 2'.format(cam_id, FILTER),
                         stdout=subprocess.PIPE, shell=True)
    (output, err) = p.communicate()
    p.status = p.wait()
    response = output.decode('utf-8')
    return response.replace('\n', '')


for cam_id in range(0, 10, 2):
    serial = get_cam_serial(cam_id)
    if len(serial) > 6:
        serials[cam_id] = serial

print('Serial numbers:', serials)