例子来源于Learning OpenCV 3
// video.cpp
#include <iostream>
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
int main( int argc, char **argv) {
// 创建了一个名为video的窗口用来显示帧
cv::namedWindow("video", cv::WINDOW_AUTOSIZE);
cv::VideoCapture cap;
// 读取视频文件
cap.open(std::string(argv[1]));
cv::Mat frame;
while (true) {
//std::cout << "display..." << std::endl;
// 按帧读取
cap >> frame;
if (frame.empty())
break;
cv::imshow("video", frame);
if (cv::waitKey(33) >= 0)
break;
}
//std::cout << "End..." << std::endl;
return 0;
}
程序首先创建一个播放窗口:
cv::namedWindow("video", cv::WINDOW_AUTOSIZE);
其原型如下:
void cv::namedWindow ( const String & winname, int flags = WINDOW_AUTOSIZE )
第一个参数是自定义的窗口名称,相当于是窗口ID,可通过这个ID来调用该窗口。如果已有同名窗口存在,则该函数不起作用。
第二个参数是标志位。WINDOW_AUTOSIZE
表示窗口自适应图片尺寸,不可手动调整。
其他可用标志有:WINDOW_NORMAL
:允许用户调整窗口大小。(不过我发现把当前正在运行的窗口关闭,如果视频文件未播放完毕,就会新打开一个窗口接着播放,此时窗口已无法再调整大小,目前暂不清楚原因)WINDOW_FREERATIO
:调整窗口时允许图片不按原比例缩放WINDOW_KEEPRATIO
:调整窗口大小时,图片按原比例缩放WINDOW_GUI_NORMAL
:不带状态栏和工具栏的普通窗口WINDOW_GUI_EXPANDED
:带状态栏和工具栏的扩展窗口
默认情况下,标志位的值为WINDOW_AUTOSIZE|WINDOW_KEEPRATIO|WINDOW_GUI_EXPANDED
接着实例化一个VideoCapture
对象读取视频
cap.open(std::string(argv[1]));
函数原型
virtual bool cv::VideoCapture::open ( const String & filename )
该方法会先调用VideoCapture::release
关闭已打开的文件。
最后显示读取的图片
cv::imshow("video", frame);
函数原型
void cv::imshow ( const String & winname, InputArray mat )
第一个参数为所要播放图片的窗口的ID,对应前面cv::namedWindow()
指定的窗口ID。如果该窗口没有被创建,则创建一个带WINDOW_AUTOSIZE
标志的窗口。
第二个参数是所要播放的图片对象,对应这个例子里视频的帧
需要注意的是,要使imshow()
能够显示出帧,还必须添加cv::waitKey()
函数,其函数原型为:
int cv::waitKey ( int delay = 0 )
参数delay表示等待时长,如果该参数大于0,表示等待时长不小于delay毫秒,如果没有键盘时间发生;如果参数小于0,则函数会一直等待直至有键盘事件发生。这里设置延时33ms(播放似乎有些卡顿?修改成10ms似乎也是如此?待后续考查。)如果当前没有已激活的窗口,则该函数不起作用。
下面对上述例子进行修改,尝试给视频添加进度条,并允许用户暂停和播放视频
#include <iostream>
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
cv::VideoCapture g_cap;
// 记录当前已播放的帧数,显示为进度条当前位置。
int g_slider_position = 0;
// g_run用来记录和控制播放状态
int g_run = 1;
int g_dontset = 0; //start out in single step mode
void onTrackbarSlide(int pos, void *) {
g_cap.set(cv::CAP_PROP_POS_FRAMES, pos);
// 如果用户手动调节了进度条,则播放模式置为暂停
if (!g_dontset) {
g_run = 1;
}
g_dontset = 0;
}
int main(int argc, char **argv) {
cv::namedWindow("video", cv::WINDOW_AUTOSIZE);
//cv::VideoCapture cap;
g_cap.open(std::string(argv[1]));
int frames = (int)g_cap.get(cv::CAP_PROP_FRAME_COUNT);
int tmpw = (int)g_cap.get(cv::CAP_PROP_FRAME_WIDTH);
int tmph = (int)g_cap.get(cv::CAP_PROP_FRAME_HEIGHT);
std::cout << "Video has " << frames << " frames of dimensions("
<< tmpw << ", " << tmph << ")." << std::endl;
cv::createTrackbar("Position", "video", &g_slider_position,
frames, onTrackbarSlide);
cv::Mat frame;
while (true) {
//cap >> frame;
//if (frame.empty())
// break;
//cv::imshow("video", frame);
//if (cv::waitKey(33) >= 0)
// break;
if (g_run != 0) {
int cur_pos;
g_cap >> frame;
if (frame.empty())
break;
cur_pos = (int)g_cap.get(cv::CAP_PROP_POS_FRAMES);
g_dontset = 1;
cv::setTrackbarPos("Position", "video", cur_pos);
cv::imshow("video", frame);
g_run -= 1;
}
char c = (char)cv::waitKey(10);
if (c == 's') { // single step
g_run = 1;
std::cout << "Single step, run = " << g_run << std::endl;
}
if (c == 'r') { // run mode
g_run = -1;
std::cout << "Run mode, run = " << g_run << std::endl;
}
if (c == 27)
break;
}
std::cout << "End..." << std::endl;
return 0;
}
和上一个例子一样,程序首先创建了名为 video 的窗口,用来播放视频,接着读入视频文件。通过调用get()
来获取视频文件的属性数据,其函数原型为:
virtual double cv::VideoCapture::get ( int propId) const
传入参数为相关属性标识符,支持获取的属性列表可以点击这里;这里分别获取了视频包含的总帧数cv::CAP_PROP_FRAME_COUNT
、每一帧的宽度cv::CAP_PROP_FRAME_WIDTH
及高度cv::CAP_PROP_FRAME_HEIGTH
。
类似的还有上方onTrackbarSlide函数中的set(int propId, double value)
,用于设定指定属性的值。
在当前窗口创建名为Position
的进度条:
cv::createTrackbar("Position", "video", &g_slider_position,
frames, onTrackbarSlide);
其函数原型为:
int cv::createTrackbar (
const String &trackbarname,
const String &winname,
int *value,
int count,
TrackbarCallback onChange = 0,
void *userdata = 0
)
trackbarname
指定创建的进度条的名称,同窗口的ID一样,可以通过指定进度条名称来指定要操作的进度条。
winname
指定进度条所在的窗口ID。
value
指定滑块的初始位置。
count
进度条的最大位置,这里为传入的视频的总帧数(最小位置总是为0)。
onChange
回调函数,一旦滑块的位置发生变化,就会调用回调函数。该回调函数的原型必须是void Foo(int, void*),其第一个参数为当前滑块位置,第二个参数为userdata。该参数可以为NULL,表示没有回调函数被调用。这里的回调函数为onTrackbarSlide。
userdata
用户传给回调函数的值,默认为0。
做完上面这些工作,终于可以开始读取视频了!大致的操作跟第一个例子差不多,只不过需要同步进度和已播放的帧数。
cur_pos = (int)g_cap.get(cv::CAP_PROP_POS_FRAMES);
这里又一次调用get(int proId)
来获取当前帧的索引值,并用该值设置滑块的位置:
cv::setTrackbarPos("Position", "video", cur_pos);
参数分别为进度条ID、窗口ID和当前位置的值。
通过waitKey()
返回值判断当前是否需要暂停、播放和退出。
效果大概酱: