【OpenCV學習】之圖像平滑處理

目標

本教程教您怎樣使用各種線性濾波器對圖像進行平滑處理,相關OpenCV函數如下:

  1. blur
  2. GaussianBlur
  3. medianBlur
  4. bilateralFilter

原理

  • 平滑 也稱 模糊, 是一項簡單且使用頻率很高的圖像處理方法。
  • 平滑處理的用途有很多, 但是在本教程中我們僅僅關注它減少噪聲的功用 (其他用途在以後的教程中會接觸到)。
  • 平滑處理時需要用到一個 濾波器 。 最常用的濾波器是 線性 濾波器,線性濾波處理的輸出像素值 (i.e. g(i,j)) 是輸入像素值 (i.e. f(i+k,j+l))的加權和 :  g(i,j) = \sum_{k,l} f(i+k, j+l) h(k,l)           h(k,l) 稱爲 , 它僅僅是一個加權係數。

不妨把 濾波器 想象成一個包含加權係數的窗口,當使用這個濾波器平滑處理圖像時,就把這個窗口滑過圖像。

  • 濾波器的種類有很多, 這裏僅僅提及最常用的:

歸一化塊濾波器 (Normalized Box Filter)

  • 最簡單的濾波器, 輸出像素值是核窗口內像素值的 均值 ( 所有像素加權係數相等)
  • 核如下:

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

高斯濾波器 (Gaussian Filter)

  • 最有用的濾波器 (儘管不是最快的)。 高斯濾波是將輸入數組的每一個像素點與 高斯內核 卷積將卷積和當作輸出像素值。
  • 還記得1維高斯函數的樣子嗎?

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

假設圖像是1維的,那麼觀察上圖,不難發現中間像素的加權係數是最大的, 周邊像素的加權係數隨着它們遠離中間像素的距離增大而逐漸減小。

Note:

 2維高斯函數可以表達爲 :   G_{0}(x, y) = A  e^{ \dfrac{ -(x - \mu_{x})^{2} }{ 2\sigma^{2}_{x} } +  \dfrac{ -(y - \mu_{y})^{2} }{ 2\sigma^{2}_{y} } }

其中 \mu 爲均值 (峯值對應位置), \sigma 代表標準差 (變量 x 和 變量 y 各有一個均值,也各有一個標準差)

 

中值濾波器 (Median Filter)

中值濾波將圖像的每個像素用鄰域 (以當前像素爲中心的正方形區域)像素的 中值 代替 。

雙邊濾波 (Bilateral Filter)

  • 目前我們瞭解的濾波器都是爲了 平滑 圖像, 問題是有些時候這些濾波器不僅僅削弱了噪聲, 連帶着把邊緣也給磨掉了。 爲避免這樣的情形 (至少在一定程度上 ), 我們可以使用雙邊濾波。
  • 類似於高斯濾波器,雙邊濾波器也給每一個鄰域像素分配一個加權係數。 這些加權係數包含兩個部分, 第一部分加權方式與高斯濾波一樣,第二部分的權重則取決於該鄰域像素與當前像素的灰度差值。
  • 詳細的解釋可以查看 鏈接

源碼

  • 本程序做什麼?

    • 裝載一張圖像
    • 使用4種不同濾波器 (見原理部分) 並顯示平滑圖像
  • 代碼一瞥:

    #include "opencv2/imgproc/imgproc.hpp"
    #include "opencv2/highgui/highgui.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[] = "Filter Demo 1";
    
    /// 函數申明
    int display_caption( char* caption );
    int display_dst( int delay );
    
    /**
     *  main 函數
     */
     int main( int argc, char** argv )
     {
       namedWindow( window_name, CV_WINDOW_AUTOSIZE );
    
       /// 載入原圖像
       src = imread( "../images/lena.jpg", 1 );
    
       if( display_caption( "Original Image" ) != 0 ) { return 0; }
    
       dst = src.clone();
       if( display_dst( DELAY_CAPTION ) != 0 ) { return 0; }
    
       /// 使用 均值平滑
       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; } }
    
        /// 使用高斯平滑
        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 );
              if( display_dst( DELAY_BLUR ) != 0 ) { return 0; } }
    
         /// 使用中值平滑
         if( display_caption( "Median Blur" ) != 0 ) { return 0; }
    
         for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 )
             { medianBlur ( src, dst, i );
               if( display_dst( DELAY_BLUR ) != 0 ) { return 0; } }
    
         /// 使用雙邊平滑
         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; } }
    
         /// 等待用戶輸入
         display_caption( "End: Press a key!" );
    
         waitKey(0);
         return 0;
     }
    
     int display_caption( char* caption )
     {
       dst = Mat::zeros( src.size(), src.type() );
       putText( dst, caption,
                Point( src.cols/4, src.rows/2),
                CV_FONT_HERSHEY_COMPLEX, 1, Scalar(255, 255, 255) );
    
       imshow( window_name, dst );
       int c = waitKey( DELAY_CAPTION );
       if( c >= 0 ) { return -1; }
       return 0;
      }
    
      int display_dst( int delay )
      {
        imshow( window_name, dst );
        int c = waitKey ( delay );
        if( c >= 0 ) { return -1; }
        return 0;
      }

     

    解釋

  • 下面看一看有關平滑的OpenCV函數,其餘部分大家已經很熟了。

  • 歸一化塊濾波器:

    OpenCV函數 blur 執行了歸一化塊平滑操作。

    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; } }
    

    我們輸入4個實參 (詳細的解釋請參考 Reference):

    • src: 輸入圖像
    • dst: 輸出圖像
    • Size( w,h ): 定義內核大小( w 像素寬度, h 像素高度)
    • Point(-1, -1): 指定錨點位置(被平滑點), 如果是負值,取核的中心爲錨點。
  • 高斯濾波器:

    OpenCV函數GaussianBlur執行高斯平滑 :

    for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 )
        { GaussianBlur( src, dst, Size( i, i ), 0, 0 );
          if( display_dst( DELAY_BLUR ) != 0 ) { return 0; } }
    
  • 中值濾波器:

    OpenCV函數medianBlur執行中值濾波操作:

    for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 )
        { medianBlur ( src, dst, i );
          if( display_dst( DELAY_BLUR ) != 0 ) { return 0; } }
    

    我們用了3個參數:

    • src: 輸入圖像
    • dst: 輸出圖像, 必須與 src 相同類型
    • i: 內核大小 (只需一個值,因爲我們使用正方形窗口),必須爲奇數。
  • 雙邊濾波器

    OpenCV函數bilateralFilter執行雙邊濾波操作:

    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; } }
    

    我們使用了5個參數:

    • src: 輸入圖像
    • dst: 輸出圖像
    • d: 像素的鄰域直徑
    • \sigma_{Color}: 顏色空間的標準方差
    • \sigma_{Space}: 座標空間的標準方差(像素單位)

結果

  • 程序顯示了原始圖像( lena.jpg) 和使用4種濾波器之後的效果圖。

  • 這裏顯示的是使用 中值濾波 之後的效果圖:

    Smoothing with a median filter
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章