基于MFC和OpenCV3.4.10实现相机切换效果

查询资料后发现,MFC并没有像Qt那样提供了关于图像的类,而借助OpenCV可以捕捉摄像头图像并显示。因此本demo最终目的是通过MFC的下拉框部件实现不同相机切换的显示效果。OpenCV欲读取摄像头图像时,需先创建一个VideoCapture对象,并传入一个index参数。

Videocapture cap;
cap.open(index);

但是,当存在多个摄像头时,并不能事先知道拟打开摄像头的序号是多少。因此,若是能将摄像头的设备列表与序号对应起来,便可以随心所欲控制想要打开的摄像头进而捕捉其图像。因此,我们需要这样一个数据类型,根据设备字符串便可索引到设备序号。

std::map<std::string, int> cameraID;

获取摄像头列表

但现在的难题在于怎么获取设备列表,幸运的是,网上已经有答案了,参考opencv获取多个摄像头名字和编号

#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include "windows.h"
#include "dshow.h"
#include <iostream>

#pragma comment(lib, "strmiids.lib")
#pragma comment(lib, "quartz.lib")

using namespace cv;
using namespace std;

int listDevices(vector<string>& list)
{
    
    

	//COM Library Initialization
	//comInit();

	//if (!silent) DebugPrintOut("\nVIDEOINPUT SPY MODE!\n\n");


	ICreateDevEnum* pDevEnum = NULL;
	IEnumMoniker* pEnum = NULL;
	int deviceCounter = 0;
	CoInitialize(NULL);

	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) {
    
    

			printf("SETUP: Looking For Capture Devices\n");
			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.
				}


				// Find the description or friendly name.
				VARIANT varName;
				VariantInit(&varName);
				hr = pPropBag->Read(L"Description", &varName, 0);

				if (FAILED(hr)) hr = pPropBag->Read(L"FriendlyName", &varName, 0);

				if (SUCCEEDED(hr))
				{
    
    

					hr = pPropBag->Read(L"FriendlyName", &varName, 0);

					int count = 0;
					char tmp[255] = {
    
     0 };
					//int maxLen = sizeof(deviceNames[0]) / sizeof(deviceNames[0][0]) - 2;
					while (varName.bstrVal[count] != 0x00 && count < 255)
					{
    
    
						tmp[count] = (char)varName.bstrVal[count];
						count++;
					}
					list.push_back(tmp);
					//deviceNames[deviceCounter][count] = 0;

					//if (!silent) DebugPrintOut("SETUP: %i) %s\n", deviceCounter, deviceNames[deviceCounter]);
				}

				pPropBag->Release();
				pPropBag = NULL;

				pMoniker->Release();
				pMoniker = NULL;

				deviceCounter++;
			}

			pDevEnum->Release();
			pDevEnum = NULL;

			pEnum->Release();
			pEnum = NULL;
		}

		//if (!silent) DebugPrintOut("SETUP: %i Device(s) found\n\n", deviceCounter);
	}

	//comUnInit();

	return deviceCounter;
}

int main()
{
    
    
	vector<string> list;
	int capid0 = 0, capid1 = 0;//摄像头ID
	int divice_num = 0;
	divice_num = listDevices(list);//设备列表
	cout << "divice_num:" << divice_num << endl;

	for (int i = 0; i < list.size(); i++)
	{
    
    
		
		//if (list[i] == "HIK 1080P Camera")
		//{
    
    
		//	capid0 = i;
		//	cout << "capid0 = " << capid0 << endl;
		//	cout << list[i]<< " is HIK 1080P Camera"<<endl;
		//}
		//if (list[i] == "2K USB Camera")
		//{
    
    
		//	capid1 = i;
		//	cout << "capid1 = " << capid1 << endl;
		//	cout << list[i] << " is 2K USB Camera" << endl;
		//}
		
		cout << "Device[" << i << "]:" << list[i] << endl;
	}

	VideoCapture cap;
	cout << "capid = " << capid0 << endl;

	cap.open(1);
	Mat frame;
	while (1)
	{
    
    
		cap >> frame;
		imshow("capshow", frame);
		if (waitKey(30) == 27)
		{
    
    
			break;
		}
	}
	return 0;
}

经测试发现确实可行

SETUP: Looking For Capture Devices
divice_num:2
Device[0]:HIK 1080P Camera
Device[1]:2K USB Camera
capid = 0

MFC搭建界面

基于对话框创建一个MFC应用

在这里插入图片描述

将环境设置成Release x64,并设置好OpenCV环境。拖入一个Static Text控件,修改其名字为:相机。

在这里插入图片描述
再拖入Combo Box控件,修改其属性中的IDIDC_CAMERA

在这里插入图片描述
并为该控件关联变量m_camera

在这里插入图片描述

MFCCameraDlg.h中添加头文件

#include <vector>
#include <string>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <map>

