Frage

Wie ich in den Bildern in meinem Programm bin bringen, möchte ich, wenn bestimmen:

  1. Sie haben einen Alpha-Kanal
  2. , wenn der alpha-Kanal verwendet wird

# 1 ist einfach genug, um mit Image.IsAlphaPixelFormat verwenden. Für # 2 obwohl, anders als durch jeden einzelnen Bildpunkt Looping, ist es eine einfache Weise, die ich feststellen kann, wenn mindestens eines des Pixel einen Alphakanal aufweist, (dh auf einen anderen Wert verwendet wird als 255)? Alles, was ich brauche, um wieder ein boolean und dann werde ich Bestimmung treffen, ob sie auf 32-Bit zu speichern oder 24-Bit.

UPDATE : Ich habe entdeckt, dass

Andere Tipps

Sie werden keine Lösung besser als diese finden, es hat mich Stunden zu optimieren:

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;
}

ich eine erweiterte Lösung erhalten, basierend auf ChrisF Antwort:

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);
    }

Es verfügt über eine optionale bg Farbe Zeichenfolge nicht transparente Bilder:

Beispiel für die Verwendung:

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

eine Reihe von Methoden für verschiedene Arten von Bildern hat mir diese letzte Methode Kombination, die einen guten Job für jedes Bild zu tun, scheint man in sie wirft, sei es ein potentiell transparent gif oder png ein Alpha-Kanal enthält. Dank Elmos Antwort für die schnellen Byte Leseverfahren.

Side Hinweis: Verwenden Sie nicht Verwendung Image.IsAlphaPixelFormat(bitmap.PixelFormat)); es sieht paletted Formate als nicht-alpha-fähig, während solche Bilder können in der Tat Transparenz besitzen. Just, nicht die 'alpha' Art. Eine solche Transparenz-fähigen 8-Bit-Bilder die HasAlpha Flag, obwohl aktiviert tun haben, so dass die noch eine nützliche Kontrolle.

[[Anmerkung: Ich habe da diese Logik erheblich vereinfacht. Sehen Sie meine andere Antwort. ]]

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;
}

Da Posting hier , fand ich, dass der LockBits Befehl aus eigentlich convert Bilddaten zu einem gewünschten Pixelformat. Dies bedeutet, dass nicht die Eingabe Rolle, können Sie einfach das Bytes überprüfen ‚als‘ 32 Bit pro Pixel ARGB Daten. Da das Format hat 4-Byte-Pixel, und die Schrittlänge in dem FRAMEWORK ist immer ein Vielfaches von 4 Bytes , die normalerweise sehr wichtige Frage der korrekten Lesen von Daten auf Scanline-Längen eingestellt wird irrelevant. Dies alles erheblich vereinfacht den Code.

Natürlich sind die ersten zwei Kontrollen von meiner anderen Antwort noch gelten; die HasAlpha Flagge Überprüfung auf den Bitmap-Flaggen und die Alpha auf den Paletteneinträge indizierter Formaten ist eine sehr schnelle Anfangs Art und Weise, wenn ein Bild, um zu bestimmen können haben Durchsichtigkeit, bevor Sie mit dem vollen Datendurchlauf umgeschaltet wird.

Ich habe auch mit, da die indexierten png Alpha-fähige Paletten herausgefunden, ist eigentlich eine Sache (obwohl unterstützt schlecht in .Net ), so dass nur auf einem einzelnen alpha-fähigen Farbprüfung auf indizierte Formate ist zu naiv.

Mit all dem im Verstand, und eine Linq-Operation, die die Palette Prüfung in einen Einzeiler dreht, der letzte eingestellte Code wird diese:

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;
}

Dies funktioniert für jeder Eingabepixelformat, sei es palettiert oder nicht.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top