项目需求控制和打开两个USB摄像头,并且根据相机的PID和VID来打开指定的相机,来区分主副相机,在Windows下可以通过AForge.Video.DirectShow库来实现,但是Ubuntu下没这个库,找了很多资料也没找到Ubuntu下怎么实现,最后只能借助Libusb库来实现,虽然不是很理想,但是应该可以勉强实现。
第一步: 先来了解 什么是USB Device Path/ 什么是PID、VID
Windows操作系统通过一个“设备路径”来唯一“标识”接入系统中的USB设备/接口,这个"设备路径"就是USB Device Path。USB Device Path 常常被传入 Win32 的API函数 CreatFile() 来与USB设备建立通信。更多解释参见 Windows USB Device Path,当然Ubuntu下也有。
VID和PID唯一标识一个设备,HardwareID是为了给系统识别的 ,他是根据PID/VID而生成的。这个与序列号没什么关系,序列号一般都是厂家固化到芯片中的信息而已。GUID只是为了标志你安装的设备是属于一个什么类当中,这个类可以显示再设备管理器中。比如:你可以定义一个类,当然这个类有与系统中任何类都不同的GUID,然后选择一个图标和类名,就可以同网卡等其他设备一起显示在设备管理器下的根目录中了
根据USB规范的规定,所有的USB设备都有供应商ID(VID)和产品识别码(PID),主机通过不同的VID和PID来区别不同的设备,VID和PID都是两个字节长,其中,供应商ID(VID)由供应商向USB执行论坛申请,每个供应商的VID是唯一的,PID由供应商自行决定,理论上来说,不同的产品、相同产品的不同型号、相同型号的不同设计的产品最好采用不同的PID,以便区别相同厂家的不同设备。
VID和PID通常情况下有两种存储方式,第一种是主控生产商的VID和PID,存储在主控的bootcode中;第二种是设备生产商的VID和PID,该VID和PID存储在主控外部的非易失性存储设备中(EEPROM或Flash)的设备固件中,当USB设备连接主机时,如果固件中有设备生产商的VID和PID,会将该VID和PID报告给主机,而忽略主控生产商的VID和PID。所以理论上一个USB存储设备的VID应该是设备生产商的VID,而不是主控生产商的VID,这两个VID应该是不同的(主控生产商自己生产的设备除外)。
由于VID和PID重复并不会对产品的使用带来严重影响,很多USB设备生产商(山寨厂居多)为了方便,并不会向USB执行论坛申请自己的VID,而是依然沿用主控生产商的VID或随便向产品写入VID和PID;同时,正规厂家只需要申请VID,PID由厂家自行确定,所以存在相同型号的产品,可能采用了不同的主控(商业需要,很正常),而他们的PID是一样的,基于上述原因通过VID和PID就不能准确识别USB设备的主控型号,这个问题大家在使用USB设备的过程中需要注意。
第二步:查看USB Device Path的方法
Windows系统下
1、鼠标右键“我的电脑”,选择“管理”,进入“计算机管理”。
2、在“计算机管理”界面,依次选择“设备管理器”、“图像设备”、摄像头的型号,选中点击鼠标右键,选择“属性”
选择硬件id,就可以看到usb相机的vid,pid是0x18EC,0x3399(这个是我电脑这款usb相机的pid、vid,不同的设备pid、vid是不一样的)的设备,右击属性里找到 Device instance path,别急,这还不是全部的USB Device Path。
最终 USB Device Path 组成格式是:\\?\(第一部分,固定的), hid#vid_1fc9&pid_0130#a&2eb8245&0&0000(第二部分,即Device instance path,\用#替换), #{4d1e55b2-f16f-11cf-88cb-001111000030}"(第三部分,HID类型标识值前加个#)。另外注意在脚本里写入此参数时需要用双引号括起来,类似如下:
"\\?\hid#vid_1fc9&pid_0130#6&20AC856D&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}"
第三步:程序获取USB Device Path方法:
windows下,C#可以通过AForge.Video.DirectShow库来获取,并且非常方便,获取方法如下
public static FilterInfoCollection videoDevices;
// 枚举所有视频输入设备
videoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
videoDevices[i].MonikerString //返回值就是Device instance path
Ubuntu下获取方法1:
因为python可以跨平台,Windows下可以Ubuntu下都可以用:但是Windows下需要把dll放到系统目录。Ubuntu下不需要
pip3 install PyQt5
pip3 install pyqt5-tools
pip install pyusb
Download libusb from libusb releases
解压 libusb-xxx.7z并且把\MS64\dll\libusb-1.0.dll复制到C:\Windows\System32
代码如下:
# FileName : usbdemo.py
# Author : Adil
# DateTime : 2019/9/1 10:26
# SoftWare : PyCharm
import usb
all_devs = usb.core.find(find_all=True)
print(all_devs)
for d in all_devs:
if (d.idVendor == 'VID_04F2') & (d.idProduct == 'PID_B541'):
print(d)
用libusb库的时候需要用知道usb相机的个数,因为libusb获取到的usb设备很多不是摄像头设备
import cv2
class Camera:
def __init__(self, cam_preset_num=10):
self.cam_preset_num = cam_preset_num
def get_cam_num(self):
cnt = 0
for device in range(0, self.cam_preset_num):
stream = cv2.VideoCapture(device)
grabbed = stream.grab()
stream.release()
if not grabbed:
break
cnt = cnt + 1
return cnt
if __name__ == '__main__':
cam = Camera()
cam_num = cam.get_cam_num()
print(cam_num)
Ubuntu下获取方法2:
Ubuntu下所以的信息都是以文件形成储存,所以可以通过读取设备文件来区别索引号和pid 、vid
def get_usb_camera_index(self):
# 获取USB摄像头的索引
camera_path = Path('/sys/class/video4linux/')
camera_list = list(camera_path.glob('video*'))
camera_list.sort()
index = 0
for i in range(len(camera_list)):
camera = camera_list[i]
name_file = camera.joinpath('name')
with open(name_file, 'r') as f:
info = f.readline()
if 'USB' in info:
index = i
break
return index
暂时先这样,后续再补充吧……