【问题标题】:Is there a way to find out about a png image transparency by reading the image header?有没有办法通过读取图像标题来了解 png 图像的透明度?
【发布时间】:2010-09-23 14:21:12
【问题描述】:

有没有办法通过读取图像标题来了解 png 图像的透明度?

【问题讨论】:

标签: c# .net png


【解决方案1】:

二进制访问

在我评论GetPixel 对于每个像素的性能不佳之后,我尝试编写一个 sn-p 来查找图像中是否存在透明像素(包括 PNG)。在这里。

public static bool IsImageTransparent(string fullName)
{
    using (Bitmap bitmap = Bitmap.FromFile(fullName) as Bitmap)
    {
        bool isTransparent;

        // Not sure if the following enumeration is correct. Maybe some formats do not actually allow transparency.
        PixelFormat[] formatsWithAlpha = new[] { PixelFormat.Indexed, PixelFormat.Gdi, PixelFormat.Alpha, PixelFormat.PAlpha, PixelFormat.Canonical, PixelFormat.Format1bppIndexed, PixelFormat.Format4bppIndexed, PixelFormat.Format8bppIndexed, PixelFormat.Format16bppArgb1555, PixelFormat.Format32bppArgb, PixelFormat.Format32bppPArgb, PixelFormat.Format64bppArgb, PixelFormat.Format64bppPArgb };

        if (formatsWithAlpha.Contains(bitmap.PixelFormat))
        {
            // There might be transparency.
            BitmapData binaryImage = bitmap.LockBits(new Rectangle(Point.Empty, bitmap.Size), ImageLockMode.ReadOnly, PixelFormat.Format64bppArgb);

            unsafe
            {
                byte* pointerToImageData = (byte*)binaryImage.Scan0;
                int numberOfPixels = bitmap.Width * bitmap.Height;

                isTransparent = false;

                // 8 bytes = 64 bits, since our image is 64bppArgb.
                for (int i = 0; i < numberOfPixels * 8; i += 8)
                {
                    // Check the last two bytes (transparency channel). First six bytes are for R, G and B channels. (0, 32) means 100% opacity.
                    if (pointerToImageData[i + 6] != 0 || pointerToImageData[i + 7] != 32)
                    {
                        isTransparent = true;
                        break;
                    }
                }
            }

            bitmap.UnlockBits(binaryImage);
        }
        else
        {
            // No transparency available for this image.
            isTransparent = false;
        }

        return isTransparent;
    }
}

优点:

  • 二进制访问,比GetPixel快得多,
  • 不需要额外的库或 WPF,
  • 适用于 GDI+ 支持的任何格式:BMP、GIF、JPEG、PNG、TIFF、Exif、WMF 和 EMF。

缺点:

  • 需要unsafe
  • 比直接读取PNG文件慢。

调色板

较少手动的方法是使用调色板。可能存在一些 .NET Framework 或第三方库可以让您这样做。我尝试了以下方法(使用 WPF):

using (Stream imageStreamSource = new FileStream(fullName, FileMode.Open, FileAccess.Read, FileShare.Read))
{
    PngBitmapDecoder decoder = new PngBitmapDecoder(imageStreamSource, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
    BitmapSource bitmapSource = decoder.Frames[0];

    return bitmapSource.Palette.Colors.Any(c => c.A != 0);
}

但我不工作,因为大多数时候bitmapSource.Palettenull。此外,与第一个 sn-p 相比,使用调色板会大大降低性能,因为在继续之前必须将每种颜色加载到颜色列表中。

【讨论】:

  • 调色板仅在您有索引图像时才存在。更不用说,调色图像(gif 和 png)做了一些奇怪的魔法,使一个调色板颜色充当“透明”。不知道这是否真的会出现在 .Net 调色板对象中,因为 .Net 中似乎不支持该系统。
  • 啊,不,迭代调色板适用于 8 位图像!您只需要检查它确实是 8 位图像。
【解决方案2】:

找出什么?如果图像具有透明度?您可以检查位深度,24 位(RGB)通常意味着没有透明度,32 位(RGBA)意味着有一个不透明度/透明度层

【讨论】:

  • 我需要确定图片是否有透明背景
【解决方案3】:

如果位深度为 24 或更低且调色板成员都不包含相关的 alpha 值,您有时可以判断它肯定没有透明度(您没有说是否要计算部分透明度是否透明)。

但是,要确保确实存在一定的透明度,确实需要检查整个图像。因此,流大小为 O(n)(图像大小大致为 O(x * y)),但在某些情况下可能会从标头中获得捷径。

【讨论】:

  • Jon Hanna,您是否有任何代码示例用于检查流是否更有效?
【解决方案4】:

如果 png 被索引,那么您可以检查 TRNS 块 Png chunks description。如果没有,那么您需要像在该方法中那样逐个像素地获取它。

【讨论】:

    【解决方案5】:

    谢谢大家。通过使用 ChrisF 的链接让它工作 Determine if Alpha Channel is Used in an Image 谢谢 ChrisF。

    这是我的代码:

        private bool IsImageTransparent(Bitmap image)
        {
            for (int i = 0; i < image.Width; i++)
                for (int j = 0; j < image.Height; j++)
            {
                var pixel = image.GetPixel(i, j);
                if (pixel.A != 255)
                    return true;
            } 
            return false;
        }
    

    【讨论】:

    • 请注意,没有什么比遍历每个像素并为每个像素使用 GetPixel 更慢和更糟糕的了。如果您想获得下降性能,您宁愿直接使用二进制数据。
    猜你喜欢
    • 1970-01-01
    • 2020-11-07
    • 2019-05-03
    • 1970-01-01
    • 2014-11-25
    • 2010-09-07
    • 2010-12-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多