确定图像中是否使用Alpha通道

当我将图像带入我的程序时,我想确定是否:

  1. 他们有一个alpha通道
  2. 如果使用那个alpha通道

使用Image.IsAlphaPixelFormat #1非常简单。 但是对于#2 ,除了遍历每个像素之外,还有一种简单的方法可以确定至少有一个像素是否具有使用的alpha通道(即设置为除255以外的其他值)? 我需要的只是一个布尔值,然后我将决定是将其保存为32位还是24位。

更新 :我发现ImageFlags.HasTranslucent应该为我提供我正在寻找的东西 – 不幸的是,它根本不起作用。 例如,具有至少alpha通道为66(半透明)的像素格式的PNG继续报告False (用法: if((img.Flags & ImageFlags.HasTranslucent) == 4) ...; )。 我已经测试了所有类型的图像,包括.bmp,其alpha值> 0且<255,并且仍然报告False 。 任何人都使用过它并知道它是否适用于GDI +?

你不必遍历每个像素(你可能会,但它取决于图像)。 设置为循环遍历所有像素,但是当您发现除255以外的alpha值时,只需突破循环使用以下伪代码:

 bool hasAlpha = false; foreach (var pixel in image) { hasAlpha = pixel.Alpha != 255; if (hasAlpha) { break; } } 

您只需检查没有任何alpha的图像的所有像素。 对于具有alpha的图像,这将很快爆发。

基于ChrisF的回答,我得到了一个更高级的解决方案:

 public bool IsImageTransparent(Bitmap image,string optionalBgColorGhost) { 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; } } //Check 4 corners to check if all of them are with the same color! if (!string.IsNullOrEmpty(optionalBgColorGhost)) { if (image.GetPixel(0, 0).ToArgb() == GetColorFromString(optionalBgColorGhost).ToArgb()) { if (image.GetPixel(image.Width - 1, 0).ToArgb() == GetColorFromString(optionalBgColorGhost).ToArgb()) { if (image.GetPixel(0, image.Height - 1).ToArgb() == GetColorFromString(optionalBgColorGhost).ToArgb()) { if (image.GetPixel(image.Width - 1, image.Height - 1).ToArgb() == GetColorFromString(optionalBgColorGhost).ToArgb()) { return true; } } } } } return false; } public static Color GetColorFromString(string colorHex) { return ColorTranslator.FromHtml(colorHex); } 

它有一个可选的bg颜色字符串到非透明图像:

用法示例:

 IsImageTransparent(new Bitmap(myImg),"#FFFFFF"); 

你找不到比这更好的解决方案,我花了几个小时来优化:

 public bool IsAlphaBitmap(ref System.Drawing.Imaging.BitmapData BmpData) { byte[] Bytes = new byte[BmpData.Height * BmpData.Stride]; Marshal.Copy(BmpData.Scan0, Bytes, 0, Bytes.Length); for (p = 3; p < Bytes.Length; p += 4) { if (Bytes[p] != 255) return true; } return false; } 

为不同类型的图像组合一堆方法让我得到了这个最终方法,这对于你转储到它的任何图像似乎都做得很好,无论是潜在的透明gif还是包含alpha通道的png。 感谢Elmo对快速字节读取方法的回答。

旁注:不要使用Image.IsAlphaPixelFormat(bitmap.PixelFormat)) ; 它将调色板格式视为不支持alpha的格式,而这些图像实际上可以具有透明度。 只是,而不是’alpha’类型。 这种启用透明度的8位图像确实启用了HasAlpha标志,因此这仍然是一个有用的检查。

 public static Boolean HasTransparency(Bitmap bitmap) { // not an alpha-capable color format. if ((bitmap.Flags & (Int32)ImageFlags.HasAlpha) == 0) return false; // Indexed formats. Special case because one index on their palette is configured as THE transparent color. if (bitmap.PixelFormat == PixelFormat.Format8bppIndexed || bitmap.PixelFormat == PixelFormat.Format4bppIndexed) { ColorPalette pal = bitmap.Palette; // Find the transparent index on the palette. Int32 transCol = -1; for (int i = 0; i < pal.Entries.Length; i++) { Color col = pal.Entries[i]; if (col.A != 255) { // Color palettes should only have one index acting as transparency. Not sure if there's a better way of getting it... transCol = i; break; } } // none of the entries in the palette have transparency information. if (transCol == -1) return false; // Check pixels for existence of the transparent index. Int32 colDepth = Image.GetPixelFormatSize(bitmap.PixelFormat); BitmapData data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat); Int32 stride = data.Stride; Byte[] bytes = new Byte[bitmap.Height * stride]; Marshal.Copy(data.Scan0, bytes, 0, bytes.Length); bitmap.UnlockBits(data); if (colDepth == 8) { // Last line index. Int32 lineMax = bitmap.Width - 1; for (Int32 i = 0; i < bytes.Length; i++) { // Last position to process. Int32 linepos = i % stride; // Passed last image byte of the line. Abort and go on with loop. if (linepos > lineMax) continue; Byte b = bytes[i]; if (b == transCol) return true; } } else if (colDepth == 4) { // line size in bytes. 1-indexed for the moment. Int32 lineMax = bitmap.Width / 2; // Check if end of line ends on half a byte. Boolean halfByte = bitmap.Width % 2 != 0; // If it ends on half a byte, one more needs to be processed. // We subtract in the other case instead, to make it 0-indexed right away. if (!halfByte) lineMax--; for (Int32 i = 0; i < bytes.Length; i++) { // Last position to process. Int32 linepos = i % stride; // Passed last image byte of the line. Abort and go on with loop. if (linepos > lineMax) continue; Byte b = bytes[i]; if ((b & 0x0F) == transCol) return true; if (halfByte && linepos == lineMax) // reached last byte of the line. If only half a byte to check on that, abort and go on with loop. continue; if (((b & 0xF0) >> 4) == transCol) return true; } } return false; } if (bitmap.PixelFormat == PixelFormat.Format32bppArgb || bitmap.PixelFormat == PixelFormat.Format32bppPArgb) { BitmapData data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat); Byte[] bytes = new Byte[bitmap.Height * data.Stride]; Marshal.Copy(data.Scan0, bytes, 0, bytes.Length); bitmap.UnlockBits(data); for (Int32 p = 3; p < bytes.Length; p += 4) { if (bytes[p] != 255) return true; } return false; } // Final "screw it all" method. This is pretty slow, but it won't ever be used, unless you // encounter some really esoteric types not handled above, like 16bppArgb1555 and 64bppArgb. for (Int32 i = 0; i < bitmap.Width; i++) { for (Int32 j = 0; j < bitmap.Height; j++) { if (bitmap.GetPixel(i, j).A != 255) return true; } } return false; }