OpenCV读取视频帧

例子来源于Learning OpenCV 3

// video.cpp
#include 

#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 

#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()返回值判断当前是否需要暂停、播放和退出。

  效果大概酱:
OpenCV读取视频帧_第1张图片

你可能感兴趣的:(opencv,opencv)