摘要:
我学习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#include #include #include 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#include 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; }