[OpenCV4] cv :: createTrackbar() 创建滑动条

目录

1.函数原型

2.解释

2.1 头文件 

2.2 功能

2.3 参数说明

3.Demo

遗留问题

个人关于void *的一点联想


官方文档:参考链接

1.函数原型

 

2.解释

 

2.1 头文件 

<opencv2/highgui.hpp>

2.2 功能

Creates a trackbar and attaches it to the specified window.

The function createTrackbar creates a trackbar (a slider or range control) with the specified name and range, assigns a variable value to be a position synchronized with the trackbar and specifies the callback function onChange to be called on the trackbar position change. The created trackbar is displayed in the specified window winname.

创建一个跟踪条(一个指定范围的滑动条)并将其绑定到指定的窗口,该函数可用于指定滑动条的名称(trackbarname)、绑定窗口的名称(winname)、滑动条初始值(int * value)、滑动条取值范围(int count)、滑动条滑动时的回调函数的函数指针(函数指针:参考博客)(滑动条的状态一旦发生改变,就会调用该回调函数)、用户自己传入的参数(void* userdata)(可以避免使用全局变量)

2.3 参数说明

Parameters

trackbarnameName of the created trackbar.
winnameName of the window that will be used as a parent of the created trackbar.
valueOptional pointer to an integer variable whose value reflects the position of the slider. Upon creation, the slider position is defined by this variable.
countMaximal position of the slider. The minimal position is always 0.
onChangePointer to the function to be called every time the slider changes position. This function should be prototyped as void Foo(int,void*); , where the first parameter is the trackbar position and the second parameter is the user data (see the next parameter). If the callback is the NULL pointer, no callbacks are called, but only value is updated.
userdataUser data that is passed as is to the callback. It can be used to handle trackbar events without using global variables.

const String & trackbarname: 滑动条名称

const String & winname: 绑定窗口名称

int * value: 用于设定滑动条初值,也存储任意时刻的滑动条的实际值

int count: 滑动条最大值(最小值默认为0)

TrackbarCallback onChange = 0: 滑动条回调函数,这个函数的函数原型应该符合 (void Func(int, void*),其中int用于传入此刻的value值(滑动条此时的值)、void*用于传入用户自己要用的数据,如果传入的为空指针,那么就只会更新(*value)的值,而不会调用回调函数)

void *userdata = 0: 用户自己的数据被用于传入给回调函数(是回调函数的第二个参数)

3.Demo

#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include <iostream>
using namespace cv;
using namespace std;
Mat src, erosion_dst, dilation_dst;
int erosion_elem = 0;
int erosion_size = 0;
int dilation_elem = 0;
int dilation_size = 0;
int const max_elem = 2;
int const max_kernel_size = 21;
void Erosion( int, void* );
void Dilation( int, void* );
int main( int argc, char** argv )
{
  src = imread( "../media/cat.jpg", IMREAD_COLOR );
  if( src.empty() )
  {
    cout << "Could not open or find the image!\n" << endl;
    cout << "Usage: " << argv[0] << " <Input image>" << endl;
    return -1;
  }
  namedWindow( "Erosion Demo", WINDOW_AUTOSIZE );
  namedWindow( "Dilation Demo", WINDOW_AUTOSIZE );
  moveWindow( "Dilation Demo", src.cols, 0 );
  createTrackbar( "Element:\n 0: Rect \n 1: Cross \n 2: Ellipse", "Erosion Demo",
          &erosion_elem, max_elem,
          Erosion );
  createTrackbar( "Kernel size:\n 2n +1", "Erosion Demo",
          &erosion_size, max_kernel_size,
          Erosion );
  createTrackbar( "Element:\n 0: Rect \n 1: Cross \n 2: Ellipse", "Dilation Demo",
          &dilation_elem, max_elem,
          Dilation );
  createTrackbar( "Kernel size:\n 2n +1", "Dilation Demo",
          &dilation_size, max_kernel_size,
          Dilation );
  Erosion( 0, 0 );
  Dilation( 0, 0 );
  waitKey(0);
  return 0;
}
void Erosion( int, void* )
{
  int erosion_type = 0;
  if( erosion_elem == 0 ){ erosion_type = MORPH_RECT; }
  else if( erosion_elem == 1 ){ erosion_type = MORPH_CROSS; }
  else if( erosion_elem == 2) { erosion_type = MORPH_ELLIPSE; }
  Mat element = getStructuringElement( erosion_type,
                       Size( 2*erosion_size + 1, 2*erosion_size+1 ),
                       Point( erosion_size, erosion_size ) );
  erode( src, erosion_dst, element );
  imshow( "Erosion Demo", erosion_dst );
}
void Dilation( int, void* )
{
  int dilation_type = 0;
  if( dilation_elem == 0 ){ dilation_type = MORPH_RECT; }
  else if( dilation_elem == 1 ){ dilation_type = MORPH_CROSS; }
  else if( dilation_elem == 2) { dilation_type = MORPH_ELLIPSE; }
  Mat element = getStructuringElement( dilation_type,
                       Size( 2*dilation_size + 1, 2*dilation_size+1 ),
                       Point( dilation_size, dilation_size ) );
  dilate( src, dilation_dst, element );
  imshow( "Dilation Demo", dilation_dst );
}

