• 图像处理_imgproc笔记(1)


    图像处理_滤波器

    (1)图像的平滑处理

        图像的平滑也称模糊,平滑处理需要一个滤波器,最常用的滤波器就是线性滤波器,线性滤波器的输出像素值是g(x,y),是输入像素值是  f(x,y)的加权和:                                                                                     g(i,j) = sum_{k,l} f(i+k, j+l) h(k,l)

       h( k,l )称为核,它仅仅是一个加权系数,那么滤波器有很多种,最常用的滤波器介绍如下:

        归一化块滤波器:是比较简单的滤波器,输出的像素值是核窗口内像素值的均值(所有像素的加权系数相等),核如下:

                                                              K = dfrac{1}{K_{width} cdot K_{height}} egin{bmatrix}
    1 & 1 & 1 & ... & 1 \
    1 & 1 & 1 & ... & 1 \
    . & . & . & ... & 1 \
    . & . & . & ... & 1 \
    1 & 1 & 1 & ... & 1
   end{bmatrix}

    高斯滤波器:最有用的滤波器。高斯滤波器是将输入数组的每一个像素点与高斯内核卷积,将卷积当作输出像素值,比如一维高斯函数的:

                                                        ../../../../_images/Smoothing_Tutorial_theory_gaussian_0.jpg

    可以发现中间像素的加权系数最大,周边的像素的加权系数随着它们远离中间像素的距离的增大而减小,二维高斯函数的表达式是

                                                                 G_{0}(x, y) = A  e^{ dfrac{ -(x - mu_{x})^{2} }{ 2sigma^{2}_{x} } +  dfrac{ -(y - mu_{y})^{2} }{ 2sigma^{2}_{y} } }

    其中u为均值,峰值对应的位置,o是代表标准差,变量x,y各有一个均值,也各有一个标准差

    中值滤波器:将图像的每一个像素用领域像素的中值替代(以当前像素为中心的正方形区域)

    双边滤波器:类似与高斯滤波器,双边滤波器也给每一个领域像素分配一个加权系数,这个加权系数包含两个部分,第一部分加权方式与高斯滤波一样(是有几何空间距离决定滤波器的系数),第二部分的权重则取决于该领域像素与当前像素的灰度差值,是一种可以包边去噪的滤波器

    双边滤波器中输出滤波器的值依赖于邻域像素值的加权组合

                                                                               

    权重系数w(i,j,k,l)取决于定义域核

                                                                          

    和值域核

                                                                             

    的卷积

                                              

    源码与结果

    #include <iostream>
    #include <vector>
    
    #include "opencv2/imgproc/imgproc.hpp"
    #include "opencv2/highgui/highgui.hpp"
    #include "opencv2/features2d/features2d.hpp"
    
    using namespace std;
    using namespace cv;
    
    /// 全局变量
    int DELAY_CAPTION = 1500;
    int DELAY_BLUR = 100;
    int MAX_KERNEL_LENGTH = 31;   //最大的核长度
    
    Mat src; Mat dst;
    char window_name[] = "Smoothing Demo";
    
    /// Function headers函数声明
    int display_caption( const char* caption );
    int display_dst( int delay );
    
    
    /**
     * function main
     */
    int main( void )
    {
      namedWindow( window_name, WINDOW_AUTOSIZE );
    
      ///载入原图想
      src = imread( "/home/salm/myopencv/images/cat.jpg", 1 );
    
      if( display_caption( "Original Image" ) != 0 ) { return 0; }
    
      dst = src.clone();
      if( display_dst( DELAY_CAPTION ) != 0 ) { return 0; }
    
    
      /// Applying Homogeneous blur    使用均值平滑
    /*blur归一化块滤波
       src: 输入图像
        dst: 输出图像
        Size( w,h ): 定义内核大小( w 像素宽度, h 像素高度)
        Point(-1, -1): 指定锚点位置(被平滑点), 如果是负值,取核的中心为锚点。
    */
      if( display_caption( "Homogeneous Blur" ) != 0 ) { return 0; }
    
      for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 )
          { blur( src, dst, Size( i, i ), Point(-1,-1) );
            if( display_dst( DELAY_BLUR ) != 0 ) { return 0; } }
    
    
      /// Applying Gaussian blur     使用高斯平滑
      if( display_caption( "Gaussian Blur" ) != 0 ) { return 0; }
    
      for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 )
          { GaussianBlur( src, dst, Size( i, i ), 0, 0 ); //Size(w, h): 定义内核的大小(需要考虑的邻域范围)。 w 和 h 必须是正奇数,否则将使用 sigma_{x} 和 sigma_{y} 参数来计算内核大小
            if( display_dst( DELAY_BLUR ) != 0 ) { return 0; } }
    
    
      /// Applying Median blur    中值滤波
      if( display_caption( "Median Blur" ) != 0 ) { return 0; }
    
      for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 )
          { medianBlur ( src, dst, i );  //i: 内核大小 (只需一个值,因为我们使用正方形窗口),必须为奇数
            if( display_dst( DELAY_BLUR ) != 0 ) { return 0; } }
    
    
      /// Applying Bilateral Filter  双边滤波
    /*
    bilateral执行双边滤波操作
        src: 输入图像
        dst: 输出图像
        d: 像素的邻域直径
        sigma_{Color}: 颜色空间的标准方差
        sigma_{Space}: 坐标空间的标准方差(像素单位)
    */
      if( display_caption( "Bilateral Blur" ) != 0 ) { return 0; }
    
      for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 )
          { bilateralFilter ( src, dst, i, i*2, i/2 );
            if( display_dst( DELAY_BLUR ) != 0 ) { return 0; } }
    
      /// Wait until user press a key
      display_caption( "End: Press a key!" );
    
      waitKey(0);
    
      return 0;
    }
    
    /**
     * @function display_caption
     */
    int display_caption( const char* caption )
    {
      dst = Mat::zeros( src.size(), src.type() );
      putText( dst, caption,
               Point( src.cols/4, src.rows/2),
               FONT_HERSHEY_COMPLEX, 1, Scalar(255, 255, 255) );
    
      imshow( window_name, dst );
      int c = waitKey( DELAY_CAPTION );
      if( c >= 0 ) { return -1; }
      return 0;
    }
    
    /**
     * @function display_dst
     */
    int display_dst( int delay )
    {
      imshow( window_name, dst );
      int c = waitKey ( delay );
      if( c >= 0 ) { return -1; }
      return 0;
    }
    

    结果为

                                                                                     Smoothing with a median filter

     腐蚀与膨胀(Eroding and Dilating)

    形态学操作是基于形状的一系列图像处理操作,通过结构元素作用于输入图像来产生输出图像,最基本的形态学操作有两种:erision  与  dilation  它们的应用广泛,主要有消除噪声,分割独立的图像元素,连接相邻的元素,寻找图像中明显的极大值或极小值区域

    通俗的说:膨胀算法是图像扩大一圈,腐蚀算法是图像缩小一圈,腐蚀是删除对象边界的某些元素,膨胀是给图像的边界添加某些元素,算法从图像的角度来看,二值图像的腐蚀与膨胀就是将一个小型的二值图像(比如一般为结构元素  一般为3*3的)在图像上进行逐点的运动并比较,根据比较的结果做出相应的处理。

    膨胀算法:用3*3的结构元素,扫描二值图像的每一个像素,用结构元素与其覆盖的二值图像做“与”运行,如果都为“0”结构图像的该元素就为0  否则就为1   结果会使得二值图像扩大一圈

    腐蚀算法:用3*3的结果元素,扫描二值图像的每一个像素,用结构元素与其覆盖的二值图像做“与”运算,结构都为1  结构图像的该元素就为1  否则就为0   结果使二值图像减小一圈

    OpenCV里面的腐蚀膨胀都是针对 白色 目标区域的。说膨胀使图像 变大一圈, 那是指 图像中的 白色目标区域 扩大了一圈

     源码

    #include "opencv2/imgproc/imgproc.hpp"
    #include "opencv2/highgui/highgui.hpp"
    #include <stdlib.h>
    #include <stdio.h>
    
    using namespace cv;
    
    /// Global variables 全局变量
    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, char** argv )
    {
      /// Load an image
      src = imread( argv[1] );
    
      if( !src.data )
        { return -1; }
    
      /// Create windows
      namedWindow( "Erosion Demo", WINDOW_AUTOSIZE );
      namedWindow( "Dilation Demo", WINDOW_AUTOSIZE );
      moveWindow( "Dilation Demo", src.cols, 0 );
    
      /// Create Erosion Trackbar
      createTrackbar( "Element:
     0: Rect 
     1: Cross 
     2: Ellipse", "Erosion Demo",
              &erosion_elem, max_elem,
              Erosion );
    
      createTrackbar( "Kernel size:
     2n +1", "Erosion Demo",
              &erosion_size, max_kernel_size,
              Erosion );
    
      /// Create Dilation Trackbar
      createTrackbar( "Element:
     0: Rect 
     1: Cross 
     2: Ellipse", "Dilation Demo",
              &dilation_elem, max_elem,
              Dilation );
    
      createTrackbar( "Kernel size:
     2n +1", "Dilation Demo",
              &dilation_size, max_kernel_size,
              Dilation );
    
      /// Default start
      Erosion( 0, 0 );
      Dilation( 0, 0 );
    
      waitKey(0);
      return 0;
    }
    
    /*
    内核选择三种形状之一:
            矩形: MORPH_RECT
            交叉形: MORPH_CROSS
            椭圆形: MORPH_ELLIPSE
    */
    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 ) );
      /// Apply the erosion operation
      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 ) );
      /// Apply the dilation operation
      dilate( src, dilation_dst, element );
      imshow( "Dilation Demo", dilation_dst );
    }
    

     更改Trackbars的位置就会产生不一样的输出图像

     更多的形态学变换

    开运算(opening):是通过对图像先腐蚀后膨胀实现  , 能够排除小团块物体(假设物体较背景明亮)

       dst = open( src, element) = dilate( erode( src, element ) )         

    闭运算:(closing):使用过先膨胀后腐蚀实现的,  能够排除小型黑洞(黑色区域)

      dst = close( src, element ) = erode( dilate( src, element ) )           

    形态梯度(morphological Gradient):膨胀图与腐蚀图之差 , 能够保留物体的边缘轮廓dst = morph_{grad}( src, element ) = dilate( src, element ) - erode( src, element )

     顶帽(Top Hat):原图像与开运算结果图之差   dst = tophat( src, element ) = src - open( src, element )

     黑帽(black Hat):闭运算结果图与原图像之差dst = blackhat( src, element ) = close( src, element ) - src

     源码

    #include "opencv2/imgproc/imgproc.hpp"
    #include "opencv2/highgui/highgui.hpp"
    #include <stdlib.h>
    #include <stdio.h>
    
    using namespace cv;
    
    /// Global variables
    Mat src, dst;
    
    int morph_elem = 0;
    int morph_size = 0;
    int morph_operator = 0;
    int const max_operator = 4;
    int const max_elem = 2;
    int const max_kernel_size = 21;
    
    const char* window_name = "Morphology Transformations Demo";
    
    
    /** Function Headers */
    void Morphology_Operations( int, void* );
    
    
    int main( int, char** argv )
    {
      ///  载入图像
      src = imread( argv[1] );
    
      if( !src.data )
        { return -1; }
    
      /// Create window
      namedWindow( window_name, WINDOW_AUTOSIZE );
    
      /// Create Trackbar to select Morphology operation
      createTrackbar("Operator:
     0: Opening - 1: Closing  
     2: Gradient - 3: Top Hat 
     4: Black Hat", window_name, &morph_operator, max_operator, Morphology_Operations );
    
      /// Create Trackbar to select kernel type
      createTrackbar( "Element:
     0: Rect - 1: Cross - 2: Ellipse", window_name,
                      &morph_elem, max_elem,
                      Morphology_Operations );
    
      /// Create Trackbar to choose kernel size
      createTrackbar( "Kernel size:
     2n +1", window_name,
                      &morph_size, max_kernel_size,
                      Morphology_Operations );
    
      /// Default start
      Morphology_Operations( 0, 0 );
    
      waitKey(0);
      return 0;
    }
    
    /**
     * @function Morphology_Operations
     */
    void Morphology_Operations( int, void* )
    {
    
      // Since MORPH_X : 2,3,4,5 and 6
      int operation = morph_operator + 2;
    
      Mat element = getStructuringElement( morph_elem, Size( 2*morph_size + 1, 2*morph_size+1 ), Point( morph_size, morph_size ) );
    
      /// Apply the specified morphology operation
      morphologyEx( src, dst, operation, element );
      imshow( window_name, dst );
    }
    
  • 相关阅读:
    常用类型转换
    配置相关的一些辅助类
    Java Class与反射相关的一些工具类
    一些中文相关的操作方法
    提供一些对象有效性校验的方法
    编码相关的封装类
    数组相关的工具类
    提供Web相关的个工具类
    URL相关的工具类
    经纬度相关的一些操作
  • 原文地址:https://www.cnblogs.com/li-yao7758258/p/6232104.html
Copyright © 2020-2023  润新知