Betrachten Sie diese 10 Bilder von verschiedenen Mengen von ungekochten Körnern von weißem Reis.
DAS SIND NUR DAUMENNÄGEL. Klicken Sie auf ein Bild, um es in voller Größe anzuzeigen.
Getreideanzahl: A: 3, B: 5, C: 12, D: 25, E: 50, F: 83, G: 120, H:150, I: 151, J: 200
Beachte das...
- Die Körner berühren sich zwar, überlappen sich jedoch nie. Die Anordnung der Körner ist niemals mehr als ein Korn hoch.
- Die Bilder haben unterschiedliche Dimensionen, aber der Maßstab des Reises ist in allen konsistent, da die Kamera und der Hintergrund stationär waren.
- Die Körner überschreiten niemals die Grenzen oder berühren die Bildgrenzen.
- Der Hintergrund ist immer der gleiche gleichmäßige gelblich-weiße Farbton.
- Kleine und große Körner werden jeweils als ein Korn gezählt.
Diese 5 Punkte sind Garantien für alle Bilder dieser Art.
Herausforderung
Schreiben Sie ein Programm, das solche Bilder aufnimmt und die Anzahl der Reiskörner so genau wie möglich zählt.
Ihr Programm sollte den Dateinamen des Bildes verwenden und die Anzahl der berechneten Körner ausgeben. Ihr Programm muss für mindestens eines dieser Bilddateiformate funktionieren: JPEG, Bitmap, PNG, GIF, TIFF (im Moment sind alle Bilder JPEGs).
Sie können Bildverarbeitungs- und Bildverarbeitungsbibliotheken verwenden.
Sie dürfen die Ausgaben der 10 Beispielbilder nicht fest codieren. Ihr Algorithmus sollte auf alle ähnlichen Reiskornbilder anwendbar sein. Es sollte auf einem anständigen modernen Computer in weniger als 5 Minuten ausgeführt werden können, wenn der Bildbereich weniger als 2000 * 2000 Pixel beträgt und weniger als 300 Reiskörner vorhanden sind.
Wertung
Nehmen Sie für jedes der 10 Bilder den absoluten Wert der tatsächlichen Anzahl der Körner abzüglich der Anzahl der Körner, die Ihr Programm vorhersagt. Summiere diese absoluten Werte, um deine Punktzahl zu erhalten. Die niedrigste Punktzahl gewinnt. Eine Punktzahl von 0 ist perfekt.
Bei Stimmengleichheit gewinnt die am höchsten bewertete Antwort. Ich kann Ihr Programm an zusätzlichen Bildern testen, um seine Gültigkeit und Richtigkeit zu überprüfen.
quelle
Antworten:
Mathematica, Kerbe: 7
Ich denke, die Namen der Funktion sind beschreibend genug:
Verarbeitung aller Bilder auf einmal:
Die Punktzahl ist:
Hier sehen Sie die Punktempfindlichkeit bezogen auf die verwendete Korngröße:
quelle
EdgeDetect[]
,DeleteSmallComponents[]
undDilation[]
werden an anderer Stelle implementiert)Python, Partitur:
2416Diese Lösung basiert wie die von Falko darauf, die "Vordergrund" -Fläche zu messen und durch die durchschnittliche Kornfläche zu dividieren.
Tatsächlich versucht dieses Programm, den Hintergrund und nicht den Vordergrund zu erkennen. Da Reiskörner niemals die Bildgrenze berühren, füllt das Programm zunächst die linke obere Ecke mit Weiß. Der Flood-Fill-Algorithmus zeichnet benachbarte Pixel, wenn der Unterschied zwischen ihnen und der Helligkeit des aktuellen Pixels innerhalb eines bestimmten Schwellenwerts liegt, und passt sich so an die allmähliche Änderung der Hintergrundfarbe an. Am Ende dieser Phase könnte das Bild ungefähr so aussehen:
Wie Sie sehen, macht es eine ziemlich gute Arbeit beim Erkennen des Hintergrunds, aber es lässt alle Bereiche aus, die zwischen den Körnern "eingeschlossen" sind. Wir behandeln diese Bereiche, indem wir die Hintergrundhelligkeit für jedes Pixel abschätzen und alle gleich oder heller werdenden Pixel zusammenfassen. Diese Schätzung funktioniert folgendermaßen: Während der Überflutungsphase berechnen wir die durchschnittliche Hintergrundhelligkeit für jede Zeile und jede Spalte. Die geschätzte Hintergrundhelligkeit bei jedem Pixel ist der Durchschnitt der Zeilen- und Spaltenhelligkeit bei diesem Pixel. Dies erzeugt so etwas:
BEARBEITEN: Schließlich wird die Fläche jeder zusammenhängenden Vordergrundregion (dh nicht weißen Region) durch die durchschnittliche, vorberechnete Kornfläche dividiert, was uns eine Schätzung der Kornzahl in dieser Region gibt. Die Summe dieser Größen ergibt das Ergebnis. Anfangs haben wir dasselbe für den gesamten Vordergrundbereich als Ganzes getan, aber dieser Ansatz ist buchstäblich feinkörniger.
Übernimmt den eingegebenen Dateinamen durch die Kommandozeile.
Ergebnisse
quelle
avg_grain_area = 3038.38;
kommen aus?hardcoding the result
?The images have different dimensions but the scale of the rice in all of them is consistent because the camera and background were stationary.
Dies ist nur ein Wert, der diese Regel darstellt. Das Ergebnis ändert sich jedoch je nach Eingabe. Wenn Sie die Regel ändern, ändert sich dieser Wert, aber das Ergebnis ist das gleiche - basierend auf der Eingabe.Python + OpenCV: Punktzahl 27
Horizontale Linienabtastung
Idee: Scannen Sie das Bild zeilenweise. Zählen Sie für jede Zeile die Anzahl der Reiskörner (indem Sie prüfen, ob das Pixel schwarz zu weiß oder umgekehrt wird). Wenn sich die Anzahl der Körner für die Linie erhöht (im Vergleich zur vorherigen Linie), bedeutet dies, dass wir auf ein neues Korn gestoßen sind. Wenn diese Zahl abnimmt, bedeutet dies, dass wir über ein Korn gefahren sind. In diesem Fall addieren Sie +1 zum Gesamtergebnis.
Aufgrund der Funktionsweise des Algorithmus ist es wichtig, ein sauberes Schwarzweißbild zu haben. Viele Geräusche führen zu schlechten Ergebnissen. Zuerst wird der Haupthintergrund mit Floodfill (Lösung ähnlich der Ell-Antwort) gereinigt, dann wird der Schwellenwert angewendet, um ein Schwarzweiß-Ergebnis zu erzielen.
Es ist alles andere als perfekt, liefert aber gute Ergebnisse in Bezug auf die Einfachheit. Es gibt wahrscheinlich viele Möglichkeiten, dies zu verbessern (indem ein besseres S / W-Bild bereitgestellt wird, in andere Richtungen gescannt wird (z. B. vertikal, diagonal) und der Durchschnitt ermittelt wird usw.).
Die Fehler pro Bild: 0, 0, 0, 3, 0, 12, 4, 0, 7, 1
quelle
Python + OpenCV: Ergebnis 84
Hier ist ein erster naiver Versuch. Es wendet eine adaptive Schwelle mit manuell eingestellten Parametern an, schließt einige Löcher mit anschließender Erosion und Verdünnung und leitet die Anzahl der Körner aus dem Vordergrundbereich ab.
Hier sehen Sie die Zwischenbilder (schwarz steht im Vordergrund):
Die Fehler pro Bild sind 0, 0, 2, 2, 4, 0, 27, 42, 0 und 7 Körner.
quelle
C # + OpenCvSharp, Ergebnis: 2
Dies ist mein zweiter Versuch. Es ist ganz anders als bei meinem ersten Versuch , der viel einfacher ist, und ich veröffentliche ihn als separate Lösung.
Die Grundidee besteht darin, jedes einzelne Korn durch eine iterative Ellipsenanpassung zu identifizieren und zu kennzeichnen. Entfernen Sie dann die Pixel für dieses Korn aus der Quelle und versuchen Sie, das nächste Korn zu finden, bis jedes Pixel markiert wurde.
Dies ist nicht die schönste Lösung. Es ist ein Riesenschwein mit 600 Codezeilen. Für das größte Bild werden 1,5 Minuten benötigt. Und ich entschuldige mich wirklich für den chaotischen Code.
Es gibt so viele Parameter und Denkweisen in dieser Sache, dass ich ziemlich Angst habe, mein Programm für die 10 Beispielbilder zu überarbeiten. Das Endergebnis von 2 ist fast definitiv ein Fall von Überanpassung: Ich habe zwei Parameter,
average grain size in pixel
undminimum ratio of pixel / elipse_area
, und am Ende erschöpft ich einfach alle Kombinationen dieser beiden Parameter , bis ich die niedrigste Punktzahl bekam. Ich bin mir nicht sicher, ob das mit den Regeln dieser Herausforderung so koscher ist.average_grain_size_in_pixel = 2530
pixel / elipse_area >= 0.73
Aber auch ohne diese überanpassenden Kupplungen sind die Ergebnisse ganz nett. Ohne eine feste Korngröße oder ein festes Pixelverhältnis beträgt die Punktzahl, einfach durch Schätzen der durchschnittlichen Korngröße aus den Trainingsbildern, immer noch 27.
Und als Ergebnis erhalte ich nicht nur die Anzahl, sondern auch die tatsächliche Position, Ausrichtung und Form jedes Korns. Es gibt eine kleine Anzahl von falsch etikettierten Körnern, aber insgesamt stimmen die meisten Etiketten genau mit den tatsächlichen Körnern überein:
A B C D E
F G H I J
(Klicken Sie auf jedes Bild für die Vollversion)
Nach diesem Markierungsschritt untersucht mein Programm jedes einzelne Korn und schätzt basierend auf der Anzahl der Pixel und dem Pixel / Ellipsen-Flächenverhältnis, ob dies der Fall ist
Die Fehlerwerte für jedes Bild sind
A:0; B:0; C:0; D:0; E:2; F:0; G:0 ; H:0; I:0, J:0
Der tatsächliche Fehler ist jedoch wahrscheinlich etwas höher. Einige Fehler im selben Bild heben sich gegenseitig auf. Insbesondere Bild H weist einige stark falsch etikettierte Körner auf, wohingegen in Bild E die Etiketten größtenteils korrekt sind
Das Konzept ist ein wenig ausgedacht:
Zuerst wird der Vordergrund durch Otsu-Thresholding auf dem Sättigungskanal getrennt (siehe meine vorherige Antwort für Details).
Wiederholen, bis keine Pixel mehr übrig sind:
Wählen Sie 10 zufällige Randpixel auf diesem Blob als Startpositionen für ein Korn
für jeden Startpunkt
Nehmen Sie an dieser Position eine Körnung mit einer Höhe und einer Breite von 10 Pixel an.
Wiederholen bis zur Konvergenz
Gehen Sie von diesem Punkt aus in verschiedenen Winkeln radial nach außen, bis Sie auf ein Randpixel stoßen (weiß zu schwarz).
Die gefundenen Pixel sollten hoffentlich die Randpixel eines einzelnen Korns sein. Versuchen Sie, Inliers von Outliers zu trennen, indem Sie Pixel verwerfen, die von der angenommenen Ellipse weiter entfernt sind als die anderen
versuche wiederholt, eine Ellipse durch eine Teilmenge der Lieferanten zu passen, behalte die beste Ellipse (RANSACK)
Aktualisieren Sie die Position, Ausrichtung, Breite und Höhe der Körnung mit dem gefundenen Elipse
Wenn sich die Kornposition nicht wesentlich ändert, stoppen Sie
Wählen Sie unter den 10 angepassten Körnern die beste Körnung entsprechend der Form und der Anzahl der Kantenpixel aus. Werfen Sie die anderen weg
Entfernen Sie alle Pixel für diese Körnung aus dem Quellbild und wiederholen Sie den Vorgang
Gehen Sie schließlich die Liste der gefundenen Körner durch und zählen Sie jedes Korn entweder als 1 Korn, 0 Körner (zu klein) oder 2 Körner (zu groß).
Eines meiner Hauptprobleme war, dass ich keine vollständige Ellipsenpunkt-Distanzmetrik implementieren wollte, da die Berechnung dieser Metrik an sich ein komplizierter iterativer Prozess ist. Also habe ich verschiedene Workarounds mit den OpenCV-Funktionen Ellipse2Poly und FitEllipse verwendet, und die Ergebnisse sind nicht allzu hübsch.
Anscheinend habe ich auch die Größenbeschränkung für Codegolf überschritten.
Eine Antwort ist auf 30000 Zeichen begrenzt, ich bin derzeit bei 34000. Also muss ich den Code unten etwas kürzen.
Der vollständige Code kann unter http://pastebin.com/RgM7hMxq eingesehen werden
Entschuldigung, mir war nicht bewusst, dass es eine Größenbeschränkung gibt.
Ich schäme mich ein wenig für diese Lösung, weil a) ich nicht sicher bin, ob sie im Geiste dieser Herausforderung liegt, und b) sie zu groß für eine Codegolf-Antwort ist und die Eleganz der anderen Lösungen fehlt.
Andererseits bin ich sehr zufrieden mit den Fortschritten, die ich bei der Kennzeichnung der Körner erzielt habe , und nicht nur bei der Zählung.
quelle
C ++, OpenCV, Punktzahl: 9
Die Grundidee meiner Methode ist recht einfach - versuchen Sie, einzelne Körner (und "doppelte Körner" - 2 (aber nicht mehr!) Körner, die nahe beieinander liegen) aus dem Bild zu entfernen und zählen Sie die Reste mit einer Methode basierend auf der Fläche (wie Falko, Ell und Belisarius). Die Verwendung dieses Ansatzes ist etwas besser als die standardmäßige "Flächenmethode", da es einfacher ist, einen guten AveragePixelsPerObject-Wert zu finden.
(1. Schritt) Zunächst müssen wir die Otsu-Binarisierung für den S-Kanal des Bildes in HSV verwenden. Der nächste Schritt ist die Verwendung des dilate-Operators, um die Qualität des extrahierten Vordergrunds zu verbessern. Dann müssen wir Konturen finden. Natürlich sind einige Konturen keine Reiskörner - wir müssen zu kleine Konturen löschen (mit einer Fläche kleiner als averagePixelsPerObject / 4. averagePixelsPerObject ist in meiner Situation 2855). Jetzt können wir endlich mit dem Zählen der Körner beginnen :) (2. Schritt) Das Finden von Einzel- und Doppelkörnern ist ganz einfach - suchen Sie in der Konturliste nach Konturen mit Flächen innerhalb bestimmter Bereiche. Wenn sich die Konturfläche im Bereich befindet, löschen Sie sie aus der Liste und fügen Sie 1 hinzu (oder 2, wenn es "doppeltes" Korn war), um Körner zu kontern. (3. Schritt) Der letzte Schritt ist natürlich das Teilen des Bereichs der verbleibenden Konturen durch den AveragePixelsPerObject-Wert und das Hinzufügen des Ergebnisses zum Körnerzähler.
Bilder (für Bild F.jpg) sollten diese Idee besser als Worte zeigen:
1. Schritt (ohne kleine Konturen (Rauschen)):
2. Schritt - nur einfache Konturen:
3. Schritt - verbleibende Konturen:
Hier ist der Code, er ist ziemlich hässlich, sollte aber problemlos funktionieren. Natürlich ist OpenCV erforderlich.
Wenn Sie die Ergebnisse aller Schritte anzeigen möchten, deaktivieren Sie alle imshow-Funktionsaufrufe (.., ..) und setzen Sie die Variable fastProcessing auf false. Bilder (A.jpg, B.jpg, ...) sollten sich in Verzeichnisbildern befinden. Alternativ können Sie natürlich auch den Namen eines Bildes als Parameter über die Befehlszeile eingeben.
Wenn etwas unklar ist, kann ich es natürlich erklären und / oder einige Bilder / Informationen bereitstellen.
quelle
C # + OpenCvSharp, Ergebnis: 71
Das ist am ärgerlichsten, ich habe versucht, eine Lösung zu finden, die tatsächlich jedes Getreide anhand der Wasserscheide identifiziert , aber ich habe es einfach. kippen. bekommen. es. zu. Arbeit.
Ich entschied mich für eine Lösung, die zumindest einige einzelne Körner trennt und diese Körner dann verwendet, um die durchschnittliche Korngröße abzuschätzen. Allerdings kann ich die Lösungen mit hartcodierter Körnung bisher nicht übertreffen.
Das wichtigste Highlight dieser Lösung: Sie setzt keine feste Pixelgröße für Getreide voraus und sollte auch dann funktionieren, wenn die Kamera bewegt oder die Reissorte geändert wird.
Meine Lösung funktioniert so:
Trennen Sie den Vordergrund, indem Sie das Bild in HSV umwandeln und Otsu-Schwellenwert auf den Sättigungskanal anwenden . Dies ist sehr einfach, funktioniert sehr gut und ich würde dies jedem empfehlen, der diese Herausforderung ausprobieren möchte:
->
Dadurch wird der Hintergrund sauber entfernt.
Ich habe dann zusätzlich die Körnerschatten aus dem Vordergrund entfernt, indem ich eine feste Schwelle auf den Wertekanal angewendet habe. (Nicht sicher, ob das wirklich viel hilft, aber es war einfach genug, um hinzuzufügen)
Dann wende ich eine Distanztransformation auf das Vordergrundbild an.
und finde alle lokalen Maxima in dieser Distanztransformation.
Hier bricht meine Idee zusammen. Um zu vermeiden, dass mehrere lokale Maxima innerhalb desselben Korns auftreten, muss ich viel filtern. Momentan halte ich nur das stärkste Maximum in einem Radius von 45 Pixeln, was bedeutet, dass nicht jedes Korn ein lokales Maximum hat. Und ich habe keine Rechtfertigung für den 45-Pixel-Radius, es war nur ein Wert, der funktioniert hat.
(Wie Sie sehen können, sind das nicht annähernd genug Samen, um für jedes Korn verantwortlich zu sein.)
Dann verwende ich diese Maxima als Samen für den Wasserscheidealgorithmus:
Die Ergebnisse sind meh . Ich hoffte größtenteils auf einzelne Körner, aber die Klumpen sind immer noch zu groß.
Jetzt identifiziere ich die kleinsten Blobs, zähle ihre durchschnittliche Pixelgröße und schätze daraus die Anzahl der Körner. Dies ist nicht das, was ich am Anfang vorhatte, aber dies war der einzige Weg, dies zu retten.
Ein kleiner Test mit einer hartcodierten Pixel-pro-Korn-Größe von 2544,4 ergab einen Gesamtfehler von 36, der immer noch größer ist als bei den meisten anderen Lösungen.
quelle
HTML + Javascript: Punktzahl 39
Die genauen Werte sind:
Es bricht bei den größeren Werten zusammen (ist nicht genau).
Erläuterung: Zählt im Allgemeinen die Anzahl der Reispixel und dividiert sie durch die durchschnittlichen Pixel pro Korn.
quelle
Ein Versuch mit PHP. Nicht die Antwort mit der niedrigsten Punktzahl, aber der ziemlich einfache Code
SCORE: 31
Selbstbewertung
95 ist ein blauer Wert, der beim Testen mit GIMP 2966 zu funktionieren schien und die durchschnittliche Korngröße aufweist
quelle