Starting to add support for proper webcam listing.

This commit is contained in:
Tadas Baltrusaitis 2018-01-26 07:59:10 +00:00
parent f9608aa758
commit 3b63728b9b
7 changed files with 527 additions and 42 deletions

View File

@ -0,0 +1,135 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{50B7D4BF-E33B-41D0-AA89-76BBA57BF5CC}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>CameraEnumerator</RootNamespace>
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<Text Include="ReadMe.txt" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<Text Include="ReadMe.txt" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,121 @@
#include "DeviceEnumerator.h"
std::map<int, Device> DeviceEnumerator::getVideoDevicesMap() {
return getDevicesMap(CLSID_VideoInputDeviceCategory);
}
std::map<int, Device> DeviceEnumerator::getAudioDevicesMap() {
return getDevicesMap(CLSID_AudioInputDeviceCategory);
}
// Returns a map of id and devices that can be used
std::map<int, Device> DeviceEnumerator::getDevicesMap(const GUID deviceClass)
{
std::map<int, Device> deviceMap;
HRESULT hr = CoInitialize(nullptr);
if (FAILED(hr)) {
return deviceMap; // Empty deviceMap as an error
}
// Create the System Device Enumerator
ICreateDevEnum *pDevEnum;
hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pDevEnum));
// If succeeded, create an enumerator for the category
IEnumMoniker *pEnum = NULL;
if (SUCCEEDED(hr)) {
hr = pDevEnum->CreateClassEnumerator(deviceClass, &pEnum, 0);
if (hr == S_FALSE) {
hr = VFW_E_NOT_FOUND;
}
pDevEnum->Release();
}
// Now we check if the enumerator creation succeeded
int deviceId = -1;
if (SUCCEEDED(hr)) {
// Fill the map with id and friendly device name
IMoniker *pMoniker = NULL;
while (pEnum->Next(1, &pMoniker, NULL) == S_OK) {
IPropertyBag *pPropBag;
HRESULT hr = pMoniker->BindToStorage(0, 0, IID_PPV_ARGS(&pPropBag));
if (FAILED(hr)) {
pMoniker->Release();
continue;
}
// Create variant to hold data
VARIANT var;
VariantInit(&var);
std::string deviceName;
std::string devicePath;
// Read FriendlyName or Description
hr = pPropBag->Read(L"Description", &var, 0); // Read description
if (FAILED(hr)) {
// If description fails, try with the friendly name
hr = pPropBag->Read(L"FriendlyName", &var, 0);
}
// If still fails, continue with next device
if (FAILED(hr)) {
VariantClear(&var);
continue;
}
// Convert to string
else {
deviceName = ConvertBSTRToMBS(var.bstrVal);
}
VariantClear(&var); // We clean the variable in order to read the next value
// We try to read the DevicePath
hr = pPropBag->Read(L"DevicePath", &var, 0);
if (FAILED(hr)) {
VariantClear(&var);
continue; // If it fails we continue with next device
}
else {
devicePath = ConvertBSTRToMBS(var.bstrVal);
}
// We populate the map
deviceId++;
Device currentDevice;
currentDevice.id = deviceId;
currentDevice.deviceName = deviceName;
currentDevice.devicePath = devicePath;
deviceMap[deviceId] = currentDevice;
}
pEnum->Release();
}
CoUninitialize();
return deviceMap;
}
/*
This two methods were taken from
https://stackoverflow.com/questions/6284524/bstr-to-stdstring-stdwstring-and-vice-versa
*/
std::string DeviceEnumerator::ConvertBSTRToMBS(BSTR bstr)
{
int wslen = ::SysStringLen(bstr);
return ConvertWCSToMBS((wchar_t*)bstr, wslen);
}
std::string DeviceEnumerator::ConvertWCSToMBS(const wchar_t* pstr, long wslen)
{
int len = ::WideCharToMultiByte(CP_ACP, 0, pstr, wslen, NULL, 0, NULL, NULL);
std::string dblstr(len, '\0');
len = ::WideCharToMultiByte(CP_ACP, 0 /* no flags */,
pstr, wslen /* not necessary NULL-terminated */,
&dblstr[0], len,
NULL, NULL /* no default char */);
return dblstr;
}

