Software zur Auswertung von Luftpistolen-Zielscheiben

  • VB.NET

Es gibt 34 Antworten in diesem Thema. Der letzte Beitrag () ist von Niko Ortner.

    Software zur Auswertung von Luftpistolen-Zielscheiben

    Hallo,
    ich habe vor mein erstes etwas schwereres Projekt zu starten.
    Funktion soll sein, eingescannte Zielscheiben zu analysieren.
    D.h. anhand der Farbunterschiede die Einschusslöcher und Ringe erkennen und die Punktzahl ausgeben.

    Meine Frage währe nun, ob dass überhaupt in VB2008 möglich ist (habe so meine Zweifel) und wenn ja, welche Themen ich mir hierzu durchlesen sollte, was ich dafür alles können sollte und was ich dazu brauche.

    Vielen Dank.
    eXtense
    Vorstellen könnte ich es mir schon. Das erste Problem wird der Bildkontrast sein.
    Du hast schwarze Bereiche und graue Bereiche auf der Scheibe. Die Löcher
    müssten eindeutig durchschossen sein. Manchmal hängt das ausgestanzte
    Lochteil noch am Karton, wenn man das nicht bemerkt und auf den Scanner legt,
    drückt man es vielleicht wieder rein.
    Hast du schon mal einen Karton eingescannt? Am besten mit einem weißen
    Hintergrund.
    Genormt sind die Scheiben natürlich.
    Allerdings soll die Software ja die exakten Koordinaten, sowie die Größen der Einschüsse bestimmen können, um eine möglichst genaue Auswertung zu erzielen.
    Desweiteren werden die Scheiben zwar mit bis zu 1200 dpi eingescannt, für ein 16x16cm Objekt von so einfacher Beschaffenheit also sehr hochauflösend, allerdings kann man ja nie das millimetergenaue Einlegen in den Scanner garantieren.
    Somit muss von Hand eben der Mittelpunkt der Scheibe bestimmt werden und ausgehend davon mit den Koordinaten und den Ringabständen die Punktzahl errechnet werden.

    Edit:
    @Lightsource
    Das stellt absolut kein Problem dar.
    Also ich schieße nun schon ca. 1 Jahr mit dieser Waffe und hängen bleibt da garnichts.
    Die Einschüsse sind alle sauber und wie mit einem Locher gestanzt.
    Bei bedarf werde ich ein Beispielbild hochladen.
    Hi
    So schwer ist das ja nicht, wenn man sich das überlegt. Die Platte an sich darf sich natürlich nicht bewegen. Wenn sie sich nicht bewegt, musst du nur auf Veränderungen reagieren, wobei du entsprechende Störungen natürlich nicht mit einkalkulierst. Veränderungen treten natürlich dann erst auf, wenn du schießt (oder jemand durchs Bild läuft). Da die Löcher vermutlich dunkler sind, als die Scheibe kannst du mögliche Reflexionen oder ähnliches ebenfalls herausrechnen. Wenn du den Fleck dann letztendlich gefunden hast, berechnest du nur noch das Zentrum des meist annähernd kreisförmigen Lochs und erhältst somit Zentrum und Radius.
    Für den Analysevorgang kannst du einfach z.B. jeden 5. Pixel der Bitmap überprüfen und auf den vorherigen Wert überprüfen. Der neue Wert wird nach Möglichkeit mit vorherigen Werten interpoliert, damit die Störungen herausgerechnet werden können (einfach (alterFarbkanal1 + neuerFarbkanal1)/2 sollte schon genügen, ansonsten einfach (alterFarbkanal1 * x + neuerFarbkanal1) / (x+1) oder so). Damit wird auch die mögliche Veränderung der Lichtverhältnisse ebenfalls halbwegs berücksichtigt. Wenn du dann einen Pixel gefunden hast, der sich verändert hat, kannst du in seiner Umgebung nach sämtlichen veränderten Bildpunkten suchen und daraus Schlüsse ziehen, wie das Loch verläuft.
    Wenn dus noch exakter machen möchtest, kannst du einfach eine Funktion durch ein normales Loch durchlegen, indem du sagst, wie die Schattierung verläuft. Bei einem abgerundeten Kopf wäre zum Beispiel die Kraftverteilung so, dass das Einschlagloch außen nicht so tief ist, wie im Zentrum. Ganz außen ist die Steigung wahrscheinlich am größten, ganz innen ist sie 0, wenn das Loch von der Tiefe her annähernd rotationssymmetrisch ist. Damit wäre der hellste Punkt wahrscheinlich im Zentrum und der dunkelste Bereich außen (wobei da dann der Winkel des Lichteinfalls eine wichtige Rolle spielen würde). Vorteil dieser Methode ist, dass du genau sagen kannst, wo sich das Loch befindet. Damit kannst du andere Störfaktoren herausrechnen oder so.
    Ist halt fraglich, ob sich das wirklich lohnt. Ich denke, ich würde einfach mal mit dem 1. Ansatz arbeiten. Der dürfte sehr schnell umsetzbar sein.

    Gruß
    ~blaze~
    Na das ist ja schonmal ein Batzen voll rumprobieren *gg*
    Es scheint allerdings ein Missverständniss aufgetreten zu sein. Mit "scannen" war das einscannen in einem Flachbettscanner gemeint.
    Somit liegt eine Bilddatei vor, es kann also niemand ins Bild laufen und grobe Störfaktoren sind auszuschließen.
    Die Methoden an sich habe ich nun verstanden, zur Realisierung ist allerdings mein erster Post zu beachten, dass ich zunächst wissen müsste in was ich mich vertieft einlesen müsste.
    Sprich, welche Befehle, Funktionen, Bibliotheken usw. ich benötige.

    Mfg,
    eXtense
    Die Koordinaten per Algorhythmus herauszufinden kann funktionieren, wenn man als Hintergrund eine feste Farbe hat, die sonst nicht vorkommt. Ich bin halt nicht so der Grafikexperte.
    Das Ausrechnen der Punkteanzahl lässt sich hingegen einfach bewältigen:
    Die Differenzen der X und Y Koordinaten des jeweiligen Loches und der X und Y Koordinaten des Mittelpunktes der Scheibe jeweils quadrieren. Daraus die Summe bilden und anschließend wieder die Quadratwurzel ziehen (Pythagoras). Anschließend noch den Radius des Loches addieren, wenn der äußerste Punkt verwendet werden soll.

    Das sieht dann so aus:
    Abstand = Math.Sqrt((Xi - Xm) ^ 2 + (Xi - Xm) ^ 2) + ri
    Xi/Yi ist die X/Y Koordinate des jeweiligen Loches,
    Xm/Ym ist die X/Y Koordinate des Mittelpunktes,
    ri ist der Radius des jeweiligen Loches.

    Anschließend abhängig vom Bereich in dem sich der Abstand befindet die Punktezahl vergeben.

    VB.NET-Quellcode

    1. Select Case Abstand
    2. Case Is >= 0 And Is < 100 'Es kann sowiso nicht kleiner als der Radius des jeweiligen Loches sein.
    3. Return 12
    4. Case Is >= 100 And Is < 200
    5. Return 11
    6. '...
    7. End Select


    Die Grenzen müssen noch an die jeweiligen Scheiben angepasst werden, aber wenn alle gleich sind muss das nur einmal passieren.

    Edit: ~blaze~ <- Da hast Du Deinen Grafikexperten.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    Nun stehe ich vor meinem ersten Problem.
    Versuche mich an der Lösung von ~blaze~.
    Allerdings weiß ich nicht wie ich 2 Pixel vergleichen soll.
    Wenn ich mit GetPixel z.B. die Farben von 200,200 und 200,205 auslese, weiß ich nicht in welcher Form die Farbwerte ausgegeben werden und wie ich sie vergleichen kann.
    Könnte mir da jemand helfen? :)

    Edit:
    Das einzige was mir einfallen würde währe natürlich eine If-Abfrage, ob die Farben übereinstimmen. Klar.
    Aber es muss ja irgendwie ein Puffer enthalten sein..das eine nähere Überprüfung erst ab einer bestimmten Abweichung stattfindet.
    Denn EXAKT die selbe Farbe, haben natürlich kaum 2 Pixel nebeneinander. Ist bei Pappe ja fast unmöglich.
    Jep, Farbwerte werden von GetPixel() als System.Drawing.Color zurückgegeben. Color Objekte haben die Eigenschaften .R, .G, und .B. Das sind die Rot, Grün und Blauwerte zwischen 0 und 255 (so wie man es kennt).
    Wie genau Du das dann machst weiß ich nicht. Du könntest den Durchschnitt der drei Werte verwenden, oder jeden Wert einzeln vergleichen... wie Du willst. (Vergiss beim Subtrahieren von Bytewerten nicht vorher in Integer umzuwandeln. Byte - Byte ergibt Byte und Byte kann keine negativen Werte darstellen. Es kommt zu einem Überlauf. Das Problem hatten wir erst vor Kurzem mal.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    Du vergleichst nur bei der Initialisierung die Pixel und schaust, wo sich die Zielscheibe befindet. Deshalb dürfen sich Brett und Kamera nicht bewegen. Dann hast du zwei Puffer: Einmal hast du den Puffer, indem die aktuellen Daten stehen und einen weiteren, in dem die Daten von alten Ergebnissen sich befinden. Dabei gilt immer für den n+1-ten Durchlauf, dass sich die Farbe c_(n+1) mit c_0 so berechnet:
    c_(n+1) = (c_n + c_0) / 2
    Das ist die Formel, die ich oben angegeben habe. Diese Formel führst du für R, G und B durch (A = 255 für alle Werte, das kannst du vernachlässigen). Die Color-Struktur enthält allerdings viel mehr Daten, als nur die Farbkanäle. Daher ist die Verarbeitung auf Basis von Integern schneller, was bei dir ja doch eine gewisse Rolle spielen kann. Schneller ist also:

    VB.NET-Quellcode

    1. Dim bitmap As Bitmap
    2. 'Format entsprechend an XRGB oder ARGB anpassen, bitte. Das ist wichtig, dass alle Pixel 4 Bytes lang sind
    3. Dim bdata As BitmapData = bitmap.LockBits(New Rectangle(Point.Empty, bitmap.Size), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb)
    4. Dim newData(bdata.Width * bdata.Height - 1) As Integer
    5. Runtime.InteropServices.Marshal.Copy(bdata.Scan0, newData, 0, newData.Length)
    6. For i As Integer = 0 To newData.Length - 1
    7. 'Hier schaust du, ob die Bitmaps uebereinstimmen
    8. Next
    9. bitmap.UnlockBits(bdata)

    Statt dem Offset kannst du auch die Schrittweise Vorgehensweise so durchführen:

    VB.NET-Quellcode

    1. Dim offset As Integer
    2. For x As Integer = 0 To bdata.Width - 1
    3. For y As Integer = 0 To bdata.Height- 1
    4. offset =(x+y * bdata.Width) * 4
    5. Next
    6. Next

    Außerdem legst du ein Array oldColors (oder so) an, in dem du die alten Werte ablegst und immer mit den neuen interpolierst. Das Array initialisierst du durch einen Aufruf von

    VB.NET-Quellcode

    1. Dim bitmap As Bitmap
    2. 'Format entsprechend an XRGB oder ARGB anpassen, bitte. Das ist wichtig, dass alle Pixel 4 Bytes lang sind
    3. Dim bdata As BitmapData = bitmap.LockBits(New Rectangle(Point.Empty, bitmap.Size), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb)
    4. oldData = New Integer((bdata.Width * bdata.Height - 1){}
    5. Runtime.InteropServices.Marshal.Copy(bdata.Scan0, oldData , 0, oldData .Length)
    6. bitmap.UnlockBits(bdata)


    Gruß
    ~blaze~
    Danke.
    Ich würde jetzt noch gern die Einschusslöscher "grob" kennzeichnen..also vor der auswertung.
    Mit einem Viereck oder Kreis, welches auf dem Form verschiebbar ist. Welche Komponente nimmt man hierzu am besten?
    Sollte natürlich einen transparenten Hintergrund haben.
    Um es ein Bisschen spezifizierter zu machen:

    Ein Enum... ach, ich mach mal ein Projekt und lad es hoch. Hab gerade Lust etwas zu coden und keine Lust das Pseudocodemäßig durchzugehen.

    Melde mich gleich wieder...
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    Hey, ich finde das ganze auch recht interessant und habe vor mich am Wochenende genauer damit zu beschäftigen. Kannst du mir bitte die maße in mm zur Verfügung stellen? Also:
    - kartonmaß
    - Durchmesser der ringe + punktebereiche
    - Lage des mittelpunktes
    - Durchmesser des Projektils
    - linienstärke

    Am besten lade auch noch gescannte scheiben hoch ;)

    Wäre sehr nett. Lg
    So, das Projekt ist fertig. Hat eine Weile gedauert, aber es hat mir auch Spaß gemacht.
    Ich hoffe die Bedienung erklärt sich weitestgehend von selbst. Die "Punktebereiche" sind die Abstände in Pixel, die der äußerste Punkt eines Einschussloches haben darf, damit er noch die angegebene Punktezahl bekommt. Diesen Abstand muss man entweder errechnen oder z.B. in einem Bildbearbeitungsprogramm messen.
    Die Kreise stellen hier nicht die Einschusslöcher dar, sondern markieren nur den äußersten Punkt der Löcher (wenn ich mich recht erinnere wird das immer so gewählt).

    .Net Framework 3.5 wird benötigt. Es ginge auch mit 2.0, aber da kommen ein Haufen Warnungen und ich will nur sichergehen, dass es auch funktioniert.
    Screenshot auch noch im Anhang
    Bilder
    • Screenshot.png

      144,35 kB, 1.163×633, 686 mal angesehen
    Dateien
    • Testanwendung.zip

      (189,85 kB, 506 mal heruntergeladen, zuletzt: )
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    @FreakJNS

    Gerne doch. Hier die Maße einer Wettkampfscheibe für Luftpistolen:
    Das exakte Kartonmaß sind 17x17cm
    Die Ringe 1 - 9 haben zueinander auf allen Seiten einen Abstand von 8mm, wobei der äußerste Ring einen Durchmesser von 15,6cm hat.
    Der 10. Ring hat einen Durchmesser von 1,15cm und einen Abstand von 3mm zum Mittelpunkt, welcher einen Durchmesser von 5mm hat.
    Die Punktevergabe verhält sich hier so, dass jeder Ring der angekratzt wurde, wenn auch nur gering, zählt. D.h. liegt der eigentliche Einschuss in Ring 7, aber die 8 wurde zu 1mm angekratzt, werden 8 Punkte gewertet.
    Dies gilt für die Ringe 1-10. Der Exakte Mittelpunkt wird nur bei einem exakten Treffer (nicht bei Ankratzen) mit 10,9 Pkt. gewertet.
    Die Lage des Mittelpunktes..naja :D in der Mitte?
    Puh..Linienstärke..mir steht als präzisestes Messwerkzeug hier ein Lineal mit 1mm-Skalierung zur Verfügung :D dürfte schwer werden..ich SCHÄTZE mal irgendwas zwischen 1/5 und 1/8mm..
    Ich würde es da eher so handhaben, dass die Software (so sind meine anfänge mal) jede geladene scheibe erstmal auf 800x800 Pixel skaliert, da dass zum auswerten mehr als genug sein sollte und somit rechenzeit spart und vorallem ein möglichst einheitliches Bild der scheiben ermöglicht. Am besten liest du da selbst die Breite in Pixeln ab, jeh nachdem wie Groß du die Scheiben verwerten willst.
    Die Diabolokugeln sind in meinem Fall 4,5mm Projektile, somit schwanken auch die Einschüsse zwischen 4,5 - 4,7mm. Letztere sind eher selten.
    Anbei ist ein Bild einer beschossenen Scheibe (Habe es mal nicht bei den 1,2k dpi belassen..währe wohl "etwas" zu groß gewesen :D aber die Größe sollte ja mehr als ausreichen). Wenn du auch ein Bild einer neuen Scheibe benötigst, einfach melden.

    @Niko Ortner

    Zunächst vielen Dank.
    Auch wenn ich mir über die Handhabung noch nicht ganz klar geworden bin *gg* wird es mir sicher an vielen Stellen in der Verarbeitung der Scheiben helfen.
    Es ist eben nur zu beachten, dass wie oben geschrieben 1. angekratzte Ringe zählen & 2. eben eingescannte Bilder von Scheiben verwertet werden.

    Edit:
    Was mir auch noch auffällt. Wie auf der angehängten Scheibe, kommt es natürlich oft vor, dass sich Einchüsse überschneiden und somit kein rundes Loch mehr vorhanden ist.
    Wie könnte man denn soetwas berücksichtigen? :huh:

    Grüße,
    eXtense
    Bilder
    • Zielscheibe_01.JPG

      916,88 kB, 1.100×1.090, 1.628 mal angesehen

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

    "Doppeltreffer" könnten ggf schwierig werden? Also wenn beide Einschüsse sehr stark überlappen, so dass es schon nicht mehr wie ne "8" aussieht, sondern eher in den Bereich "0" (also oval) geht. Da dürfte 800 Pixel schon deutlich zu klein sein? Denn bei 17 cm kommt man dann ja nur auf 5 Pixel pro Milimeter. Das wären also 0.2mm und schon die Linien werden nicht mehr sauber aufgelöst. Und wenn es um Doppeltreffer geht, muss man ja die "Rundheit" möglichst gut messen - imho und vermutlich ;)
    Mit meinem Programm kann man inetwa auswählen, wo sich der äußerste Punkt befindet. Dabei kann auch der Radius der Markierungen helfen. Man kann z.B. zuerst den Mittelpunkt des Loches anklicken, den Radius auf den tatsächlichen Wert einstellen und dann ist das Erkennen des äußersten Punktes auch nicht mehr so schwer.
    Dann einfach für beide Löcher auswählen.

    Ich könnte ein Video zusammenschneiden, in dem erklärt wird, wie das Programm bedient wird, falls Dir das hilft. Wird aber vermutlich erst am Abend fertig, weil ich bis Freitag Praktikum mache und deswegen arbeiten muss.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils