[OpenCV] 形态学函数 Dilate 和 Erode ,以及对于引用和指针的一些练习。

原图: 

 

  

CV_EXPORTS_W void dilate( InputArray src, OutputArray dst, InputArray kernel,
                          Point anchor = Point(-1,-1), int iterations = 1,
                          int borderType = BORDER_CONSTANT,
                          const Scalar& borderValue = morphologyDefaultBorderValue() );
    
Description : Dilate膨胀
    第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可,图像通道的数量可以是任意的,但图像深度应为CV_8U、CV_16U、CV_168S、CV_32F或CV_64F其中之一。
    
    第二个参数,OutputArray类型的dst,即目标图像,需要和源图片有一样的尺寸和类型。
    
    第三个参数,InputArray类型的kernel,膨胀操作的核。当为NULL时,表示的是使用参考点位于中心3x3的核。
    
    我们一般使用函数getStructuringElement配合这个参数的使用。getStructuringElement函数会返回指定形状和尺寸的结构元素(内核矩阵)。其中,getStructuringElement函数的第一个参数表示内核的形状,有如下三种形状可以选择。
    
        矩形:MORPH_RECT;
    
        交叉形:MORPH_CROSS;
    
        椭圆形:MORPH_ELLIPSE。
    
    而getStructuringElement函数的第二和第三个参数分别是内核的尺寸以及锚点的位置。
    
    一般在调用erode以及dilate函数之前,先定义一个Mat类型的变量来获得getStructuringElement函数的返回值。对于锚点的位置,有默认值Point(-1,-1),表示锚点位于中心。此外,需要注意,十字形的element形状唯一依赖于锚点的位置,而在其他情况下,锚点只是影响了形态学运算结果的偏移。
    
    getStructuringElement函数相关的调用示例代码如下。
    
    Int g_nStructElementSize=3;//结构元素(内核矩阵)的尺寸
    
    //获取自定义核
    Mat element = getStructuringEllement(MORPH_RECT,
        Size(2 * g_nStructBLlementSize + 1, 2 * g_nStructElementSize + 1 ),
        Point(g_nStructELlementSize, g_nStructElementSize));
    
    调用之后,我们可以在接下来调用erode或dilate函数时,在第三个参数填保存了getStructuringElement返回值的Mat类型变量。对应于上面的示例,就是element变量。
    
    第四个参数,Point类型的anchor,锚的位置,其有默认值(-1,-1),表示锚位于中心。
    
    第五个参数,int类型的iterations,迭代使用erode(0)函数的次数,默认值为1
    
    第六个参数,int类型的borderType,用于推断图像外部像素的某种边界模式。注意它有默认值BORDER_DEFAULT。
    
    第七个参数,const Scalar&类型的borderValue,当边界为常数时的边界值,有默认值morphologyDefaultBorderValue(),一般不用去管它。需要用到它时,可以看官方文档中的createMorphologyFilter()函数,以得到更详细的解释。
    使用Dilate函数,一般只需要填写前面的三个参数,后面的四个参数都有默认值,而且往往会结合getStructuringElement 一起使用

 

 

 

 

CV_EXPORTS_W void erode( InputArray src, OutputArray dst, InputArray kernel,
                         Point anchor = Point(-1,-1), int iterations = 1,
                         int borderType = BORDER_CONSTANT,
                         const Scalar& borderValue = morphologyDefaultBorderValue() );
Description : Erode腐蚀
    
    第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。图像通道的数量可以是任意的,但图像深度应为CV_8U、CV_16U、CV_16S8、CV_32F或CV_64F其中之一。
    
    第二个参数,OutputArray类型的dst,即目标图像,需要和源图片有一样的尺寸和类型。
    
    第三个参数,InputArray类型的kernel,腐蚀操作的内核。为NULL时,表示的是使用参考点位于中心3x3的核。一般使用函数getStructuringElement配合这个参数的使用。getStructuringElement函数会返回指定形状和尺寸的结构元素(内核矩阵,具体看上文中dilate函数的第三个参数讲解部分。
    
    第四个参数,Point类型的anchor,锚的位置。其有默认值(-1-1),表示锚位于单位(element)的中心,一般不用管它。
    
    第五个参数,int类型的iterations,迢代使用erode()函数的次数,默认值为1。
    
    第六个参数,int类型的borderType,用于推断图像外部像素的某种边界模式。注意它有默认值BORDER_DEFAULT。
    
    第七个参数,const Scalar& 类型的BorderValue , 当边界为常数时的边界值,有默认值morphologyDefaultBorderValue(),一般不用去管它。需要用到它时,可以看官方文档中的createMorphologyFilter()函数以得到更详细的解释。

 

 

 

 

    #include "opencv2/opencv.hpp"
    
    using namespace std;
    using namespace cv;
    
    int main(int argc , char * argv[]){
    
        Mat srcImage = imread("C:/dev/cpp_test/kun.png",1);
        Mat dstImage_dilate ,dstImage_erode;
        srcImage.copyTo(dstImage_dilate);
        srcImage.copyTo(dstImage_erode);
        //show source image
        imshow("SRC" , srcImage);
    
        //dilate
        Mat kernel = getStructuringElement(MORPH_RECT,Size(15,15));
        dilate(srcImage,dstImage_dilate,kernel);
    
        //erode
        erode(srcImage,dstImage_erode,kernel);
    
    
        //show dstinction image
        imshow("DST" , dstImage_dilate);
        imshow("DST2" , dstImage_erode);
    
        while(char(waitKey(10)) != 'q') {}
    
        return 0;
}

 

 

    #include "opencv2/opencv.hpp"
    #define OPREATION_SWITCH "Operation Switch" 
    #define KERNEL_SIZE "Kernel Size"
    
    using namespace std;
    using namespace cv;
    
    int g_nOprationSwitch = 0; //0 for erode , 1 for dilate
    static void switch_onchange(int , void * );
    static void process(int , void* );
    struct my_data {
        Mat src;
        Mat dst;
        int kernel_size ;
    };
    int main(int argc , char * argv[]){
        int g_nStuctElementSize = 3 ; // 3 for deafult
            
        
        Mat srcImage = imread("C:/dev/cpp_test/kun.png",1);
        Mat dstImage;
        srcImage.copyTo(dstImage);
        
        my_data param = {srcImage,dstImage, g_nStuctElementSize};
         
        
        //show source image
        namedWindow("SRC",0);
        imshow("SRC" , srcImage);
    
        //kernel
        Mat kernel = getStructuringElement(MORPH_RECT,Size(2*g_nStuctElementSize+1,2*g_nStuctElementSize+1),Point(g_nStuctElementSize,g_nStuctElementSize));
        namedWindow("DST",0);
        erode(srcImage,dstImage,kernel);
        imshow("DST",dstImage);
        //Track bar for switch
        
        createTrackbar(OPREATION_SWITCH,"DST",&g_nOprationSwitch,1,switch_onchange,  (void *) &param);
        //Track bar for kernel changed
        createTrackbar(KERNEL_SIZE,"DST",&g_nStuctElementSize,21,process, (void *)&param);
            
        //show dstinction image
        imshow("DST" , dstImage);
        
        while(char(waitKey(10)) != 'q') {}
        return 0;
    }
    
    static void process(int kernel_size, void* param){
        my_data inner_param = *(my_data *)param;
        Mat kernel = getStructuringElement(MORPH_RECT,
                                            Size( 2 * kernel_size +1 , 2 * kernel_size+1),
                                            Point(kernel_size,kernel_size));
        //judge erode / dilate
        if(g_nOprationSwitch == 0){
            erode(inner_param.src,inner_param.dst,kernel);
        } // erode
        else{
            dilate(inner_param.src,inner_param.dst,kernel);
        }//dilate
        imshow("DST",inner_param.dst);
    }
    static void switch_onchange(int g_nOprationSwitch, void* param){
        process(g_nOprationSwitch,param);
}

 

 

 

 

首先,想要本着尽量减少全局变量的原则进行传参。 顺便加固对于指针和引用的用法。

    1. 针对滚动条函数createTrackbar的参数再理解。
    CV_EXPORTS int createTrackbar(const String& trackbarname, const String& winname,
                                  int* value, int count,
                                  TrackbarCallback onChange = 0,
                                  void* userdata = 0);

    
    第一个参数 : trackbar的名字,没有争议
    第二个参数: 窗口名字,仍然没有争议
    第三个参数: callback函数的第一个参数, callback函数应该是callback_xxx(int , void *)的格式, 这里传入的是一个地址(指针类型)
    第四个参数:回调函数
    第五个参数: 需要额外传入的参数,这里只能是一个一维指针,所以,为了创建这样的一个对象,使用void * list[] = {} 的结构传入参数是不可取的,原因是这样是一个二维数组
    那么此时就需要引入一个struct对象,或者是一个类对象,那么这里只是简单数据,推荐使用struct类型。
    
    2. 再传入地址的时候,这里的地址其实是指针类型,不只是一个简单地址,所以需要切换指针到固定的类型,这样才可以进行相应转换

 

posted @ 2023-04-12 15:52  冷小男  阅读(412)  评论(0)    收藏  举报