摘要:
我學習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; }