画像でアルファチャネルが使用されているかどうかを判断します
-
28-09-2019 - |
質問
私は自分のプログラムに画像を持ち込んでいるので、次のかどうかを判断したいと思います。
- 彼らはアルファチャネルを持っています
- そのアルファチャネルが使用されている場合
#1 使用するのに十分なシンプルです Image.IsAlphaPixelFormat
. 。にとって #2 ただし、すべてのピクセルをループする以外に、少なくとも1つのピクセルに使用されるアルファチャネルがあるかどうかを判断できます(つまり、他の値に設定されています。 255
)?私が戻って必要なのはブール値だけです。それから、32ビットまたは24ビットに保存するかどうかを決めます。
アップデート: :私はそれを発見しました imageflags.hastranslucent 私が探しているものを私に提供する必要があります - 残念ながら、それはまったく機能しません。たとえば、少なくとも66(半透明)のアルファチャネルを持つピクセル形式のPNGSは、引き続き報告し続けます False
(使用法: if((img.Flags & ImageFlags.HasTranslucent) == 4) ...;
)。アルファ値> 0と<255を含む.bmpを含むすべてのタイプの画像でテストしましたが、それでも報告しています False
. 。これを使用して、GDI+で機能するかどうかを知っている人はいますか?
解決
すべてのピクセルをループする必要はありません(そうするかもしれませんが、画像に依存します)。すべてのピクセルでループするように設定しますが、255以外のアルファ値を見つけたときにループから抜け出すだけです。次の擬似コードを使用します。
bool hasAlpha = false;
foreach (var pixel in image)
{
hasAlpha = pixel.Alpha != 255;
if (hasAlpha)
{
break;
}
}
アルファがない画像のすべてのピクセルをチェックするだけです。アルファを持っている画像の場合、これは非常に迅速に発生します。
他のヒント
あなたはこれよりも良い解決策を見つけることはありません、それは私が最適化するのに何時間もかかりました:
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;
}
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");
さまざまな種類の画像のための多数の方法を組み合わせることで、この最終的な方法で、潜在的に透明なGIFであろうとアルファチャネルを含むPNGであろうと、あらゆる画像に良い仕事をするようです。高速バイト読み取り方法についてのElmoの回答に感謝します。
サイドノート:します いいえ 使用する Image.IsAlphaPixelFormat(bitmap.PixelFormat))
;そのような画像が、パレットの形式が非アルファ対応と見なされますが できる 実際、透明性を持っています。ただ、「アルファ」の種類ではありません。ただし、このような透明性対応の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;
}
投稿してから ここでの私の最初の答え 、私はそれを見つけました LockBits
コマンドは実際にできます 変換 目的のピクセル形式への画像データ。これは、入力に関係なく、ピクセルARGBデータごとにバイトを「32ビット」と確認できることを意味します。その形式には4バイトのピクセルがあり、.NETフレームワークの歩みは 常に4バイトの倍数, 、通常 非常に データの読み取りを正しく調整してスキャンラインの長さを正しく調整する重要な問題は、無関係になります。これにより、コードが大幅に簡素化されます。
もちろん、私の他の答えからの最初の2つのチェックはまだ適用されます。チェックします HasAlpha
ビットマップフラグのフラグとインデックス付きフォーマットのパレットエントリのアルファは、画像が画像を決定するかどうかを判断する非常に迅速な初期方法です。 できる 完全なデータスイープに切り替える前に、透明性があります。
それ以来、アルファ対応のパレットでインデックス付けされたPNGが実際にはものであることがわかりました(ただし .NETでサポートが不十分です)、そのため、インデックス付きフォーマットで単一のアルファ対応色をチェックするだけでは、あまりにも素朴です。
これらすべてを念頭に置いて、パレットチェックをワンライナーに変えるLINQ操作は、これが次のようになります。
public static Boolean HasTransparency(Bitmap bitmap)
{
// Not an alpha-capable color format. Note that GDI+ indexed images are alpha-capable on the palette.
if (((ImageFlags)bitmap.Flags & ImageFlags.HasAlpha) == 0)
return false;
// Indexed format, and no alpha colours in the image's palette: immediate pass.
if ((bitmap.PixelFormat & PixelFormat.Indexed) != 0 && bitmap.Palette.Entries.All(c => c.A == 255))
return false;
// Get the byte data 'as 32-bit ARGB'. This offers a converted version of the image data without modifying the original image.
BitmapData data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
Int32 len = bitmap.Height * data.Stride;
Byte[] bytes = new Byte[len];
Marshal.Copy(data.Scan0, bytes, 0, len);
bitmap.UnlockBits(data);
// Check the alpha bytes in the data. Since the data is little-endian, the actual byte order is [BB GG RR AA]
for (Int32 i = 3; i < len; i += 4)
if (bytes[i] != 255)
return true;
return false;
}
これは機能します どれか パレットであろうとなかろうと、ピクセル形式を入力します。