BitMap per Programm konstruieren

  • VB.NET

Es gibt 20 Antworten in diesem Thema. Der letzte Beitrag () ist von VaporiZed.

    BitMap per Programm konstruieren

    Hi,

    ich habe hier ein Problem, das recht knifflig ist. Ich hoffe, ihr habt ein wenig Geduld:

    Ich habe eine Routine, die von einem wählbaren Bildschirmausschnitt Screenshots im ".png" Format zieht. Die Sample Rate kann man einstellen ... z.B. 16 Frames pro Sekunde. Bei dieser Rate werden in einer viertel Stunde etwa 15.000 screenshots (Frames) gesammelt.

    Mit einem Screenshot Player kann man die gesammelten Frames abspielen und bearbeiten. Insbesondere kann ich bestimmte Ranges ausblenden, indem ich die Frames auf ".pngx" umbenenne ... die werden dann übersprungen ...

    Natürlich will ich einen Überblick haben, welche Frames aus den 15.000 Frames ausgeblendet wurden. Deshalb habe ich in den ScrollBar Regler des Players noch eine "Frame Bar" eingeblendet, wo die ausgeblendeten Bereiche rot markiert sind. (s.Anhang)

    Die Frage ist jetzt, wie man diese Frame Bar realisiert ?

    Zur Zeit ist das eine Picture Box, in die ich ein "getürktes" .jpg File geladen habe. Man müsste also vom Program aus eine BitMap erstellen, welche die ausgeblendeten Bereiche rot darstellt und diese Bitmap dann in die Picture Box der FrameBar laden.

    Dazu müsste ich aber über den Bauplan der BitMap Bescheid wissen ... ich hab mal eine einfarbige Bitmap hexadezimal ausgedruckt ... aber da werde ich nicht so recht schlau draus ....

    Wie könnte man eine solche BitMap aufbauen ?

    Ist mein Vorgehen überhaupt sinnvoll ? Oder gäbe es da bessere Lösungsansätze ?

    LG
    Peter
    Bilder
    • FrameBar.jpg

      16,5 kB, 1.237×68, 79 mal angesehen
    Da gäbe es mehrere Möglichkeiten.
    Im Paint-EventHandler der PicBox mit e.Graphics.DrawLines Linien zeichnen. Die y-Werte sind ja fix, die x-Werte ergeben sich aus der jew. Dateiposition in der Dateiliste i, der Gesamtdateizahl g und der Balkenbreite b: x = (i / g ) * b
    Du kannst auch ne neue Bitmap anlegen (Dim DeineBitmap As New Drawing.Bitmap(Breite, Höhe)) und dann mit DeineBitmap.SetPixel oder LockBits (komplizierter, aber geht schneller) arbeiten.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    Jau, dann brauch ich natürlich die Map nicht selbst aufzubauen, sondern muss nur die Color der Bits setzen. Die Performace von SetPixel ist in meinem Umfeld kein Thema - aber natürlich ist der Hinweis auf StretchImage und Height=1 nicht schädlich. :)

    Supi ! Danke an alle Ratgeber !

    Das Ganze funktioniert prima und sieht Klasse aus. Man muss halt noch darauf achten, dass die Breite der markierten Bits mindestens 0,2 % der Gesamtlänge beträgt, weil die Markierung sonst nicht oder nur schlecht zu erkennen ist.

    Damit ist mein Problem erst mal gelöst. Aber mir ist dabei doch noch eine Frage untergekommen.

    Der Header der BitMap hat wohl folgendes Layout:

    VB.NET-Quellcode

    1. Structure BitmapHeaderA
    2. Dim KennzeichenDerBitmapDatei As Integer
    3. Dim DateigrößeInByte As Long
    4. Dim ReserviertA As Integer
    5. Dim ReserviertB As Integer
    6. Dim OffsetDesErstenGrafikbytes As Long
    7. Dim Strukturgröße As Long
    8. Dim BreiteInPixel As Long
    9. Dim HöheInPixel As Long
    10. Dim Farbebenen As Integer
    11. Dim FarbtiefeInBit As Integer
    12. Dim Komnpressionstyp As Long
    13. Dim BildgrößeInBytes As Long
    14. End Structure


    Wie kann ich denn diese Struktur über meine BitMap legen, um die einzelnen Felder anzudrucken ?

    Wie gesagt: mein Problem ist gelöst - aber ich würde das halt gern interessehalber wissen ...

    LG
    Peter

    Peter329 schrieb:

    Wie kann ich denn diese Struktur über meine BitMap legen, um die einzelnen Felder anzudrucken ?
    Wenn Du mit Framework-Mitteln arbeitest, brauchst Du diese Struktur nicht.
    Wenn, dann musst Du eine BMP-Datei haben, die fängt mit so nem Header an, kannste mit nem BinaryaReader auslesen.
    Wenn Du aber PNG-Dateien hast, geht das nicht.
    Was genau willst Du denn aus diesem Header extrahieren, was nicht in der .NET-Bitmap-Klasse drinne steckt?
    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!
    ok ok... das verstehe ich. Im Object orientierten Umfeld ist so ein Ansinnen eine Struktur auszudrucken, so etwas wie ein Griff unter die Gürtellinie. :)

    "Anständige" Programmierer verwenden Properties und Functions der Klasse ...

    Aber das ist halt meine Vergangenheit .... im ASSEMBLER da geht ein Zugriff auf den Speicher mit DSECT, LOAD und USING ... in prozeduralen Sprachen (etwa PL/I) verwendet man DCL .... BASED(pointer) und pointer = ADDR( ... ) ... da hat man halt noch auf jedes Bit und Byte im Speicher Zugriff, ohne wenn und aber ...

    Und diese Freiheit vermisse ich manchmal ... nicht um Programme zu schreiben (da ist die Kapselung schon sehr sinnvoll), sondern wenn es darum geht Bits und Bytes im Test zu untersuchen ... Da würde ich halt schon gern die ungeschminkte Wahrheit sehen wollen !

    In diesem Fall hab ich die Bitmap in einen Byte Array umgewandelt ... und Hex ausgedruckt. Das sieht dann so aus ...

    Quellcode

    1. FFD8FFE0 00104A46 49460001 01010060
    2. 00600000 FFDB0043 00080606 07060508
    3. 07070709 09080A0C 140D0C0B 0B0C1912
    4. 130F141D 1A1F1E1D 1A1C1C20 242E2720
    5. 222C231C 1C283729 2C303134 34341F27
    6. 393D3832 3C2E3334 32FFDB00 43010909
    7. 090C0B0C 180D0D18 32211C21 32323232
    8. 32323232 32323232 32323232 32323232
    9. 32323232 32323232 32323232 32323232
    10. 32323232 32323232 32323232 3232FFC0
    11. 00110800 4404D503 01220002 11010311
    12. 01FFC400 1F000001 05010101 01010100
    13. 00000000 00000001 02030405 06070809
    14. 0A0BFFC4 00B51000 02010303 02040305
    15. 05040400 00017D01 02030004 11051221
    16. 31410613 51610722 71143281 91A10823
    17. 42B1C115 52D1F024 33627282 090A1617
    18. 18191A25 26272829 2A343536 3738393A
    19. 43444546 4748494A 53545556 5758595A
    20. 63646566 6768696A 73747576 7778797A
    21. 83848586 8788898A 92939495 96979899
    22. 9AA2A3A4 A5A6A7A8 A9AAB2B3 B4B5B6B7
    23. B8B9BAC2 C3C4C5C6 C7C8C9CA D2D3D4D5
    24. D6D7D8D9 DAE1E2E3 E4E5E6E7 E8E9EAF1
    25. F2F3F4F5 F6F7F8F9 FAFFC400 1F010003
    26. 01010101 01010101 01000000 00000001
    27. 02030405 06070809 0A0BFFC4 00B51100
    28. 02010204 04030407 05040400 01027700
    29. 01020311 04052131 06124151 07617113
    30. 22328108 144291A1 B1C10923 3352F015
    31. 6272D10A 162434E1 25F11718 191A2627
    32. 28292A35 36373839 3A434445 46474849
    33. 4A535455 56575859 5A636465 66676869
    34. 6A737475 76777879 7A828384 85868788
    35. 898A9293 94959697 98999AA2 A3A4A5A6
    36. A7A8A9AA B2B3B4B5 B6B7B8B9 BAC2C3C4
    37. C5C6C7C8 C9CAD2D3 D4D5D6D7 D8D9DAE2
    38. E3E4E5E6 E7E8E9EA F2F3F4F5 F6F7F8F9
    39. FAFFDA00 0C030100 02110311 003F00F9
    40. FE8AE83C 2BE15B9F 165E4969 6926D9D7
    41. 1B57683B B8627924 0180A6BB 0FF851DE
    42. 22F5FF00 D17FFC72 B78E1E6E 2A5A6BDD
    43. A5F9B309 6220A4E3 ADD764DF E48F2FA2
    44. BD43FE14 7788BD7F F45FFF00 1CA3FE14
    45. 7788BD7F F45FFF00 1CA7F569 F75FF814
    46. 7FCC5F59 8767FF00 80CBFC8F 2FA2BD43
    47. FE147788 BD7FF45F FF001CA3 FE147788
    48. BD7FF45F FF001CA3 EAD3EEBF F028FF00
    49. 987D661D 9FFE032F F23CBE8A EFB5DF85
    50. 5AAF87B4 A9750BE9 764480E3 E553B885
    51. 2D8E1CE3 A1AE7FC1 9E1BFF00 84BBC596
    52. 3A17DAFE C9F6AF33 F7DE5F99 B76C6CFF
    53. 00772339 DB8EBDEB 3A94A54E DCDD7CD3
    54. FC8D29D5 8D4BF2F4 F26BF330 68AF78FF
    55. 00866EFF 00A9B3FF 0029DFFD B68FF866
    56. EFFA9B3F F29DFF00 DB6B3343 C1E8AF78
    57. FF00866E FF00A9B3 FF0029DF FDB68FF8
    58. 66EFFA9B 3FF29DFF 00DB6803 C1E8AF78
    59. FF00866E FF00A9B3 FF0029DF FDB6A8EA
    60. 9F016C74 5B65B9D4 3C67E4C2 CE230DFD
    61. 96CD9620 9C7121EC 0D38C5C9 DA2AEC52
    62. 928ABC9D 91E2B457 AA7FC2AC F0CFFD0F
    63. DFF94793 FF008BAE 93FE19BB FEA6CFFC
    64. A77FF6DA B9D2A94F E38B5EA8 8855A753
    65. E0927E8C F07A2BDE 3FE19BBF EA6CFF00
    66. CA77FF00 6DA3FE19 BBFEA6CF FCA77FF6
    67. DACCD0F0 7A2BDE3F E19BBFEA 6CFF00CA
    68. 77FF006D A3FE19BB FEA6CFFC A77FF6DA
    69. 00F07A2B DE3FE19B BFEA6CFF 00CA77FF
    70. ...


    Und das kann ich dann per Hand in die Struktur überführen ! Es gibt Leute, die machen so etwas gerne ... ich gehöre allerdings nicht dazu ...

    Und deswegen such ich halt schon nach Möglichkeiten Dinge zu Test Zwecken mit der "Brechstange" aufzubereiten ... na ja ... meine Welt kollidiert halt manchmal mit der schönen neuen Welt der Objektorientierung ... :)

    Trotzdem, vielen Dank für eure Antworten !

    LG
    Peter
    @Peter329 Kannst Du mal beschreiben, was Du machen willst?
    Willst Du einen Header schreiben oder wilst Du Pixel reinmalen?
    Für ersteres hast Du den Konstruktor der Bitmap-Klasse,
    für letzteres kannst Du SetPixel() machen oder mit LookBits Deine Bytes direkt in den Speicher schreiben.
    docs.microsoft.com/de-de/dotne…?view=dotnet-plat-ext-3.1
    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!
    Zitat RFG: Kannst Du mal beschreiben, was Du machen willst?

    Das sollte doch klar sein, was ich haben möchte: ich möchte den HEADER der BitMap formatiert ausgegeben.

    Das ist mir schon, klar, dass ich dafür nur die Eigenschaften der Bitmap heranziehen muss ... und dann ist die Sache im Sinne der Objektorientierung gelöst. Also es geht hier nicht um ein Problem der "regulären" Programmierung.

    Ich hab ja auch gesagt, dass es mir hier um etwas anderes geht. Ich möchte wissen, wie ich ein Stück Speicher, woher das auch kommt, gemäß einer bestimmten Struktur aufbereiten kann.

    In der "alten" Welt war dies problemlos möglich, indem man den Speicher mit einer "Maske" überlagert hat. Gibt es dafür eine entsprechende Technik in der neuen Welt ?

    Mit anderen Worten, ich habe einen Byte-Array, der von irgendwo her kommt, und ich will den aufbereitet ausgeben, so wie das im BitmapHeaderA beschrieben ist.

    Byte 0 - 3: xxxxxx (Integer)
    Byte 4 - ..... (Long)
    ....

    Hat mit Objektorientierung nix zu tun ... sollte doch aber auch möglich sein. :)

    LG
    Peter

    Peter329 schrieb:

    Das sollte doch klar sein
    Ist es eben nicht.
    Was hast Du davon, wenn Du da einen Solo Bitmap-Header auf die Platte speicherst?
    Wenn Du Bits markieren, ein- oder ausblenden willst, dafür gibt es And, Or, Not, XOr.
    Geht genau so wie schon immer.
    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!
    Nicht klar .... ?

    Also, wie schon gesagt, es geht um ein prinzipielles Problem .... und nicht um ein konkretes Anwedungproblem ... Lass uns die Sache also abstrahieren:

    Ich habe von einem Freund eine Datei per E-mail erhalten.
    Aus dieser Datei habe ich mit ReadBytes die ersten 12 Bytes in Dim myBytes as Bytes() ausgelesen.
    In diesen Bytes steht jetzt irgend etwas drin, jeweils zwischen 0 und 255.

    Mein Freund sagt mir jetzt, dass diese 12 Bytes auf drei Integer Feldern beruhen.

    Byte 0 - 3: erstes Integer Feld
    Byte 4 - 7: zweites Integer Feld
    Byte 8 - 11: drittes Integer Feld

    Wie kann ich jetzt, diese drei Integer Felder ausdrucken ? Aber bitte ohne "Byte-Rechnerei". Sondern nur über die Eigenschaft "Integer".

    Abstrakt genug ? :)

    In der "alten" Welt, hätte man diese 12 Bytes mit Dim myField(3) as Integer überlagert und als myField(0), myField(1) und myField(2) ausgegeben. Das war mehrere Jahrzehnte lang gängige Programmierepraxis !

    Wie macht man das jetzt in der "neuen" Welt ?

    LG
    Peter
    @Peter329 So was mit BitConverter:

    VB.NET-Quellcode

    1. Dim bb() As Byte = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}
    2. Dim i1 As Integer = BitConverter.ToInt32(bb, 0)
    3. Dim i2 As Integer = BitConverter.ToInt32(bb, 4)
    4. Dim i3 As Integer = BitConverter.ToInt32(bb, 8)
    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!
    Beim einlesen kannst du einen BinaryReader nehmen, der hat eine Methode namens ReadInt32. Dann brauchst du den Bitconverter an dieser Stelle nicht.

    Byte 0 - 3: erstes Integer Feld
    Byte 4 - 7: zweites Integer Feld
    Byte 8 - 11: drittes Integer Feld

    Wäre dann

    Quellcode

    1. int value1 = theBinaryReader.ReadInt32();
    2. int value2 = theBinaryReader.ReadInt32();
    3. int value3 = theBinaryReader.ReadInt32();


    Deutlich besser zu lesen, wie auch einfacher anzuwenden finde ich.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Takafusa“ ()

    Peter329 schrieb:

    Oder gäbe es da bessere Lösungsansätze ?
    Siehe post#2 - Stichwort PaintEvent.
    Das ist jetzt vor lauter BitmapPixel bisserl untergegangen.
    (ich will mich auch nicht drauf versteifen, dass das nu "besser" sei, als eine Bitmap zu hinzufrickeln, aber kommt mir .Net-mässiger vor, weil es Events und Klassen verwendet, die genau für OwnerDrawing vorgesehen sind.)
    ok ok ... ich denke, ich habe das verstanden. Die Sache mit den Overlays geht nicht in VB ! Das liegt wohl daran, dass das Konzept der Variablen in VB sehr viel komplexer ist, als in den alten prozeduralen Sprachen. Ein String ist beispielsweise im z/OS ASSEMBLER oder COBOL oder PL/1 nichts weiter als eine Adresse und eine Länge, die unter einem Namen ansprechbar ist. In den OO-Sprachen sind Strings etwas ganz anderes. Pointer sind eben nicht so verfügbar, wie etwa in der (prozeduralen) Sprache C, wo man mit "&" die Adresse einer Variablen hat und mit "*" den Wert des Speichers, wo ein Pointer hinzeigt.

    Und deshalb ziehe ich meine Frage zurück ! Danke für eure unendliche Geduld und freundliche Nachsicht, mich auf den rechten Pfad der Objectorientierung zurück zu holen. Ich habe viel gelernt ... auch wenn vielleicht nicht alle so ganz verstehen, worüber ich nachgedacht habe. :)

    Also, um eure Antworten zusammen zu fassen: Um die Bitrmap auszugeben, muss die Struktur auf irgend eine Weise feldweise befüllt werden und zwar typenkonform, etwa durch eine geeignete Konvertierung. Damit gebe ich mich jetzt zufrieden.

    @EDR

    Jau, zurück zum eigentlichen Thema: mir scheint das mit dem Paint Event auf den ersten Blick auch "besser" im Sinne der reinen Lehre. Die Kodierung benötigt den gleichen Aufwand ... und über Performance müssen wir in meinem Fall eh nicht sprechen ...

    Allein ein Problem, hat mich jetzt davon abgehalten, mein Coding umzustellen. Die Picture Box habe ich mit StretchImage definiert ... und damit passt sich die Bitmap den Sizeänderungen der Form automatisch an.

    Mit der "Paint" Technik, müsste ich beim SizeEvent der Form, die FrameBar neu berechnen ... und das gefällt mir dann halt nicht so recht.

    LG
    Peter

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „Peter329“ ()

    ... na ja, ist schon klar. Da hab ich mich unpräzise ausgedrückt. Wenn die Size der Form sich ändert, dann muss das Layout der FrameBar über das Paint Event der PictureBox neu berechnet werden. Wohingegen die BitMap mit StretchImage automatisch skaliert wird. Was immer jetzt besser ist.

    Insofern sind wir uns doch grundsätzlich völlig einig.

    Ansonsten funktioniert die Lösung inzwischen ausgezeichnet .... die ausgeblendeten Bereiche werden farbig angezeigt, schmale ausgeblendete Bereiche werden auf mindestes 0,5 % der Gesamtbreite "vergrößert", damit sie sichtbar werden ...wenn ich die FrameBar mit der Mouse anklicke, dann springt das Video an diese Stelle.... und alles läuft rund und performant ! So wie ich es mir vorgestellt hatte ! ich bin richtig happy. Danke noch mal an alle Ratgeber !

    LG
    Peter
    @ErfinderDesRades
    Was dann aber sicherlich beim Form-Resizing anspringt, wenn die PicBox-Property Dock auf was anderes eingestellt ist als None oder zwei gegenüberliegende Anchor-Punkte gesetzt sind.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    Peter329 schrieb:

    Wenn die Size der Form sich ändert
    sollte es genügen, die Controls zu invalidaten.
    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!