以编程方式用白色填充替换图像中的透明区域?

3

我有一张PNG图片,正在使用.NET中的System.Drawing API进行操作。它有很大的透明区域,我想用白色填充替换透明区域,以便在图像中没有透明区域。在图像编辑程序中很容易实现...但是到目前为止,我在C#中尝试了很多次都没有成功。

有人可以给我一些指导吗?


1
这完全取决于你如何定义“透明”和“用白色填充替换”。PNG支持alpha透明度,因此在图形编辑程序中,如果像素的颜色是黑色并且透明度为50%,那么你的白色可能最终会变成灰色。 - Euphoric
1
我相信这些都是有用的解决方案,但我在尝试它们时遇到了麻烦,并发现客户端有一个更简单的解决方案。在我的情况下,我正在从Flex应用程序发送图像到.NET以保存到服务器,而我选择使用Flex / Flash API重新着色图像,然后将其传送到服务器。 - Josh
4个回答

4
我不确定如何检测透明像素。我知道如果 Alpha 为 0,它就是完全透明的,如果为 255,它就是不透明的。我不确定是否应该检查 Alpha == 0 还是 Alpha != 255;如果你可以尝试并给我反馈,那将很有帮助。
来自 MSDN 引用: Alpha 组件指定颜色的透明度:0 表示完全透明,255 表示完全不透明。同样,A 值为 255 表示不透明颜色。A 值从 1 到 254 表示半透明颜色。随着 A 接近 255,颜色变得更加不透明。
    void  Foo(Bitmap image)
    {
        for (int y = 0; y < image.Height; ++y)
        {
            for (int x = 0; x < image.Width; ++x)
            {
                // not very sure about the condition.                   
                if (image.GetPixel(x, y).A != 255)
                {
                    image.SetPixel(x,y,Color.White);
                }
            }
        }

    }

我测试过了,它的效果很好,但是比Euphoric的答案慢得多。GetPixel似乎会锁定每个调用,但你可以使用一个锁来完成相同的操作。或者甚至添加一些特殊函数:http://www.codeproject.com/Tips/240428/Work-with-bitmap-faster-with-Csharp - Meep

4

我的例子:

    public void FillPngWhite(Bitmap bmp)
    {
        if (bmp.PixelFormat != PixelFormat.Format32bppArgb)
            throw new ApplicationException("Not supported PNG image!");

        // Lock the bitmap's bits.  
        Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
        BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, bmp.PixelFormat);

        // Get the address of the first line.
        IntPtr ptr = bmpData.Scan0;

        // Declare an array to hold the bytes of the bitmap.
        int bytes  = Math.Abs(bmpData.Stride) * bmp.Height;
        byte[] rgbaValues = new byte[bytes];

        // Copy the RGB values into the array.
        System.Runtime.InteropServices.Marshal.Copy(ptr, rgbaValues, 0, bytes);

        // array consists of values RGBARGBARGBA

        for (int counter = 0; counter < rgbaValues.Length; counter += 4)
        {
            double t = rgbaValues[counter + 3]/255.0; // transparency of pixel between 0 .. 1 , easier to do math with this
            double rt = 1 - t; // inverted value of transparency

            // C = C * t + W * (1-t) // alpha transparency for your case C-color, W-white (255)
            // same for each color
            rgbaValues[counter] = (byte) (rgbaValues[counter]*t + 255*rt); // R color
            rgbaValues[counter + 1] = (byte)(rgbaValues[counter + 1] * t + 255 * rt); // G color
            rgbaValues[counter + 2] = (byte)(rgbaValues[counter + 2] * t + 255 * rt); // B color

            rgbaValues[counter + 3] = 255; // A = 255 => no transparency 
        }
        // Copy the RGB values back to the bitmap
        System.Runtime.InteropServices.Marshal.Copy(rgbaValues, 0, ptr, bytes);

        // Unlock the bits.
        bmp.UnlockBits(bmpData);
    }

这个例子与众不同的地方在于:

我使用 LockBits 而不是 GetPixelSetPixel。它更快,但理解起来有点困难。这是从MSDN修改得到的一个例子。

我考虑了真实的透明度值,正如我在你的问题中所说的评论。这将使带有50%透明度(128)的黑色看起来像灰色而不是黑色。原因是当我想象在图形编辑器中“用白色替换透明度”时,我会创建一个在图像下方填充白色的新图层,然后将两个图层合并。这个例子将产生同样的效果。


0

一旦您获得了位图对象的句柄,只需执行以下操作:

Bitmap yourImage = HOWEVER YOU LOAD YOUR IMAGE;
int width = YOUR IMAGE WIDTH;
int height = YOUR IMAGE HEIGHT;

Color c;
Color white = new Color(255,255,255,255)
for(int w = 0; w < width; w++)
for(int h = 0; h < height; h++)
{
    c = yourImage.GetPixel(w,h);
    yourImage.SetPixel(w,h, ((((short)(c.A)) & 0x00FF) <= 0)? white:c); //replace 0 here with some higher tolerance if needed
}

Color.A 的取值范围是从 0 到 255。为什么要使用 abs(c.A) - Bala R
根据系统的不同,这个值在1个字节中存储,取值范围为0到255。这个字节可以是有符号的,也就是说255会显示为-1,254会显示为-2,以此类推。 - user545199
实际上更好的方法是 (((short)(c.A)) & 0x00FF)。 - user545199
c#中的A是byte类型,byte的范围是0到255,对吧?http://msdn.microsoft.com/en-us/library/5bdb6693(VS.80).aspx - Bala R
可能吧,但我过去曾经遇到过问题,所以在我看来,保守一点更好。 - user545199

0
这可能过于简化您的问题,但如果它是在表单或其他可用控件上,您可以在将图像放在其上之前将背景涂成白色。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接