Domanda

Come sto portando in immagini nel mio programma, voglio stabilire se:

  1. hanno un canale alfa
  2. se si utilizza che l'alfa-canale

1 # è abbastanza semplice con l'utilizzo di Image.IsAlphaPixelFormat. Per 2 ° però, diverso girare su ogni singolo pixel, c'è un modo semplice è possibile determinare se almeno uno dei pixel ha un canale alfa utilizzato (ovvero un set per qualche altro valore che 255)? Tutto fa ho bisogno è un valore booleano e poi mi farò decidere se sia per salvarla fuori a 32 bit o 24-bit.

Aggiorna : ho scoperto che ImageFlags.HasTranslucent dovrebbe fornire me con quello che sto cercando - purtroppo, non funziona affatto. Ad esempio, PNG con formati pixel che hanno almeno canale alfa 66 (semi-trasparente) continuano a relazione False (Usage: if((img.Flags & ImageFlags.HasTranslucent) == 4) ...;). Ho provato in tutti i tipi di immagini, tra cui bmp che hanno un valore di alfa> 0 e <255 e segnala ancora False. Chiunque abbia mai utilizzare questo e sapere se funziona anche in GDI +?

È stato utile?

Soluzione

Non c'è bisogno di scorrere ogni pixel (beh si potrebbe, ma dipende l'immagine). Impostare a ciclo su tutti i pixel, ma basta uscire dal giro quando si trova un valore alfa diverso da 255 utilizzare il seguente pseudo codice:

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

Si dovrà solo controllare tutti i pixel per le immagini che non hanno alcun alfa. Per le immagini che hanno alpha questa scoppierà abbastanza rapidamente.

Altri suggerimenti

Non troverete una soluzione migliore di questo, ci ho messo ore per ottimizzare:

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

ottengo una soluzione più avanzata, sulla base di ChrisF risposta:

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

Ha una stringa bg colore facoltativo di immagini non trasparenti:

Esempio di utilizzo:

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

La combinazione di una serie di metodi per diversi tipi di immagini mi ha fatto questo ultimo metodo, che sembra fare un lavoro buono per qualsiasi immagine si dump in esso, sia esso un GIF potenzialmente trasparente o un png contenente un canale alfa. Grazie alla risposta di Elmo per il metodo di lettura di byte veloce.

Nota a margine: fare non uso Image.IsAlphaPixelFormat(bitmap.PixelFormat)); vede formati con tavolozza come non-alfa-grado, mentre tali immagini possono , infatti, possedere la trasparenza. Basta, non il 'alpha' tipo. Tale trasparenza abilitati immagini a 8 bit hanno la bandiera HasAlpha abilitato, però, in modo che è ancora un controllo utile.

[[Nota: allora ho enormemente semplificata questa logica. Vedere il mio altra risposta. ]]

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

Dal distacco mia prima risposta qui , ho scoperto che il comando LockBits può effettivamente convert dati di immagine in un formato di pixel desiderato. Ciò significa che, non importa l'ingresso, si può semplicemente verificare i byte 'come' a 32 bit per i dati di pixel ARGB. Da quel formato ha pixel 4 byte, e il passo nell'ambito Net è sempre un multiplo di 4 byte , la normalmente molto importante problema di regolare correttamente la lettura dei dati a lunghezze scanline diventa irrilevante. Questo semplifica enormemente tutto il codice.

Naturalmente, i primi due assegni da mia altra risposta applicano ancora; controllando la bandiera HasAlpha sulle bandiere bitmap e l'alfa sulle voci di tavolozza di formati indicizzati è un modo molto veloce iniziale per determinare se un'immagine possono hanno Trasparenza, prima di passare alla spazzata completa dei dati.

Ho anche scoperto che dal png indicizzato con palette alfa-grado è in realtà una cosa (anche se mal supportato in Net ), in modo che solo il controllo su un singolo colore alfa-grado sui formati indicizzati è troppo naive.

Con tutto questo in mente, ed un'operazione linq che trasforma il controllo palette in una battuta, il codice impostato finale diventa questo:

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

Questo funziona per qualsiasi di ingresso formato pixel, sia esso tavolozza oppure no.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top