鸟语天空
美颜
post by:追风剑情 2021-7-7 11:59

一、原理

参考博文
Unity Shader 实现磨皮效果
Bilateral Filters(双边滤波算法)原理及实现(二)
对皮肤美白算法的一些研究

美白公式
效果:图像两端(明-暗)变化弱,中间变化强。

11111.png

w(x,y)为输入的像素颜色,v(x,y)为输出的像素颜色,beta为调节参数。

双边滤波公式
效果:保边去噪

22222.png

2222.png

11111.png(高斯滤波函数)

3333.png

g(i,j)为输出的像素颜色,f(k,l)为当前输入的像素颜色,S(i,j)是指以(i,j)为中心的(2N+1)*(2N+1)的卷积核覆盖的像素区域。

二、Shader

//一个简单美颜Shader
//功能:磨皮、美白
Shader "Custom/BeautyFaceShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        //模糊大小 (值越大越模糊)
        _Radius ("Radius", Range(0, 10)) = 5
        //调节距离空间权重,值越大权重越大
        _SigmaS("SigmaS", float) = 3
        //调节颜色空间权重,值越大权重越大
        _SigmaR("SigmaR", float) = 1
        //亮度值
        _Brightness("Brightness", float) = 20
        //对比度
        _Contrast("Contrast", float) = 1.18
        //饱和度
        _Saturation("Saturation", float) = 1
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            half4 _MainTex_TexelSize;
            float _Radius;
            float _SigmaS;
            float _SigmaR;
            float _Brightness;
            float _Contrast;
            float _Saturation;

            //计算像素亮度值
            float Luminance(float3 color)
            {
                return dot(color, float3(0.2125, 0.7154, 0.0721));
            }

            //提亮暗部区域
            float3 Brightness(float3 col)
            {
                //此公式: 使图像暗部变化强,亮部变化弱
                //col.r = log(col.r * _Brightness);
                //col.g = log(col.g * _Brightness);
                //col.b = log(col.b * _Brightness);
                
                //原理: https://blog.csdn.net/weixin_33716557/article/details/86197248?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-7.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-7.control
                //此公式: 使图像两端变化弱,中间变化强
                col.r = log(col.r * (_Brightness - 1) + 1) / log(_Brightness);
                col.g = log(col.g * (_Brightness - 1) + 1) / log(_Brightness);
                col.b = log(col.b * (_Brightness - 1) + 1) / log(_Brightness);
                return col;
            }
            //原理: https://blog.csdn.net/u013066730/article/details/87919412
            //转载: https://www.jianshu.com/p/90feece27a04?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation
            //双边滤波算法-保边去噪
            float3 BilateralFilter(float2 uv)
            {
                float i = uv.x;
                float j = uv.y;
                float sigmaSSquareMult2 = (2 * _SigmaS * _SigmaS);
                float sigmaRSquareMult2 = (2 * _SigmaR * _SigmaR);

                float3 centerCol = tex2D(_MainTex, uv).rgb;                 // 中心点像素的颜色 //
                float centerLum = Luminance(centerCol);                     // 中心点像素的亮度 //

                float3 sum_up;                                              // 分子 //
                float3 sum_down;                                                // 分母 //
                for (int k = -_Radius; k <= _Radius; k++)
                {
                    for (int l = -_Radius; l <= _Radius; l++)
                    {
                        float2 uv_new = uv + _MainTex_TexelSize.xy * float2(k, l);
                        float3 curCol = tex2D(_MainTex, uv_new).rgb;        // 当前像素的颜色 //
                        float curLum = Luminance(curCol);                       // 当前像素的亮度 //
                        float3 deltaColor = curCol - centerCol;
                        float len = dot(deltaColor, deltaColor);
                        // float exponent = -((i-k)*(i-k)+(j-l)*(j-l))/sigmaSSquareMult2 - (curLum-centerLum)*(curLum-centerLum)/sigmaRSquareMult2;
                        float exponent = -((i - k) * (i - k) + (j - l) * (j - l)) / sigmaSSquareMult2 - len / sigmaRSquareMult2;
                        float weight = exp(exponent);
                        sum_up += curCol * weight;
                        sum_down += weight;
                    }
                }

                float3 rgb = sum_up / sum_down;
                return rgb;
            }

            v2f vert(appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }

            float4 frag(v2f i) : SV_Target
            {
                //保边去噪
                float3 rgb = BilateralFilter(i.uv);
                //该像素对应的亮度值
                fixed luminance = Luminance(rgb);
                //使用该亮度值创建一个饱和度为0的颜色
                fixed3 luminanceColor = fixed3(luminance, luminance, luminance);
                //创建一个对比度度为0的颜色
                fixed3 avgColor = fixed3(0.5, 0.5, 0.5);

                //调整饱和度
                rgb = lerp(luminanceColor, rgb, _Saturation);
                //调整对比度
                rgb = lerp(avgColor, rgb, _Contrast);
                //调整亮度
                rgb = Brightness(rgb);

                return fixed4(rgb, 1);
            }
            ENDCG
        }
    }
}


效果

11111.png

评论:
发表评论:
昵称

邮件地址 (选填)

个人主页 (选填)

内容