2010-06-17 11 views
9

Jak mam przynosząc obrazy w moim programie, chcę sprawdzić, czy:Ustal, czy Alpha kanał jest używany w obrazie

  1. mają alfa-kanałowy
  2. jeśli to jest kanał alfa wykorzystywane

# 1 jest dość prosta z użyciem Image.IsAlphaPixelFormat. Jednak w przypadku # 2, poza zapętlaniem się każdego pojedynczego piksela, czy istnieje prosty sposób określenia, czy co najmniej jeden z pikseli ma użyty kanał alfa (tj. Ustawiony na inną wartość niż 255)? Wszystko, czego potrzebuję, to boolean, a następnie zdecyduję, czy zapisać go do wersji 32-bitowej czy 24-bitowej.

UPDATE: Odkryłem, że ImageFlags.HasTranslucent powinny zapewnić mi z czego szukam - niestety, to nie działa w ogóle. Na przykład pliki PNG z formatami pikseli, które mają co najmniej kanał alfa 66 (półprzezroczysty) nadal raportują False (użycie: if((img.Flags & ImageFlags.HasTranslucent) == 4) ...;). Przetestowałem na wszystkich typach obrazów, w tym .bmp, które mają wartość alfa> 0 i < 255 i nadal zgłasza False. Ktoś z tego korzysta i wie, czy działa nawet w GDI +?

+0

Połączyłeś się z lokalną kopią pomocy, a nie z wersją online. – ChrisF

+0

Stany pomocy "Określa, że ​​dane pikseli mają wartości alfa inne niż 0 (przezroczyste) i 255 (nieprzezroczyste)."co nie jest dokładnie tym, czego chcesz, jeśli chcesz powiedzieć prawdę również z 0. – ChrisF

+0

Dziwne, nie mam lokalnej kopii pomocy. Tak, tak," HasTranslucent "dostanie mnie dość daleko, ale nie 100% ponieważ nadal będę potrzebował wartości 0. –

Odpowiedz

6

Nie musisz przechodzić przez każdy piksel (cóż, możesz, ale to zależy od obrazu). Skonfigurowany do pętli nad wszystkimi pikselami, ale po prostu wyrwać się z pętli, gdy znajdziesz wartość alpha inny niż 255 użyć następującego kodu pseudo:

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

Musisz tylko sprawdzić wszystkie pikseli dla zdjęć które nie mają żadnej alfa. W przypadku obrazów, które mają alfa, będzie to dość szybko.

+0

Dzięki Chris F. Wydaje się, że jest to jedyny sposób, aby to zrobić. Pozwolę, aby pytanie stało trochę dłużej, aby sprawdzić, czy pojawi się inne rozwiązanie. –

+0

@Otaku - I'd zainteresuj się, czy jest inny sposób: – ChrisF

+0

Sprawdź powyższą aktualizację –

5

uzyskać bardziej zaawansowane rozwiązania, oparte na ChrisF odpowiedź:

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

Ma opcjonalny ciąg bg kolorów do nieprzejrzystych obrazów:

Przykład użycia:

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

Nie wiesz, do czego ten dodatkowy kod jest przydatny, ale ... dlaczego nie po prostu daj ostatni argument jako Col lub obiekt zamiast jako String? Nie wspominając już o tym, że analizujesz ten łańcuch 5 razy w kodzie, zamiast tylko raz, a następnie zapisujesz go w zmiennej typu "Kolor". – Nyerguds

3

Nie znajdziesz lepszego rozwiązania problemu, optymalizacja zajęła mi wiele godzin:

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

Ostrożnie. Aby to zrobić, musisz sprawdzić liczbę bitów na piksel. Działa to tylko dla obrazów o 32 bpp. – Nyerguds

+1

Jedyne, co potencjalnie może być szybsze, to przechodzenie przez pamięć bezpośrednio, bez kopiowania, przez "niebezpieczne" i wskaźniki. – IllidanS4

+0

@ IllidanS4 oczywiście, aw praktyce jest to droga do zrobienia. –

2

Łącząc kilka metod dla różnych typów obrazów, dostałem tę ostatnią metodę, która wydaje się robić dobrą robotę dla każdego zrzucanego do niej obrazu, czy to potencjalnie przezroczystego gifu czy png zawierającego kanał alfa. Dzięki odpowiedzi Elmo na szybką metodę czytania bajtów.

Nota boczna: do nie użyć Image.IsAlphaPixelFormat(bitmap.PixelFormat)); widzi formaty z paletami jako nie obsługujące alfa, podczas gdy takie obrazy mają w rzeczywistości przezroczystość. Po prostu nie "alfa". Takie 8-bitowe obrazy z obsługą przezroczystości mają włączoną flagę HasAlpha, więc nadal jest to przydatne.

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

Ups, naprawiono błąd w obliczaniu pozycji wiersza dla obrazów z paletą. – Nyerguds

Powiązane problemy