如何使用 OpenCV 打开指定摄像头

在计算机视觉应用中,经常需要从特定的摄像头设备获取视频流。例如,在多摄像头环境中,当使用 OpenCV 的 cv::VideoCapture 类打开摄像头时,如果不指定摄像头的 ID,可能会随机打开系统中的某个摄像头,或者按照设备连接的顺序打开第一个可用的摄像头。
比如:

    // 打开两个摄像头cv::VideoCapture cap0(0);if (!cap0.isOpened()) {cap0.open(0);}cv::VideoCapture cap1(1);if (!cap0.isOpened() || !cap1.isOpened()) {std::cerr << "Error: Cannot open camera" << std::endl;return;}

在多摄像头环境下,这种方式可能无法满足应用需求。此外,直接使用摄像头 ID 的方式可能不够稳定,因为设备的连接顺序或系统分配的 ID 可能会发生变化。

那如何使用 OpenCV 打开指定的摄像头呢?我们知道,摄像头都会在安装后,操作系统会生成一个设备ID信息,

操作系统就是根据摄像头的 PID(产品 ID)和 VID(供应商 ID)来精确识别并打开某个摄像头的。

Image

如图所示,对应关系分别如下:

VID_0BDA&PID_3787 (Front Camera)
VID_0BDA&PID_5846 (HBVCAM Camera)
VID_0BDA&PID_D567 (USB Camera)

解决办法

那OpenCV是否支持在打开摄像头时,根据个信息进行指定呢?当然可以。

在 Windows 系统中,摄像头设备通常通过 DirectShow API 进行管理和操作。而 OpenCV 是一个功能强大的开源计算机视觉库,提供了与摄像头交互的接口。结合两者的优势,可以方便地实现对指定摄像头的访问。

通过以下步骤实现对指定摄像头的打开:

  1. 1. 使用 DirectShow API 枚举系统中的摄像头设备,并获取每个设备的详细信息,包括设备路径、PID 和 VID 等。

  2. 2. 根据用户指定的 PID 和 VID,在设备列表中查找匹配的设备,并获取其对应的设备 ID。

  3. 3. 使用 OpenCV 的 cv::VideoCapture 类,结合设备 ID 和 DirectShow API,打开指定的摄像头设备。

