在學習的道路上,看了許多博客,受益良多。隨着看過的內容越來越多,有時很難再找到之前看過的內容,遂決定自己也開一個博客,記錄學習的歷程。主要是為了方便自己查閱,也許某一天,也能幫助到別人。
最近在做畢業設計,需要使用到中值濾波這樣的常規圖像處理算法,往常都是使用OpenCV中自帶的函數進行濾波,非常方便。然而,這次濾波的對象不是一個“正經”的圖,它是內存中的一塊連續數據,轉換成OpenCV中的Mat並不方便,也不搞笑。只好采用直接讀取數據進行濾波,需要自己寫一段中值濾波。
好在中值濾波思想很簡單,很容易就能想通是怎么做的,簡單的來說就是以一個點的一定鄰域內數據的中值來代替這個點的數值。
以下內容來自foreverhuylee的博客:關於中值濾波算法,以及C語言實現
什么是中值濾波?
中值濾波是對一個滑動窗口內的諸像素灰度值排序,用其中值代替窗口中心象素的原來灰度值,它是一種非線性的圖像平滑法,它對脈沖干擾級椒鹽噪聲的抑制效果好,在抑制隨機噪聲的同時能有效保護邊緣少受模糊。
中值濾波可以過濾尖峰脈沖。目的在於我們對於濾波后的數據更感興趣。濾波后的數據保留的原圖像的變化趨勢,同時去除了尖峰脈沖對分析造成的影響。
以一維信號的中值濾波舉例。對灰度序列80、120、90、200、100、110、70,如果按大小順序排列,其結果為70、80、90、10O、110、120、200,其中間位置上的灰度值為10O,則該灰度序列的中值即為100。一維信號中值濾波實際上就是用中值代替規定位置(一般指原始信號序列中心位置)的信號值。對前面所舉的序列而言,中值濾波的結果是用中值100替代序列80、120、90、200、100、110、70中的信號序列中心位置值200,得到的濾波序列就是80、120、90、100、100、110、70。如果在此序列中200是一個噪聲信號,則用此方法即可去除這個噪聲點。
二維中值濾波算法是:對於一幅圖像的象素矩陣,取以目標象素為中心的一個子矩陣窗口,這個窗口可以是3*3 ,5*5 等根據需要選取,對窗口內的象素灰度排序,取中間一個值作為目標象素的新灰度值。窗口示例如ooooxoooo上面x為目標象素,和周圍o組成3*3矩陣Array,然后對這9個元素的灰度進行排序,以排序后的中間元素Array[4]為x的新灰度值,如此就完成對象素x的中值濾波,再迭代對其他需要的象素進行濾波即可。
圖像處理中,中值濾波的實現方法
1 unsigned char GetMedianNum(int * bArray, int iFilterLen) 2 { 3 int i,j;// 循環變量 4 unsigned char bTemp; 5 6 // 用冒泡法對數組進行排序 7 for (j = 0; j < iFilterLen - 1; j ++) 8 { 9 for (i = 0; i < iFilterLen - j - 1; i ++) 10 { 11 if (bArray[i] > bArray[i + 1]) 12 { 13 // 互換 14 bTemp = bArray[i]; 15 bArray[i] = bArray[i + 1]; 16 bArray[i + 1] = bTemp; 17 } 18 } 19 } 20 21 // 計算中值 22 if ((iFilterLen & 1) > 0) 23 { 24 // 數組有奇數個元素,返回中間一個元素 25 bTemp = bArray[(iFilterLen + 1) / 2]; 26 } 27 else 28 { 29 // 數組有偶數個元素,返回中間兩個元素平均值 30 bTemp = (bArray[iFilterLen / 2] + bArray[iFilterLen / 2 + 1]) / 2; 31 } 32 33 return bTemp; 34 }
1 /************************************************************************* 2 * 函數名稱: 3 * MedianFilter() 4 * 參數: 5 * int iFilterH - 濾波器的高度 6 * int iFilterW - 濾波器的寬度 7 * int iFilterMX - 濾波器的中心元素X坐標 8 * int iFilterMY - 濾波器的中心元素Y坐標 9 * 說明: 10 * 該函數對DIB圖像進行中值濾波。 11 ************************************************************************/ 12 #define iFilterW 1 13 #define iFilterH 1 14 #define iFilterMX 1 15 #define iFilterMY 1 16 #define WIDTHBYTES(bits) (((bits) + 31) / 32 * 4) 17 18 19 unsigned char GetMedianNum(int * bArray, int iFilterLen); 20 void MedianFilter(unsigned char *pImg1,unsigned char *pImg,int nWidth,int nHeight) 21 { 22 unsigned char *lpSrc; // 指向源圖像的指針 23 unsigned char *lpDst; // 指向要復制區域的指針 24 int aValue[iFilterH*iFilterW]; // 指向濾波器數組的指針 25 int i,j,k,l; // 循環變量 26 int lLineBytes; // 圖像每行的字節數 27 lLineBytes = WIDTHBYTES(nWidth * 8); 28 for ( i=0;i<nWidth;i++,pImg++ ) 29 (*pImg)=0; 30 // 開始中值濾波 31 // 行(除去邊緣幾行) 32 for(i = iFilterMY; i < nHeight - iFilterH + iFilterMY + 1; i++) 33 { 34 // 列(除去邊緣幾列) 35 for(j = iFilterMX; j < nWidth - iFilterW + iFilterMX + 1; j++) 36 { 37 // 指向新DIB第i行,第j個象素的指針 38 lpDst = pImg + lLineBytes * (nHeight - 1 - i) + j; 39 40 // 讀取濾波器數組 41 for (k = 0; k < iFilterH; k++) 42 { 43 for (l = 0; l < iFilterW; l++) 44 { 45 // 指向DIB第i - iFilterMY + k行,第j - iFilterMX + l個象素的指針 46 lpSrc = pImg1 + lLineBytes * (nHeight - 1 - i + iFilterMY - k) + j - iFilterMX + l; 47 48 // 保存象素值 49 aValue[k * iFilterW + l] = *lpSrc; 50 } 51 } 52 53 // 獲取中值 54 * lpDst = GetMedianNum(aValue, iFilterH * iFilterW); 55 } 56 } 57 58 } 59 60 unsigned char GetMedianNum(int * bArray, int iFilterLen) 61 { 62 int i,j; // 循環變量 63 unsigned char bTemp; 64 65 // 用冒泡法對數組進行排序 66 for (j = 0; j < iFilterLen - 1; j ++) 67 { 68 for (i = 0; i < iFilterLen - j - 1; i ++) 69 { 70 if (bArray[i] > bArray[i + 1]) 71 { 72 // 互換 73 bTemp = bArray[i]; 74 bArray[i] = bArray[i + 1]; 75 bArray[i + 1] = bTemp; 76 } 77 } 78 } 79 80 // 計算中值 81 if ((iFilterLen & 1) > 0) 82 { 83 // 數組有奇數個元素,返回中間一個元素 84 bTemp = bArray[(iFilterLen + 1) / 2]; 85 } 86 else 87 { 88 // 數組有偶數個元素,返回中間兩個元素平均值 89 bTemp = (bArray[iFilterLen / 2] + bArray[iFilterLen / 2 + 1]) / 2; 90 } 91 92 return bTemp; 93 }