3:OpenCV—视频播放

发布于:2025-05-19 ⋅ 阅读:(18) ⋅ 点赞:(0)

播放来自文件或相机的视频

在本教程中,我将向您展示如何使用OpenCV从文件或相机/网络摄像头捕获和播放视频。尽管OpenCV没有针对视频处理进行优化,但它提供了一个简单的API来播放视频。在我们的OpenCV程序中,我们所要做的就是从视频文件或相机中提取帧(图像)并在连续循环中显示。

OpenCV与视频的一个限制是它无法捕获视频中的任何声音。因此,您必须使用另一个库(如 FFmpeg)从视频文件或麦克风捕获声音。

播放视频文件

在本节中,我将向您展示如何播放视频文件。首先,应通过将有效位置传递给视频文件来构造 VideoCapture 对象。然后,应逐帧读取视频捕获对象。最后,这些帧应按顺序显示在窗口中。以下是执行上述任务的示例 OpenCV 代码。


#include <iostream>
#include <opencv2\opencv.hpp>
using namespace cv;
using namespace std;
int main()
{
    // 从一个媒体文件读取并创建VideoCapture对象
    VideoCapture cap("D:\\project\\vs-opencv\\OpenCVDemo2\\x64\\Debug\\点云应用.mp4");
    // 判断是成功打开
    if (!cap.isOpened())
    {
        cout << "读取视频失败";
        cin.get();
        return -1;
    }

    // 获取总fps
    double fps = cap.get(CAP_PROP_FPS);
    cout << "每秒帧率为:" << fps << endl;

    // 创建窗体用于播放视频
    String videoWinName = "播放视频";
    namedWindow(videoWinName, WINDOW_NORMAL);

    // 开始循环读取每一帧进行窗体
    while (true)
    {
        Mat frame;
        bool isSuccess = cap.read(frame);
        // 判断是否读取成功
        if (!isSuccess)
        {
            cout << "读取视频贞失败";
            break;
        }

        // 显示视频贞
        imshow(videoWinName, frame);

        // 按ESC键结束播放
        if (waitKey(10) == 27)
        {
            cout << "按下了ESC键,停止播放视频";
            break;
        }
    }
    return 0;
}

将上述简单代码片段复制并粘贴到 IDE 中并运行它。请注意,您必须将代码中的“D:/My OpenCV website/一群鹿奔跑.mp4”替换为计算机中视频的有效位置。然后,您应该会看到如下图所示的窗口。

解释

让我们逐行浏览上面的代码。


//#include "stdafx.h"

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

using namespace cv;
using namespace std;

这些是我们程序中使用的包含文件和命名空间。

VideoCapture cap("C:/Users/Desktop/点云应用.mp4"); 

这是 VideoCapture 类中为数不多的构造函数之一。此构造函数将打开视频文件并初始化 VideoCapture 对象,以便从指定文件中读取视频流。

此类的析构函数将取消分配与打开的视频文件关联的任何内存。因此,无需在程序中显式取消分配内存。
如果上一次对 VideoCapture 构造函数的调用成功,cap.isOpened() 函数将返回 true。否则它将返回 false。如果函数返回 false,程序将向控制台打印一条消息,等待任何按键并以退出代码 -1 退出程序。

在播放视频之前,必须检查 VideoCapture 对象是否已成功初始化,以避免可能的崩溃。


if (cap.isOpened() == false)  
{
 cout << "Cannot open the video file" << endl;
 cin.get(); //wait for any key press
 return -1;
}
//cap.set(CAP_PROP_POS_MSEC, 300); 

如果您想在中间开始视频,您可以取消注释此行。您也可以在 while 循环中调用此函数。

bool VideoCapture::set**(int propId, double value)**

可以使用 VideoCapture::set 函数更改 VideoCapture 对象的某些属性。对于支持的属性,此函数将返回 true。否则它将返回 false。

  1. propID - 要更改的 VideoCapture 对象的属性。可以更改的最常见属性是

    • CAP_PROP_POS_MSEC - 视频文件的当前位置(以毫秒为单位)
  • CAP_PROP_POS_FRAMES - 接下来要捕获的帧的相对位置(从 0 开始,例如 - 0、1、2、3 等)
  1. value - 指定属性的新值

ouble fps = cap.get(CAP_PROP_FPS); 
cout << "Frames per seconds : " << fps << endl;

这2行将获取视频的帧率并在控制台中打印。您也可以在 while 循环中调用此函数。

double VideoCapture::**get(int propId) const**

此函数返回 VideoCapture 对象的指定属性的值。对于不受支持的属性,此函数将返回 0。

  1. propId - 此参数指定要获取的 VideoCapture 对象的属性。最常见的属性是

    • CAP_PROP_POS_MSEC - 视频文件的当前位置(以毫秒为单位)
  • CAP_PROP_POS_FRAMES - 接下来要捕获的帧的相对位置(从 0 开始,例如 - 0、1、2、3 等)
  • CAP_PROP_FRAME_WIDTH - 视频中帧的宽度
  • CAP_PROP_FRAME_HEIGHT - 视频中帧的高度
  • CAP_PROP_FPS - 视频的帧速率
  • CAP_PROP_FOURCC - 编解码器的4个字符的代码
  • CAP_PROP_FRAME_COUNT - 视频文件中的总帧数

