OpenCV-跟我學一起學數字圖像處理之中值濾波


中值濾波(median filter)在數字圖像處理中屬於空域平滑濾波的內容(spatial filtering)。對消除椒鹽噪聲具有很好的效果。

  • 數學原理

為了講述的便捷,我們以灰度圖為例。RGB三通道的彩色圖可以通過每一個通道各自的中值濾波聯合得到。

數字圖像是以矩陣的方式存儲的,具體存儲方式可以參見OpenCV手冊。中值濾波是通過所謂的mask operation操作進行的。以3x3的mask為例。設圖像的矩陣形式如下:

0,0 0,1 0,2 0,3 0,4 0,5
1,0 1,1 1,2 1,3 1,4 1,5
2,0 2,1 2,2 2,3 2,4 2,5
3,0 3,1 3,2 3,3 3,4 3,5
4,0 4,1 4,2 4,3 4,4 4,5
5,0 5,1 5,2 5,3 5,4 5,5

圖中每個方框代表一個像素,每個方框里的數字對代表該像素在圖中的位置。中值濾波從位置(1,1)開始,建立一個3x3的mask

i-1,j-1 i-1,j i-1,j+1
i,j-1 i,j i,j+1
i+1,j-1 i+1,j i+1,j+1

 

讓(1,1)與(i,j)對其,取出模板所覆蓋的范圍內像素值,取這九個值得中值然后賦給(1,1),作為(1,1)位置的新像素值。然后移動模板的中心(i,j)至(1,2),重復上面的操作,直至遍歷整幅圖片。這里面應該注意的是,我們在做中值濾波時,模板里的中值是賦給一個新建的全新圖像,被濾波的圖像像素值不會改變。這樣就保證了每一次模板覆蓋的區域都是原圖像的像素。

注:

  1. mask並不一定要用3x3的矩陣來做,模板的大小不是一定的。
  2. 從上面的操作過程可以看到,在圖像的最后一行和第一行,最后一列和第一列的像素都沒有被遍歷到,我們一般有兩種方案解決這個問題。
    1. 可以將該位置的所有像素賦值為零。
    2. 可以在原圖像的上下左右都加上相應的全零向量,然后再進行上述的中值濾波操作,這樣所有原圖的像素都可以被遍歷到。
  • 基於OpenCV中值濾波程序

OpenCV中也用現成的中值濾波函數medianblur,具體用法為

medianBlur(InputArray src,OutputArray dst,int ksize);

其中InputArray src為Mat類的被濾波圖片,OutputArray是Mat類的濾波后輸出結果,ksize是mask的大小,如,如果用3x3的模板,ksize就傳值3;

基於OpenCV的中指濾波代碼段如下,

 1 //load the Original Image and get some informations
 2 Mat src = imread("010.jpg",1);
 3 namedWindow("OriginalImage");
 4 imshow("OriginalImage",src);
 5 CV_Assert(src.depth() == CV_8U);
 6 const int nr = src.rows;
 7 const int nc = src.cols;
 8 const int nchannels = src.channels();
 9 
10 //OpenCV Solution
11 Mat result_opencv;
12 medianBlur(src,result_opencv,3);
13 namedWindow("median filter_opencv");
14 imshow("median filter_opencv",result_opencv);

仿真結果:

原圖:

用medianBlur中值濾波的結果:

  • 基於中值濾波原理編寫的中值濾波函數仿真

這次我們進行的不再是灰度圖的仿真,所以有必要簡單介紹一下彩色圖在Mat類里保存的方式,見下圖(來源:opencv.org)。

圖片來源:opencv.org

從圖中我們可以看到,彩色圖片中像素的三通道是保存在同一行中的,順序是BGR。假如指針p[i]定位到的是(0,1)的藍色通道,那么p[i+Mat.channels]定位到的就是下一列的藍色通道。

基於中值濾波原理編寫的中值濾波函數代碼段如下

 1 //own median filter algorithm
 2 uchar* previous = NULL;
 3 uchar* current = NULL;
 4 uchar* next = NULL;
 5 uchar* current_result_own = NULL;
 6 int arr[9];   //use 3*3 mask
 7 for(int i=1;i<nr-1;i++)
 8 {
 9     previous = src.ptr<uchar>(i-1);
10     current = src.ptr<uchar>(i);
11     next = src.ptr<uchar>(i+1);
12     current_result_own = result_own.ptr<uchar>(i);
13     for(int j=nchannels;j<nchannels*(nc-1);j++)
14     {
15         for(int k=0;k<3;k++)
16         {
17             arr[k] = previous[j+(k-1)*nchannels];
18             arr[k+3] = current[j+(k-1)*nchannels];
19             arr[k+6] = next[j+(k-1)*nchannels];
20         }
21         bubble_sort(arr,9);
22         current_result_own[j] = arr[4];
23     }
24 }
25     
26 //set the pixels on the borders to zeros
27 result_own.row(0).setTo(Scalar(0));
28 result_own.row(nr-1).setTo(Scalar(0));
29 result_own.col(0).setTo(Scalar(0));
30 result_own.col(nc-1).setTo(Scalar(0));
31 
32 //show the result
33 namedWindow("median filter_own");
34 imshow("median filter_own",result_own);

其中bubble_sort是我用冒泡排序法寫的排序函數,代碼段如下,

 1 //************************//
 2 //bubble sort
 3 //************************//
 4 void bubble_sort(int* arr,int num)
 5 {
 6     int temp;
 7     for(int i=1;i<num-1;i++)
 8     {
 9         for(int j=0;j<num-i;j++)
10            {
11                if(arr[j]>arr[j+1])
12                {
13                    temp = arr[j];
14                    arr[j] = arr[j+1];
15                    arr[j+1] = temp;
16                }
17            }
18     }
19 }

仿真結果:


免責聲明!

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



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