《学习OpenCV 3(中文版)》 —跳转
跳转
为了强化前面这个简单的程序以及发现更多有用的函数,现在是时候反思一下了。我们也许已经注意到示例2-3中的视频播放器不可以快速在视频中进行跳转。所以我们下一个
注4: 你也许想要定义其他时间长度,在这个情况下,我们简单说明33毫秒是因为这能让视频以30FPS的速率播放,并且能够允许用户在播放的时候打断。根据以往的经验,最好检查 cv::VideoCapture结构来确定视频真正的帧率(更多详情请参阅第8章)。任务就是添加一个滑动条,使得用户可以进行视频跳转。为了执行更多的控制,我们将会允许用户通过按下S键来执行单步模式,以及按下R键来恢复连续视频播放模式,无论何时用户通过滑动条跳转到视频一个新的时间点,我们都会使用单步模式在那个点进行播放。
HighGUI工具提供了许多简单的工具用于处理视频和图像,而不仅仅是我们刚才演示的那些功能。一个特别有用的工具就是我们前面所提到的滑动条,它能够使用户轻松从视频的一个部分跳转到另一个部分。要创建一个滑动条,我们会调用cv::createTrackbar() 并指明要在哪个窗口中进行显示,为了完成所需要的功能,我们还需要一个能够执行重新定位的回调函数,示例2-4给出了代码表述。
示例2-4:加入了滑动条的基本浏览窗口
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp" #include <iostream> #include <fstream> using namespace std;
int g_slider_position = 0;
int g_run = 1, g_dontset = 0; //start out in single step mode cv::VideoCapture g_cap; 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( "Example2_4", cv::WINDOW_AUTOSIZE ); g_cap.open( 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); cout << "Video has " << frames << " frames of dimensions("
<< tmpw << ", " << tmph << ")." << endl;
cv::createTrackbar("Position", "Example2_4", &g_slider_position, frames, onTrackbarSlide);
cv::Mat frame; for(;;) { if( g_run != 0 ) {
g_cap >> frame; if(frame.empty()) break;
int current_pos = (int)g_cap.get(cv::CAP_PROP_POS_FRAMES); g_dontset = 1;
cv::setTrackbarPos("Position", "Example2_4", current_pos); cv::imshow( "Example2_4", frame ); g_run-=1; }
char c = (char) cv::waitKey(10); if( c == 's' ) // single step
{g_run = 1; cout << "Single step, run = " << g_run << endl;} if( c == 'r' ) // run mode
{g_run = -1; cout << "Run mode, run = " << g_run <<endl;} if( c == 27 ) break;
} return(0);
}
本质上,代码中添加一个全局变量来表示滑动条的位置并且添加一个回调函数来更改这个变量来重新定位视频读取的位置。在创建滑动条和回调函数之后,我们就开始运行程序,注5让我们从全局变量开始更详细地查看整个程序。
int g_slider_position = 0; int g_run = 1;
int g_dontset = 0; // start out in single-step mode VideoCapture g_cap;
在程序的开始处,我们定义了一个全局变量g_slider_position来存储滑动条位置。由于回调函数需要访问帧读取结构g_cap,所以我们最好将这个结构也提升为全局变量。为了保证代码的易读性。我们会在每一个全局变量前加上g_前缀以表明这是一个全局变量。同理,全局变量g_run将在新的跳转触发之后置0。当它为正的时候,指示在停止之前程序要播放多少张图片;当它为负的时候,表示系统处于连续视频播放模式。
为了防止冲突,在用户单击了滑动条跳转到一个视频的新位置之后,我们将会通过设置 g_run变量为1使视频进入单步模式。即使是这样,也存在一个小问题:当视频前进的时候,我们希望滑动条也能够随着视频的当前进度前进。我们通过在程序中调用滑动条的回调函数实现这一功能。但是,我们并不希望在这个时候进入单步模式。为了避免这样的事情发生,我们引入最后一个全局变量g_dontset来避免在调整进度条位置的时候触发单步模式。
注5: 注意,有一些AVI格式和MPEG格式的文件不允许在视频中向后跳转。
void onTrackbarSlide(int pos, void *) { g_cap.set(cv::CAP_PROP_POS_FRAMES, pos);
if( !g_dontset ) g_run = 1; g_dontset = 0;
}
现在,定义一个用户调整滑动条的时候执行的回调程序。这个程序将会传入一个32位的整型数值以表示当前的位置。这将会是进度条存在的新的位置。在回调函数的内部,我们在新的位置通过调用g_cap.set()来真正使进度条移到我们希望的位置。if()语句设置程序是否进入单步模式。只有在用户触发滑动条事件的时候,这个设置才会生效;在系统自动调用该回调函数的时候,单步模式不会生效。
调用g_cap.set()是我们以后经常使用的一个操作,它通常配合g_mcap.get()使用。这些程序允许我们配置(或是询问,这将在稍后介绍)cv::VideoCapture结构中的许多变量。在本例中,我们输入参数CV::CAP_PROP_POS_FRAMES,这个参数指示我们想要帧集合的读取位置。注6
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); cout << "Video has " << frames << " frames of dimensions(" << tmpw << ", " << tmph << ")." << endl;
主程序的核心和示例2-3一致,所以我们将把重点放在我们添加了什么上面。第一个不同是在打开视频之后我们用g_cap.get()来确定总帧数以及视频的高和宽。这些数字打印出来之后,我们需要这些视频中和帧有关的信息来校准滑动条(在下一步)。
createTrackbar("Position", "Example2_4", &g_slider_position, frames, onTrackbarSlide);
下一步我们将创建一个滑动条,这个函数cv::createTrackbar()允许我们给滑动条一个标签注7(在本例中,是Position)并且指明在哪一个窗口放置我们的滑动条。我们提供一个和滑动条绑定的变量——滑动条能够达到的最大值(视频的总帧数)以及一个在滑动条移动时候的回调函数(不需要的时候也可以输入NULL)。
if( g_run != 0 ) {
注6: 因为HighGUI的代码是高度自动化的,所以当一个新的视频位置被请求的时候,将自动处理一些(诸如请求的帧并非是一个关键帧等)问题。如果遇到这种情况,它会从请求位置之前的一个关键帧开始播放并且快进到请求的位置,免得我们考虑这些繁琐的问题。
g_cap >> frame; if(!frame.data) break; int current_pos = (int)g_cap.get(cv::CAP_PROP_POS_FRAMES); g_dontset = 1;
cv::setTrackbarPos("Position", "Example2_4", current_pos); cv::imshow( "Example2_4", frame ); g_run-=1;
}
在while循环中,为了读取和显示视频中的帧,我们还会获取在视频中的当前位置,设置g_dontset使下一个滑动条的回调函数不会让我们进入单步模式,之后我们将会委托滑动条的回调函数来更新滑动块在滑动条上的位置以便显示给用户。全局变量g_run将会减1,它的效果是让我们保持单步模式或者依据用户的设置让视频正常播放。接下来我们将会看到以下结构:
char c = (char) cv::waitKey(10); if( c == 's' ) // single step
{g_run = 1; cout << "Single step, run = " << g_run << endl;} if( c == 'r' ) // run mode
{g_run = -1; cout << "Run mode, run = " << g_run <<endl;} if( c == 27 ) break;
在while循环的底部,我们会等待用户的键盘输入。如果S键被按下,我们将会进入单步模式(g_run将会被设置为1,会使得程序只读取一张图片)。如果R被按下,我们将会进入连续视频模式(g_run将会被设置为-1,之后进一步递减使其对于之后任意帧都为负)。最后,如果Esc被按下,程序将会终止。再次提醒你注意,为了让代码更简洁,我们略过了回收窗口垃圾这一步cv::destroyWindow()。
- 点赞
- 收藏
- 关注作者
评论(0)