GodZza

导航

图片去黑底

前文【让粒子可以在白色背景显示 [BLENDING SHADER 实操]】说到最后的效果 有点BUG, 在一些过渡的地方有些黑边。 这个问题倒是和算法无关,是和图片本身有关系。 下面是效果对比图:

有黑边 没黑边(完美)

 

其原因是做图的时候,为了方便做图, 开始是不会直接用"透明"这个概念做图,而是将图做成黑色底,然后最后将黑底转为Alpha=0 的颜色,所以 透明图的RGBA是(0,0,0,1)。 而这种做法对 Additive 是没影响的。但是对于我这个Shader 却是有问题了。

在网上也有人遇到过类似的问题:(渲染tga格式的序列帧,这种黑边怎么优化呀

其根本原因是没有使用 Additive 的叠加方式(使用了就没问题,会有一种光了一点点的感觉,当然在PS上没办法看到Additive效果)

而图片去黑的方法也有很多,使用PS也行,在AE 中也有个叫UnMult的插件。

然而同事反应 用PS 太麻烦了(虽然有Action),还是需要手动做几步操作,而且纯黑白的图效果不太好。

 

 

 

 所以我干脆就使用C#写了个插件(仅限PNG格式)

首先得明白去黑底的原理

去黑底的图片分两种
1.纯黑白(没有其他RGB颜色的图)

2.一种色有除黑白外其他颜色的图片

无论是那种图,其去黑底的原理都是 “越黑的像素越透明”。只是实现的细节上有区别。

对于

1.纯黑白的处理就简单多了,所有像素设置成Rgba(1,1,1, (src.r+src.g+src.b)/3) 即可。
2.多彩色的图片,不能使用纯黑白的处理,否则所有图都会变成黑白,其原理是找出原像素点中RGB分量中最高的值 maxV =Max(src.r,src.g,src.b),一般其透明度就是该值maxV。
因为其他值相对maxV比较少,所以rgb 分别加上 255-maxV 的差值,其原理是将该像素点"变亮"了,从而取消了黑底对该像素点的影响。而黑底将转化为透明度

而使用多彩色图片转换的算法对纯黑白的支持也不太好, 有可能会残留灰底,其原因是 有可能在美工做图时 没有真正地使用"纯灰度",也就是说 rgb 的分值不是相等的,这里对2种图片分别做了处理,在工具中使用 /F 参数是针对纯黑白的图处理。

 

具体代码如下:

 

 

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
namespace ImageBlackToAlpha
{
    class Program
    {
        static byte Max(params byte[] values)
        {
            if (values == null || values.Length == 0)
                return 0;
            var max = values[0];
            for (var i = 1; i < values.Length; ++i)
            {
                max = Math.Max(max, values[i]);
            }
            return max;
        }

        static byte Min(params byte[] values)
        {
            if (values == null || values.Length == 0)
                return 0;
            var min = values[0];
            for (var i = 1; i < values.Length; ++i)
            {
                min = Math.Min(min, values[i]);
            }
            return min;
        }

        static void Main(string[] args)
        {
            if (args.Count() < 2)
            {
                Console.WriteLine("Usage: \"ImageBlackToAlpha\" [+ source] [destination [/F]]");
                Console.WriteLine(" source        InputPngPath ");
                Console.WriteLine(" destination   OutputPngPath");
                Console.WriteLine(" /F  Forces all RGB to White(254,254,254)");

                return;
            }

            var toWhite = false;
            var image = Image.FromFile(args[0]);
            var bitmap = new Bitmap(image);
            var save = new Bitmap(bitmap.Width, bitmap.Height);

            if (args.Count() == 3)
            {
                toWhite = args[2] == "/F" || args[2] == "/f";
            }

            for (var x = 0;x< bitmap.Width;++x)
            {
                for(var y = 0; y < bitmap.Height; ++y)
                {
                    var clr = bitmap.GetPixel(x, y);

                    if (toWhite)
                    {
                        // 只支持 纯黑白贴图
                        var alpha = (0 + clr.R + clr.G + clr.B) / 3;
                        alpha = Math.Min(clr.A, alpha);
                        //因为 纯白(RGB)而Alpha小于255 会出现 全透明的问题 所以不能使用 255//bug?
                        clr = Color.FromArgb(alpha, 254, 254, 254);
                    }
                    else
                    {
                        var max = Max(clr.R, clr.G, clr.B);
                        //因为 纯白(RGB)而Alpha小于255 会出现 全透明的问题 所以不能使用 255//bug?
                        var sub = 254 - max;
                        var alpha = Math.Min(clr.A, max);
                        clr = Color.FromArgb(alpha, clr.R + sub, clr.G + sub, clr.B + sub);
                    }
                    save.SetPixel(x, y, clr);
                }
            }
            
            save.MakeTransparent(Color.Transparent);
            save.Save(args[1], ImageFormat.Png);
        }
    }
}

效果图:

 

 注:在测试过程中发现rgb(1,1,1)[RGB32(255,255,255)] 的时候,无论透明度设置成多少,该像素依然是全透明,不清楚这是PNG的 Feature 还是 System.Drawing.Imaging 处理的问题,这里统一使用了 254 代替255 避开了这个BUG。

posted on 2017-09-15 17:04  GodZza  阅读(4283)  评论(1编辑  收藏  举报