FastGraphicsLib Pixel einfärben mit transparenter Farbe

  • VB.NET

Es gibt 18 Antworten in diesem Thema. Der letzte Beitrag () ist von Siestaeg.

    FastGraphicsLib Pixel einfärben mit transparenter Farbe

    Hallo Leute,

    ich bin gerade dabei ein kleines Programm zu schreiben welches mir eine Vorschau von Bildern erstellt.
    So weit so gut, das Vorschaubild ist nix Anderes als verschieden große Punkte, welche ich anhand des Schwarzwertes errechne.
    Dabei arbeite ich mit transparenten Punkten. z.B. ARGB(100,150,150,150) welche ich auf ein Transparentes Bitmap hinterlege um nachträglich beliebige Hintergründe einfügen zu können.
    Nun möchte ich aber auch die Pixelfarbe der Punkte ändern. Mit der Bitmap.getpixel- und .setpixel Methode krieg ich das auch hin, nur mit dem FastGraphicsLib leider nicht, was ich aber möchte, da es doch um ein vielfaches schneller ist.
    In meinem Code habe ich beide Routinen zusammengebracht um diese vergleichen zu können.

    Hoffe ihr könnt mir ssagen, was ich falsch mache.

    VB.NET-Quellcode

    1. Public Function PixelRePaint(ByVal NewColor As Color) As Bitmap
    2. Dim Testmode As Boolean = False
    3. Dim bmp As Bitmap 'Vergleichstest
    4. bmp = New Bitmap(PictureBox1.BackgroundImage) 'Vergleichstest
    5. Dim fg As FastGraphicsLib.FastGraphics = FastGraphicsLib.FastGraphics.FromBitmap(PictureBox1.BackgroundImage)
    6. For x = 0 To fg.Bitmap.Width - 1
    7. For y = 0 To fg.Bitmap.Height - 1
    8. Dim c As New Color
    9. c = fg.GetPixel(x, y)
    10. Dim ct As New Color 'Vergleichstest
    11. ct = bmp.GetPixel(x, y) 'Vergleichstest
    12. ' If ct.A <> c.A Then Stop 'Vergleichstest
    13. ' If ct.A = 100 Then Stop 'Vergleichstest
    14. If c.A > 0 Then
    15. fg.SetPixel(x, y, System.Drawing.Color.FromArgb(c.A, NewColor.R, NewColor.G, NewColor.B))
    16. End If
    17. If ct.A > 0 And Testmode = True Then 'Vergleichstest
    18. bmp.SetPixel(x, y, System.Drawing.Color.FromArgb(ct.A, NewColor.R, NewColor.G, NewColor.B))
    19. End If
    20. Next
    21. Next
    22. If Testmode = True Then
    23. Return bmp 'Vergleichstest
    24. Else
    25. fg.Unlock()
    26. Return fg.Bitmap
    27. End If
    28. End Function


    Habe ein Bild angehängt, dann seht ihr was ich meine. Ich möchte die Punkte im Vordergrund zum Beispiel rot einfärben. Der Rest des Bildes ist Transparent, das Hintergrundbild liegt auf der Form.
    Bilder
    • prev.jpg

      100,99 kB, 658×863, 176 mal angesehen

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

    Könntest du bitte ein Soll-Bild anhängen - steh grade auf dem Schlauch und weiß nicht was genau falsch läuft...

    Alternativ zur Lib könntest du direkt Lockbits benutzen. Das ist garnicht so schwer und würde dann genau auf dein Programm zugeschnitten sein:
    msdn.microsoft.com/en-us/library/5ey6h79d.aspx (siehe unten, da ist auch Beispielcode)

    Hier eine kleine Hilfe wie du X/Y-Koordinaten auf das ByteArray überträgst und umgekehrt (Post #17)
    [VB 2010] Bitmap Farbinformationen in Array schreiben
    Hi,
    anbei mal ein paar Bilder.

    Startend vom Original (Holzhintergrund mit schwarzen leicht transparenten Punkten) möchte ich Farbänderungen durchführen. Das mit dem Hintergrund funktioniert. Dieser liegt ja auch auf der Form. Also Bild Wechsel, Hintergrund geändert.
    Das Bild mit den Punkten befindet sich in einer Picturebox und ist bis auf die Punkte transparent. Die Punktfarbe möchte ich eben ändern, um so verschiedene Effekte zu erreichen.

    Danke

    Img1 - Holz / Schwarz
    Img2 - Holz / Blau
    Img3 - Weiss / Rot
    Bilder
    • img1.jpg

      83,52 kB, 678×889, 119 mal angesehen
    • img2.jpg

      87,28 kB, 678×889, 114 mal angesehen
    • img3.jpg

      63,63 kB, 678×889, 107 mal angesehen
    Hi Leute,

    habs auch mit der ColorMap probiert, aber da kann ich leider nur jeweils eine Farbe austauschen und diese ersetzen.
    Ich habe zwar nur eine Farbe in diesem Bild aber mit unterschiedlichen A (Transparenz) Werten.
    Kann ich der ColorMap auch sagen, alle Farben ausser A=0 oder A=255 ändern mit dem A Wert des zu ändernden Pixels ??


    Danke
    Das sollte doch kein Problem sein, dort wo du die Kreise zeichnest (ich hoffe doch GDI+ ?!) kannst du die Farbe einstellen - wenn nötig auch dynamisch. Was die A,R,G,B-Werte genau bedeuten weißt du auch?

    Kann aber auch sein, dass ich dich falsch verstehe: Kann es sein, dass das Bild mit den Kreisen aus einer Datei geladen wird und die Holzplatte nur ein (separater) Hintergrund ist? Dein Ziel ist es die Färbung der Kreise zu ändern, dann ist die ColorMatrix genau das richtige für dich:

    msdn.microsoft.com/de-de/libra….imaging.colormatrix.aspx

    Dazu solltest du eine Methode schreiben die das Input-Bild (das mit den Kreisen) bekommt und gefärbt zurückgibt, etwa so:

    VB.NET-Quellcode

    1. public Function färbeBild(byval bild as Bitmap, byval färbung as color) as Bitmap


    Die Matrix (siehe Link, Codebeispiel unten) musst du verändern, so dass sie dein Bild auch entsprechend färbt:

    VB.NET-Quellcode

    1. dim fr as single = färbung.R / 255.0F
    2. dim fg as single = färbung.G / 255.0F
    3. dim fb as single = färbung.B / 255.0F
    4. Dim colorMatrixElements As Single()() = { _
    5. New Single() {fr, 0, 0, 0, 0}, _
    6. New Single() {0, fg, 0, 0, 0}, _
    7. New Single() {0, 0, fb, 0, 0}, _
    8. New Single() {0, 0, 0, 1, 0}, _
    9. New Single() {0, 0, 0, 0, 1}}


    Habe es nicht getestet aber vom Prinzip her sollte das hinhauen. Wenn du schon dabei bist etwas mit GDI zu arbeiten kannst du auch gleich dein Gesamtbild damit zeichnen (anstatt PictureBox über Form zu legen). So könntest du es auch ganz einfach als Bilddatei speichern. Ein weiterer Schritt wäre den Umweg über die verfärbteBitmap auszubauen - du kannst (siehe MSDN-Link) ein Bild direkt manipuliert mit GDI zeichnen. Lockbits entfällt bei all diesen Vorgehensweisen - sollte also auch so schon sehr schnell sein.
    lg

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

    Hallo FreakJNS,

    so weit war ich mit der ColorMap schon. Das Problem bei der Colormap, ich muss auch den A Wert der Farbe berücksichtigen.
    Also wenn ich eine schwarze Farbe Habe dann hat diese : A255; R0; G0; B0
    Ich habe, eine Farbe, die kenne ich auch, aber die A-Werte sind verschieden. Ich starte z.B. mit A100, wo sich zwei Kreise überlappen ist dieser dann z.B. 150,
    bei drei Kreisen 180, ... bis 255 was schwarz entspricht.
    Wie sage ich der Colormap, dass sie alle Alphas berücksichtigen muss, und diese in der neuen Farbe auch wieder setzen muss ?

    Ich weiß was die ARGB Werte bedeuten, und der Grund warum ich den Hintergrund getrennt vom Bitmap habe, ist der, dass ich ganz einfach nicht das ganze Bild nochmal rechnen muss, sondern nur den Hintergrund austausche um ein anderes Bild zu erzeugen. Genauso möchte ich aber die Vordergrundfarbe (Punkte) ändern können, ohne jedesmal das ganze Bild neu zu rechnen.

    Die Basis für die Ausgangsrechnung zur Erzeugung des Bildes ist eine Punktewolke mit X,Y,Z Koordinaten, deren Berechnung etwas zu Zeitintensiv ist, um diese für jede Vorschau neu durchlaufen zu lassen.

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

    ich habs mit diesem code versucht

    VB.NET-Quellcode

    1. Public Function PixelPaint(ByVal Ausgangsbild As Image, ByVal OldColor As Color, ByVal NewColor As Color) As Bitmap
    2. Dim NeuBild As New Bitmap(Ausgangsbild.Width, Ausgangsbild.Height)
    3. Dim ColMap(0) As ColorMap
    4. ColMap(0) = New ColorMap
    5. ColMap(0).OldColor = OldColor
    6. ColMap(0).NewColor = NewColor
    7. Dim ImgAttr As New ImageAttributes
    8. ImgAttr.SetRemapTable(ColMap)
    9. Using G As Graphics = Graphics.FromImage(NeuBild)
    10. G.DrawImage(Ausgangsbild, _
    11. New Rectangle(0, 0, Ausgangsbild.Width, Ausgangsbild.Height), 0, 0, _
    12. Ausgangsbild.Width, _
    13. Ausgangsbild.Height, _
    14. GraphicsUnit.Pixel, _
    15. ImgAttr)
    16. End Using
    17. Return NeuBild
    18. End Function


    Bei diesem Code gebe ich aber eine "OldColor" mit, welche dann ersetzt wird. So funktionierts zu mindest nicht.
    ist logisch, weil nur GENAU EINE Color berücksichtigt wird - du sagst die Farben haben unterschiedliche Alpha-Werte, somit sind sie - selbst wenn R,G,B gleich sind - unterschiedlich. Ich vermute es würde Funktionieren wenn du für alle A-Werte (0 bis 255) einen Eintrag in der ColorMap machen würdest. Mit einer Schleife würde das sehr leicht gehen. Aber ein Problem hast du dann, wenn der R,G oder B-Wert nur minimal abweicht (=> JPG-Format wegen Komprimierung, Farbverlauf, etc)... Bei der ColorMatrix wäre das Problem zumindest beseitigt...

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
    2. Dim bitmap As New Bitmap("D:\a.jpg")
    3. Dim gefärbtesBild As Bitmap = färbeBild(bitmap, Color.Blue)
    4. Me.BackgroundImage = gefärbtesBild
    5. End Sub
    6. Public Function färbeBild(ByVal bild As Bitmap, ByVal färbung As Color) As Bitmap
    7. Dim image As New Bitmap(bild)
    8. Dim imageAttributes As New ImageAttributes()
    9. Dim width As Integer = image.Width
    10. Dim height As Integer = image.Height
    11. Dim fr As Single = färbung.R / 255.0F
    12. Dim fg As Single = färbung.G / 255.0F
    13. Dim fb As Single = färbung.B / 255.0F
    14. Dim colorMatrixElements As Single()() = { _
    15. New Single() {fr, 0, 0, 0, 0}, _
    16. New Single() {0, fg, 0, 0, 0}, _
    17. New Single() {0, 0, fb, 0, 0}, _
    18. New Single() {0, 0, 0, 1, 0}, _
    19. New Single() {0, 0, 0, 0, 1}}
    20. Dim colorMatrix As New ColorMatrix(colorMatrixElements)
    21. imageAttributes.SetColorMatrix(colorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap)
    22. Using g As Graphics = Graphics.FromImage(image)
    23. g.DrawImage(image, New Rectangle(0, 0, width, height), 0, 0, width, height, GraphicsUnit.Pixel, imageAttributes)
    24. End Using
    25. Return image
    26. End Function
    Ich mag jetzt auf dem Schlauch stehen, aber ich krieg das mit den Farben hinzufügen nicht hin.

    VB.NET-Quellcode

    1. Dim ColMap(256) As ColorMap
    2. For a = 0 To 255
    3. Dim cCol As New Color
    4. cCol = Color.FromArgb(a, OldColor.R, OldColor.G, OldColor.B)
    5. ColMap(a) = New ColorMap
    6. ColMap(a).OldColor = cCol
    7. ColMap(a).NewColor = NewColor
    8. Next
    9. Dim ImgAttr As New ImageAttributes
    10. ImgAttr.SetRemapTable(ColMap)


    P.S. Ich verwende keine abgespeicherten Bilder, das Bild wird immer aus der oben genannten Punktewolke berechnet.
    Also keine Verluste durch JPG, ...
    das heißt doch, dass du die "Punktwolke" irgendwo (mit GDI?!) erstellst. Wenn ja, dann zeige mal den Code davon - ich wette man kann genau da ansetzten und die Farbe verändern.

    Dein Code aus Post#10 wirft bestimmt eine Exception - weil ColMap(256) erzeugt ein Array mit 257 Elementen - das Letzte ist also Nothing und crasht das Programm. Mache es mal so:

    ColMap(Byte.MaxValue-1)

    und

    For a = 0 to Byte.MaxValue-1

    Ansonsten gehe doch einfach mal von der ColorMap weg und teste die ColorMatrix
    Hi, danke, jetzt hat's geklappt.

    das Bild aus der Wolke wird mit folgender Routine erstellt, wobei die Punkte als "Punkteliste As List(Of Punkt)" vorliegen und der Punkt eine Struktur ist.

    VB.NET-Quellcode

    1. Public Structure Punkt
    2. Dim x As Single
    3. Dim y As Single
    4. Dim z As Single
    5. Dim letzter As Boolean
    6. Dim Radius As Single
    7. Dim Breite As Integer
    8. Dim Hohe As Integer
    9. Dim Schnitt As Boolean
    10. End Structure


    Routine:

    VB.NET-Quellcode

    1. For Each p As Punkt In Punkteliste
    2. If StartForm.Aborter = True Then Exit For
    3. If p.z < 0 Then
    4. Using g = Graphics.FromImage(bmp), Brush = New SolidBrush(Color.FromArgb(100, Color.Black))
    5. ctr += 1
    6. g.SmoothingMode = Drawing2D.SmoothingMode.None
    7. Dim mult As Single = (12 * p.z) * Bohrdurchmesser
    8. g.FillEllipse(Brush, p.x * 12, p.y * 12, mult, mult)
    9. End Using
    10. Application.DoEvents()
    11. End If
    12. Next
    Du machst einen groben Fehler: Du legst die For-Schleife über das Erstellen des Graphics-Obejekts. Da geht dir sicher jede Menge Performance Flöten - darum sicher auch das DoEvents (weg damit!)^^

    Hier mal etwas umgeformt:

    VB.NET-Quellcode

    1. Using g = Graphics.FromImage(bmp), Brush = New SolidBrush(Color.FromArgb(100, Color.Black))
    2. g.SmoothingMode = Drawing2D.SmoothingMode.None
    3. For Each p As Punkt In Punkteliste
    4. If StartForm.Aborter = True Then Exit For
    5. Dim mult As Single = (12 * p.z) * Bohrdurchmesser
    6. If p.z < 0 Then
    7. ctr += 1
    8. g.FillEllipse(Brush, p.x * 12, p.y * 12, mult, mult)
    9. End If
    10. Next
    11. End Using


    Das reduziert den Aufwand, den du im inneren der Schleife betreibst drastisch. Weiterhin: Der Brush gibt ja die Farbe an - setzte doch jetzt hier an und verändere dort die Farbgebung anstatt hinterher mit ColorMap/Matrix. (Falls du Programmier-Anfänger bist jedenfalls ein Lob für den Code - das hätte deutlich schlimmer aussehen können!). Das mit der Structure Punkt ist da denke ich in Ordnung - wenn du nicht genau weißt was eine Structure macht und was eine Klasse, dann benutze besser Klassen - das nur für die Zukunft^^

    lg

    Edit: der Code sollte bei den wenigen Punkten in wenigen zehn Millisekunden abgelaufen sein - sehe also keinen Grund für DoEvents oder ein Abbrechen der ForSchleife, nichtmal für Threading.
    Danke für den Tip, es geht zwar recht zügig, aber Geschwindigkeit zu erhöhen ist immer gut.
    Das ganze bereits beim Code erzeugen zu implementieren, werde ich versuchen. Derzeit habe ich die Punktewolke nur so, lang, bis ich meinen Maschinencode erstellt habe, aber das könnte ich ändern.
    Das Do-Event habe ich in jeder Schleife, da dies eine Abfolge mehrerer Berechnungen ist, startend mit der Berechnung der Punkte über Winkelfunktionen, und die Punktezahl auch mal 500000 Punkte überschreiten kann, hab ich das überall reingepackt.
    Ich möchte hier nicht für mein Produkt werben, aber wenn's dich interessiert, was ich mit dem Ganzen bezwecke, kann ich dir gern nen Link per PN schicken, dann kannst du dir das fertige Ergebnis ansehen (nicht die Vorschau, das Endprodukt).

    Mega Dank an dich
    Ja, würde mich interessieren wie es später aussieht^^

    Das .DoEvents ist echt nicht schön. Wenn du vorhast das auf soviele Punkte auszuweiten würde ich das in einem separaten Thread auslagern. Ein Backgroundworker wäre dafür passen und sehr einfach in der Handhabung - zudem könntest du eine Fortschrittsanzeige umsetzen. (Afaik heißt es, dass man Aufgaben die mehr als 30-50ms brauchen auslagern soll)
    Hi,

    ich ermittle derzeit mit folgender Methode Grauwerte aus meinem Bild.

    VB.NET-Quellcode

    1. For y = CInt(p.y * ScaleFactor - (p.Hohe / 2 * ScaleFactor)) To CInt(p.y * ScaleFactor + (p.Hohe / 2 * ScaleFactor))
    2. If y > 0 And y < bmp.Height Then
    3. For x = CInt(p.x * ScaleFactor - (p.Breite / 2 * ScaleFactor)) To CInt(p.x * ScaleFactor + (p.Breite / 2 * ScaleFactor))
    4. If x > 0 And x < bmp.Width Then
    5. c = bmp.GetPixel(CInt(x), CInt(y))
    6. If c.A > 0 Then
    7. pixCtr += 1
    8. pixR = pixR + c.R
    9. pixG = pixG + c.G
    10. pixB = pixB + c.B
    11. Ca = c.A
    12. PixWorked = True
    13. End If
    14. End If
    15. Next
    16. End If
    17. Next


    ich dachte mit Lockbits, sollte es wesentlich schneller gehen, und habe deshalb versucht den Code, den ich hier im Forum gefunden habe an meine Bedürfnisse anzupassen.

    VB.NET-Quellcode

    1. Dim tmp As New Bitmap(Breite, hohe)
    2. tmp = StartForm.picToWork.BackgroundImage
    3. Dim rect As New Rectangle(0, 0, Breite, hohe)
    4. Dim bmpData As System.Drawing.Imaging.BitmapData = tmp.LockBits(rect, Drawing.Imaging.ImageLockMode.WriteOnly, tmp.PixelFormat)
    5. Dim ptr As IntPtr = bmpData.Scan0
    6. Dim bytes As Integer = Math.Abs(bmpData.Stride) * hohe
    7. Dim rgbValues(bytes - 1) As Byte
    8. System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes)
    9. Dim offset As Integer = 0
    10. For tY = 0 To hohe - 1
    11. For tX = 0 To Breite - 1
    12. 'Du kennst tX und tY (Position auf der Bitmap) und die Positionen der einzelnen Farbkanal-Bytes im rgbValues-Array. Alles was du brauchst
    13. Dim bP As New bPunkt
    14. Try
    15. bP.f = (rgbValues(offset + 0) + rgbValues(offset + 1) + rgbValues(offset + 2)) / 3
    16. bP.a = rgbValues(offset + 4)
    17. bP.x = tX
    18. bP.y = tY
    19. offset += 4
    20. l.Add(bP)
    21. Catch ex As Exception
    22. End Try
    23. Next
    24. Next
    25. 'System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, rgbValues.Length)
    26. tmp.UnlockBits(bmpData)


    Ich ermittle mit meinem Code den durchschnittlichen Grauwert der in einem Rechteck enthaltenen Bildpunkte.

    Könnt ihr mir helfen ?

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

    mach mal das TryCatch weg - da geht bestimmt was schief^^ Byte+Byte+Byte ist schnell mal außerhalb des Wertebereichts von Byte - daran wirds liegen.

    schreibe also

    VB.NET-Quellcode

    1. dim R as integer = rgbValues(offset + n)
    2. 'usw


    und rechne mit den Integer-Zahlen weiter. Option Strict On natürlich. Am Schluss kannst du den Integer (der dann einen Wert zwischen 0 und 255 angenommen haben sollte) wieder zu Byte casten. Probleme mit den Arraygrenzen sollte es nicht geben - wichtig ist aber, dass die Bitmap auch wirklich als 32bbp-Bitmap geladen wird, denn sonst gibt es evtl weniger Farbkanäle die du auswerten kannst.

    lg

    Edit: Anstatt etwas eigenes zu basteln könntest du auch die ColorMatrix-Klasse verwenden (Codebeispiel siehe MSDN) um ein Bild in ein Graustufenbild umzuwandeln.
    bobpowell.net/grayscale.htm (kannst ja mal die Matrizen da ausprobieren)

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

    Hi FreakJNS,

    genau das wars, Danke.
    Es geht mir hier um einen Mittelwert eines kleinen Rechtecks, von denen ich viele in einem Bild habe.
    Ich skaliere das Bild von z.B. 1.000.000 Punkte auf 10.000 Punkte Maschinencode, und .... gerade frage ich mich, ob ich nicht wirklich erst das Bild skaliere und dann eden Pixel auswerte.

    Danke jetzt habe ich wieder was zum grübeln. ?(
    Hi Lete,

    steh nochmal auf dem Schlauch. ich hab ne Klasse,

    VB.NET-Quellcode

    1. Public Class bPunkt
    2. Public f As Integer
    3. Public a As Integer
    4. Public x As Integer
    5. Public y As Integer
    6. Private Function GetVal(ByVal var As String)
    7. If LCase(var) = "f" Then
    8. Return f
    9. ElseIf LCase(var) = "a" Then
    10. Return a
    11. ElseIf LCase(var) = "x" Then
    12. Return x
    13. ElseIf LCase(var) = "y" Then
    14. Return y
    15. End If
    16. End Function
    17. End Class


    und die Punkte schreibe ich in eine List (of bPunkt).
    In den Punkten ist ja der Wert X,Y enthalten, welche mir bei der Auswertung bereits bekannte Koordinaten sind.
    Wie müsste eine Funktion aussehen, wo ich sage a = a von Punkt mit den Koordinaten x,y, ohne dass ich jedesmal mit for each die ganze Liste durchackern muss ?