1. 程式人生 > >計算機視覺(二)-opencv之createTrackbar()詳解

計算機視覺(二)-opencv之createTrackbar()詳解

摘要:

我學習openCV3看的是《學習openCV3》這本書,很厚的一本,不知道是不是因為自己看的還不是很多,個人覺得裡面的有些重要函式講的不是很詳細,比如createTrackbar()這個函式,這個函數出現在這本書的第三個例項程式,書中只是說明了這是一個建立滾動條的程式,然而對裡面的引數講解以及與他相對應的回撥函式講解都不是很完美,因此我就打開了它的定義以及到網上找了一些博主的文章來學習,但是感覺講的都不是很全,下面我結合自己的實驗加上自己的理解,講解一下我對這個函式的看法。

函式說明:

createTrackbar()函式的函式原型為:

CV_EXPORTS int createTrackbar(const String& trackbarname, const String& winname,int* value, int count,TrackbarCallback onChange = 0, void* userdata = 0);

trackbarname:這個引數用來給這個滾動條取一個名字;

winname:這個引數用來指定你要吧這個滾動條用到那個視窗上;

value:這個引數用來設定滑塊初始值位置,同時記錄滑塊以後的位置;

count:這個引數用來指定滾動條可以滾動的最大值;

onChange:這個引數可以理解為一個函式型別的變數(當然這樣說感覺有點怪),用來接收回調函式函式名的,預設值為0;

userdata:這個變數這個引數是使用者傳給回撥函式的資料,用來處理軌跡條事件,預設值為0。

這裡面一共有6個引數,其中value這個引數容易理解有偏差,onchange,userdata這倆引數可能難以理解;

下面先說我對value這個引數的看法:

value這個引數首先要知道它是用來給滑塊位置一個初始值的,也就是告訴我們滑塊最初位置在哪,而不是滑塊可以滑動範圍的最小值,滑塊可以滑動的範圍永遠都是[0, count],count即為第四個引數,然後還要理解一個概念,程式碼執行過程中,不是value的值影響滑塊的位置,而是由於使用者對滑塊的移動改變了value的值,也就是說value是被動的。

然後在說對onChange該引數的理解:

首先我們看一下回調函式的函式原型:

void (*TrackbarCallback)(int pos, void* userdata);

當我們沒看回調函式原型時可能很難理解有些程式碼中定義回撥函式時為什麼必須要有那倆引數,當看了回撥函式原型後我相信有C++基礎的人也都能明白了,那麼我來說說回撥函式和createTrackbar()函式的關係;首先,我們看到回撥函式和createTrackbar函式都有一個形參名為userdata,那這是不是巧合呢?答案當然不是,你可以這樣理解,回撥函式是一個必須依託於createTrackbar()函式而使用的函式,他不能單獨拿來使用,那他的兩個形參是怎麼用的呢?首先第一個形參pos,它表示的是當前滑塊所在的位置,它的值是createTrackbar()傳給他的,也就是createTrackbar()形參value的值,這個傳輸過程是在createTrackbar()內部實現的,無需深究,然後回撥函式形參userdata的值就是通過createTrackbar()的形參userdata直接得到的,所以createTrackbar()的形參userdata其實就是專門給回撥函式準備的。

函式的使用上的問題:

createTrackbar()在使用上可能也會比較神奇,比如說你可能看到如下程式:

#include <iostream>
 #include <opencv2/core/core.hpp>
 #include <opencv2/highgui/highgui.hpp>
 #include <imgproc.hpp>

using namespace std;
 using namespace cv;

//TrackBar發生改變的回撥函式
void onChangeTrackBar(int pos, void* userdata);

//主函式
int main()
 {
     //trackbar的值
    int posTrackBar = 0;
     //trackbar的最大值
    int maxValue = 255;

    //讀入影象,以灰度圖形式讀入
     Mat img = imread("F:\\圖片\\timg.jpg", 0);

    //新建視窗
    namedWindow("二值化");
     imshow("二值化", img);

    //建立trackbar,我們把img作為資料傳進回撥函式中
    createTrackbar("pos", "二值化", &posTrackBar, maxValue, onChangeTrackBar, &img);

    waitKey();

    return 0;
 }

// 回撥函式
void onChangeTrackBar(int pos, void* usrdata)
 {
     // 強制型別轉換
    Mat src = *(Mat*)(usrdata);
     Mat dst;

    // 二值化
    threshold(src, dst, pos, 255, 0);
     imshow("二值化", dst);
 }

 上面這個是一個很簡單的用於影象二值化處理的程式碼,當你執行它時你會神奇的發現,這裡面沒有一個迴圈,但是你卻可以一直滑動滾動條的滑塊,而且影象會出現相應的變化,如圖滑塊到不同位置的效果:

這裡其實是靠createTrackbar事件處理得到的,學過一點計算機的人就可以理解為該函式其實一直執行在後臺檢測是否有滑塊移動這種中斷產生,然後當你移動滑塊時就會觸發中斷,這時回撥函式就相當於中斷服務函式來處理中斷(不完全準確但是可以這麼理解,就像牛頓的經典定律,不是完全準確的描述這個世界,但是在巨集觀層面來說,用來理解世界是很好的定律)。

 

結語:

 

以上全是我個人通過實驗驗證過的,如果有什麼不對的地方,歡迎大家評論區指出。最後附上一些測試用程式碼:

視訊的連續播放,進度可調:

#include <opencv.hpp>
#include<iostream>

using namespace std;

int g_slider_position = 0;
int g_run = 1, g_dontset = 0;
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() {
    cv::namedWindow("show_video", 0);
    g_cap.open("F:\\圖片\\123.mp4");
    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", "show_video", &g_slider_position, frames, onTrackbarSlide);
    cv::Mat frame;
    while (1) {
        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", "show_video", current_pos);
            cv::imshow("show_video", frame);

            g_run = -1;
        }
        char c = (char)cv::waitKey(33);
        if (c == 's') {
            g_run = 1;
            cout << "Single step,run = " << g_run << endl;
        }
        if (c == 'r') {
            g_run = -1;
            cout << "Run mode,run" << g_run << endl;
        }
        if (c == 27) {
            cv::destroyWindow("show_video");
            break;
        }
    }
    cv::destroyWindow("show_video");
    return 0;
}

&n