Pregunta

Como estoy con lo que en las imágenes en mi programa, yo quiero para determinar si:

  1. tienen un canal alfa
  2. si se usa que el alfa-canal

# 1 es bastante simple con el uso de Image.IsAlphaPixelFormat. Para # 2 aunque, aparte de bucle a través de cada píxel, hay una manera simple que puede determinar si al menos uno de los píxeles tiene un canal alfa que se utiliza (es decir, conjunto a algún otro valor de 255)? Todo lo que necesito es volver un valor lógico y luego voy a hacer la determinación de si para guardarlo a 32 bits o 24 bits.

Actualizar : He descubierto que ImageFlags.HasTranslucent me debe proporcionar con lo que estoy buscando - por desgracia, no funciona en absoluto. Por ejemplo, PNGs con formatos de píxeles que tienen al menos el canal alfa de 66 (semi-transparente) siguen False informe (Uso: if((img.Flags & ImageFlags.HasTranslucent) == 4) ...;). He probado en todo tipo de imágenes, incluyendo .bmp que tiene un valor alfa> 0 y <255 y todavía informes False. Cualquier persona que ha consumido alguna vez esto y sabe si funciona incluso en GDI +?

¿Fue útil?

Solución

Usted no tiene que recorrer cada píxel (también se podría, pero depende de la imagen). Establecido para un bucle sobre todos los píxeles, pero sólo salir del bucle cuando encuentre un valor alfa que no sea 255 Utiliza el siguiente pseudo código:

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

Sólo se tendrá que comprobar todos los píxeles para imágenes que no tienen ninguna alfa. Para las imágenes que sí tienen alfa que esto romperá a cabo con bastante rapidez.

Otros consejos

No encontrará una solución mejor que esto, me tomó horas para Optimizar:

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

Me conseguir una solución más avanzada, basada en respuesta 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);
    }

Tiene una cadena opcional bg color a las imágenes no transparentes:

Ejemplo de uso:

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

La combinación de un montón de métodos para diferentes tipos de imágenes me consiguió este último método, que parece hacer un trabajo bueno para cualquier imagen que volcar en él, ya sea un gif transparente o potencialmente un png contiene un canal alfa. Gracias a la respuesta de Elmo para el método de lectura de bytes rápido.

Nota al margen: hacer no Image.IsAlphaPixelFormat(bitmap.PixelFormat)) uso; se ve formatos con paleta como no-alfa-capaces, mientras que este tipo de imágenes pueden en el hecho de poseer la transparencia. Simplemente, no del tipo 'alfa'. Esta transparencia habilitados para imágenes de 8 bits hacerlo tiene habilitada la bandera HasAlpha, aunque, por lo que sigue siendo un eficaz control.

[[Nota: Tengo desde muy simplificada esta lógica. Véase mi otra respuesta. ]]

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

Desde la publicación mi primera respuesta aquí , descubrí que el comando LockBits puede en realidad convertir datos de imagen a un formato de píxel deseado. Esto significa que, independientemente de la entrada, simplemente puede comprobar los bytes 'como' 32 bits por píxel de datos ARGB. Desde ese formato tiene píxeles de 4 bytes, y la zancada en el marco .Net es siempre un múltiplo de 4 bytes , normalmente muy importante cuestión de ajustar correctamente los datos de lectura a longitudes de línea de exploración se vuelve irrelevante. Esto simplifica enormemente todo el código.

Por supuesto, los dos primeros cheques de mi otra respuesta todavía se aplican; comprobando la bandera HasAlpha en las banderas de mapa de bits y la alfa en las entradas de la paleta de formatos indexado es una manera muy rápida inicial para determinar si una imagen puede tienen Transparencia, antes de cambiar al barrido completo de datos.

también tengo desde descubrió que png indexado con paletas-alfa capaz es en realidad una cosa (aunque escaso apoyo en .Net ), por lo que sólo la comprobación de un solo color-alfa capaces de formatos indexado es demasiado ingenuo.

Con todo esto en mente, y una operación de LINQ que convierte la comprobación de la paleta en una sola línea, el código final ajustado se convierte en la siguiente:

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

Esto funciona para cualquier formato de píxel de entrada, ya sea con paleta o no.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top