在開局之前,首先說明一點,Photoshop的USM銳化只是本文所指USM的一種比較特殊的例子而已。
通過增強圖像的高頻部分的內容,圖像的視覺效果可以極大的得到改觀。為達到這個目的,通常可以使用經典USM技術來實現。這個技術的流程可用下圖來實現:
用具體的公式表達即為:
y(n,m)= x(n,m)+ λz(n,m) (1)
其中, x(n,m)為輸入圖像,y(n,m)為輸出圖像,而z(n,m)為校正信號,一般是通過對x進行高通濾波獲取。λ是用於控制增強效果的的一個縮放因子。
在傳統的USM算法中,z(n,m)一般可以通過下式獲取:
z(n,m)=4x(n,m)-x(n-1,m) -x(n +1,m)-x(n, m-1) -x(n,m+1) (2)
當然也可以用如下的模板:
貼部分參考代碼:
Width = Bitmap.Width; Height = Bitmap.Height; Stride = Bitmap.Stride; NewHeight = Height + 2; NewStride = (Width + 2) * BytePerPixel; ExpandPtr = (byte*)Win32Api.GlobalAlloc(Win32Const.GPTR, NewStride * NewHeight); // 為保證邊緣部分處理方便,擴展邊界部分 Utility.GetExpandImage(Bitmap, 1, ExpandPtr); // 拷貝圖像到緩沖區,以及填充邊緣像素 for (Y = 0; Y < Height; Y++) // 處理灰度圖像 { Pointer = Bitmap.Pointer + Y * Stride; ExpandP = ExpandPtr + (Y+1) * NewStride+1; for (X = 0; X < Width; X++) { HighPass = (ExpandP[X] << 2) - ExpandP[X - 1] - ExpandP[X + 1] - ExpandP[X - NewStride] - ExpandP[X + NewStride]; // z(n,m) Value = Pointer[X] + Amount * HighPass / 100; // x(n,m)+ λz(n,m),式中的Amount即這里的λ Pointer[X] = (byte)((((ushort)Value | ((short)(255 - Value) >> 15)) & ~Value >> 15)); // 防止數據溢出 } }
Win32Api.GlobalFree((IntPtr)ExpandPtr);
在很多場合,這個方法也能獲得較好的效果,比如下述的Lena圖。
原圖 Amount=25 Amount=100
不過這個方面也有以下的缺點: 1) 線性的高通濾波使得效果對噪音非常敏感,這會導致一些不希望的扭曲,特別在圖像變換比較緩慢的地方的噪音,比如上圖3中草帽的左側平坦區域。2)對於圖像的邊緣(高對比度區域)會出現增強過頭的現象,如上圖草帽的邊緣。 這兩點會導致輸出圖像存在一些令人看上去不舒服的地方。
很多論文中提出了一些自適應通過動態的改變式(1)中的λ參數來控制調節結果。在圖像的變換比較平坦的區域,λ取值小,在較大的對比度(邊緣處)地方取適當的λ,而在中對比度處取較大的λ值,以使得這部分的得到最大的增強。 比如這篇文章 Image Enhancement via Adaptive Unsharp Masking 中就提出了一種逐步更新的方式。不過類似這樣的文章都普遍存在一個問題,那就是可控參數過多,且這些從參數的取值需要過多的人工參與,我認為這樣的算法,是不具有實用的價值的。
在Photoshop的銳化菜單中也有一項USM銳化,其實這個功能也是符合式1的定義的。通過其UI界面我們可以發現其有3個參數:半徑、數量、閾值,其內部的算法過程可以用如下的簡單代碼表示:
Width = Bitmap.Width; Height = Bitmap.Height; Stride = Bitmap.Stride; BytePerPixel = Bitmap.BitCount / 8; FastBitmap Clone = Bitmap.Clone(); // 備份圖像數據 BlurEffect.GaussianBlur(Clone, Radius); // 對備份的數據進行高斯模糊處理 for (Y = 0; Y < Height; Y++) { Pointer = Bitmap.Pointer + Y * Stride; PointerC = Clone.Pointer + Y * Stride; for (X = 0; X < Width; X++) { Value = Pointer[X] - PointerC[X]; if (Utility.Abs (Value) > Threshold) { Value = Pointer[X] + Amount * Value / 100; Pointer[X] = (byte)((((ushort)Value | ((short)(255 - Value) >> 15)) & ~Value >> 15)); } } } Clone.Dispose();
眾所周知,高斯模糊時低通濾波,那么 Value = Pointer[X] - PointerC[X](原值-低通)則相當於高通的結果,如果原值和低通的差異的絕對值大於指定的閾值,則對改點進行所謂的銳化。
這里對式(1)多引進了一個參數閾值,通過調節該值,來決定達到何種程度對比度的像素才需要增強。
實際的效果表明,這種方式的銳化要比傳統的USM銳化能獲得更好的調節效果。
以下為Threshold=0,Amount=50時不同半徑的效果。
原圖 Radius=5 Radius=50 Radius=200
隨着半徑的增大,圖像的對比度逐漸變強,邊緣越發明顯,但是所有的調節后的圖像都未出現明顯的噪音增強,效果非常之理想,對於Lena圖,處理后的圖片中可以明顯的看出在眼睛部位,眼白和眼球的對比更為清晰、明顯,而且整幅圖形從感覺上說原始圖像較為朦朧,處理后的清晰不少。
Amount參數對效果的影響很明顯可以獲知,越大,對比度越高,但是由於其和結果的線性關系,這個參數的變化對結果的影響比其他兩個參數更敏感。
實際上,上述高斯模糊也可以用中值模糊、方框模糊來代替,所得到的效果和高斯濾波非常相似。在Paint.net的效果-》相片-》尖銳化濾鏡就是用的中值的方式實現的。
關於實現代碼,基本上我在上面已經提及,其實最關鍵還是高斯模糊的實現。如果你覺得有難度,正如上文所說,也可以用均值來代替,而均值模糊編碼則非常簡單。
同樣,提供個編譯好的文件給有興趣研究該算法的朋友看看效果:
http://files.cnblogs.com/Imageshop/USM.rar
***************************作者: laviewpbt 時間: 2013.5.19 聯系QQ: 33184777 轉載請保留本行信息*************************