LockBits und GetPixel

  • VB.NET

Es gibt 14 Antworten in diesem Thema. Der letzte Beitrag () ist von ~blaze~.

    LockBits und GetPixel

    Hallo liebes Forum :),

    ich habe eine Frage die ich mir auch schon versucht habe selbst zu lösen es hat aber leider nicht funktioniert :( .
    Ich will per Lockbits aus einer Bild-Datei einen bestimmten Pixel auslesen.
    ich habe es schon soweit geschafft das ich per Lockbits das Bild in einen Byte-Array habe doch ab da komme ich leider nicht weiter :(.

    PS ich weiß das es dafür eine Lib hier gibt (fastgraphicslib) aber ich will etwas im Sourcecode Austausch hochladen was mit einer Geschlossenen lib etwas schwierig ist :D.
    (es soll auch keine fastgraphicslib werden :)
    MFG 0x426c61636b4e6574776f726b426974
    InOffical VB-Paradise IRC-Server
    webchat.freenode.net/
    Channel : ##vbparadise
    So richtig gut funktioniert LockBits nur in C#, weil Du da mit unmanaged Code direkt in diesem Array arbeiten kannst.
    In VB.NET kannst Du mit der Property Scan0 As IntPtr des Rückgabewertes BitmapData über Marshal mit einem 32-Bit-Wert pro Pixel da was machen, was aber nicht so richtig performat gegenüber Get/SetPixel ist (nicht getestet).
    Was hast Du denn vor?
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    wie oben schon beschrieben will ich eine ganze Bild-Datei einlesen und dann schauen ob bestimmte Farben vorhanden sind (mehr will ich noch nicht sagen :) )
    MFG 0x426c61636b4e6574776f726b426974
    InOffical VB-Paradise IRC-Server
    webchat.freenode.net/
    Channel : ##vbparadise
    Sieh Dir mal dieses Beispiel an. Da werden die Pixeldaten in ein Array kopiert, und da hast Du natürlich gegenüber Get/SetPixel doch einen erheblichen Performance-Vorteil. :thumbsup:
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    RodFromGermany schrieb:

    So richtig gut funktioniert LockBits nur in C#
    Dassis nich wahr.
    Mit Lockbits kann man sich die Pixel ganz wunnebar in ein Array transferieren, und der VB-Array-Zugriff ist sogar ein winzbischen schneller als c#'s Pointer-Arithmetik.
    Vor allem kann man auch in ein 2-dim-Array transferieren, und hat so für jedes Pixel die Farbkanäle als Sub-Array (Byte) zugreifbar.
    Oder man transferierts in ein 1-dim Color-Array und kann mit der Color-Struktur drin rumfuhrwerken.

    gugge Convolution-Filter

    ErfinderDesRades schrieb:

    Dassis nich wahr.
    Hatten wir schon:

    RodFromGermany schrieb:

    Da werden die Pixeldaten in ein Array kopiert,
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Hi
    da würde ich aber dann eine eigene Color-Struktur anlegen für, die ein Integer für den Argb-Wert enthält. Die Color-Struktur selbst enthält viel zu viele Kontextinformationen, wie den Namen, etc. Ich würde das ganze einfach in eine C#-Programmbibliothek auslagern und eben einen Zeiger MyColor* verwenden, um die Daten aus der Bitmap zu laden. Hierbei wäre es sinnvoll, Begin- und End-Operationen zu verwenden oder einen Using-Block, der eben die Bitmap sperrt und wieder entsperrt. MyColor muss dann die gleiche Größe, wie das PixelFormat, auf dem gesperrt wird, haben (also vorzugsweise eben 32-Bit).

    C-Quellcode

    1. public unsafe class BitmapHelper
    2. private MyColor* _scan0;
    3. private Bitmap _bitmap;
    4. private int _width, height;
    5. private int _handleCounter;
    6. public BitmapHelper(Bitmap bitmap)
    7. {
    8. _bitmap = bitmap;
    9. _width = bitmap.Width;
    10. _height = bitmap.Height;
    11. }
    12. public IDisposable Acquire()
    13. {
    14. //Bitmap sperren, sofern noch nicht gesperrt, beim IDisposable.Dispose-Aufruf des Rueckgabewerts die Bitmap wieder freigeben, wenn _handleCounter == 0 oder so
    15. if (_handleCounter == 0)
    16. {
    17. _scan0 = (MyColor*)_bitmap.LockBits(Rectangle.Empty, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb).Scan0;
    18. _handleCounter++;
    19. }
    20. public MyColor GetColor(int x, int y)
    21. {
    22. if (_scan0 == null)
    23. throw new InvalidOperation("Bitmap has not been acquired.");
    24. if (x < 0 || x >= _width)
    25. throw new ArgumentOutOfRangeException("x");
    26. if (y < 0 || y >= _height)
    27. throw new ArgumentOutOfRangeException("y");
    28. return _scan0[x + y * _width];
    29. }
    30. //SetColor analog, halt _scan0[x + y * _width] setzen
    31. }
    32. public struct MyColor
    33. public uint Argb;
    34. public MyColor(uint argb)
    35. {
    36. Argb = argb;
    37. }
    38. public byte A
    39. {
    40. get { return (byte)(Argb >> 24); }
    41. set { Argb = (Argb & 0xffffff) | ((uint)value << 24); }
    42. }
    43. public byte R
    44. {
    45. get { return (byte)((Argb >> 16) & 0xff); }
    46. set { Argb = (Argb & 0xff00ffffu) | ((uint)value << 16); }
    47. }
    48. //usw.
    49. }


    Gruß
    ~blaze~
    dass die Color-Structure noch weiteres Brimborium verfügbar macht, belastet ja weder Speicher noch Performance.

    Den Zugriff auf die Farbkanäle hat die Color-Struktur glaub performanter gelöst, weil da braucht man eiglich keinen Bitshift.
    Allerdings wäre für 3-Kanal-Bitmaps doch eine eigene Color-Structure nötig, weil die Framework-Color ist ja inklusive Alpha-Kanal.
    (Bei meine Übungen war das egal - ich hab alle abweichenden Bitmaps prinzipiell erstmal nach Pixelformat.Format32bppArgb konvertiert)

    Und ich würd auch nicht bei jedem x/y - Zugriff die Grenzen abprüfen - das kostet ja gemein Performance.
    Ist halt Unsafe, und dann wird auch unsafe gecodet ;)
    @~blaze~: Wenn schon ein Struct, dann kann man das Structlayout auf mit einem Attribut fixieren, so kann man auch keinen Int Argb nehmen, sondern die einzelnen Bytes direkt. Das spart bei häufigen Zugriffen auf die einzelnen Farben die Bitmasken und sollte somit die Performance positiv beeinflussen. Oder, was ich noch besser finden würde: Beides mit einem Union bzw. das Layouting der einzelnen Felder mit den FieldOffset-Attribut festlegen. Gute Idee btw. baue ich gleich in meine Library ein, falls sich das lohnt. :>

    Edit: Dementsprechend würde ich es so machen:
    Spoiler anzeigen

    C-Quellcode

    1. [StructLayout(LayoutKind.Explicit)]
    2. public struct UnmanagedColor
    3. {
    4. [FieldOffset(0)]
    5. public uint Argb;
    6. [FieldOffset(3)]
    7. private byte _a;
    8. [FieldOffset(2)]
    9. private byte _r;
    10. [FieldOffset(1)]
    11. private byte _g;
    12. [FieldOffset(0)]
    13. private byte _b;
    14. public MyColor(uint argb)
    15. {
    16. Argb = argb;
    17. }
    18. public byte A { get { return _a; } set { _a = value; } }
    19. public byte R { get { return _r; } set { _r = value; } }
    20. public byte G { get { return _g; } set { _g = value; } }
    21. public byte B { get { return _b; } set { _b = value; } }
    22. }

    Vor allem kann man auch in ein 2-dim-Array transferieren, und hat so für jedes Pixel die Farbkanäle als Sub-Array (Byte) zugreifbar.
    Bei einem 2-Dimensionalen Array muss man aber wiederum auch darauf achten, wie man durch das Array läuft, da sich das sonst extrem schlecht auf die Performance auswirken könnte.

    @Thread
    Wie man die Pixel an einer bestimmten Stelle aus einem eindimensionalen Array holt, steht hier.
    Kurz: int index = PixelSize * Width * y + PixelSize * x;
    PixelSize ist die Größe eines Pixels in Bytes (je nachdem in welchem Pixelformat man gelockt hat).
    Von meinem iPhone gesendet

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „nikeee13“ ()

    nikeee13 schrieb:

    Bei einem 2-Dimensionalen Array muss man aber wiederum auch darauf achten, wie man durch das Array läuft, da sich das sonst extrem schlecht auf die Performance auswirken könnte.

    naja - mit For i - und dassis ebenso Performant wie c#'s Pointer (nur angenehmer zu coden, findich).

    Ich hab mich übrigens verwechselt: Prinzipiell kannman eine Bitmap sogar in ein 3-dim-Byte-Array schubsen ( bytes(y, x, kanal) ).

    Aber inne Pixelhelpers verwende ich nur 2 dim ( bytes(y, x) ), und steppe dann halt in entsprechenden Sprüngen von Kanal-Pix zu Kanal-Pix.
    Bei 2-dimensionalen Arrays leider nicht. Dort kommt es darauf an, ob man bei der geschachtelten Schleife X oder Y in der äußeren Schleife durchläuft. Es macht ggf. sogar einen Unterschied, ob man ein 2-dimensionales Array oder ein verschachteltes Array verwendet. Oft ist es sogar besser, man rechnet sich den Array-Index bei einem eindimensionalen Array selbst aus. Das auch noch dazu kommt sind die Range-Checks aus .NET, die man aber vieleicht mit einer Compileroption oder einem Codeblock ausschalten kann(?).
    Siehe:
    stackoverflow.com/questions/46…slower-than-normal-arrays

    Natürlich bleibt ein 2-dimensionales (oder verschachteltes) Array leichter zu Handeln - das steht außer Frage. Wenn es aber auf Perfomance ankommt, muss man dort evtl. einlenken.
    Von meinem iPhone gesendet
    Lockbits kann nur in richtiges mehrdimensionales Array blitten ( bytes(y, x) ) - verschachteltes Array ( bytes(y)(x) ) geht nicht.

    Nanu? Da wird behauptet, mehrdim Arrays seien 23% langsamer?
    naja, ok, bei mehrdim musserja 2 Indizees zum Addressieren auswerten, bei 1-dim nur einen.

    Annererseits würdich sagen: Für 23% reißich mir nicht den A... auf. Da entwickel ich lieber in strukturierterem Code, bei dem ich Zeilen und Spalten identifizieren kann.
    Kommt halt drauf an. For i As Integer ist evtl. nicht so performant, wie wenn man direkt for(MyColor* cs = start, MyColor* cend = start + width * height; cs < start; cs++) rechnet. Die Indizierung fällt ja entsprechend weg. Für 23% reiß ich mir mit 5 Zeilen mehr Code bzw. 5 Zeilen leicht undurchsichtigerem Code schon mal gern den A... auf ;). Insbesondere ist C# da ja wunderbar zum Programmieren.

    btw. das mit dem FieldOffset hab' ich mir auch gedacht, aber ich finde das eher unschön und hab's bisher noch nie im echten Einsatz gesehen. Ist das gängige Praxis?

    Gruß
    ~blaze~
    Was heißt gängig?
    Kaum jemand kennt das Feature, aber ist Bestandteil der Sprache, und ich glaub, die Framework-Color funzt auch damit.

    zur Performance: in deim Fall gewinnste vmtl. viel mehr als 23%, wennde die Grenz-Prüfungen rausschmeißt. Und nochmehr, wennde sone FieldOffset-Struktur nimmst.

    Und bei Convolution-Filter werden ja Nachbarpixel untersucht, sowohl vertikale als auch horizontale Nachbarn.
    Das würde glaub mit eindimensionalen oder Pointer-Übungen garnimmer schön aussehen.

    Also kommt malwieder auch auf die Anwendung an.

    Und ebenfalls beim Convolution-Filter zeigt sich mal wieder, wie irrelevant so hochgezüchtete Optimierungen sind: Da greifst du mw. 10% schneller zu (die vollen 23% wirst du nicht schaffen;)), und dann findet da in jedem Zugriff eine Double-Division statt (und ist unvermeidbar)!
    Jo - ist geschwätzt - die Division frisst locker 100 mal so viel wie der Array-Zugriff, also 23% Gewinn beim Array-Zugriff interessieren nie nie niemanden. Was zählt ist Wartbarkeit des Codes.
    Da macht Zeigerarithmetik zumindest in C# wirkl. keinen Sinn, außer indirekt in einer Funktion, wie oben. Bei sowas schlägt man aber eh bei Weitem keine 23% Gewinn mehr raus (bzgl. des gesamten) insofern wär's eine sinnfreie Optimierung.
    Zeiger sind übrigens reine Gewohnheitssache in den meisten Fällen - zumindest, wenn man sie richtig verwendet und aufpasst, dass man den richtigen Bereich adressiert. Da ist dann nicht viel Unterschied bzgl. der Wartung.
    Btw. wäre der Zugriff über Zeiger wahrscheinlich sogar dafür da, die Arraybereich-Sicherheit zu umgehen.

    Gruß
    ~blaze~