Opencv 区分摄像头 windows iserialNumber

问题背景:

      根据摄像头序列号来获得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 

#include   

#include  
#include 
#include 

#include 

#include     


#include 
#include 
#include "DistinguishCamera.h"
#include 

#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);
	return buf;
}

static BOOL GetParentDeviceInstanceId(_Out_ PWCHAR pszParentDeviceInstanceId, _Out_ PDEVINST phParentDeviceInstanceId, _In_ DEVINST hCurrentDeviceInstanceId) {

	// Handle to parent Device Instance ID
	BOOL result = CM_Get_Parent(phParentDeviceInstanceId, hCurrentDeviceInstanceId, 0);
	if (result == CR_SUCCESS) {

		// Device ID as String
		memset(pszParentDeviceInstanceId, 0, MAX_DEVICE_ID_LEN);
		result = CM_Get_Device_IDW(*phParentDeviceInstanceId, pszParentDeviceInstanceId, MAX_DEVICE_ID_LEN, 0);
		if (result == CR_SUCCESS) {
			return TRUE;
		}
	}

	return FALSE;
}

//检验序列号是否合法,规则是最后四位为"C[Version][L|R][int][int]",输出次序值
static int CheckSerialNumber(string serialNumber)
{
	int k = serialNumber.length() - 1;
	while (serialNumber[k] != '\\') k--;
	if (serialNumber[k + 1] == 'C' && (serialNumber[k + 2] >= '0' && serialNumber[k + 2] <= '9') &&(serialNumber[k + 3] == 'L' || serialNumber[k + 3] == 'R'))
	{
		int id = (serialNumber[k + 4] - '0') * 10 + (serialNumber[k + 5] - '0') * 2;
		if (serialNumber[k + 3] == 'R') id++;
		return id;
	}
	else
		return -1;//illegal
}

static int getSerialNumber(string childDistanceId)
{
	USES_CONVERSION;
	// GUID to match devices by class
	GUID guid;
	CLSIDFromString(GUID_CAMERA_STRING, &guid);

	// Get matching devices info
	HDEVINFO devInfo = SetupDiGetClassDevs(&guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);// DIGCF_ALLCLASSES);

	// Device Instance ID as string
	WCHAR szDeviceInstanceId[MAX_DEVICE_ID_LEN];

	if (devInfo != INVALID_HANDLE_VALUE) {

		DWORD devIndex = 0;
		SP_DEVINFO_DATA devInfoData;
		devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);

		// Loop over devices found in SetupDiGetClassDevs
		while (SetupDiEnumDeviceInfo(devInfo, devIndex, &devInfoData))
		{

			// Read Device Instance ID of current device
			memset(szDeviceInstanceId, 0, MAX_DEVICE_ID_LEN);
			SetupDiGetDeviceInstanceIdW(devInfo, &devInfoData, szDeviceInstanceId, MAX_PATH, 0);

			//compare the device's instanceId
			char *szDeviceInstId = W2A(szDeviceInstanceId);
			transform(childDistanceId.begin(), childDistanceId.end(), childDistanceId.begin(), ::toupper);
			if (childDistanceId.compare(szDeviceInstId) == 0)
			{

				// Handle of current defice instance id
				DEVINST hCurrentDeviceInstanceId = devInfoData.DevInst;

				// Handle of parent Device Instance ID
				DEVINST hParentDeviceInstanceId;

				// Parent Device Instance ID as string
				WCHAR pszParentDeviceInstanceId[MAX_DEVICE_ID_LEN];

				// Initialize / clean variables
				memset(szDeviceInstanceId, 0, MAX_DEVICE_ID_LEN);
				hParentDeviceInstanceId = NULL;

				if (GetParentDeviceInstanceId(pszParentDeviceInstanceId, &hParentDeviceInstanceId, hCurrentDeviceInstanceId))
				{
					string parentDeviceInstanceId = W2A(pszParentDeviceInstanceId);

					return (CheckSerialNumber(parentDeviceInstanceId));

					//if (DeviceIdMatchesPattern(pszParentDeviceInstanceId, pszParentDeviceInstanceIdPattern)) {

					//	// Parent Device Instance ID matches given regexp - print it out and exit
					//	wprintf(L"%s\n", pszParentDeviceInstanceId);
					//	getchar();
					//	return 0;
					//}

					// Parent Device Instance ID does not match the pattern - check parent's parent
					//hCurrentDeviceInstanceId = hParentDeviceInstanceId;
					break;

				}
				else {
						// There is no parent. Stop the loop.
						break;
					 }
			}
			devIndex++;
		}

	}

	return 0;
}


