在开发IDS-ueye多相机程序的时候发现了一个郁闷的问题,就是官方提供的SDK中打开相机的API咋一看不能指定打开哪个相机,其API原型如下所示:
IDSEXP is_InitCamera (HIDS* phCam, HWND hWnd);
看这个打开相机的API,很显然,如果想指定其打开某个特定的相机,其输入参数只能是
HIDS* phCam,然而联系官方提供的demo源码上下文会发现phCam这个值并不是我们指定的,而是传入initCamera函数后底层自动返回的一个数。代码段如下所示:
m_hCam = (HIDS)0;
nRet = initCamera(&m_hCam, m_hWndDisplay);
HIDS是int的重定义,很显然通过取地址,m_hCam是个输出类型的参数。所以这个就比较郁闷了,现在接了好几个相机,如何才能指定打开哪个相机呢···
后来,经过一番的郁闷,发现了解决方案。在浏览多相机demo的时候发现了一个猫腻。代码如下:
m_Camera.hCam = ((HIDS)(GetDocument()->m_nCamDevID | IS_USE_DEVICE_ID));
nRet = InitCamera(&m_Camera.hCam, m_hWnd);
这个时候初始化HIDS m_Camera.hCam的时候并不是用0来初始化,难道这个参数可以用来指定打开哪个相机么···,可是我也试过将这个参数初始化为1或者2或者3,都无济于事啊!那到底是什么原因捏(注:GetDocument()->m_nCamDevID 是MFC界面上用户选择后的控件文本)。后来在另一处发现了这个值要怎么指定···。这边有一个数据结构PUEYE_CAMERA_LIST,如下所示:
typedef struct _UEYE_CAMERA_LIST
{
ULONG dwCount;
UEYE_CAMERA_INFO uci[1];
}UEYE_CAMERA_LIST, *PUEYE_CAMERA_LIST;
typedef struct _UEYE_CAMERA_INFO
{
DWORD dwCameraID; /*<! \brief this is the user definable camera ID */
DWORD dwDeviceID; /*<! \brief this is the systems enumeration ID */
DWORD dwSensorID; /*<! \brief this is the sensor ID e.g. IS_SENSOR_UI141X_M */
DWORD dwInUse; /*<! \brief flag, indicates whether the camera is in use or not */
IS_CHAR SerNo[16]; /*<! \brief zero terminated serial number string */
IS_CHAR Model[16]; /*<! \brief zero terminated short model name string */
DWORD dwStatus; /*<! \brief various flags with camera status */
DWORD dwReserved[2]; /*<! \brief reserved */
IS_CHAR FullModelName[32]; /*<! \brief zero terminated full model name string,
\note Use this string for display purpose only!
Do not depend on the contents of this string! */
DWORD dwReserved2[5]; /*<! \brief reserved */
}UEYE_CAMERA_INFO, *PUEYE_CAMERA_INFO;
经过下面的这一段代码会发现
GetDocument()->m_nCamDevID的值其实就是dwDeviceID。代码如下: // first request number of cameras to determine the array size
int nCameraCount = 0;
// get number of cameras
if (is_GetNumberOfCameras(&nCameraCount) == IS_SUCCESS)
{
// allocate the required list size
PUEYE_CAMERA_LIST pucl = (PUEYE_CAMERA_LIST)new char[sizeof(DWORD) + nCameraCount * sizeof(UEYE_CAMERA_INFO)];
pucl->dwCount = nCameraCount;
// receive the camera list
// receive the camera list
if(is_GetCameraList(pucl) == IS_SUCCESS)
{
for(int i = 0; i < nCameraCount; i++)
{
int ix = 1;
int icon = 0;
// Type = 0 -> USB, Type = 1 -> ETH
int Type = (pucl->uci[i].dwDeviceID >= 1000);
// If ETH camera
if(Type == 1)
{
// If device is not used
if(pucl->uci[i].dwInUse == 0)
{
// Check if starter firmware is compatible to the driver (Only SE)
if((pucl->uci[i].dwStatus & DEVSTS_INCLUDED_STARTER_FIRMWARE_INCOMPATIBLE) != 0)
{
icon = 8;
strTemp = L"Yes";
}
else
{
icon = 3;
strTemp = L"Yes";
}
}
else
{
icon = 4;
strTemp = L"No";
}
m_ctrlCameras.InsertItem( i, strTemp, icon);
m_ctrlCameras.SetItemText( i, ix++, L"ETH");
}
// If USB camera
else if(Type == 0)
{
bool FirmwareDownloadSupported = (pucl->uci[i].dwStatus & FIRMWARE_DOWNLOAD_NOT_SUPPORTED) == 0;
bool InterfaceSpeedSupported = (pucl->uci[i].dwStatus & INTERFACE_SPEED_NOT_SUPPORTED) == 0;
// If device is not used
if(pucl->uci[i].dwInUse == 0)
{
if(FirmwareDownloadSupported && InterfaceSpeedSupported)
{
icon = 0;
strTemp = L"Yes";
}
else
{
icon = 1;
strTemp = L"No";
}
}
else
{
icon = 1;
strTemp = L"No";
}
m_ctrlCameras.InsertItem( i, strTemp, icon);
m_ctrlCameras.SetItemText( i, ix++, L"USB");
}
// Camera ID
strTemp.Format(L"%d", pucl->uci[i].dwCameraID );
m_ctrlCameras.SetItemText( i, ix++, strTemp);
// Device ID
strTemp.Format(L"%d", pucl->uci[i].dwDeviceID );
m_ctrlCameras.SetItemText( i, ix++, strTemp);
// Model
WCHAR temp[100];
mbstowcs(temp, pucl->uci[i].Model, 100);
strTemp.Format(L"%s", temp);
m_ctrlCameras.SetItemText( i, ix++, strTemp);
// Serial Number
mbstowcs(temp, pucl->uci[i].SerNo, 100);
strTemp.Format(L"%s", temp);
m_ctrlCameras.SetItemText( i, ix++, strTemp);
// Firmware compatibility
if((pucl->uci[i].dwStatus & DEVSTS_INCLUDED_STARTER_FIRMWARE_INCOMPATIBLE) != 0)
{
m_ctrlCameras.SetItemText(i, ix++, L"1");
}
else
{
m_ctrlCameras.SetItemText(i, ix++, L"0");
}
// set data to the device ID
m_ctrlCameras.SetItemData(i, pucl->uci[i].dwDeviceID);
}
}
else
{
strTemp = L"Could not receive camera list";
AfxMessageBox(strTemp);
}
/* free allocated memory */
delete pucl;
}
else
{
strTemp = L"Could not receive camera count";
AfxMessageBox(strTemp);
}
通过上面那段代码还会发现为什么我初始化HIDS m_Cam为0或者1,2的时候还是会打开错误,因为我用的是IDS的网口相机,所对应的m_Cam这个值必须加上1000,也就是要指定为1000,1001或者1002,证据如下:
// Type = 0 -> USB, Type = 1 -> ETH
int Type = (pucl->uci[i].dwDeviceID >= 1000); // 网口的ID要+1000
// If ETH camera
if(Type == 1)
{
// 网口
}
// If USB camera
else if(Type == 0)
{
// USB
}
所以日了狗了。所以在打开指定相机的时候可以先获取相机数及其各相机的参数,也就是PUEYE_CAMERA_LIST数据结构,然后对其特定信息进行比对(比如SerNo属性),就可以打开对应的相机。完整打开相机的正确姿势如下:
bool open(QString usr_select_serno){ // 用户选择的相机SN号
// first request number of cameras to determine the array size
int nCameraCount = 0;
// get number of cameras
if (is_GetNumberOfCameras(&nCameraCount) == IS_SUCCESS){
// allocate the required list size
PUEYE_CAMERA_LIST pucl = (PUEYE_CAMERA_LIST)new char[sizeof(DWORD) + nCameraCount * sizeof(UEYE_CAMERA_INFO)];
pucl->dwCount = nCameraCount;
// receive the camera list
if (is_GetCameraList(pucl) == IS_SUCCESS) return true;
else return false;
}
else{
return false;
}
int usr_select_index = -1;
for (int i = 0; i < pucl->dwCount; i++){
QString SerNo = QString(pucl->uci[i].SerNo);
if (SerNo == usr_select_serno){ // 相机SN等于用户选择的相机SN,即打开对应的相机。
usr_select_index = i;
}
}
// init camera (open next available camera)
m_hCam = (HIDS)pucl->uci[usr_select_index].dwDeviceID | IS_USE_DEVICE_ID;
int nRet = initCamera(&m_hCam, NULL);
if (nRet == IS_SUCCESS) return true;
else return false;
}
OK,大功告成(注:界面用Qt做的,所以用QString)。