Drawing2D.GraphicsPath.IsVisible(x, y) ist langsam. Wie beschleunigen?

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 4 Antworten in diesem Thema. Der letzte Beitrag () ist von RodFromGermany.

    Drawing2D.GraphicsPath.IsVisible(x, y) ist langsam. Wie beschleunigen?

    Hi, ich habe festgestellt, dass ich ein Leitstungsproblem habe. 20 Sekunden, bis die Sub durch ist. Dabei ist sie schon asynchron.

    Worum geht's eigentlich?
    Es geht um mein Kantendetektionsprogramm. Dieser Thread beschäftigt sich jedoch mit einem Schritt vorher. Und zwar zeichne ich freihand einen GraphicsPath um das auszuschneidende Objekt. Das Innere des Paths soll geweißt werden. Dafür holt sich mein Code die xmin-, xmax-, ymin- und ymax-Werte aus den GraphicsPath.PathPoints, um 2 For-Schleifen nutzen zu können, und fragt dann mit .IsVisible(x,y) ab, ob mein derzeitiges x und y innerhalb des Paths liegen.
    Ich zeichne übrigens freihand, weil ich es unvorteilhaft fand, ein Rechteck um einen Kreis ziehen zu müssen.

    Was möchte ich erreichen?
    Hat jemand einen schnelleren Code? Es geht in diesem Beispiel um circa 800 Punkte.


    Spoiler anzeigen

    VB.NET-Quellcode

    1. Private Async Sub Button_Bereich_waehlen_Click(sender As Object, e As EventArgs) Handles Button_Bereich_waehlen.Click
    2. Await Task.Run(Sub() Bereich_waehlen_und_weissen())
    3. End Sub
    4. Private Sub Bereich_waehlen_und_weissen()
    5. If Picture1 Is Nothing Then Return
    6. Me.Invoke(Sub() Button_Bereich_waehlen.BackColor = Color.FromArgb(255, 255, 0))
    7. Me.Invoke(Sub() Button_Bereich_waehlen.Enabled = False)
    8. Me.Invoke(Sub() Button_Kantenerkennung_professionell.Enabled = False)
    9. Dim xmin As Single = selbstgezeichneterPath.PathPoints.Min(Function(p) p.X)
    10. Dim xmax As Single = selbstgezeichneterPath.PathPoints.Max(Function(p) p.X)
    11. Dim ymin As Single = selbstgezeichneterPath.PathPoints.Min(Function(p) p.Y)
    12. Dim ymax As Single = selbstgezeichneterPath.PathPoints.Max(Function(p) p.Y)
    13. Dim gFd As Integer = gewaehlteFarbeDurchschnitt.ToArgb() 'Durchschnittsfarbe: wie man sieht, hat der Taschenrechner Licht- und Schattenseiten.
    14. For x As Integer = CInt(xmin) To CInt(xmax) Step 1
    15. For y As Integer = CInt(ymin) To CInt(ymax) Step 1
    16. If selbstgezeichneterPath.IsVisible(x, y) Then
    17. Dim aktuelle_Farbe As Integer = Picture1.GetPixel(x, y).ToArgb()
    18. If Not (CDbl(aktuelle_Farbe) <= CDbl(gFd * (1.0 + ProzentAbweichung / 100.0)) OrElse CDbl(aktuelle_Farbe) >= CDbl(gFd * (1.0 - ProzentAbweichung / 100.0))) Then
    19. Picture1.SetPixel(x, y, Color.White)
    20. End If
    21. End If
    22. Next
    23. Next
    24. Me.Invoke(Sub() Button_Bereich_waehlen.BackColor = Color.FromArgb(0, 200, 0))
    25. Me.Invoke(Sub() PictureBox1.Invalidate())
    26. Me.Invoke(Sub() Button_Bereich_waehlen.Enabled = True)
    27. Me.Invoke(Sub() Button_Kantenerkennung_professionell.Enabled = True)
    28. Me.Invoke(Sub() Application.DoEvents())
    29. End Sub


    *Topic verschoben*
    Bilder
    • Präsentation1.png

      14,72 kB, 1.280×720, 71 mal angesehen
    • Diagnostic.png

      71,47 kB, 1.382×305, 63 mal angesehen
    • freihand-Path zeichnen.png

      637,91 kB, 767×969, 73 mal angesehen
    • geweißt.png

      551,53 kB, 763×976, 71 mal angesehen

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Marcus Gräfe“ ()

    @ErfinderDesRades Ja aber ich muss erst mit gewaehlteFarbeDurchschnitt das Grau des Taschenrechners erkennen. Weil der Freihand-Path ist ja viel zu weit außen. Den kann ich nicht einfach so komplett weißen.



    Update: ich habe einen LINQ-Approach gefunden. Dieser ist zig mal schneller als IsVisible, leider setzt er manchmal aus. Kann da bitte jemand drüberschauen?

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Private Async Sub Button_Bereich_waehlen_Click(sender As Object, e As EventArgs) Handles Button_Bereich_waehlen.Click
    2. Await Task.Run(Sub() Bereich_waehlen_und_weissen())
    3. End Sub
    4. Private Sub Bereich_waehlen_und_weissen()
    5. If Picture1 Is Nothing Then Return
    6. Me.Invoke(Sub() Button_Bereich_waehlen.BackColor = Color.FromArgb(255, 255, 0))
    7. Me.Invoke(Sub() Button_Bereich_waehlen.Enabled = False)
    8. Me.Invoke(Sub() Button_Kantenerkennung_professionell.Enabled = False)
    9. Dim xmin As Single = selbstgezeichneterPath.PathPoints.Min(Function(p) p.X)
    10. Dim xmax As Single = selbstgezeichneterPath.PathPoints.Max(Function(p) p.X)
    11. Dim ymin As Single = selbstgezeichneterPath.PathPoints.Min(Function(p) p.Y)
    12. Dim ymax As Single = selbstgezeichneterPath.PathPoints.Max(Function(p) p.Y)
    13. Dim s As New List(Of PointF)
    14. s.AddRange(selbstgezeichneterPath.PathPoints)
    15. Dim gFd As Integer = gewaehlteFarbeDurchschnitt.ToArgb()
    16. For x As Integer = CInt(xmin) To CInt(xmax) Step 1
    17. For y As Integer = CInt(ymin) To CInt(ymax) Step 1
    18. Dim p As PointF = New PointF(x, y)
    19. Dim IsInside As Boolean = s.Exists(Function(f) f.Y = p.Y AndAlso f.X < p.X) AndAlso s.Exists(Function(f) f.Y = p.Y AndAlso f.X > p.X)
    20. If IsInside Then 'selbstgezeichneterPath.IsVisible(x, y) Then
    21. Dim aktuelle_Farbe As Integer = Picture1.GetPixel(x, y).ToArgb()
    22. If Not (CDbl(aktuelle_Farbe) <= CDbl(gFd * (1.0 + ProzentAbweichung / 100.0)) OrElse CDbl(aktuelle_Farbe) >= CDbl(gFd * (1.0 - ProzentAbweichung / 100.0))) Then
    23. Picture1.SetPixel(x, y, Color.White)
    24. End If
    25. End If
    26. Next
    27. Next
    28. Me.Invoke(Sub() Button_Bereich_waehlen.BackColor = Color.FromArgb(0, 200, 0))
    29. Me.Invoke(Sub() PictureBox1.Invalidate())
    30. Me.Invoke(Sub() Button_Bereich_waehlen.Enabled = True)
    31. Me.Invoke(Sub() Button_Kantenerkennung_professionell.Enabled = True)
    32. Me.Invoke(Sub() Application.DoEvents())
    33. End Sub


    Beiträge zusammengefügt. ~Thunderbolt
    Bilder
    • Screenshot 2021-01-25 211104.png

      572,72 kB, 728×975, 108 mal angesehen

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

    Update: Ich habe nun geschrieben

    VB.NET-Quellcode

    1. Dim IsInside As Boolean = s.Exists(Function(f) (f.Y = p.Y OrElse f.Y = p.Y + 1) AndAlso f.X <= p.X) AndAlso s.Exists(Function(f) (f.Y = p.Y OrElse f.Y = p.Y + 1 OrElse f.Y = p.Y - 1) AndAlso f.X >= p.X)

    Damit geht's... Ist von der Logik her natürlich falsch, aber was soll's.

    Ich habe auch Ansätze gesehen, deren Verkettung viel größer war, da wurde sogar dividiert (Post 69 in folgendem Link). Aber ob das gut ist...


    qastack.com.de/programming/217…point-is-within-a-polygon

    Naja, ich setz das hier mal auf erledigt.
    @Bartosz Statt n Mal Me.Invoke(Sub()...) solltest Du einen Sub mit allen Inhalten schreiben und den ein Mal invoken.

    Bartosz schrieb:

    Ich zeichne übrigens freihand, weil ich es unvorteilhaft fand, ein Rechteck um einen Kreis ziehen zu müssen.
    ein MouseDown, ein MouseMove, ein MouseUp und Du hast ein Rechteck um Deine Region.
    Fraihand halte ich für suboptimal, zumal sie dynamisch nicht korrigierbar ist.

    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!