天天看點

圖像程式設計學習筆記8——圖像的平滑(去噪)

以下文字内容copy于<<數字圖像處理程式設計入門>>,code為自己實作,是win32控制台程式。

先舉個例子說明一下什麼是平滑(smoothing),如下面兩幅圖所示:可以看到,圖3.2比圖3.1柔和一些(也模糊一些)。是不是覺得很神奇?其實實作起來很簡單。我們将原圖中的每一點的灰階和它周圍八個點的灰階相加,然後除以9,作為新圖中對應點的灰階,就能實作上面的效果。

圖像程式設計學習筆記8——圖像的平滑(去噪)

這麼做并非瞎蒙,而是有其道理的。大概想一想,也很容易明白。舉個例子,就象和面一樣,先在中間加點水,然後不斷把周圍的面和進來,攪拌幾次,面就均勻了。

用信号處理的理論來解釋,這種做法實作的是一種簡單的低通濾波器(low pass

filter)。哇,好深奧呀!不要緊,這些理論的内容并不多,而且知道一些理論也是很有好處的。在灰階連續變化的圖象中,如果出現了與相鄰象素的灰階相差很大的點,比如說一片暗區中突然出現了一個亮點,人眼能很容易覺察到。就象看老電影時,由于膠片太舊,螢幕上經常會出現一些亮斑。這種情況被認為是一種噪聲。灰階突變在頻域中代表了一種高頻分量,低通濾波器的作用就是濾掉高頻分量,進而達到減少圖象噪聲的目的。

為了友善地叙述上面所說的“将原圖中的每一點的灰階和它周圍八個點的灰階相加,然後除以9,作為新圖中對應點的灰階”這一操作,我們采用如下的表示方法:

圖像程式設計學習筆記8——圖像的平滑(去噪)

(3.1)

這種表示方法有點象矩陣,我們稱其為模闆(template)。中間的黑點表示中心元素,即,用哪個元素做為處理後的元素。例如[2.

1]表示将自身的2倍加上右邊的元素作為新值,而[2 1.]表示将自身加上左邊元素的2倍作為新值。

通常,模闆不允許移出邊界,是以結果圖象會比原圖小,例如模闆是

圖像程式設計學習筆記8——圖像的平滑(去噪)

,原圖是

圖像程式設計學習筆記8——圖像的平滑(去噪)

,經過模闆操作後的圖象為

圖像程式設計學習筆記8——圖像的平滑(去噪)

;其中數字代表灰階,x表示邊界上無法進行模闆操作的點,通常的做法是複制原圖的灰階,不進行任何處理。

模闆操作實作了一種鄰域運算(neighborhoodoperation),即某個象素點的結果灰階不僅和該象素灰階有關,而且和其鄰域點的值有關。在以後介紹的細化算法中,我們還将接觸到鄰域運算。模闆運算的數學涵義是一種卷積(或互相關)運算,你不需要知道卷積的确切含義,隻要有這麼一個概念就可以了。

模闆運算在圖象進行中經常要用到,可以看出,它是一項非常耗時的運算。以

圖像程式設計學習筆記8——圖像的平滑(去噪)

(3.2)

為例,每個象素完成一次模闆操作要用9個乘法、8個加法、1個除法。對于一幅n×n(寬度×高度)的圖象,就是9n2個乘法,8n2個加法和n2個除法,算法複雜度為o(n2),這對于大圖象來說,是非常可怕的。是以,一般常用的模闆并不大,如3×3,4×4。有很多專用的圖象處理系統,用硬體來完成模闆運算,大大提高了速度。另外,可以設法将二維模闆運算轉換成一維模闆運算,對速度的提高也是非常可觀的。例如,(3.2)式可以分解成一個水準模闆和一個垂直模闆,即,

圖像程式設計學習筆記8——圖像的平滑(去噪)

(3.3)

我們來驗證一下。

設圖象為 

圖像程式設計學習筆記8——圖像的平滑(去噪)

,經過(3.2)式處理後變為

圖像程式設計學習筆記8——圖像的平滑(去噪)

