C/C++和OpenCV实现调用摄像头

2025-06-01 14:50

本文主要是介绍C/C++和OpenCV实现调用摄像头,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《C/C++和OpenCV实现调用摄像头》本文主要介绍了C/C++和OpenCV实现调用摄像头,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一...

OpenCV 是一个强大的计算机视觉库,它使得从摄像头捕获和处理视频流变得非常简单。本文将指导你如何使用 C/C++ 和 OpenCV 来调用摄像头、读取视频帧并进行显示。

准备工作

在开始之前,请确保你已经正确安装了 OpenCV 库,并且你的开发环境(如 Visual Studio, Code::blocks, CLion, 或者使用 CMake/GCC 的命令行环境)已经配置好可以链接 OpenCV 库。

你需要包含以下头文件:

#include <opencv2/opencv.hpp> // 包含 OpenCV 的核心功能和高级 GUI (highgui)
#include <IOStream>          // 用于标准输入输出

1. 打开摄像头

要从摄像头捕获视频,我们首先需要创建一个 cv::VideoCapture 对象。它的构造函数可以接受一个整数作为参数,该整数表示摄像头的索引。通常,0 代表系统默认的内置摄像头,1 代表第一个外部摄像头,以此类推。

cv::VideoCapture cap; // 创建一个 VideoCapture 对象

int cameraIndex = 0; // 通常 0 是默认摄像头
cap.open(cameraIndex); // 或者直接 cv::VideoCapture cap(0);

// http://www.chinasem.cn检查摄像头是否成功打开
if (!cap.isOpened()) {
    std::cerr << "错误: 无法打开摄像头 " << cameraIndex << std::endl;
    return -1; // 或者进行其他错误处理
}

提示:

  • 你也可以传递一个视频文件的路径字符串给 cv::VideoCapture 的构造函数或 open() 方法来读取视频文件。
  • 如果有多个摄像头,你可以尝试不同的索引(0, 1, 2, …)直到找到你想要的摄像头。

2. 读取视频帧

一旦摄像头成功打开,我们就可以在一个循环中逐帧读取视频。cv::VideoCapture::read() 方法或重载的 >> 运算符可以用来获取新的帧。

read() 方法会返回一个布尔值,表示是否成功读取到一帧。读取到的帧会存储在一个 cv::Mat 对象中。

cv::Mat frame; // 创建一个 Mat 对象来存储每一帧

while (true) {
    bool success = cap.read(frame); // 读取新的一帧
    // 或者 cap >> frame;

    if (!success || frame.empty()) {
        std::cerr << "错误: 无法从摄像头读取帧" << std::endl;
        break; // 如果读取失败或帧为空,则退出循环
    }

    // 在这里可以对 'frame' 进行处理,例如:
    // cv::cvtColor(frame, grayFrame, cv::COLOR_BGR2GRAY); // 转换为灰度图
    // cv::GaussianBlur(frame, blurredFrame, cv::Size(5, 5), 0); // 高斯模糊

    // ... (接下来的步骤:显示帧)
}

3. 显示视频帧

OpenCV 的 highpythongui 模块提供了显示图像和视频的功能。我们可以使用 cv::imshow() 函数来显示捕获到的帧。通常还需要配合 cv::waitKey() 来控制帧的显示时间和处理用户输入。

    // ... (在读取帧的循环内部)

    cv::imshow("摄像头画面", frame); // 在名为 "摄像头画面" 的窗口中显示帧

    // 等待按键,延迟 1 毫秒。
    // 如果按下 'ESC'键 (ASCII 值为 27),则退出循环
    // waitKey 返回按下键的 ASCII 值,如果没有按键则返回 -1
    int key = cv::waitKey(1);
    if (key == 27) { // ESC 键
        std::cout << "ESC键被按下,正在关闭..." << std::endl;
        break;
    } else if (key != -1) {
        // 可以添加其他按键的逻辑
        // std::cout << "按键: " << key << std::endl;
    }

cv::waitKey(delay) 函数会等待指定的 delay 毫秒数。

  • 如果 delay 为 0 或负数,它会无限期等待直到有按键按下。
  • 如果 delay 为正数,它会等待 delay 毫秒。如果在等待期间有按键按下,函数会返回按键的 ASCII 值;否则返回 -1。
  • 对于视频流,通常使用一个较小的值(如 1 或 30)来确保视频流畅播放,并允许程序响应按键事件。

4. 释放资源

当不再需要摄像头或程序即将退出时,务必释放 cv::VideoCapture 对象,并销毁所有创建的窗口。

// ... (在主函数末尾或退出前)

cap.release(); // 释放 VideoCapture 对象
cv::destroyAllWindows(); //销毁所有由 OpenCV 创建的窗口

虽然 cv::VideoCapture 对象在析构时会自动释放摄像头,但显式调用 release() 是一个好习惯。

5. 获取和设置摄像头属性 (可选)

cv::VideoCapture 对象还允许你获取和设置摄像头的一些属性,例如帧的宽度、高度、FPS(每秒帧数)等。这些属性由 cv::CAP_PROP_* 枚举定义。

  • 获取属性cap.get(cv::CAP_PROP_FRAME_WIDTH)
  • 设置属性cap.set(cv::CAP_PROP_FRAME_WIDTH, newValue)
// 获取摄像头默认的帧宽度和高度
double frameWidth = cap.get(cv::CAP_PROP_FRAME_WIDTH);
double frameHeight = cap.get(cv::CAP_PROP_FRAME_HEIGHT);
double fps = cap.get(cv::CAP_PROP_FPS);

std::cout << "默认宽度: " << frameWidth << std::endl;
std::cout << "默认高度: " << frameHeight << std::endl;
std::cout << "默认FPS: " << fps << std::endl;

// 尝试设置新的宽度和高度 (摄像头可能不支持所有值)
// bool setWidthSuccess = cap.set(cv::CAP_PROP_FRAME_WIDTH, 1280);
// bool setHeightSuccess = cap.set(cv::CAP_PROP_FRAME_HEIGHT, 720);
// if (setWidthSuccess && setHeightSuccess) {
//     std::cout << "成功设置分辨率为 1280x720" << std::endl;
// } else {
//     std::cout << "警告: 未能成功设置期望的分辨率" << std::endl;
//     // 再次获取实际生效的宽度和高度
//     frameWidth = cap.get(cv::CAP_PROP_FRAME_WIDTH);
//     frameHeight = cap.get(cv::CAP_PROP_FRAME_HEIGHT);
//     std::cout << "当前宽度: " << frameWidth << std::endl;
//     std::cout << "当前高度: " << frameHeight << std::endl;
// }

注意: 并非所有摄像头都支持通过 set() 方法修改所有属性,或者可能只支持特定的预设值。设置后最好再次 get() 以确认实际生效的值。

完整示例代码

下面是一个将以上所有步骤整合在一起的完整示例:

#include <opencv2/opencv.hpp>
#include <iostream>