以下是完整的 C++ 代码,展示了如何使用 OpenCV 和 DirectShow API 打开指定 PID 和 VID 的摄像头:

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <opencv2/opencv.hpp>
#include <DShow.h>
#include <atlstr.h>
#pragma comment(lib,"Strmiids.lib")// 定义导出函数的宏
#ifdef _WIN32
#define DLL_EXPORT __declspec(dllexport)
#else
#define DLL_EXPORT
#endif// 获取摄像头ID的函数
DLL_EXPORT int getCamIDFromPidVid(const char* pidvid) {std::vector<std::string> devList; // 设备列表int iCameraNum = 0; // 设备个数ICreateDevEnum* pDevEnum = NULL;IEnumMoniker* pEnum = NULL;HRESULT hr = CoInitialize(NULL);if (FAILED(hr)) {std::cerr << "COM 初始化失败,错误码: " << hr << std::endl;return-1;}hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, reinterpret_cast<void**>(&pDevEnum));if (FAILED(hr)) {std::cerr << "创建设备枚举器失败,错误码: " << hr << std::endl;CoUninitialize();return-1;}hr = pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnum, 0);if (hr != S_OK && hr != S_FALSE) {std::cerr << "枚举视频输入设备类别失败,错误码: " << hr << std::endl;pDevEnum->Release();CoUninitialize();return-1;}if (hr == S_FALSE) {std::cerr << "没有找到视频输入设备" << std::endl;pDevEnum->Release();CoUninitialize();return-1;}IMoniker* pMoniker = NULL;ULONG cFetched;while (pEnum->Next(1, &pMoniker, &cFetched) == S_OK) {IPropertyBag* pPropBag;hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, reinterpret_cast<void**>(&pPropBag));if (SUCCEEDED(hr)) {VARIANT varName;VariantInit(&varName);varName.vt = VT_BSTR;hr = pPropBag->Read(L"DevicePath", &varName, NULL);if (SUCCEEDED(hr) && varName.vt == VT_BSTR && varName.bstrVal != nullptr) {std::wstring wstrDevicePath(varName.bstrVal);std::string strDevicePath(wstrDevicePath.begin(), wstrDevicePath.end());devList.push_back(strDevicePath);iCameraNum++;} else {std::cerr << "读取设备路径失败,错误码: " << hr << std::endl;}VariantClear(&varName);pPropBag->Release();}pMoniker->Release();}pEnum->Release();pDevEnum->Release();// 将输入的pidvid转换为小写std::string lowerPidvid = pidvid;std::transform(lowerPidvid.begin(), lowerPidvid.end(), lowerPidvid.begin(), ::tolower);int iRet = -1;for (int i = 0; i < devList.size(); i++) {// 将设备路径转换为小写std::string lowerDevicePath = devList[i];std::transform(lowerDevicePath.begin(), lowerDevicePath.end(), lowerDevicePath.begin(), ::tolower);if (lowerDevicePath.find(lowerPidvid) != std::string::npos) {iRet = i;break;}}CoUninitialize();return iRet;
}// 主函数示例
int main() {// 替换为你的摄像头的PID和VID,支持大写和小写std::string targetPidVid = "VID_XXXX&PID_XXXX"; // 例如:"VID_046D&PID_0825"int camId = getCamIDFromPidVid(targetPidVid.c_str());if (camId == -1) {std::cout << "未找到匹配的摄像头" << std::endl;return-1;}std::cout << "摄像头ID: " << camId << std::endl;// 使用OpenCV打开摄像头cv::VideoCapture cap;cap.open(camId, cv::CAP_DSHOW);if (!cap.isOpened()) {std::cerr << "无法打开摄像头,ID: " << camId << std::endl;return-1;}// 尝试读取一帧,验证摄像头是否真的可用cv::Mat frame;if (!cap.read(frame)) {std::cerr << "无法从摄像头读取帧,ID: " << camId << std::endl;cap.release();return-1;}std::cout << "摄像头已成功打开" << std::endl;while (true) {cap >> frame;if (frame.empty()) {std::cerr << "无法读取帧" << std::endl;break;}cv::imshow("Camera", frame);if (cv::waitKey(1) == 27) { // 按ESC键退出break;}}cap.release();cv::destroyAllWindows();return0;
}

注意细节

  1. 1. 确保安装了 OpenCV 库,并正确配置了开发环境。

  2. 2. 根据实际摄像头的 PID 和 VID 修改代码中的 targetPidVid 变量值。

  3. 3. 在编译代码时,链接必要的库文件,如 Strmiids.lib 和 OpenCV 相关的库。

  4. 4. 在选择摄像头时,我们要确保多个摄像头要各不一样(这样即可保证通过VID/PID来区分摄像头),但每一种都要采购统一(保证在不同电脑上VID/PID都一样)。

  5. 5. 上述相关思想也可以在 *nix 等系统中使用。

通过上述代码和方法,可以实现根据摄像头的 PID 和 VID 精确打开指定的摄像头设备,适用于多摄像头环境和需要精确设备识别的场景。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/89182.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/89182.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JAVA面试宝典 -《分布式ID生成器:Snowflake优化变种》

&#x1f680; 分布式ID生成器&#xff1a;Snowflake优化变种 一场订单高峰&#xff0c;一次链路追踪&#xff0c;一条消息投递…你是否想过&#xff0c;它们背后都依赖着一个“低调却关键”的存在——唯一ID。本文将带你深入理解分布式ID生成器的核心原理与工程实践&#xff0…

苹果的机器学习框架将支持英伟达的CUDA平台

苹果专为Apple Silicon设计的MLX机器学习框架即将迎来CUDA后端支持&#xff0c;这意义重大。原因如下。 这项开发工作由GitHub开发者zcbenz主导&#xff08;据AppleInsider报道&#xff09;&#xff0c;他于数月前开始构建CUDA支持的原型。此后他将项目拆分为多个模块&#xff…

golang语法-----变量、常量

变量1、声明与初始化&#xff08;1&#xff09;标准声明 (先声明&#xff0c;后赋值)var age int // 声明一个 int 类型的变量 age&#xff0c;此时 age 的值是 0 fmt.Println(age) // 输出: 0age 30 // 给 age 赋值 fmt.Println(age) // 输出: 30//int 的零…

Jenkins+Docker(docker-compose、Dockerfile)+Gitee实现自动化部署

项目目录结构 project-root/ ├── pom.xml ├── docker │ ├── copy.sh │ ├── file │ │ ├── jar │ │ │ └── 存放执行copy.sh以后jar包的位置 │ │ └── Dockerfile │ └── docker-compose.yml ├── docker-only-test │ ├─…

TASK01【datawhale组队学习】地瓜机器人具身智能概述

https://github.com/datawhalechina/ai-hardware-robotics 参考资料地址 具身智能&#xff08;Embodied AI&#xff09; 具身智能 智能的大脑 行动的身体。 比例&#xff08;Proportional&#xff09;、积分&#xff08;Integral&#xff09;、微分&#xff08;Derivative&a…

uni-app 配置华为离线推送流程

1、首先需要创建一个华为开发者账号&#xff0c;我这个是个人开发账号 申请开发者账号 2、去AppGallery Connect登陆我们刚刚创建好的账号&#xff0c;点击页面的APP进入到如下3 AppGallery Connect ‎‎‎‎‎ ‎3、在AppGallery Connect 网站中创建一个 Android应用、点击…

当下主流摄像头及其核心参数详解

&#x1f4d6; 推荐阅读&#xff1a;《Yocto项目实战教程:高效定制嵌入式Linux系统》 &#x1f3a5; 更多学习视频请关注 B 站&#xff1a;嵌入式Jerry 当下主流摄像头及其核心参数详解 一、摄像头发展概述 摄像头作为现代智能设备&#xff08;如手机、安防、车载、工业等&am…

下载了docker但是VirtualBox突然启动不了了

今天下docker后发现 eNSP 路由器&#xff0c;防火墙启动不了了去virtualbox检查的时候发现无法启动&#xff1a;报错&#xff1a;不能为虚拟电脑 AR_Base 打开一个新任务.Raw-mode is unavailable courtesy of Hyper-V. (VERR_SUPDRV_NO_RAW_MODE_HYPER_V_ROOT).返回代码: E_F…

C++11之lambda表达式与包装器

lambda与包装器lambda语法捕捉列表lambda的应用lambda的原理包装器functionbindlambda语法 lambda 表达式本质是⼀个匿名函数对象&#xff0c;跟普通函数不同的是他可以定义在函数内部。 lambda 表达式语法使⽤层⽽⾔没有类型&#xff0c;所以我们⼀般是⽤auto或者模板参数定义…

有痛呻吟!!!

XiTuJueJin:YYDS 分盘 有些平台吃相太难看&#xff0c;同样的文章&#xff0c;我还先选择现在这里发布&#xff0c;TMD. 莫名其妙将我的文章设置为仅VIP可见&#xff0c;还是今天才发现&#xff0c;之前只是将一两篇设置为仅VIP可见&#xff0c;今天突然发现这种标识的都自动…

2025年7-9月高含金量数学建模竞赛清单

2025年7-9月高含金量数学建模竞赛 ——“高教社杯”国赛 & “华为杯”研赛作为过来人&#xff0c;真心觉得参加数学建模比赛是我本科阶段做的最值的事之一。 它锻炼的那种把实际问题转化成模型求解的思维&#xff0c;对做研究、写论文甚至以后工作都帮助很大。我当时就是靠…