bool getCameraOrderBySerialNumber(vector *order, int numOfCamera)
{
	int numOfFoundedCamera = 0;
	USES_CONVERSION;
	// Init COM
	HRESULT hr = NULL;
	hr = CoInitialize(NULL);
	if (FAILED(hr)){
		printf("Error, Can not init COM.");
		return false;
	}
	//	printf("===============Directshow Filters ===============\n");
	ICreateDevEnum *pSysDevEnum = NULL;
	hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void **)&pSysDevEnum);
	if (FAILED(hr)){
		return hr;
	}

	IEnumMoniker *pEnumCat = NULL;
	//Category	
	/************************************************************************
	Friendly Name	                      CLSID
	-------------------------------------------------------------------------
	Audio Capture Sources	              CLSID_AudioInputDeviceCategory
	Audio Compressors	                  CLSID_AudioCompressorCategory
	Audio Renderers	                      CLSID_AudioRendererCategory
	Device Control Filters	              CLSID_DeviceControlCategory
	DirectShow Filters	                  CLSID_LegacyAmFilterCategory
	External Renderers	                  CLSID_TransmitCategory
	Midi Renderers	                      CLSID_MidiRendererCategory
	Video Capture Sources	              CLSID_VideoInputDeviceCategory
	Video Compressors	                  CLSID_VideoCompressorCategory
	WDM Stream Decompression Devices	  CLSID_DVDHWDecodersCategory
	WDM Streaming Capture Devices	      AM_KSCATEGORY_CAPTURE
	WDM Streaming Crossbar Devices	      AM_KSCATEGORY_CROSSBAR
	WDM Streaming Rendering Devices	      AM_KSCATEGORY_RENDER
	WDM Streaming Tee/Splitter Devices	  AM_KSCATEGORY_SPLITTER
	WDM Streaming TV Audio Devices	      AM_KSCATEGORY_TVAUDIO
	WDM Streaming TV Tuner Devices	      AM_KSCATEGORY_TVTUNER
	WDM Streaming VBI Codecs	          AM_KSCATEGORY_VBICODEC
	************************************************************************/
	//hr = pSysDevEnum->CreateClassEnumerator(CLSID_VideoCompressorCategory, &pEnumCat, 0);
	hr = pSysDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnumCat, 0);
	//hr = pSysDevEnum->CreateClassEnumerator(CLSID_AudioCompressorCategory, &pEnumCat, 0);
	//hr = pSysDevEnum->CreateClassEnumerator(CLSID_AudioInputDeviceCategory, &pEnumCat, 0);
	//hr = pSysDevEnum->CreateClassEnumerator(CLSID_MediaMultiplexerCategory, &pEnumCat, 0);
	//hr = pSysDevEnum->CreateClassEnumerator(CLSID_LegacyAmFilterCategory, &pEnumCat, 0);

	if (hr != S_OK) {
		pSysDevEnum->Release();
		return false;
	}

	// Obtain a class enumerator for the video input category.
	IMoniker *pMoniker = NULL;
	ULONG monikerFetched;
	int deviceCounter = 0;
	//Filter
	while (pEnumCat->Next(1, &pMoniker, &monikerFetched) == S_OK)
	{
		IPropertyBag *pPropBag;
		VARIANT varName;
		IBaseFilter *pFilter = NULL;
		hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pPropBag);
		if (FAILED(hr))
		{
			pMoniker->Release();
			continue;
		}
		VariantInit(&varName);
		//		hr = pPropBag->Read(L"FriendlyName", &varName, 0);
		hr = pPropBag->Read(L"DevicePath", &varName, 0);

		//"FriendlyName": The name of the device.
		//"Description": A description of the device.
		//Filter Info================
		//修改格式使其和com产生的串格式对应
		string varNameBak = W2A(varName.bstrVal);
		while (varNameBak[0] != 'u')
			varNameBak.erase(0, 1);//remove one char
		int k = 0;
		while (varNameBak[k] != '{')
		{
			if (varNameBak[k] == '#')
			{
				varNameBak.erase(k, 1);
				varNameBak.insert(k, "\\");
			}
			k++;
		}
		varNameBak.erase(k - 1);	//remove all char in id > k
		//printf("[%s]\n", W2A(varName.bstrVal));
		//cout << varNameBak << endl;

		int index = getSerialNumber(varNameBak);
		if (index != -1)
		{
			order->at(index) = deviceCounter;
			numOfFoundedCamera++;
		}

		VariantClear(&varName);

		pPropBag->Release();
		pMoniker->Release();
		deviceCounter++;
	}
	pEnumCat->Release();
	pSysDevEnum->Release();
	//printf("=================================================\n");
	CoUninitialize();

	//检测所有摄像头是否合法
	//ID连续且以组划分
	if (numOfFoundedCamera == numOfCamera)
	{
		for (int i = 0; i < numOfCamera; i++)
			if (order->at(i) == -1) return false;
		return true;
	}
	else
	{
		return false;
	}
}

//int main()
//{
//	vector *order = new vector(10, -1);
//	getCameraOrderBySerialNumber(order);
//	return 0;
//}


如有问题请联系我的qq,787721594。

你可能感兴趣的:(C++,Windows)