这里使用官方的腐蚀和膨胀形态学操作示例代码,分析:

int erosion_elem = 0;
int erosion_size = 0;
int dilation_elem = 0;
int dilation_size = 0;

这里erosion_elem、dilation_elem就是初始值,也是后面实时监测的滑动条的位置的值,赋初值为0,表示最初滑动条滑条位于0处(最左侧)。erosion_size、dilation_size也是滑动条的初始值,最初也赋值为0。可以看到这里的一共创建了4个不同的滑动条。滑动条的最大值是通过:

int const max_elem = 2;
int const max_kernel_size = 21;

max_elem, max_kernel_size 进行指定的

namedWindow( "Erosion Demo", WINDOW_AUTOSIZE );
  namedWindow( "Dilation Demo", WINDOW_AUTOSIZE );
  moveWindow( "Dilation Demo", src.cols, 0 );
  createTrackbar( "Element:\n 0: Rect \n 1: Cross \n 2: Ellipse", "Erosion Demo",
          &erosion_elem, max_elem,
          Erosion );
  createTrackbar( "Kernel size:\n 2n +1", "Erosion Demo",
          &erosion_size, max_kernel_size,
          Erosion );
  createTrackbar( "Element:\n 0: Rect \n 1: Cross \n 2: Ellipse", "Dilation Demo",
          &dilation_elem, max_elem,
          Dilation );
  createTrackbar( "Kernel size:\n 2n +1", "Dilation Demo",
          &dilation_size, max_kernel_size,
          Dilation );

在创建了显示窗口 “Erosion Demo"、“Dilation Demo”之后,通过createTrackbar进行绑定,每个窗口都绑定了两个滑动条,滑动条的名字对应createTrackbar的第一个参数:

可以看到初值与设置的情况相同,都为0,最大值与设置的情况也相同,为2与21

Erosion( 0, 0 );
Dilation( 0, 0 );
waitKey(0);

这里调用了函数Erosion和Dilation,最后使用了waitKey():参考博客

void Erosion( int, void* );
void Dilation( int, void* );

这里的Erosion和Dilation就是滑动条状态发生改变的时候的回调函数,函数的类型与上文中参数说明一致,这两个函数的函数名(即两个函数指针),作为createTrackbar的倒数第二个参数,指定了回调函数,但是作者偷懒使用了全局变量,所以最后一个参数也就直接使用了默认参数(userdata没有传参)。

返回值为空,第一个参数为int,即此时的滑动条的值,第二个参数为void * ,是用户自己补充的辅助数据,用于提高程序的模块化特性,避免全局变量的使用。

下面分析一个回调函数Erosion:

void Erosion( int, void* )
{
  int erosion_type = 0;
  if( erosion_elem == 0 ){ erosion_type = MORPH_RECT; }
  else if( erosion_elem == 1 ){ erosion_type = MORPH_CROSS; }
  else if( erosion_elem == 2) { erosion_type = MORPH_ELLIPSE; }
  Mat element = getStructuringElement( erosion_type,
                       Size( 2*erosion_size + 1, 2*erosion_size+1 ),
                       Point( erosion_size, erosion_size ) );
  erode( src, erosion_dst, element );
  imshow( "Erosion Demo", erosion_dst );
}

这里,原作者偷懒没有传参数,而是使用了全局变量,当然,这也提醒我们规矩就是用来打破的,设计者尽管可以这样设计规则,但是使用者可以不这样使用。

根据全局变量erosion_elem(根据上面的代码可以看出这个变量其实是用于设置腐蚀的结构元素的类型,即RECT/CROSS/ELLIPSE矩形/十字/椭圆),在确定了结构元素之后,就根据另外一个全局变量erosion_size来确定此时的Kernel的大小,然后根据这几个参数调用函数getStructuringElement构建kernel,最后调用erode函数对src进行处理,得到erosion_dst,并使用imshow进行展示。

以上就分析了整个运行的流程,即每次改动滑动条的位置(无论是上面一行的类型还是下面一行的大小的设置),都会触发回调函数的调用,之后就会在回调函数中执行回调函数的代码,重新imshow(),这样就可以试试看到最新的变化结果了,相比于通过修改代码中的参数,这样通过GUI操作显然更加高效。


遗留问题

目前这个GUI界面的滑动条的名称显示不完全,希望路过的朋友知道原因留言告知,谢谢。


个人关于void *的一点联想

首先感觉这个void* 有点像python中的可变参数列表*args,可以将一个数据的数组名(数组首地址)传入,这样就相当于有了一个数组可以作为参数使用了。

但是经过尝试,发现这样并不能实现python中的*args的效果,因为要实现这种混合类型的传参,需要传入一个void* 数组,即传入的参数本身是一个void** 类型,之前在做perfLab性能优化实验的时候看到过类似的源码:

所以在这里需要传入void* arglist[], 即void** arglist 才可以,最初的想法是不对的。

即createTrackbar中的onChange函数的原型:void Foo(int, void*)后面的void* 类型只能传入一个参数,这里不能使用数组,但是应该可以使用结构体或者类的方式来打包传入多个参数。

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值