Monday, April 9, 2012

How to determine how many cameras are connected to a computer and connect to and use ptz cameras

How to determine how many cameras are connected to a computer and connect to and use ptz cameras:

I figure this is a nice easy first post. This is some code I struggled with finding a couple years ago on automatically determining how many cameras are connected to a computer via directshow (this code is also useful in a ptz application as I will show right after). I use OpenCV to capture from the camera and the directshow library to use the ptz camera functions. I also used the vector and stringstream library for my own ease (#include<vector> #include<sstream>)

The DisplayError function is a generic wrapper for you to fill in, whether you use printf or a messagebox.

int getDeviceCount() {
  try {
    ICreateDevEnum *pDevEnum = NULL;
    IEnumMoniker *pEnum = NULL;
    int deviceCounter = 0;
    HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, reinterpret_cast<void**>(&pDevEnum));
    if (SUCCEEDED(hr)) {
      // Create an enumerator for the video capture category.
      hr = pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnum, 0);
      if (hr == S_OK) {
        IMoniker *pMoniker = NULL;
        while (pEnum->Next(1, &pMoniker, NULL) == S_OK) {
          IPropertyBag *pPropBag;
          hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void**)(&pPropBag));
          if (FAILED(hr)) {
            pMoniker->Release();
            continue; // Skip this one, maybe the next one will work.
          }
          pPropBag->Release();
          pPropBag = NULL;
          pMoniker->Release();
          pMoniker = NULL;
          deviceCounter++;
        }
        pEnum->Release();
        pEnum = NULL;
      }
      pDevEnum->Release();
      pDevEnum = NULL;
    }
    return deviceCounter;
  } catch(Exception & e) {
    DisplayError(e.ToString());
  } catch(...) {
    DisplayError("Error Caught Counting # of Devices");
  }
  return 0;
}


This can easily be modified to connect to x number of ptz cameras with a quick ptz class

Here is our ptz class:

class ptz {
public:
  struct controlVals {
  public:
    long min, max, step, def, flags;
  };
  IBaseFilter *filter;
  IAMCameraControl *camControl;

  bool valid, validMove;
  CvCapture *capture;
  bool Initialize() {
    camControl = NULL;
    controlVals panInfo = {0}, tiltInfo = {0};
    HRESULT hr = filter->QueryInterface(IID_IAMCameraControl, (void **)&camControl);
    if(hr != S_OK)
      return false;
    else
      return true;
  }

  void ptz(int instance) {

    threadNum = instance;
    // sets up a continuous capture point through the msvc driver
    capture = cvCaptureFromCAM(threadNum);
    if (!capture)
      valid = false;
    else
      valid = true;

  }
  void Destroy() {
    if(camControl) {
      camControl->Release();
      camControl = NULL;
    }
    if (filter) {
      filter->Release();
      filter = NULL;
    }
  }
};


And here is the modified getDeviceCount code which now connects to all the cameras and gets the ptz information using direct show:

int getDeviceCount(vector<ptz> &cameras) {
  try {
    ICreateDevEnum *pDevEnum = NULL;
    IEnumMoniker *pEnum = NULL;
    int deviceCounter = 0;

    HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, reinterpret_cast<void**>(&pDevEnum));
    if (SUCCEEDED(hr)) {
      // Create an enumerator for the video capture category.
      hr = pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnum, 0);
      if (hr == S_OK) {
        IMoniker *pMoniker = NULL;
        do {
          if (pEnum->Next(1, &pMoniker, NULL) == S_OK) {
            IPropertyBag *pPropBag;
            hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void**)(&pPropBag));
            if (FAILED(hr)) {
              pMoniker->Release();
              continue; // Skip this one, maybe the next one will work.
            }
            if (SUCCEEDED(hr)) {
              ptz tmp = ptz(deviceCounter);
              HRESULT hr2 = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter, (void**) & (tmp.filter));
              if (tmp.valid)
                tmp.validMove = tmp.Initialize();
            }
            pPropBag->Release();
            pPropBag = NULL;
            pMoniker->Release();
            pMoniker = NULL;
            deviceCounter++;
          } else {
            ptz tmp = ptz(deviceCounter);
            cameras.push_back(tmp);
            deviceCounter++;
            break;
          }
        }
        while (cameras[deviceCounter -1].valid);
        pEnum->Release();
        pEnum = NULL;
      }
      pDevEnum->Release();
      pDevEnum = NULL;
    }
    return deviceCounter;
  } catch(Exception & e) {
    DisplayError(e.ToString());
  } catch(...) {
    DisplayError("Error Caught Counting # of Devices");
  }
  return 0;
}


Notice now how we have integrated OpenCV into our ptz class. Now as we find cameras we can capture the camera information using OpenCV and then use directshow to grab the information used for ptz. To move the camera we can use  a pan and a tilt function like the following:

HRESULT MechanicalPan(IAMCameraControl *pCameraControl, long value) {
  HRESULT hr = 0;
  try {
    long flags = KSPROPERTY_CAMERACONTROL_FLAGS_RELATIVE | KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL;
    hr = pCameraControl->Set(CameraControl_Pan, value, flags);
    if (hr == 0x800700AA)
      Sleep(1);
    else if (hr != S_OK && hr != 0x80070490) {
      stringstream tmp;
      tmp << "ERROR: Unable to set CameraControl_Pan property value to " << value << ". (Error " << std::hex << hr << ")";
      throw Exception(tmp.str().c_str());
    }
    // Note that we need to wait until the movement is complete, otherwise the next request will
    // fail with hr == 0x800700AA == HRESULT_FROM_WIN32(ERROR_BUSY).
  } catch(Exception & e) {
    DisplayError(e.ToString());
  } catch(...) {
    DisplayError("Error Caught panning camera");
  }
  return hr;
}
// ----------------------------------------------------------------------------
HRESULT MechanicalTilt(IAMCameraControl *pCameraControl, long value) {
  HRESULT hr = 0;
  try {
    long flags = KSPROPERTY_CAMERACONTROL_FLAGS_RELATIVE | KSPROPERTY_CAMERACONTROL_FLAGS_MANUAL;
    hr = pCameraControl->Set(CameraControl_Tilt, value, flags);
    if (hr == 0x800700AA)
      Sleep(1);
    else if (hr != S_OK && hr != 0x80070490) {
      stringstream tmp;
      tmp << "ERROR: Unable to set CameraControl_Tilt property value to " << value << ". (Error " << std::hex << hr << ")";
      throw Exception(tmp.str().c_str());
    }
    // Note that we need to wait until the movement is complete, otherwise the next request will
    // fail with hr == 0x800700AA == HRESULT_FROM_WIN32(ERROR_BUSY).
  } catch(Exception & e) {
    DisplayError(e.ToString());
  } catch(...) {
    DisplayError("Error Caught tilting camera");
  }
  return hr;
}




Consider donating to further my tinkering.


Places you can find me

No comments:

Post a Comment