Ich habe ein lustiges Projekt gemacht: Ein Sudoku aus einem Eingabebild mit OpenCV lösen (wie bei Google-Brillen usw.). Und ich habe die Aufgabe erledigt, aber am Ende habe ich ein kleines Problem gefunden, für das ich hierher gekommen bin.
Ich habe die Programmierung mit der Python-API von OpenCV 2.3.1 durchgeführt.
Folgendes habe ich getan:
- Lesen Sie das Bild
- Finde die Konturen
- Wählen Sie die mit der maximalen Fläche (und auch etwas gleich dem Quadrat).
Finde die Eckpunkte.
zB unten angegeben:
( Beachten Sie hier, dass die grüne Linie korrekt mit der tatsächlichen Grenze des Sudoku übereinstimmt, sodass das Sudoku korrekt verzogen werden kann . Überprüfen Sie das nächste Bild.)
Verzerren Sie das Bild zu einem perfekten Quadrat
zB Bild:
OCR durchführen (für die ich die Methode verwendet habe, die ich in OCR für einfache Ziffernerkennung in OpenCV-Python angegeben habe )
Und die Methode hat gut funktioniert.
Problem:
Schauen Sie sich dieses Bild an.
Wenn Sie Schritt 4 für dieses Bild ausführen, erhalten Sie das folgende Ergebnis:
Die gezeichnete rote Linie ist die ursprüngliche Kontur, die den wahren Umriss der Sudoku-Grenze darstellt.
Die gezeichnete grüne Linie ist eine ungefähre Kontur, die den Umriss des verzerrten Bildes darstellt.
Was natürlich einen Unterschied zwischen der grünen und der roten Linie am oberen Rand des Sudoku gibt. Während ich mich verziehe, erhalte ich nicht die ursprüngliche Grenze des Sudoku.
Meine Frage :
Wie kann ich das Bild an der richtigen Grenze des Sudoku verziehen, dh an der roten Linie, ODER wie kann ich den Unterschied zwischen roter und grüner Linie beseitigen? Gibt es dafür eine Methode in OpenCV?
quelle
Antworten:
Ich habe eine Lösung, die funktioniert, aber Sie müssen sie selbst in OpenCV übersetzen. Es ist in Mathematica geschrieben.
Der erste Schritt besteht darin, die Helligkeit im Bild anzupassen, indem jedes Pixel mit dem Ergebnis einer Schließoperation geteilt wird:
Der nächste Schritt besteht darin, den Sudoku-Bereich zu finden, damit ich den Hintergrund ignorieren (maskieren) kann. Dazu verwende ich die Analyse verbundener Komponenten und wähle die Komponente mit der größten konvexen Fläche aus:
Durch das Ausfüllen dieses Bildes erhalte ich eine Maske für das Sudoku-Gitter:
Jetzt kann ich einen Ableitungsfilter 2. Ordnung verwenden, um die vertikalen und horizontalen Linien in zwei separaten Bildern zu finden:
Ich verwende wieder die Analyse verbundener Komponenten, um die Gitterlinien aus diesen Bildern zu extrahieren. Die Gitterlinien sind viel länger als die Ziffern, daher kann ich die Dicke des Messschiebers verwenden, um nur die mit den Gitterlinien verbundenen Komponenten auszuwählen. Wenn ich sie nach Position sortiere, erhalte ich 2x10 Maskenbilder für jede der vertikalen / horizontalen Gitterlinien im Bild:
Als nächstes nehme ich jedes Paar vertikaler / horizontaler Gitterlinien, erweitere sie, berechne den Pixel-für-Pixel-Schnittpunkt und berechne die Mitte des Ergebnisses. Diese Punkte sind die Schnittpunkte der Gitterlinien:
Der letzte Schritt besteht darin, zwei Interpolationsfunktionen für die X / Y-Abbildung durch diese Punkte zu definieren und das Bild mithilfe dieser Funktionen zu transformieren:
Alle Vorgänge sind grundlegende Bildverarbeitungsfunktionen, daher sollte dies auch in OpenCV möglich sein. Die Spline-basierte Bildtransformation ist möglicherweise schwieriger, aber ich glaube nicht, dass Sie sie wirklich brauchen. Wenn Sie die Perspektiventransformation verwenden, die Sie jetzt für jede einzelne Zelle verwenden, erhalten Sie wahrscheinlich ausreichend gute Ergebnisse.
quelle
Nikies Antwort löste mein Problem, aber seine Antwort war in Mathematica. Also dachte ich, ich sollte hier seine OpenCV-Anpassung geben. Aber nach der Implementierung konnte ich sehen, dass OpenCV-Code viel größer ist als Nikies Mathematica-Code. Außerdem konnte ich in OpenCV keine von Nikie durchgeführte Interpolationsmethode finden (obwohl dies mit scipy möglich ist, werde ich es zu gegebener Zeit mitteilen.)
1. Bildvorverarbeitung (Schließvorgang)
Ergebnis:
2. Sudoku-Quadrat finden und Maskenbild erstellen
Ergebnis:
3. Vertikale Linien finden
Ergebnis:
4. Horizontale Linien finden
Ergebnis:
Natürlich ist dieser nicht so gut.
5. Gitterpunkte finden
Ergebnis:
6. Behebung der Mängel
Hier macht Nikie eine Art Interpolation, über die ich nicht viel weiß. Und ich konnte keine entsprechende Funktion für dieses OpenCV finden. (Vielleicht ist es da, ich weiß es nicht).
Schauen Sie sich dieses SOF an, in dem erklärt wird, wie dies mit SciPy gemacht wird, das ich nicht verwenden möchte: Bildtransformation in OpenCV
Also habe ich hier 4 Ecken von jedem Unterquadrat genommen und auf jedes eine Warp-Perspektive angewendet.
Dafür finden wir zuerst die Zentroide.
Die resultierenden Zentroide werden jedoch nicht sortiert. Schauen Sie sich das Bild unten an, um die Reihenfolge zu sehen:
Also sortieren wir sie von links nach rechts, von oben nach unten.
Nun siehe unten ihre Reihenfolge:
Schließlich wenden wir die Transformation an und erstellen ein neues Bild der Größe 450x450.
Ergebnis:
Das Ergebnis ist fast das gleiche wie bei Nikie, aber die Codelänge ist groß. Möglicherweise gibt es bessere Methoden, aber bis dahin funktioniert dies in Ordnung.
Grüße ARK.
quelle
Sie könnten versuchen, eine Art gitterbasierte Modellierung Ihrer willkürlichen Verzerrung zu verwenden. Und da das Sudoku bereits ein Gitter ist, sollte das nicht zu schwer sein.
Sie können also versuchen, die Grenzen jeder 3x3-Subregion zu erkennen und dann jede Region einzeln zu verzerren. Wenn die Erkennung erfolgreich ist, erhalten Sie eine bessere Annäherung.
quelle
Ich möchte hinzufügen, dass die obige Methode nur funktioniert, wenn das Sudoku-Brett gerade steht. Andernfalls schlägt der Verhältnis-Test für Höhe / Breite (oder umgekehrt) höchstwahrscheinlich fehl und Sie können keine Sudoku-Kanten erkennen. (Ich möchte auch hinzufügen, dass Sobel-Operationen (dx und dy) weiterhin funktionieren, wenn Linien, die nicht senkrecht zu den Bildrändern stehen, weiterhin Kanten in Bezug auf beide Achsen haben.)
Um gerade Linien erkennen zu können, sollten Sie an kontur- oder pixelweisen Analysen wie contourArea / boundingRectArea, Punkten oben links und unten rechts arbeiten ...
Bearbeiten: Ich konnte überprüfen, ob eine Reihe von Konturen eine Linie bildet oder nicht, indem ich eine lineare Regression anwendete und den Fehler überprüfte. Die lineare Regression zeigte jedoch eine schlechte Leistung, wenn die Steigung der Linie zu groß ist (dh> 1000) oder sehr nahe bei 0 liegt. Daher ist es logisch, den obigen Ratio-Test (in der am häufigsten bewerteten Antwort) vor der linearen Regression anzuwenden, und hat bei mir funktioniert.
quelle
Um nicht abgeleitete Ecken zu entfernen, habe ich eine Gammakorrektur mit einem Gammawert von 0,8 angewendet.
Der rote Kreis zeigt die fehlende Ecke.
Der Code lautet:
Dies ist zusätzlich zu Abid Rahmans Antwort, wenn einige Eckpunkte fehlen.
quelle
Ich dachte, dies sei ein großartiger Beitrag und eine großartige Lösung von ARK. sehr gut angelegt und erklärt.
Ich habe an einem ähnlichen Problem gearbeitet und das Ganze gebaut. Es gab einige Änderungen (z. B. xrange to range, Argumente in cv2.findContours), die jedoch sofort funktionieren sollten (Python 3.5, Anaconda).
Dies ist eine Zusammenstellung der obigen Elemente, wobei ein Teil des fehlenden Codes hinzugefügt wurde (dh Beschriftung von Punkten).
quelle