SpringBoot为什么使用new RuntimeException() 来获取调用栈?

为什么不直接使用 Thread.currentThread().getStackTrace()&#xff1f;这确实看起来有点“奇怪”或者“绕”&#xff0c;但其实这是 Java 中一种非常常见、巧妙且合法的技巧&#xff0c;用于在运行时动态获取当前代码的调用栈信息。Spring 选择用 new RuntimeException().getS…

小白成长之路-haproxy负载均衡

文章目录一、概述1、HAProxy简介2、HAProxy特点和优点&#xff1a;3、HAProxy保持会话的三种解决方法4、HAProxy的balance 8种负载均衡算法1&#xff09;RR&#xff08;Round Robin&#xff09;2&#xff09;LC&#xff08;Least Connections&#xff09;3&#xff09;SH&#…

Kafka 与 RocketMQ 消息确认机制对比分析

目录 生产者消息确认机制 Kafka 生产者 ACK 机制 RocketMQ 生产者确认机制 消费者消息确认机制 Kafka 消费者确认机制 RocketMQ 消费者确认机制 核心差异对比 选型建议 消息确认机制是分布式消息中间件的核心功能之一&#xff0c;它直接关系到消息传递的可靠性和系统性能…

C/C++---rdbuf()函数

在C中&#xff0c;rdbuf() 是I/O流库中的一个核心成员函数&#xff0c;主要用于访问和操作流对象的缓冲区。这个函数在底层数据处理、流重定向以及自定义流操作等场景中应用广泛。下面将从多个方面详细解析 rdbuf() 函数。 基本概念与函数原型 rdbuf() 是 std::basic_ios 类的成…

【LLM】从零到一构建一个小型LLM--MiniGPT

从零到一构建一个小型LLM (Small Language Model)暂时起名为MiniGPT。这个模型将专注于因果语言建模 (Causal Language Modeling)&#xff0c;这是许多现代LLM&#xff08;如GPT系列&#xff09;的核心预训练任务。模型设计&#xff1a; 我们设计的模型是一个仅包含解码器 (Dec…

网络安全威胁下的企业困境与破局技术实践

前言&#xff1a;网络安全威胁下的企业困境 在数字化转型的浪潮中&#xff0c;企业对信息技术的依赖程度日益加深&#xff0c;但随之而来的网络安全威胁也愈发严峻。据统计&#xff0c;全球每年因网络安全事件造成的经济损失高达数万亿美元&#xff0c;其中中小企业更是成为了网…

[RAG system] 信息检索器 | BM25 Vector | Pickle格式 | HybridRetriever重排序

第六章&#xff1a;信息检索器 在上一章中&#xff0c;我们成功完成了知识库摄入流程。这是巨大的进步~ 我们精心准备了文档"块"&#xff08;类似独立的索引卡&#xff09;&#xff0c;并将其存储在两套智能归档系统中&#xff1a;向量数据库&#xff08;用于基于含…

Android 高通平台修改音频参数效果文件-优化音频效果

Android 高通平台如何音频效果 修改音频参数效果文件-优化音频效果 按如下方式修改。 开发云 - 一站式云服务平台 diff --git a/vendor/qcom/proprietary/mm-audio/audcal/family-b/acdbdata//MTP/workspaceFile.qwsp b/vendor/qcom/proprietary/mm-audio/audcal/family-b/acdb…

Install Docker Engine on UbuntuMySQL

Install Docker Engine on Ubuntu&&MySQL安装docker安装mysql客户端连接数据库我真气鼠了&#xff0c;今天得到一个血泪的教训&#xff0c;以后一定看官方文档&#xff01;&#xff01;&#xff01;学的课用的centos&#xff0c;指令全是yum&#xff0c;我这边不通用&a…