Listing Multiple Cameras for OpenCV-Python on Windows
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_FUNC
initdevice(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
How to build and use Python extension
Create setup.py:
from distutils.core import setup, Extension
module_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 device
import cv2
def select_camera(last_index):
number = 0
hint = "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 number
def open_camera(index):
cap = cv2.VideoCapture(index)
return cap
def main():
# print OpenCV version
print("OpenCV version: " + cv2.__version__)
# Get camera list
device_list = device.getDeviceList()
index = 0
for name in device_list:
print(str(index) + ': ' + name)
index += 1
last_index = index - 1
if last_index < 0:
print("No device is connected")
return
# Select a camera
camera_number = select_camera(last_index)
# Open camera
cap = open_camera(camera_number)
if cap.isOpened():
width = cap.get(3) # Frame Width
height = cap.get(4) # Frame Height
print('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:
break
cap.release()
cv2.destroyAllWindows()
if __name__ == "__main__":
main()