CMFCCameraDlg类声明中添加两个私有变量:

cv::VideoCapture cap;                 //相机捕获对象
std::map<std::string, int> cameraID;  //相机列表序号映射表

转到MFCCameraDlg.cpp中,先添加需要的头文件及lib

#include "windows.h"
#include "dshow.h"

#pragma comment(lib, "strmiids.lib")
#pragma comment(lib, "quartz.lib")

CMFCCameraDlg::OnInitDialog()函数中添加窗口初始化代码

// TODO: 在此添加额外的初始化代码

//获取所有摄像头设备信息
std::vector<std::string> list;
int divice_num = listDevices(list);//设备列表

for (int i = 0; i < divice_num; i++) {
    
     //构建映射表
	cameraID.insert(std::pair<std::string, int>(list[i], i));
}

for (int i = 0; i < divice_num; i++) {
    
     //添加下拉框选项
	m_camera.AddString(StoWs(list[i]));
}

由于类中缺少listDevicesStoWs这两个函数,为其添加好(,h中声明,.cpp中实现)

public:
	int listDevices(std::vector<std::string>& list);   
	LPCTSTR StoWs(const std::string& s); 				//将std::string转成LPCTSTR
int CMFCCameraDlg::listDevices(std::vector<std::string>& list) {
    
    
	//COM Library Initialization
	//comInit();

	//if (!silent) DebugPrintOut("\nVIDEOINPUT SPY MODE!\n\n");


	ICreateDevEnum* pDevEnum = NULL;
	IEnumMoniker* pEnum = NULL;
	int deviceCounter = 0;
	CoInitialize(NULL);

	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) {
    
    

			printf("SETUP: Looking For Capture Devices\n");
			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.
				}


				// Find the description or friendly name.
				VARIANT varName;
				VariantInit(&varName);
				hr = pPropBag->Read(L"Description", &varName, 0);

				if (FAILED(hr)) hr = pPropBag->Read(L"FriendlyName", &varName, 0);

				if (SUCCEEDED(hr))
				{
    
    

					hr = pPropBag->Read(L"FriendlyName", &varName, 0);

					int count = 0;
					char tmp[255] = {
    
     0 };
					//int maxLen = sizeof(deviceNames[0]) / sizeof(deviceNames[0][0]) - 2;
					while (varName.bstrVal[count] != 0x00 && count < 255)
					{
    
    
						tmp[count] = (char)varName.bstrVal[count];
						count++;
					}
					list.push_back(tmp);
					//deviceNames[deviceCounter][count] = 0;

					//if (!silent) DebugPrintOut("SETUP: %i) %s\n", deviceCounter, deviceNames[deviceCounter]);
				}

				pPropBag->Release();
				pPropBag = NULL;

				pMoniker->Release();
				pMoniker = NULL;

				deviceCounter++;
			}

			pDevEnum->Release();
			pDevEnum = NULL;

			pEnum->Release();
			pEnum = NULL;
		}

		//if (!silent) DebugPrintOut("SETUP: %i Device(s) found\n\n", deviceCounter);
	}

	//comUnInit();

	return deviceCounter;
}
LPCTSTR CMFCCameraDlg::StoWs(const std::string& s) {
    
    
	int len;
	int slength = (int)s.length() + 1;
	len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0);
	wchar_t* buf = new wchar_t[len];
	MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, buf, len);
	LPCTSTR r(buf);
	delete[] buf;
	return r;
}

运行代码,下拉框中便出现可选选项

在这里插入图片描述
最后实现选择对应选项的消息处理函数,双击控件即可进入

void CMFCCameraDlg::OnCbnSelchangeCamera()
{
    
    
	// TODO: 在此添加控件通知处理程序代码

	//1.获取当前选中的选项
	int index = m_camera.GetCurSel();

	//2.根据字符串索引相机设备的序号
	CString str;
	m_camera.GetLBText(index, str);
	std::string strStr(CW2A(str.GetString())); //将CString转成std::string
	int cameraIndex = cameraID[strStr];

	//3.将序号传入VideoCapture对象
	cap.open(cameraIndex);
	cv::Mat frame;
	while (1)
	{
    
    
		cap >> frame;
		imshow("capshow", frame);
		if (cv::waitKey(30) == 27)
		{
    
    
			break;
		}
	}
}

实际测试效果如下

在这里插入图片描述
默认显示的图像分辨率是 640 × 480 640\times480 640×480,这是OpenCV默认的

想要修改分辨率,可以添加代码

//设置分辨率,编码格式
cap.set(cv::CAP_PROP_FRAME_WIDTH, 1920);
cap.set(cv::CAP_PROP_FRAME_HEIGHT, 1080);
cap.set(cv::CAP_PROP_FOURCC, cv::VideoWriter::fourcc('M', 'J', 'P', 'G'));

猜你喜欢

转载自blog.csdn.net/Star_ID/article/details/127203220