由於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倍多,而效果上區別不大,可作為實時性特別強的場合的備用算法。