Ausschnitt von Kreis berechnen

  • C#
  • .NET (FX) 4.5–4.8

Es gibt 16 Antworten in diesem Thema. Der letzte Beitrag () ist von seh.

    Ausschnitt von Kreis berechnen

    Hallo zusammen, hallo @RodFromGermany

    ich hab mal wieder ein kleines Anliegen. Die Farbstreifenerkennung von damals funktioniert im Moment wie sie soll und deshalb sollte ich mich nun auch mal mit den Felgen beschäftigen.
    Nun gibt es sogenannte Matchpunkte auf Felgen und Reifen wenn der Reifen vom Hersteller kommt.
    Um die Gewichtung der Räder beizubehalten muss entweder Felge oder Reifen gedreht werden, bis die Matchpunkte untereinander liegen.
    Im Moment geht es hier nur um die Erkennung der Matchpunkte, ist das geschafft, ist der Rest ein Kinderspiel.

    Nun zum Glück bekomme ich vom Kunden einige Raddaten vorgegeben. Felgenmittelpunkt auf dem Bild, Felgendurchmesser (in Millimeter, Umrechnung in Pixel <> Millimeter erfolgt bereits), kompletter Raddurchmesser (also Felge + Reifen) und noch einige andere Dinge, z.B. Farbe vom Matchpunkt, und noch viel mehr für dieses Topic uninteressante Daten.

    Ich hatte mir jetzt überlegt, dass ich mit dem Reifenmatchpunkt anfange. Dieser ist denke ich einfacher als der Felgenmatchpunkt weil dieser in verschiedenen Farben und auf verschiedenen Hintergründen vorkommt. (Felgen variieren farblich und manchmal muss ein weißer Matchpunkt auf silberner Felge erkannt werden, was dann schon ziemlich schwierig wird, weil auf den silbernen Felgen dann auch noch Seifenflecken drauf sind.)

    Also zu meiner Vorgehensweise:

    Da ich Raddurchmesser sowie Felgendurchmesser vorgegeben habe, dachte ich mir wäre es sinnvoll, den Reifen aus dem Bild freizustellen. Dafür muss ich ja eigentlich nur die Fläche vom Kreis der sich durch den Felgendurchmesser ergibt von der Fläche des Kreises abziehen der sich durch den Raddurchmesser ergibt. So weit so gut. Nur wie mache ich das genau? Ich habe selbst schon eine Lösung dafür, bin mir nur nicht sicher ob es nicht vielleicht doch noch viel eleganter und dadurch auch performanter geht.

    Meine Lösung bisher: Iteriere alle Pixel vom kompletten Bild und berechne zu jedem Pixel die Distanz zum Mittelpunkt.
    Wenn die Distanz größer ist als der Radius vom Raddurchmesser oder kleiner ist als der Radius vom Felgendurchmesser wird der Pixel auf schwarz gesetzt (im Moment noch, später werde ich von den Pixeln im validen Bereich ein kleineres Bild erstellen).
    Somit habe ich dann den Reifen freigestellt. (Abbildung: freigestellt.bmp)

    Diese Lösung funktioniert, ist aber wahrscheinlich alles andere als performant. Da ich großen Wert auf Performance legen muss und mich selbst auch immer von eleganteren Lösungen überzeugen lassen möchte, frage ich hier, wie man das ganze anders lösen kann.
    Das Problem an der Sache ist, dass ich immer die Länge vom Vektor berechnen muss. Wenn ich die Länge vom Vektor berechnen möchte muss ich die Wurzel ziehen. Da ich weiß, dass die Wurzel ziehen schon lange dauert, dachte ich mir, quadriere ich einfach die Distanz und vergleich also nicht Länge, sondern LenthSquared. So bin ich dann durch das weglassen von Math.Sqrt() wenigstens schon mal etwas schneller.

    Aber gibt es für das ganze nicht doch eine ganz andere wesentlich elegantere Lösung?
    Mit meiner Lösung bin ich jetzt bei der Freistellung schon bei 500ms (beachtet wurde bereits, dass in den zwei loops keine Properties abgerufen werden. Properties sind in C# nämlich langsamer als ganz normale Member Variablen.)

    Hier meine Lösung:

    C#-Quellcode

    1. var rows = CVImage.Rows;
    2. var cols = CVImage.Cols;
    3. var minDist = (AppSettings.Instance.Rim.RimDiameter / AppSettings.Instance.Calibration.CalibrationFactor) / 2;
    4. var maxDist = (AppSettings.Instance.Wheel.WheelDiameter / AppSettings.Instance.Calibration.CalibrationFactor) / 2;
    5. var RimCenter = new Vec2(AppSettings.Instance.Rim.RimCenterX, AppSettings.Instance.Rim.RimCenterY);
    6. var binarizedImage = CVImage.Clone();
    7. var imgData = CVImage.Data;
    8. for(int x = 0; x < cols; x++)
    9. {
    10. for(int y = 0; y < rows; y++)
    11. {
    12. Vec2 pos = new Vec2(x, y);
    13. var len = (RimCenter - pos).LengthSquared();
    14. if (len < (minDist * minDist) || len > (maxDist * maxDist))
    15. {
    16. imgData[y, x, 0] = 0;
    17. imgData[y, x, 1] = 0;
    18. imgData[y, x, 2] = 0;
    19. }
    20. }
    21. }
    22. binarizedImage.Data = imgData;


    Danke im Voraus
    Bilder
    • CoreApplication_2017-07-17_16-20-31.png

      1,12 MB, 1.920×1.040, 231 mal angesehen
    • freigestellt.jpg

      159,04 kB, 2.432×2.040, 215 mal angesehen

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

    @seh fast 15 MB Bilddati - mach da ein JPG oder PNG draus, diese BMP ist etwas übertrieben groß.
    =====
    Vielleicht beschreibst Du mal mit wenigen Worten, was da passieren soll. Richtig schlau geworden bin ich da nämlich noch nicht.
    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!

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

    @seh Mach ein wenig Bildverarbeitung:
    Histogramm und alles darstellen / verwnden, was im Bereich von Min bis Max liegt, hier 0 - 76.
    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 Das ist das Bild was ich verarbeiten soll.

    Sorry, ich denke ich habe mich einfach noch nicht klar genug ausgedrückt.

    Ich will auf diesem Bild eine Blob Detection machen, aber nur in dem Reifenbereich (um den weißen Matchpunkt zu finden).
    Für diese Freistellung habe ich für jeden einzelnen Pixel auf dem Bild einen Vektor vom Mittelpunkt zum Pixel gebildet und dessen Länge anschließend verglichen mit dem minDist und maxDist im Quellcodeschnipsel im Eingangsthread.
    Dadurch konnte ich dann die Pixel die außerhalb des Reifens sind schwarz setzen.

    Nur dauert mir das zu lange. Jetzt meine Frage, gibt es eine andere Möglichkeit in der Mathematik und Bildverarbeitung rauszufinden ob der Pixel in diesem Bereich liegt oder nicht.
    Was ich ja schonmal machen kann ist den Reifen freizustellen. Ein Bild kann leider nicht rund sein also gibt es 4 Ecken. Diese müssen dann schwarz gepixelt werden.
    Und die komplette Felge muss dann auch schwarz gepixelt werden.
    Bilder
    • 273430300034L1105296-1.jpg

      231,78 kB, 2.432×2.040, 165 mal angesehen

    seh schrieb:

    um den weißen Matchpunkt zu finden
    Den roten?

    Du solltest Dich mal ein wenig mit (elementarer) Bildverarbeitung beschäftigen.
    Du musst mit dem Bild spielen und dann erkennen, was zu tun 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!
    de.wikipedia.org/wiki/Bresenha…svariante_des_Algorithmus

    Daraus musst nur noch die Fill Variante machen:
    Pixel ganz normal ausfüllen wie du es sonst auch schon machst und einfach nach Rechts(vom linken Kreisrand) bzw. Links(vom rechten Kreisrand) bis zur Mittellinie Zeilenweise auffüllen(oder natürlich Oben bzw. Unten).

    Man könnte aber auch den Kopiervorgang umschreiben, dafür müsste man halt den Innen und Außenkreis mittels Bresenham machen und einfach nur die Daten(Zeilenweise) kopieren, die sich zwischen den jeweiligen Kreisen befinden, dafür musst du nur dafür sorgen, dass der innere kreis weniger schnell iteriert wird, sodass die beiden y Koordinaten gleich bleiben(oder X, jenachdem wie man halt kopiert).
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---

    RodFromGermany schrieb:

    seh schrieb:

    um den weißen Matchpunkt zu finden
    Den roten?
    vb-paradise.de/index.php/Attac…06ba5f7cc82ff01be96dc9ce6
    Du solltest Dich mal ein wenig mit (elementarer) Bildverarbeitung beschäftigen.
    Du musst mit dem Bild spielen und dann erkennen, was zu tun ist.

    Cool, was ist das? Canny und dann HoughCircles ?
    @jvbsl Dazu brauchst Du einen sehr guten Mittelpunkt, der sollte aber aus dem reinen Reifenbild sehr schnell zu finden sein:
    Inneren Punkt schätzen, nach +-x und +-y bewegen, bis der Rand kommt und dann von beiden x und beiden y die Mitte nehmen.
    Das ggf. 2 oder 3 Mal machen und die Werte mitteln.
    ========
    @seh Das ist meine Bildverarbeitung. Histogramm, Wert zur Diskriminante finden, diskriminieren, Median-Filter, Dilation, Erosion, Laplace-Filter.
    Feddich.
    In diesem Bild den großen Kreis finden und leicht radial nach außen gehen und den rot markierten Kreis finden.
    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!

    jvbsl schrieb:

    de.wikipedia.org/wiki/Bresenha…svariante_des_Algorithmus

    Daraus musst nur noch die Fill Variante machen:
    Pixel ganz normal ausfüllen wie du es sonst auch schon machst und einfach nach Rechts(vom linken Kreisrand) bzw. Links(vom rechten Kreisrand) bis zur Mittellinie Zeilenweise auffüllen(oder natürlich Oben bzw. Unten).

    Man könnte aber auch den Kopiervorgang umschreiben, dafür müsste man halt den Innen und Außenkreis mittels Bresenham machen und einfach nur die Daten(Zeilenweise) kopieren, die sich zwischen den jeweiligen Kreisen befinden, dafür musst du nur dafür sorgen, dass der innere kreis weniger schnell iteriert wird, sodass die beiden y Koordinaten gleich bleiben(oder X, jenachdem wie man halt kopiert).

    Danke Dir! Ich treffe mich heute abend noch mit einem Kollegen im TeamSpeak. Der studiert Mathematik auf Lehramt, ich werde ihm nochmal genau erläutern was ich vor habe und dann hat er vielleicht auch eine ganz einfache Lösung.

    @RodFromGermany Ja, ich bin halt in der Bildverarbeitung noch recht neu, bin mich dort allerdings auch nicht aktiv am fortbilden, immer nur für die aktuelle Aufgabe die beste Lösung finden, daher melde ich mich auch im Forum. Ich habe leider auch nicht die Zeit mich aktiv damit zu beschäftigen, der Fokus für mich liegt eher auf der Software die ich schreibe, die Schnittstelle usw. Ich bin zwar sehr interessiert mich dort einzulesen und rumzuprobieren aber ich kenne auch noch nicht alle Möglichkeiten die es so gibt. Ich muss da leider ein bisschen auf Zeit spielen und darauf vertrauen, dass ich mit der Zeit und mit der Quantität an solchen Aufgaben mich in dem Bereich fortbilde.

    Wie spielst du so schnell mit den Bildern? Welche Software nutzt du, Gimp?
    @seh Das ist eine extrem wichtige Sache, wenn Du viel mit Bildern zu tun hast.
    An meinem Programm schreibe ich schon seit über 15 Jahren, erst in C++, dann in C#. Da muss ich wohl bald ne ganz neue Version machen.
    Immer, wenn ein Bild bei mir vorbei kommt, wird es damit analysiert, und dann ist es relativ einfach, weitere Schritte festzulegen.
    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 so wie ich das verstanden hatze , hat er bereits die Werte des Mittelpunkt und Durchmesser.

    @seh außerdem wie immer wenns um Bildbearbeitung und performanz geht-> gpu. KP ob man für opencv auch custom Programme schreiben kann.
    Ansonsten halt OpenCL oder wenn es ganz bestimmt nur nvidia wird cuda
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---

    seh schrieb:

    in der Reihenfolge
    Dilation und Erosion ggf. in der anderen Reihenfolge, jeweils mehrfach anwenden und Anzahl beider Operationen muss gleich sein.
    @jvbsl Durchmesser ja, Mittelpunkt hab ich nicht gesehen.
    Allerdings ist die Herkunft merkwürdig. In einem Bild rechne ich nur in Pixeln, hinterher wird vermöge einer Kalibrierung nach metrisch umgerechnet.
    So was hab ich da auch nicht entdeckt. :/
    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 Okay danke dir, ich habe jetzt mal ein wenig rumgespielt und habe ein Bild wo es schwieriger für mich wird.
    Der Matchpunkt in der Abbildung ist der weiße Fleck, bzw. die 3 weißen Flecken unter dem schwarzen Matchpunkt auf der Felge. Kannst du dieses Bild mal durch dein Programm laufen lassen mit dem selben Filtern von gestern?
    Bilder
    • 257189900034I5405291-2.jpg

      233,65 kB, 2.432×2.040, 182 mal angesehen