Using OpenCV APIs to capture video from a camera is convenient. However, OpenCV does not provide an API for listing all available devices. If you have multiple cameras connected to your PC, you have no idea how to choose the right one. To get device information on Windows, you need to invoke DirectShow APIs. In this post, I will share how to create a Python extension that lists camera devices for OpenCV-Python on Windows.
Bridging DirectShow APIs to OpenCV-Python
How to run DirectShow sample
To use DirectShow APIs, read the Microsoft’s tutorial — Selecting a Capture Device, which shares how to list video and audio devices in C++.
To run the sample, create an empty Win32 Console Application in Visual Studio: File > New > Project > Templates > Visual C++ > Win32.
Create main.cpp, and copy all codes snippets from the tutorial page to the C++ file.
Build and run the project.
How to wrap up DirectShow C++ code
Define Python module and change main() function to a Python method:
static PyObject *getDeviceList(PyObject *self, PyObject *args){PyObject* pyList = NULL;HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);if (SUCCEEDED(hr)){IEnumMoniker *pEnum;hr = EnumerateDevices(CLSID_VideoInputDeviceCategory, &pEnum);if (SUCCEEDED(hr)){pyList = DisplayDeviceInformation(pEnum);pEnum->Release();}CoUninitialize();}return pyList;}static PyMethodDef Methods[] ={{"getDeviceList", getDeviceList, METH_VARARGS, NULL},{NULL, NULL, 0, NULL}};PyMODINIT_FUNCinitdevice(void){(void) Py_InitModule("device", Methods);}
Create Python list to save device information:
PyObject* pyList = PyList_New(0);Char *pValue = _com_util::ConvertBSTRToString(var.bstrVal);HRESULT hr = PyList_Append(pyList, Py_BuildValue("s", pValue));
Note: you have to convert BSTR to string. To use ConvertBSTRToString, we need a header file #include <comutil.h>, and the corresponding library #pragma comment(lib, “comsuppw.lib”).
How to build and use Python extension
Create setup.py:
from distutils.core import setup, Extensionmodule_device = Extension('device',sources = ['device.cpp'],library_dirs=['G:\Program Files\Microsoft SDKs\Windows\v6.1\Lib'])setup (name = 'WindowsDevices',version = '1.0',description = 'Get device list with DirectShow',ext_modules = [module_device])
Build and install the extension:
python setup.py build install
If you see the error ‘Unable to find vcvarsall.bat’, set Visual Studio environment:
- Visual Studio 2010 (VS10): SET VS90COMNTOOLS=%VS100COMNTOOLS%
- Visual Studio 2012 (VS11): SET VS90COMNTOOLS=%VS110COMNTOOLS%
- Visual Studio 2013 (VS12): SET VS90COMNTOOLS=%VS120COMNTOOLS%
- Visual Studio 2015 (VS14): SET VS90COMNTOOLS=%VS140COMNTOOLS%
Create test.py to use the extension for OpenCV-Python:
import deviceimport cv2def select_camera(last_index):number = 0hint = "Select a camera (0 to " + str(last_index) + "): "try:number = int(input(hint))# select = int(select)except Exception ,e:print("It's not a number!")return select_camera(last_index)if number > last_index:print("Invalid number! Retry!")return select_camera(last_index)return numberdef open_camera(index):cap = cv2.VideoCapture(index)return capdef main():# print OpenCV versionprint("OpenCV version: " + cv2.__version__)# Get camera listdevice_list = device.getDeviceList()index = 0for name in device_list:print(str(index) + ': ' + name)index += 1last_index = index - 1if last_index < 0:print("No device is connected")return# Select a cameracamera_number = select_camera(last_index)# Open cameracap = open_camera(camera_number)if cap.isOpened():width = cap.get(3) # Frame Widthheight = cap.get(4) # Frame Heightprint('Default width: ' + str(width) + ', height: ' + str(height))while True:ret, frame = cap.read();cv2.imshow("frame", frame)# key: 'ESC'key = cv2.waitKey(20)if key == 27:breakcap.release()cv2.destroyAllWindows()if __name__ == "__main__":main()
Source Code
Originally published at www.codepool.biz on August 21, 2017.