OpenCV 之 圖像平滑


1  圖像平滑

  圖像平滑,一種圖像空間濾波方法 (低通濾波),可對圖像進行去噪 或 模糊化 (blurring)

  以 3X3 的濾波器為例 (即 a=b=1),則矩陣 Mx 和 Mf 對應的元素乘積之和,就是 g(x, y)

  其中,$ M_x = \begin{bmatrix} w(-1,-1) & w(-1,0) & w(-1,1) \\ w(0,-1) & w(0,0) & w(1,1) \\ w(1,-1) & w(1,0) & w(1,1) \\ \end{bmatrix} \qquad M_f = \begin{bmatrix} f(x-1,y-1) & f(x-1,y) & f(x-1,y+1) \\ f(x,y-1) & f(x,y) & f(x+1,y+1) \\ f(x+1,y-1) & f(x+1,y) & f(x+1,y+1) \\ \end{bmatrix}$

 

2  OpenCV 函數

  OpenCV 中主要有四個函數,分別是盒式濾波 (box),高斯濾波 (Gaussian),中值濾波 (median),雙邊濾波 (bilateral)

2.1  盒式濾波

 2.1.1 boxFilter

 輸出圖像的任一像素灰度值,等於其所有鄰域像素灰度值的平均值

  模糊化核為,$ K = \alpha \begin{bmatrix}  1 & 1 & ... & 1 & 1 \\ 1 & 1 & ... & 1 & 1 \\ \: & \: & ... & & & \\ 1 & 1 & ... & 1 & 1 \end{bmatrix} $  其中,$\alpha = \begin{cases} \frac{1}{ksize.width * ksize.height} & \text{when normalize = true} \\  1 & \text{otherwise} \\ \end{cases} $

void cv::boxFilter (     
    InputArray   src, // 輸入圖像
    OutputArray  dst, // 輸出圖像
    int    ddepth,      // 輸出圖像深度,-1 表示等於 src.depth()
 Size  ksize,       // 模糊化核 (kernel) 的大小
    Point  anchor = Point(-1,-1),       // 錨點位置,缺省值表示 anchor 位於模糊核的正中心
    bool   normalize = true,            // 是否歸一化處理
    int    borderType = BORDER_DEFAULT  // 邊界模式
)

2.1.2  blur

  取 ddepth = -1,normalize = true,則可由 boxFilter 得到模糊化函數 (blur)

boxFilter( src, dst, -1, ksize, anchor, true, borderType );

  blur 本質上是一個輸入和輸出圖像深度 (ddepth) 相同,並且做歸一化處理的盒式濾波器

void cv::blur (    
    InputArray  src,  
    OutputArray dst,      
Size ksize, Point anchor = Point(-1,-1), int borderType = BORDER_DEFAULT )

2.2  中值濾波

  中值濾波最為簡單,常用來消除椒鹽噪聲。輸出圖像 g (x, y)  的像素值,等於以輸入圖像 f (x, y) 為中心點的鄰域像素 (ksize x ksize) 平均值

void cv::medianBlur ( InputArray src, OutputArray dst, int ksize // 濾波器孔徑大小,一般為奇數且大於 1,比如 3, 5, 7, ... ) 

2.3  高斯濾波

  高斯濾波最為有用,它是根據當前像素和鄰域像素之間,空間距離的不同,計算得出一個高斯核 (鄰域像素的加權系數),

  然后,高斯核從左至右、從上到下遍歷輸入圖像,與輸入圖像的像素值求卷積和,得到輸出圖像的各個像素值

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

  無須理會公式的復雜,只需要記住一點即可:鄰域像素距離當前像素越遠 (saptial space),則其相應的加權系數越小

  為了便於直觀理解,可看下面這個一維高斯核,推而廣之將 G(x) 曲線以 x=0 這條軸為中心線,旋轉360度可想象其二維高斯核

   

void cv::GaussianBlur ( 
    InputArray  src, 
    OutputArray  dst,
    Size ksize,       // 高斯核的大小
    double  sigmaX,      // 高斯核在x方向的標准差
    double  sigmaY = 0,  // 高斯核在y方向的標准差,缺省為 0,表示 sigmaY = sigmaX
    int     borderType = BORDER_DEFAULT 
)  

  注意: 高斯核的大小 Size(width, height),w 和 h 二者不必相同但必須都是奇數,若都設為 0,則從 sigma 自動計算得出

2.4  雙邊濾波

  上面三種方法都是低通濾波,因此在消除噪聲的同時,也常會將邊緣信息模糊化。雙邊濾波和高斯濾波類似,但是它將鄰域像素的加權系數分為兩部分,

  第一部分與高斯濾波的完全相同,第二部分則考慮當前像素和鄰域像素之間灰度值的差異,從而在消除噪聲的基礎上,也較好的保留了圖像的邊緣信息

void cv::bilateralFilter (
    InputArray  src,
    OutputArray  dst,
    int     d,    // 像素鄰域直徑,若為非正值,則從 sigmaSpace 自動計算得出 double  sigmaColor,  // 顏色空間的標注方差 double  sigmaSpace,  // 坐標空間的標准方差 int     borderType = BORDER_DEFAULT 
)

   注意 1)  雙邊濾波相比以上三種濾波方法,其處理速度很慢,因此,一般建議取 d=5 用於實時圖像處理,d=9 適合於非實時的圖像領域

   注意 2)  sigmaColor 和 sigmaSpace 可取相同值,一般在 10 ~ 150 之間,小於 10,則沒什么效果,大於 150,則效果太強烈,看起來明顯“卡通化”

 

