Unsharp Mask(USM)銳化算法的的原理及其實現。


      在開局之前,首先說明一點,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  轉載請保留本行信息*************************

 

 


免責聲明!

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



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