View File

@ -0,0 +1,31 @@
#pragma once
#include <Windows.h>
#include <dshow.h>
#pragma comment(lib, "strmiids")
#include <map>
#include <string>
struct Device {
int id; // This can be used to open the device in OpenCV
std::string devicePath;
std::string deviceName; // This can be used to show the devices to the user
};
class DeviceEnumerator {
public:
DeviceEnumerator() = default;
std::map<int, Device> getDevicesMap(const GUID deviceClass);
std::map<int, Device> getVideoDevicesMap();
std::map<int, Device> getAudioDevicesMap();
private:
std::string ConvertBSTRToMBS(BSTR bstr);
std::string ConvertWCSToMBS(const wchar_t* pstr, long wslen);
};

View File

@ -0,0 +1,36 @@
#include <map>
#include <iostream>
#include "DeviceEnumerator.h"
int main()
{
/*
The id field of the Device struct can be used with an OpenCV VideoCapture object
*/
DeviceEnumerator de;
// Audio Devices
std::map<int, Device> devices = de.getAudioDevicesMap();
// Print information about the devices
for (auto const &device : devices) {
std::cout << "== AUDIO DEVICE (id:" << device.first << ") ==" << std::endl;
std::cout << "Name: " << device.second.deviceName << std::endl;
std::cout << "Path: " << device.second.devicePath << std::endl;
}
// Video Devices
devices = de.getVideoDevicesMap();
// Print information about the devices
for (auto const &device : devices) {
std::cout << "== VIDEO DEVICE (id:" << device.first << ") ==" << std::endl;
std::cout << "Name: " << device.second.deviceName << std::endl;
std::cout << "Path: " << device.second.devicePath << std::endl;
}
}

32
lib/3rdParty/CameraEnumerator/readme.md vendored Normal file
View File