3  代碼示例

3.1 OpenCV

  OpenCV 中的示例,通過逐漸增大像素鄰域的大小 Size(w, h),將上述濾波過程動態化,非常形象的展示了鄰域大小對濾波效果的影響

  代碼摘抄

  1 /**
  2  * file Smoothing.cpp
  3  * brief Sample code for simple filters
  4  * author OpenCV team
  5  */
  6 #include <iostream>
  7 #include <vector>
  8 
  9 #include "opencv2/imgproc/imgproc.hpp"
 10 #include "opencv2/imgcodecs.hpp"
 11 #include "opencv2/highgui/highgui.hpp"
 12 #include "opencv2/features2d/features2d.hpp"
 13 
 14 using namespace std;
 15 using namespace cv;
 16 
 17 /// Global Variables
 18 int DELAY_CAPTION = 1500;
 19 int DELAY_BLUR = 100;
 20 int MAX_KERNEL_LENGTH = 31;
 21 
 22 Mat src; Mat dst;
 23 char window_name[] = "Smoothing Demo";
 24 
 25 /// Function headers
 26 int display_caption( const char* caption );
 27 int display_dst( int delay );
 28 
 29 
 30 /**
 31  * function main
 32  */
 33 int main( void )
 34 {
 35   namedWindow( window_name, WINDOW_AUTOSIZE );
 36 
 37   /// Load the source image
 38   src = imread( "../data/lena.jpg", 1 );
 39 
 40   if( display_caption( "Original Image" ) != 0 ) { return 0; }
 41 
 42   dst = src.clone();
 43   if( display_dst( DELAY_CAPTION ) != 0 ) { return 0; }
 44 
 45 
 46   /// Applying Homogeneous blur
 47   if( display_caption( "Homogeneous Blur" ) != 0 ) { return 0; }
 48 
 49   for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 )
 50       { blur( src, dst, Size( i, i ), Point(-1,-1) );
 51         if( display_dst( DELAY_BLUR ) != 0 ) { return 0; } }
 52 
 53 
 54   /// Applying Gaussian blur
 55   if( display_caption( "Gaussian Blur" ) != 0 ) { return 0; }
 56 
 57   for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 )
 58       { GaussianBlur( src, dst, Size( i, i ), 0, 0 );
 59         if( display_dst( DELAY_BLUR ) != 0 ) { return 0; } }
 60 
 61 
 62   /// Applying Median blur
 63   if( display_caption( "Median Blur" ) != 0 ) { return 0; }
 64 
 65   for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 )
 66       { medianBlur ( src, dst, i );
 67         if( display_dst( DELAY_BLUR ) != 0 ) { return 0; } }
 68 
 69 
 70   /// Applying Bilateral Filter
 71   if( display_caption( "Bilateral Blur" ) != 0 ) { return 0; }
 72 
 73   for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 )
 74       { bilateralFilter ( src, dst, i, i*2, i/2 );
 75         if( display_dst( DELAY_BLUR ) != 0 ) { return 0; } }
 76 
 77   /// Wait until user press a key
 78   display_caption( "End: Press a key!" );
 79 
 80   waitKey(0);
 81 
 82   return 0;
 83 }
 84 
 85 /**
 86  * @function display_caption
 87  */
 88 int display_caption( const char* caption )
 89 {
 90   dst = Mat::zeros( src.size(), src.type() );
 91   putText( dst, caption,
 92            Point( src.cols/4, src.rows/2),
 93            FONT_HERSHEY_COMPLEX, 1, Scalar(255, 255, 255) );
 94 
 95   imshow( window_name, dst );
 96   int c = waitKey( DELAY_CAPTION );
 97   if( c >= 0 ) { return -1; }
 98   return 0;
 99 }
100 
101 /**
102  * @function display_dst
103  */
104 int display_dst( int delay )
105 {
106   imshow( window_name, dst );
107   int c = waitKey ( delay );
108   if( c >= 0 ) { return -1; }
109   return 0;
110 }
View Code

3.2  濾波對比

  實際中,可直接調用以上四個濾波函數,代碼如下:

 1 #include "opencv2/imgproc/imgproc.hpp"
 2 #include "opencv2/highgui/highgui.hpp"
 3 
 4 using namespace cv;
 5 
 6 int main()
 7 {
 8     Mat src = imread("E:/smooth/bird.jpg");
 9     if(src.empty()) {
10         return -1;
11     }
12     imshow("original", src);
13 
14     Mat dst;
15 
16     blur(src, dst, Size(3,3));
17     imshow("blur", dst);
18 
19     medianBlur(src,dst,3);
20     imshow("medianBlur",dst);
21 
22     GaussianBlur(src,dst,Size(3,3),0);
23     imshow("GaussianBlur",dst);
24 
25     bilateralFilter(src,dst,9,50,50);
26     imshow("bilateralFilter",dst);
27 
28     waitKey(0);
29 }

  四種濾波方法的效果圖,如下所示:

 

參考資料

 <Digital Image Processing> 3rd, chapter 3

 <Learning OpenCV3>

 OpenCV Tutorials \ Image Processing (imgproc module) \ Smoothing Images

 圖像卷積與濾波的一些知識點,zouxy09

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM