添加以下三个功能:
1、在第二个程序上添加一个滑块轨迹条(播放进度条);
2、用户按下S则进入single-step模式,按下R则正常播放;
3、每当用户使用轨迹条跳转到视频中的新位置时,我们将在single-step模式中暂停。
HighGUI工具包提供了许多用于处理图像和视频的简单工具,超出了我们刚刚演示的简单显示功能。
一个特别有用的机制是前面提到的轨迹条,它使用户能够轻松地从视频的一部分跳到另一部分。
我们调用cv::createTrackbar()创建一个轨迹条,并指定轨迹条的出现窗口。
本质上,策略是添加一个全局变量来表示轨迹栏位置,然后添加一个回调来更新此变量并重新定位视频中的读取位置。
当用户点击轨迹条跳到视频中的新位置时,通过设置g_run = 1使视频以single-step模式暂停。
随着视频的进展,我们希望滑块轨迹条在显示窗口中的位置根据我们在视频中的位置前进。
我们通过让主程序在每次获得新视频帧时调用trackbar回调函数来更新滑块位置来实现这一点。
但是,我们不希望通过这些编程式调用trackbar回调来使我们进入单步模式。
为了避免这种情况,我们引入了最终的全局变量g_dontset,以允许我们在不触发单步模式的情况下更新轨迹条的位置。
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <fstream>
// #include <string>
using namespace std;
using namespace cv;
int g_slider_position = 0; // 保持轨迹条的位置状态
int g_run = 1, g_dontset = 0; // 只要g_run不是0,就显示新的帧,正数表示停止前显示的帧数,负数表示系统以连续视频模式运行
VideoCapture g_cap; // 回调函数需要访问视频捕捉对象,所以这个也设置为全局变量
void on_trackbar_slide(int pos, void *); // 回调函数,为了获得所需的功能,我们需要一个回调函数来执行重定位
int main(int argc, char ** argv)
{
namedWindow("Example2-4", WINDOW_AUTOSIZE);
g_cap.open(static_cast<string>(argv[1]));
// int frames = static_cast<int>(g_cap.get(CAP_PROP_FRAME_COUNT));
int frames = static_cast<int>(g_cap.get(7)); // 用视频的帧数校准滑块轨迹条
// int tmpw = static_cast<int>(g_cap.get(CAP_PROP_FRAME_WIDTH));
int tmpw = static_cast<int>(g_cap.get(3));
// int tmph = static_cast<int>(g_cap.get(CAP_PROP_FRAME_HEIGHT));
int tmph = static_cast<int>(g_cap.get(4));
cout << "Video has " << frames << " frames of dimensions(" << tmpw
<< ", " << tmph << ").\n";
createTrackbar("Position", "Example2-4", &g_slider_position, frames,
on_trackbar_slide); // 创建轨迹条,命名,指定出现窗口,初始位置,最大值(视频帧数),回调函数(若不需要,则返回NULL)
Mat frame;
while (true)
{
if (g_run != 0)
{
g_cap >> frame;
if (frame.empty())
break;
// int current_pos = static_cast<int>(g_cap.get(CAP_PROP_POS_FRAMES));
int current_pos = static_cast<int>(g_cap.get(1));
g_dontset = 1; // 设置g_dontset使得下一个回调不会进入single-step模式
setTrackbarPos("Position", "Example2-4", current_pos);
imshow("Example2-4", frame);
g_run -= 1; // 保持single mode或使视频运行取决于其由用户按键设置的先前状态的效果
}
char c = static_cast<char>(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) // 按下Esc
break;
}
return 0;
}
void on_trackbar_slide(int pos, void *) // pos表示轨迹条的新位置
{
// g_cap.set(CAP_PROP_POS_FRAMES, pos); 表示我们希望以帧为单位设置读取位置
g_cap.set(1, pos); // 使用g_cap.set()中的新请求位置,推进视频播放到新的位置
if (!g_dontset) // 在下一个新帧进入后进入single step模式,但前提是该回调是由用户单击触发的,而不是从主函数(设置g_dontset)调用
g_run = 1;
g_dontset = 0;
}