使用OpenCV播放视频几乎与显示单张图片一样简单。唯一需要改变的是需要某种循环来依次读取每一帧。
例3. OpenCV显示视频文件
#include "opencv2/highgui/highgui.hpp"#include "opencv2/imgproc/imgproc.hpp"using namespace cv;int main(int argc, char** argv){namedWindow("例子3", 0);VideoCapture cap;cap.open("E:/ 1.mp4");Mat frame; for (;;){cap >> frame;if (frame.empty()) break;imshow("例子3", frame);if (waitKey(33) >= 0)break;}return 0;}
首先创建一个显示窗口 ("例子3")。 然后实例化视频捕获对象VideoCapture。 该对象可以打开和关闭ffmpeg支持的许多类型的视频文件。
cap.open(); 可以传入视频文件名称。
Mat frame;这条语句定义一个Mat对象,用于显示每一帧视频。
capture对象被赋予一个包含要打开的视频的路径和文件名的字符串。 一旦打开,将包含有关正在读取的视频文件的所有信息,VideoCapture对象被初始化为视频的开头。在该程序中。
cap >> frame; 利用循环一次读取每一帧到图像矩阵变量Mat中。
if( frame.empty() ) 检查是否为空
break;
一旦进入循环,将从捕获对象流中逐帧读取视频文件。如果视频帧已成功读入,则通过imshow()显示。
if( cv::waitKey(33) >= 0 ) break;
一旦显示了帧图像,就等待33 ms。如果用户在这段时间内点击某个键,将退出读取循环。 否则,33毫秒会通过,将再次执行循环。 退出时,所有分配的数据在超出范围时自动释放。
例3中的视频播放器用户无法在视频中快速移动的。 下面我们添加一个滑动条,还可以允许用户通过按S键对视频进行单步操作,并通过按R键进入运行模式,并且只要用户通过滑动条跳转到视频中的新位置,我们会在单步模式下暂停。
HighGUI工具包提供了许多用于处理图像和视频的简单工具,超出了刚刚演示的简单功能。一个特别有用的是滑动条,它使用户能够轻松地从视频的一部分跳到另一部分视频。 要创建一个滑动条,需要调用createTrackbar()并指出希望滑动条出现在哪个窗口中。为了获得所需的功能,需要一个将执行重定位的回调。 例4是具体的实现方法。
例4。 在窗口中添加一个滑动条,以便在视频文件中移动,通过点击"S"进行单步操作,通过点击"R"连续播放
#include "opencv2/highgui/highgui.hpp"#include "opencv2/imgproc/imgproc.hpp"using namespace std;using namespace cv;int g_slider_position = 0;int g_run = 1, g_dontset = 0;VideoCapture g_cap;void onTrackbarSlide(int pos, void *){g_cap.set(CAP_PROP_POS_FRAMES, pos);if (!g_dontset)g_run = 1;g_dontset = 0;}int main(int argc, char** argv){namedWindow("例4", 0);g_cap.open("E:/1.mp4");int frames = (int)g_cap.get(CAP_PROP_FRAME_COUNT);int width = (int)g_cap.get(CAP_PROP_FRAME_WIDTH);int height = (int)g_cap.get(CAP_PROP_FRAME_HEIGHT);createTrackbar("Position", "例4", &g_slider_position, frames, onTrackbarSlide);Mat frame;for (;;){if (g_run != 0){g_cap >> frame;if (frame.empty()) break;int current_pos = (int)g_cap.get(CAP_PROP_POS_FRAMES);g_dontset = 1;setTrackbarPos("Position", "例4", current_pos);imshow("例4", frame);g_run -= 1;}char c = (char)waitKey(10);if (c == 's') // 单步{g_run = 1;}if (c == 'r') // 连续{g_run = -1;}if (c == 27)break;}return(0);}
上面是添加一个全局变量来表示滑动条的位置,然后添加一个回调来更新此变量并重新定位视频中的读取位置。看看具体的细节。
int g_slider_position = 0;
int g_run = 1;
int g_dontset = 0;
VideoCapture g_cap;
首先定义一个全局变量g_slider_position,表示滑动条的位置状态。 回调将需要访问捕获对象g_cap,所以也将其定义为全局变量。将全局变量前面加g_是一个习惯。另一个全局变量g_run显示新的帧。 正数表示停止前显示的帧数; 负数表示系统以连续视频模式运行。
为了避免混淆,当用户点击滑动条跳转到视频中的新位置时,通过设置g_run = 1将视频暂停在单步状态。但是,有一个问题,随着视频的播放,希望滑块轨迹条在显示窗口中的位置根据视频中的位置前进。这可以通过让主程序在每次获得新帧时调用trackbar回调函数来更新滑动条的位置。 但是,又不希望通过调用trackbar回调来进入单步模式。 为了避免这种情况,引入了最终的全局变量g_dontset,以允许在不触发单步模式的情况下更新滑动条的位置。
这里定义一个回调,供用户拖动滑动条时使用。 这个回调传递一个32位整数pos,得到新的trackbar位置。 在此回调中,使用g_cap.set()中的新位置将视频播放切换到新位置。 if()语句将程序设置为在下一次进入后进入单步模式,但前提是该回调是由用户单击触发的,而不是从主函数中调用。
g_cap.set()和g_cap.get()在视频操作中经常会看到的。 这允许我们配置VideoCapture对象的各种属性。 在这种情况下,传递参数: CAP_PROP_POS_FRAMES,这表示希望以帧为单位设置读取位置。
int frames = (int) g_cap.get(CAP_PROP_FRAME_COUNT);
int width = (int) g_cap.get(CAP_PROP_FRAME_WIDTH);
int height = (int) g_cap.get(cvCAP_PROP_FRAME_HEIGHT);
例4与例3的核心内容相同。区别是添加了滑动条来控制视频的播放。createTrackbar("Position", "例4", &g_slider_position, frames, onTrackbarSlide);
函数createTrackbar()还允许给滑动条一个标签并指定放置位置的窗口,提供一个绑定到滑动条的变量,滑动条的最大值(视频中的帧数)和一个回调函数(如果不需要,则返回NULL)。
在循环中,除了读取和显示视频帧外,还获取视频中的当前位置,设置g_dontset,以便下一次回调不会进入单步模式,然后调用回调更新显示给用户的滑动条的位置。 全局g_run递减,其效果是让其保持单步模式,或者让视频依赖于用户按键设置的状态运行。
在循环的底部,判断用户的键盘输入。 如果"S"被按下,进入单步模式(g_run被设置为1,这允许读取单个帧)。 如果按下"R",会进入连续模式(g_run设置为-1)。 最后,如果Esc被按下,程序将终止。如果你刚好有一些视频文件,可以按照上面的例子试运行一下,看是否达到了上面提到的效果。