@ -0,0 +1,32 @@
# Device Enumerator for OpenCV
This project contains a *C++* class that allows the enumeration of devices using DirectShow in Windows, in order to select and obtain the ID that needs to be used with OpenCV when creating, for example, a VideoCapture object to grab frames from a camera. I decided to put this up as "How to get the ID of a device to use inside OpenCV?" is a question that pops up continuously.
## Contents
The project contains the class itself (DeviceEnumerator.h and DeviceEnumerator.cpp) and also an example of how to use the class (OpenCVDeviceEnumerator.cpp)
## License
MIT License
Copyright (c) 2018 David Gil de Gómez Pérez (Studiosi)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -49,6 +49,8 @@
#include <OpenCVWrappers.h>
#include <ImageReader.h>
#include "DeviceEnumerator.h"
#include "SequenceCapture.h"
#pragma managed
@ -211,68 +213,176 @@ namespace UtilitiesOF {
}
};
// A utility for listing the currently connected cameras together with their ID, subset of supported resolutions and a thumbnail
static List<System::Tuple<int, List<System::Tuple<int, int>^>^, OpenCVWrappers::RawImage^>^>^ GetCameras()
{
auto managed_camera_list = gcnew List<System::Tuple<int, List<System::Tuple<int, int>^>^, OpenCVWrappers::RawImage^>^>();
// Go through camera id's untill no new cameras are found
int num_cameras = 0;
while (true)
{
cv::VideoCapture cap(num_cameras);
if (cap.isOpened())
num_cameras++;
else
break;
static void split(const std::string &s, char delim, std::vector<string> &elems) {
std::stringstream ss;
ss.str(s);
std::string item;
while (std::getline(ss, item, delim)) {
elems.push_back(item);
}
}
// Camera listing is camera name and supported resolutions
static Dictionary<System::String^, List<System::Tuple<int, int>^>^>^ GetListingFromFile(std::string filename)
{
// Check what cameras have been written (using OpenCVs XML packages)
cv::FileStorage fs_read(filename, cv::FileStorage::READ);
auto managed_camera_list_initial = gcnew Dictionary<System::String^, List<System::Tuple<int, int>^>^>();
cv::FileNode camera_node_list = fs_read["cameras"];
// iterate through a sequence using FileNodeIterator
for (size_t idx = 0; idx < camera_node_list.size(); idx++)
{
std::string camera_name = (std::string)camera_node_list[idx]["name"];
cv::FileNode resolution_list = camera_node_list[idx]["resolutions"];
auto resolutions = gcnew System::Collections::Generic::List<System::Tuple<int, int>^>();
for (size_t r_idx = 0; r_idx < resolution_list.size(); r_idx++)
{
string res = resolution_list[r_idx]["res"];
std::vector<std::string> elems;
split(res, 'x', elems);
int x = stoi(elems[0]);
int y = stoi(elems[1]);
resolutions->Add(gcnew System::Tuple<int, int>(x, y));
}
managed_camera_list_initial[gcnew System::String(camera_name.c_str())] = resolutions;
}
fs_read.release();
return managed_camera_list_initial;
}
static void WriteCameraListingToFile(System::Collections::Generic::Dictionary<System::String^, System::Collections::Generic::List<System::Tuple<int, int>^>^>^ camera_list, std::string filename)
{
cv::FileStorage fs("camera_list.xml", cv::FileStorage::WRITE);
fs << "cameras" << "[";
for each(System::String^ name_m in camera_list->Keys)
{
std::string name = msclr::interop::marshal_as<std::string>(name_m);
fs << "{:" << "name" << name;
fs << "resolutions" << "[";
auto resolutions = camera_list[name_m];
for (int j = 0; j < resolutions->Count; j++)
{
stringstream ss;
ss << resolutions[j]->Item1 << "x" << resolutions[j]->Item2;
fs << "{:" << "res" << ss.str();
fs << "}";
}
fs << "]";
fs << "}";
}
fs << "]";
fs.release();
}
// A utility for listing the currently connected cameras together with their ID, name, subset of supported resolutions and a thumbnail
static List<System::Tuple<int, System::String^, List<System::Tuple<int, int>^>^, OpenCVWrappers::RawImage^>^>^ GetCameras(System::String^ root_directory_m)
{
auto managed_camera_list = gcnew List<System::Tuple<int, System::String^, List<System::Tuple<int, int>^>^, OpenCVWrappers::RawImage^>^>();
DeviceEnumerator de;
// Get a listing of all connected video devices
std::map<int, Device> cameras = de.getVideoDevicesMap();
// Print information about the devices
for (auto const &device : cameras) {
std::cout << "== VIDEO DEVICE (id:" << device.first << ") ==" << std::endl;
std::cout << "Name: " << device.second.deviceName << std::endl;
std::cout << "Path: " << device.second.devicePath << std::endl;
}
size_t num_cameras = cameras.size();
// Pre-load supported camera resolutions if already computed
std::string root_directory = msclr::interop::marshal_as<std::string>(root_directory_m);
auto camera_resolution_list = GetListingFromFile(root_directory + "camera_list.xml");
for (size_t i = 0; i < num_cameras; ++i)
{
//std::string name = cameras[i].name();
//System::String^ name_managed = gcnew System::String(name.c_str());
auto resolutions = gcnew List<System::Tuple<int, int>^>();
// A common set of resolutions for webcams
std::vector<std::pair<int, int>> common_resolutions;
common_resolutions.push_back(std::pair<int, int>(320, 240));
common_resolutions.push_back(std::pair<int, int>(640, 480));
common_resolutions.push_back(std::pair<int, int>(1280, 960));
// Grab some sample images and confirm the resolutions
cv::VideoCapture cap1(i);
// Thumbnail to help with camera selection
cv::Mat sample_img;
OpenCVWrappers::RawImage^ sample_img_managed = gcnew OpenCVWrappers::RawImage();
// Go through resolutions if they have not been identified
for (auto beg = common_resolutions.begin(); beg != common_resolutions.end(); ++beg)
{
auto resolution = gcnew System::Tuple<int, int>(beg->first, beg->first);
auto resolutions = gcnew List<System::Tuple<int, int>^>();
// Before trying the resolutions, check if the resolutions have already been computed for the camera of interest
std::string device_name = cameras[i].deviceName;
System::String^ device_name_m = gcnew System::String(device_name.c_str());
if (camera_resolution_list->ContainsKey(device_name_m))
{
resolutions = camera_resolution_list[device_name_m];
// Grab a thumbnail from mid resolution
cv::VideoCapture cap1(i);
auto resolution = resolutions[(int)(resolutions->Count / 2)];
cap1.set(CV_CAP_PROP_FRAME_WIDTH, resolution->Item1);
cap1.set(CV_CAP_PROP_FRAME_HEIGHT, resolution->Item2);
// Add only valid resolutions as API sometimes provides wrong ones
int set_width = cap1.get(CV_CAP_PROP_FRAME_WIDTH);
int set_height = cap1.get(CV_CAP_PROP_FRAME_HEIGHT);
// Read several frames, as the first one often is over-exposed
for (int k = 0; k < 2; ++k)
cap1.read(sample_img);
}
else
{
resolution = gcnew System::Tuple<int, int>(set_width, set_height);
if (!resolutions->Contains(resolution))
// A common set of resolutions for webcams
std::vector<std::pair<int, int>> common_resolutions;
common_resolutions.push_back(std::pair<int, int>(320, 240));
common_resolutions.push_back(std::pair<int, int>(640, 480));
common_resolutions.push_back(std::pair<int, int>(960, 720));
common_resolutions.push_back(std::pair<int, int>(1280, 720));
common_resolutions.push_back(std::pair<int, int>(1280, 960));
common_resolutions.push_back(std::pair<int, int>(1920, 1080));
// Grab some sample images and confirm the resolutions
cv::VideoCapture cap1(i);
// Go through resolutions if they have not been identified
for (size_t i = 0; i < common_resolutions.size(); ++i)
{
resolutions->Add(resolution);
auto resolution = gcnew System::Tuple<int, int>(common_resolutions[i].first, common_resolutions[i].second);
cap1.set(CV_CAP_PROP_FRAME_WIDTH, resolution->Item1);
cap1.set(CV_CAP_PROP_FRAME_HEIGHT, resolution->Item2);
// Add only valid resolutions as API sometimes provides wrong ones
int set_width = cap1.get(CV_CAP_PROP_FRAME_WIDTH);
int set_height = cap1.get(CV_CAP_PROP_FRAME_HEIGHT);
// Grab a thumbnail from mid resolution
if(i == (int)common_resolutions.size()/2)
{
// Read several frames, as the first one often is over-exposed
for (int k = 0; k < 2; ++k)
cap1.read(sample_img);
}
resolution = gcnew System::Tuple<int, int>(set_width, set_height);
if (!resolutions->Contains(resolution))
{
resolutions->Add(resolution);
}
}
cap1.~VideoCapture();
// Ass the resolutions were not on the list, add them now
camera_resolution_list[device_name_m] = resolutions;
WriteCameraListingToFile(camera_resolution_list, root_directory + "camera_list.xml");
}
sample_img.copyTo(sample_img_managed->Mat);
cap1.~VideoCapture();
managed_camera_list->Add(gcnew System::Tuple<int, List<System::Tuple<int, int>^>^, OpenCVWrappers::RawImage^>(i, resolutions, sample_img_managed));
managed_camera_list->Add(gcnew System::Tuple<int, System::String^, List<System::Tuple<int, int>^>^, OpenCVWrappers::RawImage^>(i, device_name_m, resolutions, sample_img_managed));
}
return managed_camera_list;