int main() {
    // 1. 打开摄像头
    cv::VideoCapture caandroidp;
    int cameraIndex = 0; // 尝试不同的索引,如果默认摄像头不工作
    cap.open(cameraIndex);

    // 检查摄像头是否成功打开
    if (!cap.isOpened(android)) {
        std::cerr << "错误: 无法打开摄像头 " << cameraIndex << std::endl;
        // 尝试下一个摄像头索引,如果需要
        // cameraIndex = 1;
        // cap.open(cameraIndex);
        // if (!cap.isOpened()) {
        //     std::cerr << "错误: 仍然无法打开摄像头 " << cameraIndex << std::endl;
        //     return -1;
        // }
        return -1;
    }

    std::cout << "摄像头 " << cameraIndex << " 已成功打开." << std::endl;

    // (可选) 获取和打印摄像头属性
    double frameWidth = cap.get(cv::CAP_PROP_FRAME_WIDTH);
    double frameHeight = cap.get(cv::CAP_PROP_FRAME_HEIGHT);
    double fps = cap.get(cv::CAP_PROP_FPS);
    std::cout << "帧宽度: " << frameWidth << std::endl;
    std::cout << "帧高度: " << frameHeight << std::endl;
    std::cout << "FPS: " << fps << std::endl; // 注意:FPS 可能不准确或不被所有摄像头支持

    cv::Mat frame; // 用于存储每一帧
    std::string windowName = "摄像头画面 - 按 ESC 退出";
    cv::namedwindow(windowName, cv::WINDOW_AUTOSIZE); // 创建一个窗口

    // 2. 读取并显示视频帧
    while (true) {
        bool success = cap.read(frame); // 或者 cap >> frame;

        if (!success || frame.empty()) {
            std::cerr << "错误: 无法从摄像头读取帧或视频已结束" << std::endl;
            break;
        }

        // 在这里可以对 'frame' 进行图像处理
        // 例如:显示帧号或时间戳
        // cv::putText(frame, "Frame: " + std::to_string(cap.get(cv::CAP_PROP_POS_FRAMES)),
        //             cv::Point(10, 30), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(0, 255, 0), 2);

        // 3. 显示帧
        cv::imshow(windowName, frame);

        // 等待按键,延迟 (1000/FPS) ms 以大致匹配视频帧率,或者简单用 1-30ms
        // 如果 fps > 0, 使用 int delay = 1000.0 / fps; 否则使用一个默认值
        int delay = (fps > 0) ? static_cast<int>(1000.0 / fps) : 30;
        if (delay <= 0) delay = 30; // 防止 delay 为0或负数导致无限等待

  android      int key = cv::waitKey(delay); // 等待约 30ms,或根据FPS计算

        if (key == 27) { // ESC 键的 ASCII 值
            std::cout << "ESC键被按下,正在关闭..." << std::endl;
            break;
        } else if (key == 's' || key == 'S') { // 示例:按 's' 保存当前帧
            std::string filename = "captured_frame.png";
            cv::imwrite(filename, frame);
            std::cout << "当前帧已保存为 " << filename << std::endl;
        }
    }

    // 4. 释放资源
    cap.release();
    cv::destroyAllWindows();

    return 0;
}

编译和运行 (以 g++ 为例):

假设你的 OpenCV 安装在标准路径,并且你已经设置了 pkg-config:

g++ your_code.cpp -o camera_app `pkg-config --cflags --libs opencv4`
./camera_app

如果未使用 pkg-config,你可能需要手动指定包含目录和库文件:

g++ your_code.cpp -o camera_app -I/path/to/opencv/include -L/path/to/opencv/lib -lopencv_core -lopencv_highgui -lopencv_videoio -lopencv_imgproc
./camera_app

(具体的库名称可能因 OpenCV 版本和模块而略有不同,如 opencv_videoio 是处理视频 I/O 的关键库)。

常见问题与调试技巧

  • 无法打开摄像头:
    • 确保摄像头已连接并且驱动程序已正确安装。
    • 检查是否有其他应用程序正在使用该摄像头。
    • 尝试不同的 cameraIndex (0, 1, 2, …)。
    • linux 上,检查 /dev/video* 设备文件是否存在以及你是否有权限访问它们。
  • 视频流卡顿或延迟:
    • 确保 cv::waitKey() 的延迟参数设置合理。太小的值可能导致 CPU 占用过高,太大的值则导致卡顿。
    • 图像处理步骤如果过于复杂,会增加每帧的处理时间。
  • 窗口不显示或一闪而过:
    • 确保在主循环之后调用 cv::destroyAllWindows(),并且 cv::waitKey() 在循环内部被正确调用以刷新窗口事件。
  • 帧为空 (frame.empty() 为 true):
    • 这可能发生在视频文件结束,或者摄像头出现问题时。

到此这篇关于C/C++和OpenCV实现调用摄像头的文章就介绍到这了,更多相关C++ OpenCV调用摄像头内容请搜索China编程(www.chinasem.cn)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程China编程(www.chinasem.cn)!

这篇关于C/C++和OpenCV实现调用摄像头的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1154876

相关文章

C/C++ chrono简单使用场景示例详解

《C/C++chrono简单使用场景示例详解》:本文主要介绍C/C++chrono简单使用场景示例详解,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友... 目录chrono使用场景举例1 输出格式化字符串chrono使用场景China编程举例1 输出格式化字符串示

C#代码实现解析WTGPS和BD数据

《C#代码实现解析WTGPS和BD数据》在现代的导航与定位应用中,准确解析GPS和北斗(BD)等卫星定位数据至关重要,本文将使用C#语言实现解析WTGPS和BD数据,需要的可以了解下... 目录一、代码结构概览1. 核心解析方法2. 位置信息解析3. 经纬度转换方法4. 日期和时间戳解析5. 辅助方法二、L

C++/类与对象/默认成员函数@构造函数的用法

《C++/类与对象/默认成员函数@构造函数的用法》:本文主要介绍C++/类与对象/默认成员函数@构造函数的用法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录名词概念默认成员函数构造函数概念函数特征显示构造函数隐式构造函数总结名词概念默认构造函数:不用传参就可以

C++类和对象之默认成员函数的使用解读

《C++类和对象之默认成员函数的使用解读》:本文主要介绍C++类和对象之默认成员函数的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、默认成员函数有哪些二、各默认成员函数详解默认构造函数析构函数拷贝构造函数拷贝赋值运算符三、默认成员函数的注意事项总结一

使用Python和Matplotlib实现可视化字体轮廓(从路径数据到矢量图形)

《使用Python和Matplotlib实现可视化字体轮廓(从路径数据到矢量图形)》字体设计和矢量图形处理是编程中一个有趣且实用的领域,通过Python的matplotlib库,我们可以轻松将字体轮廓... 目录背景知识字体轮廓的表示实现步骤1. 安装依赖库2. 准备数据3. 解析路径指令4. 绘制图形关键

Python中OpenCV与Matplotlib的图像操作入门指南

《Python中OpenCV与Matplotlib的图像操作入门指南》:本文主要介绍Python中OpenCV与Matplotlib的图像操作指南,本文通过实例代码给大家介绍的非常详细,对大家的学... 目录一、环境准备二、图像的基本操作1. 图像读取、显示与保存 使用OpenCV操作2. 像素级操作3.

C/C++中OpenCV 矩阵运算的实现

《C/C++中OpenCV矩阵运算的实现》本文主要介绍了C/C++中OpenCV矩阵运算的实现,包括基本算术运算(标量与矩阵)、矩阵乘法、转置、逆矩阵、行列式、迹、范数等操作,感兴趣的可以了解一下... 目录矩阵的创建与初始化创建矩阵访问矩阵元素基本的算术运算 ➕➖✖️➗矩阵与标量运算矩阵与矩阵运算 (逐元

C/C++的OpenCV 进行图像梯度提取的几种实现

《C/C++的OpenCV进行图像梯度提取的几种实现》本文主要介绍了C/C++的OpenCV进行图像梯度提取的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的... 目录预www.chinasem.cn备知识1. 图像加载与预处理2. Sobel 算子计算 X 和 Y

c/c++的opencv图像金字塔缩放实现

《c/c++的opencv图像金字塔缩放实现》本文主要介绍了c/c++的opencv图像金字塔缩放实现,通过对原始图像进行连续的下采样或上采样操作,生成一系列不同分辨率的图像,具有一定的参考价值,感兴... 目录图像金字塔简介图像下采样 (cv::pyrDown)图像上采样 (cv::pyrUp)C++ O

c/c++的opencv实现图片膨胀

《c/c++的opencv实现图片膨胀》图像膨胀是形态学操作,通过结构元素扩张亮区填充孔洞、连接断开部分、加粗物体,OpenCV的cv::dilate函数实现该操作,本文就来介绍一下opencv图片... 目录什么是图像膨胀?结构元素 (KerChina编程nel)OpenCV 中的 cv::dilate() 函