在【opencv四】利用opencv读取显示视频中读取的视频只能正常播放,无法像我们使用播放器时能在视频中拖动进度条。本文旨在提出一个滑块功能,实现视频的进度条拖动。而之前用到过的头文件highgui.hpp除了之前用到过的读取和显示功能外,还拥有上述提到的滑块功能,使得我们能够简单的跳转到视频的某一部分。利用cv::createTrackbar()来创建滑块,并指定该滑块显示在哪个窗口中。
下述代码的功能为:在基本查看器窗口中添加一个trackbar滑块,用于在视频文件中移动。

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <fstream>

using namespace std;
using namespace cv;

int g_slider_position = 0;
int g_run = 1;
int 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()
{
	namedWindow("Example3",WINDOW_AUTOSIZE);
	g_cap.open("H:\\vs2017\\opencv_learning\\ConsoleApplication1\\video.mp4");
	int frames = (int)g_cap.get(CAP_PROP_FRAME_COUNT);
	int tmpw = (int)g_cap.get(CAP_PROP_FRAME_WIDTH);
	int tmph = (int)g_cap.get(CAP_PROP_FRAME_HEIGHT);
	cout << "Video has" << frames << "frames of dimensions(" << tmpw << "," << tmph << ")." << endl;

    createTrackbar("Position","Example3",&g_slider_position,frames,onTrackbarSlide);

	Mat frame;
	while (1)
	{
		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","Example3",current_pos);
			imshow("Example3",frame);

			g_run -= 1;
		}

		char c = (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)
			break;
	}
	return 0;

}

代码解析:
实际上,该策略是添加一个全局变量来表示追踪视频帧位置,然后添加一个回调函数来更新该变量并重新定位视频中的读位置,用于后续的输出。一个调用创建trackbar并附加回调,然后我们就可以开始运行了。
首先看下代码中的全局变量的初始化及其作用。

int g_slider_position = 0;
int g_run = 1;
int g_dontset = 0;
VideoCapture g_cap;
  • g_slider_position:该全局变量表示当前滑块的位置。
  • g_run:该全局变量为非零的时候,就显示新的帧。
  • g_dontset:允许我们在不触发单步模式的情况下更新轨迹条的位置。

为了避免混淆,当用户单击trackbar跳转到视频中的新位置时,我们将通过设置g_run = 1使视频停留在单步状态。然而,这带来了一个微妙的问题:随着视频的推进,我们希望滑动轨迹条在显示窗口中的位置根据我们在视频中的位置而前进。我们通过让主程序调用trackbar回调函数来在每次获得新的视频帧时更新滑块的位置来实现这一点。但是,我们不希望这些对trackbar回调的编程调用将我们置于单步模式。为了避免这种情况,我们引入了最后一个全局变量g_dontset,它允许我们在不触发单步模式的情况下更新轨迹条的位置。


void onTrackbarSlide(int pos, void *)
{
    g_cap.set(CAP_PROP_POS_FRAMES,pos);
    if (!g_dontset)
    { 
        g_run = 1;
        g_dontset = 0;
    }
}

该函数的功能:当用户拖动滑块的时候,利用函数callback(回调)出当前滑块的位置(视频帧数)。然后可以使得之后的显示函数 ,通过该函数回调得到的视频帧位置进行显示,最终能够实现滑块位置的拖动带动着视频的移动。

  • g_cap.set(CAP_PROP_POS_FRAMES,pos):将当前滑块的位置,来将视频设置(回退 or 推进)到新的位置。
  • if()语句:保证了视频跳转时,模式固定在单步模式。

g_cap.set() 和 g_cap.get()是两个在未来会经常出现的基于视频对象的方法。这些方法主要用来配置 or 询问VideoCapture对象的各种属性。在这个例子中利用set函数将CAP_PROP_POS_FRAMES参数设置为pos,该参数的含义为以帧为单位来设置读取位置。


int frames = (int)g_cap.get(CAP_PROP_FRAME_COUNT);
int tmpw = (int)g_cap.get(CAP_PROP_FRAME_WIDTH);
int tmph = (int)g_cap.get(CAP_PROP_FRAME_HEIGHT);
cout << "Video has" << frames << "frames of dimensions(" << tmpw << "," << tmph << ")." << endl;
  • int frames = (int)g_cap.get(CAP_PROP_FRAME_COUNT):读取g_cap对象得到的视频信息中视频总帧数。
  • int tmpw = (int)g_cap.get(CAP_PROP_FRAME_WIDTH):视频图片的宽度。
  • int tmph = (int)g_cap.get(CAP_PROP_FRAME_HEIGHT):视频图片的高度。
    之所以要提取整个视频的帧数,原因在于之后需要利用总帧数来校正滑块。

createTrackbar("Position","Example3",&g_slider_position,frames,onTrackbarSlide);

createTrackbar函数:用来生成视频进度条。

  • 第一个参数,“Position”为进度条的名称,如下图所示。

  • 第二个参数,“Example3”为指定当前进度条发到哪一个窗口中,前提是该窗口已经被创建。

  • 第三个参数,该参数绑定了进度条的位置,如图所示Position后面的数字。

  • 第四个参数,该参数是进度条位置的最大值(也就是当前视频的总帧数)

  • 第五个参数,当进度条滑块移动的时候,会调用的callback(回调函数),如果没有就输入”NULL“
    【opencv五】利用opencv给读入的视频添加拖动滑块_滑块


    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","Example3",current_pos);
            imshow("Example3",frame);

            g_run -= 1;
        }

在这个语句块中,程序除了对视频进行读取帧,显示外。

  • 还通过g_cap.get()函数提取视频的当前帧数。
  • 并将g_dontset设置为1,使得下一个callback函数不会将系统置于单步模式。
  • 并调用setTrackbarPos更新显示滑块的位置。
  • 全局g_run是递减的,其效果是使我们保持单步模式,或者让视频根据用户按键设置的先前状态运行。

char c = (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)
			break;

在这个语句块中,程序从用户那里寻找键盘输入。

  • 如果S按钮被按下,程序进入单步模式(g_run= 1,允许读取单个帧)。
  • 如果R按钮被按下,程序进入连续视频模式(g_run= -1,进一步的衰减使得它对任何可能的视频大小都是负的,该模式下整个程序将会停在当前帧,直到S按钮被按下,进入单步模式)
  • 如果Esc按钮被按下,程序结束。