以前,我使用带有以太网连接和不同 IP 地址的工业相机来设置多个相机。现在我正在尝试使用 OpenCV 进行多相机设置,但我不确定如何匹配 OpenCV VideoCapture
特定相机的 ID。
我可能应该以我目前的情况为例,让我的问题更清楚。我目前连接了 3 个摄像头。如果重要的话,我正在使用 Ubuntu 18.04。这是我来自 lsusb
的输出(除了我连接的 3 个罗技网络摄像头,省略了所有内容):
$ 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
s 和一个 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
太好了,所以我现在有一种方法可以根据存储在摄像头内存中的序列号(
D2DF1D2E
、 99A8F15E
和 C83E952F
)来唯一标识每个摄像头。问题是,在 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_X
, Y
, 和 Z
是 OpenCV VideoCapture
身份证件。现在,我正在通过以下手动过程将相机与 OpenCV VideoCapture ID 相关联:1) 编写一个这样的测试脚本:
# 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()
2) 为
VideoCapture
尝试数字 0-99参数,直到我为我的 3 个连接的相机找到 3 个魔数(Magic Number)。在我当前的示例中,它们是 0、2 和 4。3) 每次我找到一个有效的
VideoCapture
ID,在每个摄像头前挥手,直到我确定是哪一个 VideoCapture
ID 是为了,然后写下我项目中需要对应的相机,例如在我的情况下:0 => serial D2DF1D2E => cam X
2 => serial 99A8F15E => cam Y
4 => serial C83E952F => cam Z
4) 编辑我的代码(或存储的配置文件或数据库字段),以便 cam X 使用
VideoCapture
ID 0,凸轮 Y 使用 VideoCapture
ID 2 等我应该澄清相机 X、Y 和 Z 位于不同的位置并用于不同的目的,即如果我使用
VideoCapture
cam X 的 ID 4 应用程序将无法工作(它们必须按上述方式映射)。显然,对于生产应用程序,此例程是 Not Acceptable 。
我意识到我可以做这样的事情:
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))
获取有效 OpenCV 的列表
VideoCapture
IDs,但我还是得做手动手波的事情来确定哪个OpenCV VideoCapture
ID 对应于每个摄像机。更糟糕的是,将哪个摄像头连接到设备上的哪个物理端口会改变 OpenCV
VideoCapture
ID,因此如果任何摄像机连接发生变化,或者添加或删除了一个摄像头,则必须对所有摄像机重复手动过程。所以我的问题是 ,是否有一些天才的方法(在代码中,而不是手动方法)将每个摄像头的序列号或存储在摄像头内存中的其他一些唯一 ID 与 OpenCV 似乎为
VideoCapture
提供的魔数(Magic Number)相关联。身份证?换一种方式提出我的问题 ,我需要写一个函数
camSerialNumToOpenCvVidCapId
可以这样使用:vidCapForCamX = cv2.VideoCapture(camSerialNumToOpenCvVidCapId(D2DF1D2E))
vidCapForCamY = cv2.VideoCapture(camSerialNumToOpenCvVidCapId(99A8F15E))
vidCapForCamZ = cv2.VideoCapture(camSerialNumToOpenCvVidCapId(C83E952F))
这可能吗?怎么做?
附言我对 OpenCV C++ 或 Python 感到很满意,任何使用其中任何一个的有用答案都将不胜感激。
- - 编辑 - -
这个问题:
OpenCV VideoCapture device index / device number
有与使用 Windows API 调用有关的响应(未接受),但我使用的是 Ubuntu。
--- 编辑 2 ---
@ Micka,这是我在/dev/中的相机:
$ 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
我不确定这是否有帮助
--- 编辑 3 ---
在考虑更多之后,我真正需要的是 OpenCV 中的一个 cam 属性来唯一地识别每个相机。获得可用列表后
VideoCapture
上面提到的 ID,如果有这样的属性:serialNum = cv2.get(cv2.CAP_PROP_SERIAL_NUM)
那么它会很容易,但似乎没有这样的属性或任何类似的东西(在检查
cv2.CAP_PROP_*
的 PyCharm 自动完成并阅读 VideoCapture
的 OpenCV 文档之后)。
最佳答案
对于您找到的解决方案,您需要 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)
关于带有多个网络摄像头的 OpenCV - 如何判断代码中的哪个摄像头?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58962748/