问题背景:
根据摄像头序列号来获得Open(index)。
使用Opencv打开多个摄像头的时候,一般都是用VideoCapture.open(index)的方式。这种方式虽然很方便,但是并不能传入其他信息来指定打开哪一个摄像头。
windows平台下,Opencv打开USB摄像头的逻辑代码主要在cap_dshow.cpp中,阅读源码发现其打开设备的顺序,只与枚举顺序有关,使用的是DirectShow中的IPropertyBag类,可以获取DevicePath,FriendName等属性,而其中没有关于序列号(iserialNumber)的方法。而在实际使用过程中,一般我们用的摄像头都是一个厂商的,设备实例路径,设备名都一样,有工具能修改或者厂商可以修改设备名来加以区分,而设备名是不可靠的,会根据插拔的顺序和Hub变化,不是与设备唯一绑定的。在厂商提供了修改序列号的工具后,发现序列号是与设备唯一绑定的,是可靠的,可以用来区分USB摄像头。故我们只要找到能获取序列号的办法,和Opencv的directShow代码结合考虑,可以获取固定序列号的固定index,再用videoCapture打开该index设备。其中需要注意的一点是,只有camera所在的hub序列号可以修改,本身无法修改,所以我们在获取序列号的过程中,需要获取父设备。
代码如下:
预解释:这里用了4位的序列号,格式为[L,R][版本号][行][列],例如L000,R010。程序中CheckSerialNumber即是对序列号的校验。代码分为2步,第一步利用directShow枚举设备,获得设备实例路径,第二步是获得实例路径之后,采用windows Api二次枚举,获取父设备的序列号,得到一个对应关系,例如L001,应该是第二个打开,他的Opencv枚举Index可能为3,那么我在打开这个摄像头的时候,就会Open(3)。
// ConsoleApplication1.cpp : 定义控制台应用程序的入口点。
//
#pragma comment (lib, "setupapi.lib")
#pragma comment (lib, "cfgmgr32.lib")
#pragma comment (lib, "strmiids.lib")
#include <initguid.h>
#include <windows.h>
#include <setupapi.h>
#include <cfgmgr32.h>
#include <usbiodef.h>
#include <regex>
#include <stdio.h>
#include <dshow.h>
#include <atlconv.h>
#include "DistinguishCamera.h"
#include <iostream>
#define GUID_CAMERA_STRING L"{65e8773d-8f56-11d0-a3b9-00a0c9223196}"
static char* GuidToString(const GUID &guid)
{
int buf_len = 64;
char *buf = (char *)malloc(buf_len);
_snprintf(
buf,
buf_len,
"{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
guid.Data1, guid.Data2, guid.Data3,
guid.Data4[0], guid.Data4[1],
guid.Data4[2], guid.Data4[3],
guid.Data4[4], guid.Data4[5],
guid.Data4[6], guid.Data4[7]);
//printf("%s\n",buf);