【opencv五】利用opencv給讀入的視訊新增拖動滑塊

weixin_33782386發表於2018-12-14

【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“


    5529997-f7d6dd5c5b174dd3.png

    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按鈕被按下,程式結束。

0.給自己打個小廣告

本人csdn部落格地址:https://blog.csdn.net/qiu931110/
本人211碩士畢業,目前從事深度學習,機器學習計算機視覺演算法行業,目前正在將我的各類學習筆記釋出在我的公眾號中,希望感興趣一起學習的同學們可以關注下~~~
本人微信公眾號:yuanCruise

5529997-eb95f6b6db8e1430.png

相關文章