,經過(3.3)式處理後變為

圖像程式設計學習筆記8——圖像的平滑(去噪)

,兩者完全一樣。如果計算時不考慮周圍一圈的象素,前者做了4×(9個乘法,8個加法,1個除法),共36個乘法,32個加法,4個除法;後者做了4×(3個乘法,2個加法)+4×(3個乘法,2個加法)+4個除法,共24個乘法,16個加法,4個除法,運算簡化了不少,如果是大圖,效率的提高将是非常客觀的。

平滑模闆的思想是通過将一點和周圍8個點作平均,進而去除突然變化的點,濾掉噪聲,其代價是圖象有一定程度的模糊。上面提到的模闆(3.1),就是一種平滑模闆,稱之為box模闆。box模闆雖然考慮了鄰域點的作用,但并沒有考慮各點位置的影響,對于所有的9個點都一視同仁,是以平滑的效果并不理想。實際上我們可以想象,離某點越近的點對該點的影響應該越大,為此,我們引入了權重系數,将原來的模闆改造成

圖像程式設計學習筆記8——圖像的平滑(去噪)

,可以看出,距離越近的點,權重系數越大。

新的模闆也是一個常用的平滑模闆,稱為高斯(gauss)模闆。為什麼叫這個名字,這是因為這個模闆是通過采樣2維高斯函數得到的。

圖像程式設計學習筆記8——圖像的平滑(去噪)

,分别用兩種平滑模闆處理(周圍一圈象素直接從原圖拷貝)。采用box模闆的結果為

圖像程式設計學習筆記8——圖像的平滑(去噪)

,采用高斯模闆的結果為

圖像程式設計學習筆記8——圖像的平滑(去噪)

可以看到,原圖中出現噪聲的區域是第2行第2列和第3行第2列,灰階從2一下子跳到了6,用box模闆處理後,灰階從3.11跳到4.33;用高斯模闆處理後,灰階從3.跳到4.56,都緩和了跳變的幅度,從這一點上看,兩者都達到了平滑的目的。但是,原圖中的第3,第4行總的來說,灰階值是比較高的,經模闆1處理後,第3行第2列元素的灰階變成了4.33,與第3,第4行的總體灰階相比偏小,另外,原圖中第3行第2列元素的灰階為6,第3行第3列元素的灰階為4,變換後,後者4.56反而比前者4.33大了。而采用高斯模闆沒有出現這些問題,究其原因,就是因為它考慮了位置的影響。

舉個實際的例子:下圖中,從左到右分别是原圖,用高斯模闆處理的圖,用box模闆處理的圖,可以看出,采用高斯模闆,在實作平滑效果的同時,要比box模闆清晰一些。

圖像程式設計學習筆記8——圖像的平滑(去噪)

功能實作函數代碼:

[cpp] 

double tem[9] = {1.0,2.0,1.0,2.0,4.0,2.0,1.0,2.0,1.0};  

void smooth()  

{  

    int height = bmpinfoheader.biheight;     

    int width = bmpinfoheader.biwidth;    

    int imgsize = bmpinfoheader.bisizeimage;  

    int linebyte = (width * 8 +31) / 32 * 4;  //每行像素所占位元組數  

    //處理是基于原圖的,是以原圖的資料不能改變,用pnewbmpdata存儲改變之後的資料  

    memcpy(pnewbmpdata,pbmpdata,imgsize);   //把原圖資料複制給pnewbmpdata  

    double sum;  

    for(int i = 1; i < height - 1; i++ )  

    {  

        for(int j = 1; j < width - 1; j++ )  

        {  

            sum = 0;    //清零  

            sum += (double)(*(pbmpdata + (i-1) * linebyte + j - 1)) * tem[0];   //該點左下角  

            sum += (double)(*(pbmpdata + (i-1) * linebyte + j)) * tem[1];       //下  

            sum += (double)(*(pbmpdata + (i-1) * linebyte + j + 1)) * tem[2];  //右下  

            sum += (double)(*(pbmpdata + i * linebyte + j - 1)) * tem[3];  //左  

            sum += (double)(*(pbmpdata + i * linebyte + j)) * tem[4];  //該點位置  

            sum += (double)(*(pbmpdata + i * linebyte + j + 1)) * tem[5];  //右  

            sum += (double)(*(pbmpdata + (i+1) * linebyte + j - 1)) * tem[6];  //左上  

            sum += (double)(*(pbmpdata + (i+1) * linebyte + j)) * tem[7];   //上  

            sum += (double)(*(pbmpdata + (i+1) * linebyte + j + 1)) * tem[8];  //右上  

            *(pnewbmpdata + i * linebyte + j) = (unsigned char)(sum / 16.0);     

        }  

    }  

}  

中值濾波也是一種典型的低通濾波器,它的目的是保護圖象邊緣的同時去除噪聲。所謂中值濾波,是指把以某點(x,y)為中心的小視窗内的所有象素的灰階按從大到小的順序排列,将中間值作為(x,y)處的灰階值(若視窗中有偶數個象素,則取兩個中間值的平均)。中值濾波是如何去除噪聲的呢?舉個例子就很容易明白了。

圖像程式設計學習筆記8——圖像的平滑(去噪)

圖中數字代表該處的灰階。可以看出原圖中間的6和周圍的灰階相差很大,是一個噪聲點。經過3×1視窗(即水準3個象素取中間值)的中值濾波,得到右邊那幅圖,可以看出,噪聲點被去除了。

下面将中值濾波和上面介紹的兩種平滑模闆作個比較,看看中值濾波有什麼特點。我們以一維模闆為例,隻考慮水準方向,大小為3×1(寬×高)。box模闆為 

圖像程式設計學習筆記8——圖像的平滑(去噪)

,高斯模闆為

圖像程式設計學習筆記8——圖像的平滑(去噪)

 。

先考察第一幅圖:

圖像程式設計學習筆記8——圖像的平滑(去噪)

從原圖中不難看出左邊區域灰階值低,右邊區域灰階值高,中間有一條明顯的邊界,這一類圖象稱之為“step”(就象灰階上了個台階)。應用平滑模闆後,圖象平滑了,但是也使邊界模糊了。應用中值濾波,就能很好地保持原來的邊界。是以說,中值濾波的特點是保護圖象邊緣的同時去除噪聲。

再看第二幅圖:

圖像程式設計學習筆記8——圖像的平滑(去噪)

不難看出,原圖中有很多噪聲點(灰階為正代表灰階值高的點,灰階為負代表灰階值低的點),而且是雜亂無章,随機分布的。這也是一類很典型的圖,稱之為高斯噪聲。經過box平滑,噪聲的程度有所下降。gauss模闆對付高斯噪聲非常有效。而中值濾波對于高斯噪聲則無能為力。

最後看第三幅圖:

圖像程式設計學習筆記8——圖像的平滑(去噪)

從原圖中不難看出,中間的灰階要比兩邊高許多。這也是一類很典型的圖,稱之為脈沖 (impulse)。可見,中值濾波對脈沖噪聲非常有效。

綜合以上三類圖,不難得出下面的結論:中值濾波容易去除孤立點,線的噪聲同時保持圖象的邊緣;它能很好的去除二值噪聲,但對高斯噪聲無能為力。要注意的是,當視窗内噪聲點的個數大于視窗寬度的一半時,中值濾波的效果不好。這是很顯然的。

code:

/** 

* 函數名: medianfilter 

* 功  能: 對圖像進行水準中值濾波處理 

*/  

void medianfilter()  

    unsigned char g[3];   //要取的三個點  

    //注意邊界點不處理,是以i從1到高度-2,j類似  

            g[0] = *(pbmpdata + i * linebyte + j - 1);  

            g[1] = *(pbmpdata + i * linebyte + j);  

            g[2] = *(pbmpdata + i * linebyte + j + 1);  

            sort(g,g+3);   //排序  

            *(pnewbmpdata + i * linebyte + j) = g[1];