tring window_name = "My First Video";
namedWindow(window_name, WINDOW_NORMAL); //create a window

第一行指定窗口的名称。此名称稍后将用于标识代码中的窗口。

第二行将创建一个传递窗口名称和标志WINDOW_NORMAL的窗口。使用此标志,创建的窗口将变得可重新调整大小。

第二行将在while循环中逐个读取视频文件中的帧并将其存储在*帧*中。


Mat frame;
bool bSuccess = cap.read(frame); // read a new frame from video

if (bSuccess == false) 
{
 cout << "Video is completed" << endl;
 break;
}

当没有更多的帧要从视频文件中捕获时,*cap.read(frame)* 函数将返回 false。放置此 if 块以在 bSuccess 变为 false 时中断视频文件末尾的 while 循环。
此行将在上一步中创建的窗口中显示视频文件的每一帧。

此函数后应跟 waitKey(int) 函数,以便提供足够的时间在指定的持续时间(以毫秒为单位)内在窗口中绘制和显示帧。如果不调用waitKey(int)函数,则框架将不会显示在窗口中。

waitKey 函数等待 10 毫秒。如果在指定时间之前按下了某个键,它将返回按下的键的 ASCII 值。如果该值为 27(Esc 键的 ASCII 值为 27),程序将在 if 块内执行并中断 while 循环。如果在 10 毫秒内没有按下任何键,该函数将返回 -1,程序将继续 while 循环。


imshow(window_name, frame);
if (waitKey(10) == 27)
{
 cout << "Esc key is pressed by user. Stoppig the video" << endl;
 break;
}
总结

在上面的部分中,您已经了解到,
- 如何从文件加载视频
- 如何逐个读取视频的每一帧,并在一段时间循环中将其显示在窗口中
- 如何打破视频末尾的while循环
- 如果在视频结束前按下特定键,如何中断 while 循环

从相机/网络摄像头捕获和播放视频

在本节中,将学习如何从连接到个人计算机的相机/网络摄像头捕获和播放视频。

该程序与上述程序的主要区别在于使用VideoCapture构造函数。在此程序中,只需要将相机/网络摄像头的索引提供给 VideoCapture 对象的构造函数,而不是视频文件名。这是示例 OpenCV 代码。


#include <iostream>
#include <opencv2\opencv.hpp>
using namespace cv;
using namespace std;
int main()
{
    // 从一个媒体文件读取并创建VideoCapture对象
    VideoCapture cap(0);
    // 判断是成功打开
    if (!cap.isOpened())
    {
        cout << "读取视频失败";
        cin.get();
        return -1;
    }

    // 获取总fps
    double fps = cap.get(CAP_PROP_FPS);
    cout << "每秒帧率为:" << fps << endl;

    double dWidth = cap.get(CAP_PROP_FRAME_WIDTH); //获取贞的宽度
    double dHeight = cap.get(CAP_PROP_FRAME_HEIGHT); //获取贞高度

    cout << "贞的分辨率 : " << dWidth << " x " << dHeight << endl;

    // 创建窗体用于播放视频
    String videoWinName = "实时采集图像";
    namedWindow(videoWinName, WINDOW_NORMAL);

    // 开始循环读取每一帧进行窗体
    while (true)
    {
        Mat frame;
        bool isSuccess = cap.read(frame);
        // 判断是否读取成功
        if (!isSuccess)
        {
            cout << "读取视频贞失败";
            break;
        }

        // 显示视频贞
        imshow(videoWinName, frame);

        // 按ESC键结束播放
        if (waitKey(10) == 27)
        {
            cout << "按下了ESC键,停止图像采集";
            break;
        }
    }
    return 0;
}
解释
VideoCapture cap(0);

此行将使用默认相机和默认后端初始化 VideoCapture 对象。

VideoCapture**::VideoCapture(int index)**

这是 VideoCapture 类中为数不多的构造函数之一。此构造函数初始化并打开参数索引指定的相机。

可以为参数索引传递 0 以使用连接到计算机的默认相机。如果您的计算机连接到 1 台以上的相机,则可以使用正整数作为索引。

此类的析构函数将取消分配与此对象关联的任何内存。因此,无需在程序中显式取消分配内存。


ouble dWidth = cap.get(CV_CAP_PROP_FRAME_WIDTH); //get the width of frames of the video
double dHeight = cap.get(CV_CAP_PROP_FRAME_HEIGHT); //get the height of frames of the video
cout << "Resolution of the video : " << dWidth << " x " << dHeight << endl;

第一行将获得相机输出帧的宽度(以像素为单位)。下一行将获得相机输出帧的高度。然后它打印相机输出的分辨率,即(宽 x 高)。
所有其他代码行与播放视频文件的第一个程序相同。


网站公告

今日签到

点亮在社区的每一天
去签到