2011-11-12 13 views
19

Próbuję uzyskać wszystkie wartości bajtów z bitmapy (System.Drawing.Bitmap). Dlatego zablokować bajty i je skopiować:PixelFormat.Format32bppArgb wydaje się mieć nieprawidłową kolejność bajtów

public static byte[] GetPixels(Bitmap bitmap){ 
    if(bitmap-PixelFormat.Equals(PixelFormat.Format32.bppArgb)){ 
     var argbData = new byte[bitmap.Width*bitmap.Height*4]; 
     var bd = bitmap.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat); 
     System.Runtime.InteropServices.Marshal.Copy(bd.Scan0, argbData, 0, bitmap.Width * bitmap.Height * 4); 
     bitmap.UnlockBits(bd); 
    } 
} 

Przetestowałem ten obraz z bardzo prostego 2x2 PNG z pikseli (czerwony, zielony, niebieski, biały), który został utworzony w programie Photoshop. Ze względu na format, spodziewałem się następujące wartości w argbData:

255 255 0 0 255 0 255 0 
255 0  0 255 255 255 255 255 

Ale mam:

0  0 255 255  0 255 0 255 
255 0 0 255 255 255 255 255 

Ale to jest formatem BGRA. Czy ktoś wie, dlaczego bajty wydają się zamienione? Nawiasem mówiąc, kiedy używam obrazu bezpośrednio do Image.Source, jak pokazano poniżej, obraz jest wyświetlany poprawnie. Więc jaka jest moja wina?

<Image Source="D:/tmp/test2.png"/> 

Odpowiedz

33

Pixel danych jest ARGB, 1 bajt dla alfa , 1 dla czerwonego, 1 dla zielonego, 1 dla niebieskiego. Alfa jest najbardziej znaczącym bajtem, niebieski jest najmniej znaczący. Na maszynie mało-endianowej, takiej jak Twoja i wielu innych, mały koniec jest przechowywany jako pierwszy, więc kolejność bajtów to bb gg rr aa. Tak więc 0 0 255 255 równa się kolor niebieski = 0, zielony = 0, czerwony = 255, alfa = 255. To jest czerwone.

Ta szczegółowość zlecenia endianina znika, gdy rzutujesz bd.Scan0 na int * (wskaźnik na liczbę całkowitą), ponieważ liczby całkowite są również zapisywane jako little-endian.

+4

Doskonała! Mogę tylko dodać, że możesz sprawdzić kolejność bajtów ("endianness"), w której dane są przechowywane w tej architekturze komputera za pomocą [BitConverter.IsLittleEndian] (http://msdn.microsoft.com/en-us/library/system .bitconverter.islittleendian.aspx). – DmitryG

+1

To dobry punkt na temat 'BitConverter.IsLittleEndian', ale myślę, że @ komentarz HansPassant o endian-ness jest naprawdę cenny - to jest inne miejsce, w którym endianizm powinien ** nie ** być brany pod uwagę. [Tutaj] (http://commandcenter.blogspot.com.au/2012/04/byte-order-fallacy.html) świetny artykuł na ten temat .. – Jonno

2

AFAIK jest to technicznie oparty na COLORREF (który jest używany w systemie Windows GDI/GDI + wszędzie) i które są przechowywane w pamięci RGBA ... zobacz http://msdn.microsoft.com/en-us/library/dd183449%28VS.85%29.aspx

+0

jeśli rozumiem tę stronę, oznacza to, że za każdym razem, gdy przygotuję RGB, nie jest to prawdziwy bajt. Zawsze oznacza BGR? A może jest jakaś flaga wskazująca na to? – 0xBADF00D

+1

@hichaeretaqua Z tego co wiem, korzystając z GDI, zawsze otrzymujesz BGR/BGRA ... – Yahia

0

W formacie piksela Bpp32Argb. Nie potrzebujesz dostępu bajt po bajcie.

Trun Scan0 na wskaźnik Int32 w niebezpiecznym kontekście.

unsafe 
{ 
    var ptr=(int*)bmData.Scan0; 
} 

Możesz wykonać operację bitową jak poniżej Aby uzyskać dostęp do kanałów kolorów pierwszego piksela.

I nie trzeba dbać o porządek bajtów.

var a=(ptr[0] & 0xFF000000)>>24; 
var r=(ptr[0] & 0x00FF0000)>>16; 
var g=(ptr[0] & 0x0000FF00)>>8; 
var b=(ptr[0] & 0x000000FF); 

BTW można pracować z Color.ToArgb() powrócił int łatwo.

+0

Przyjęta odpowiedź już o tym wspomina, ale niestety nie można w pewnych okolicznościach używaj niebezpiecznego kodu. W przypadku instancji pracowałem dla kilku firm, które w ogóle nie zezwalają na niebezpieczny kod. – 0xBADF00D

Powiązane problemy