程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 圖像柔光效果(SoftGlow)的原理及其實現

圖像柔光效果(SoftGlow)的原理及其實現

編輯:C#入門知識

  由於CSDN博客和博客園的編輯方面有不一致的地方,導致文中部分圖片錯位,為不影響浏覽效果,建議點擊打開鏈接。 

     圖像柔光效果在很多商業軟件中都有實現,比如美圖秀秀,光影魔術手等。其能針對原始圖像產生一副新的比較平滑感覺光線比較柔和的效果,給人一種朦胧美,如下面幾幅圖所示:

         \  \  \  

     目前,關於該算法的可控參數,美圖秀秀只提供了一個程度(0-100%)控制量,其算法調節的效果和幅度都較小,光影魔術手有柔化程度和高光柔化兩個參數,其中柔化程度控制柔化的朦胧效果,高光柔化調節圖像的亮度。 還有一些開源的軟件如Imagestone、paint.net、gimp也有softglow算法,他們都提供了3個控制量:半徑、亮度、對比度(銳度),其中Imagestone其實是翻譯的gimp的算法,而GIMP和paint.net的算法在基本原理上是一樣的,細節上有所區別而已。

     我們以paint.net的實現過程為例進行說明,在paint.net的源代碼中,GlowEffect.cs為實現該效果的文件,我抽取其部分源代碼簡要說明下這個算法的過程。

[csharp] view plaincopyprint?public GlowEffect()  : base(StaticName, StaticImage, null, EffectDirectives.None, true) 

    this.blurEffect = new BlurEffect(); 
    this.bcAdjustment = new BrightnessAndContrastAdjustment(); 
    this.screenBlendOp = new UserBlendOps.ScreenBlendOp(); 

        public GlowEffect()  : base(StaticName, StaticImage, null, EffectDirectives.None, true)
        {
            this.blurEffect = new BlurEffect();
            this.bcAdjustment = new BrightnessAndContrastAdjustment();
            this.screenBlendOp = new UserBlendOps.ScreenBlendOp();
        }
     以及代碼片段:

[csharp] view plaincopyprint?public override unsafe void Render( 
         EffectConfigToken parameters,  
         RenderArgs dstArgs,  
         RenderArgs srcArgs,  
         System.Drawing.Rectangle[] rois,  
         int startIndex,  
         int length) 
     { 
         // First we blur the source, and write the result to the destination surface  
         // Then we apply Brightness/Contrast with the input as the dst, and the output as the dst  
         // Third, we apply the Screen blend operation so that dst = dst OVER src  
 
         ThreeAmountsConfigToken token = (ThreeAmountsConfigToken)parameters; 
 
         AmountEffectConfigToken blurToken = new AmountEffectConfigToken(token.Amount1); 
         this.blurEffect.Render(blurToken, dstArgs, srcArgs, rois, startIndex, length); 
 
         BrightnessAndContrastAdjustmentConfigToken bcToken = new BrightnessAndContrastAdjustmentConfigToken(token.Amount2, token.Amount3); 
         this.bcAdjustment.Render(bcToken, dstArgs, dstArgs, rois, startIndex, length); 
 
         for (int i = startIndex; i < startIndex + length; ++i) 
         { 
             Rectangle roi = rois[i]; 
 
             for (int y = roi.Top; y < roi.Bottom; ++y) 
             { 
                 ColorBgra* dstPtr = dstArgs.Surface.GetPointAddressUnchecked(roi.Left, y); 
                 ColorBgra* srcPtr = srcArgs.Surface.GetPointAddressUnchecked(roi.Left, y); 
 
                 screenBlendOp.Apply(dstPtr, srcPtr, dstPtr, roi.Width); 
             } 
         } 
     } 

   public override unsafe void Render(
            EffectConfigToken parameters,
            RenderArgs dstArgs,
            RenderArgs srcArgs,
            System.Drawing.Rectangle[] rois,
            int startIndex,
            int length)
        {
            // First we blur the source, and write the result to the destination surface
            // Then we apply Brightness/Contrast with the input as the dst, and the output as the dst
            // Third, we apply the Screen blend operation so that dst = dst OVER src

            ThreeAmountsConfigToken token = (ThreeAmountsConfigToken)parameters;

            AmountEffectConfigToken blurToken = new AmountEffectConfigToken(token.Amount1);
            this.blurEffect.Render(blurToken, dstArgs, srcArgs, rois, startIndex, length);

            BrightnessAndContrastAdjustmentConfigToken bcToken = new BrightnessAndContrastAdjustmentConfigToken(token.Amount2, token.Amount3);
            this.bcAdjustment.Render(bcToken, dstArgs, dstArgs, rois, startIndex, length);

            for (int i = startIndex; i < startIndex + length; ++i)
            {
                Rectangle roi = rois[i];

                for (int y = roi.Top; y < roi.Bottom; ++y)
                {
                    ColorBgra* dstPtr = dstArgs.Surface.GetPointAddressUnchecked(roi.Left, y);
                    ColorBgra* srcPtr = srcArgs.Surface.GetPointAddressUnchecked(roi.Left, y);

                    screenBlendOp.Apply(dstPtr, srcPtr, dstPtr, roi.Width);
                }
            }
        }
    由以上代碼初步得出結論:他們是以高斯模糊以及亮度對比度調節兩個濾鏡為基礎,稍作混合即可。

      第一步:備份原始圖像;

      第二步:對原始圖像按指定的半徑進行高斯模糊;

      第三步:對模糊後的圖像繼續進行亮度和對比度的調整;

      第四步:用原始圖像的備份數據同原始圖像(經過上述二及三步驟處理後的圖像)按照Photoshop的濾色(Screen)方式進行混合。

      關於濾色方式的混合算法這裡簡單的提一下:  Blend = X + Y - X * Y / 255 ;  其中X和Y分別表示基色和混合色,Blend表示結果色。

      算法的源碼可以參考我在上面說的幾個開源的軟件,當然這可能需要你有一定的編程基礎,畢竟那些軟件的框架都比較復雜。

      關於算法的執行速度可以說只取決於第二步,因為亮度對比度的調節實際上是個查表的過程(PS的亮度對比度指令其實要比大家想象的復雜點的,這個有機會再談,也可以參考阿發伯的博文http://blog.csdn.net/maozefa/article/details/4778934),而第四步其實也是可以用查表的方式來加速的(不過一定要用一位的方式)。高斯模糊這個老生長談的問題,在我所搜索過的網頁中是沒有誰給出過一個完整的、完美的、執行速度和指定半徑無關的、可運行的VB或VC或JAVA程序源代碼(一般都是給出參考文章介紹)。我自己有這個方面的代碼,不過我也不願意共享。真正有興趣的我推薦你去找GIMP的算法代碼,在GIMP的整個源碼系統,至少給出了三種高斯模糊優化的代碼,分別位於blur-gauss.c(給出了2種:RLE及IIR優化代碼)以及contrast-retinex.c中(代碼特別簡潔),當然,那些代碼如果提取出來還應該進行代碼層面的整理和優化。Paint.net也提供了高斯模糊函數,不過其實質並不是高斯模糊,而是一種用線性分布的權重函數代替恆值權重,不過那個算法裡面沒有浮點運算,並且還存在比Paint.net裡的代碼快很多即執行時間於半徑無關的優化算法,而且該優化算法比任何真正的高斯模糊優化算法要快1倍多,而效果上區別不大,可作為實時性特別強的場合的備用算法。

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved