Lösen von Sudoku aus einem Bild mit Deep Learning

Inhalt

Dieser Artikel wurde im Rahmen der Data Science Blogathon

Einführung

Hallo Leser!

Deep Learning wird in vielen Anwendungen eingesetzt, wie Objekterkennung, Gesichtserkennung, Aufgaben zur Verarbeitung natürlicher Sprache und vieles mehr. In diesem Blog werde ich ein Modell erstellen, mit dem ungelöste Sudoku aus einem Bild mit Deep Learning gelöst werden können, wir gehen zu Bibliotheken wie OpenCV und TensorFlow. Wenn Sie mehr über OpenCV erfahren möchten, Überprüfen Sie dies Verknüpfung. Dann legen wir los.

  • Wenn Sie mehr über Python-Bibliotheken für die Bildverarbeitung wissen möchten, dann schau dir das an Verknüpfung.
  • Für weitere Artikel, Klicke hier.
Bilder von Sudoku Deep Learning

Bild Quelle

Der Blog ist in drei Teile gegliedert:

Teil 1: Ziffernklassifizierungsmodell

Wir werden zunächst ein neuronales Netz auf dem Char74k-Bilddatensatz für Ziffern aufbauen und trainieren. Dieses Modell hilft, die Ziffern der Bilder zu klassifizieren.

Teil 2: Sudoku aus einem Bild lesen und erkennen

Dieser Abschnitt enthält, Identifizieren des Puzzles aus einem Bild mit Hilfe von OpenCV, Sortieren Sie die Ziffern im erkannten Sudoku-Rätsel mit Teil 1, endlich die Werte der Sudoku-Zellen abrufen und in einem Array speichern.

Teil 3: Rätsel lösen

Wir werden die Matrix, die wir in Pat-2 erhalten haben, als Matrix speichern und schließlich eine Rekursionsschleife ausführen, um das Rätsel zu lösen.

IMPORTIEREN VON BIBLIOTHEKEN

Wir werden alle erforderlichen Bibliotheken mit den folgenden Befehlen importieren:

numpy als np importieren
Pandas als pd importieren
import seaborn als sns
import matplotlib.pyplot als plt
Importieren von OS, willkürlich
CV2 importieren
von glob importieren glob
sklearn importieren
aus sklearn.model_selection import train_test_split
Tensorflow als tf importieren
von tensorflow import keras
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from keras.preprocessing.image import ImageDataGenerator, load_img
von keras.utils.np_utils import to_categorical
von tensorflow.keras.models importieren Sequential
von tensorflow.keras.layers importieren Aktivierung, Aussteigen, Dicht, Ebnen, BatchNormalisierung, Conv2D, MaxPooling2D
von tensorflow.keras.optimizers importieren RMSprop
aus tensorflow.keras Backend als K . importieren
aus tensorflow.keras.preprocessing import image
von sklearn.metrics import precision_score, Klassifizierungsbericht
aus pathlib-Importpfad
aus PIL-Import Bild

Teil 1: Ziffernklassifizierungsmodell

In diesem Abschnitt, Wir werden ein Ziffernklassifizierungsmodell verwenden.

LADE DATEN

Wir verwenden einen Bilddatensatz, um die Zahlen in einem Bild zu klassifizieren. Daten werden als Features wie Bilder und Labels als Labels angegeben.

#Laden der Daten
data = os.listdir("Ziffern/Ziffern" )
Daten_X = []     
data_y = []  
data_classes = len(Daten)
für mich in Reichweite (0,Datenklassen):
data_list = os.listdir("Ziffern/Ziffern" +"/"+str(ich))
    für j in data_list:
pic = cv2.imread("Ziffern/Ziffern" +"/"+str(ich)+"/"+J)
pic = cv2.resize(bild,(32,32))
data_X.append(bild)
data_y.append(ich)
wenn len(Daten_X) == len(data_y) :
drucken("Gesamtdatenpunkte = ",len(Daten_X))
# Etiketten und Bilder
data_X = np.array(Daten_X)
data_y = np.array(data_y)

588411-1604171

GETEILTER DATENSATZ

Wir teilen den Datensatz in Zugsätze auf, Testen und Validieren, wie wir es bei jedem maschinellen Lernproblem tun.

#Aufteilen der Zugvalidierungs- und Test-Sets
train_X, test_X, train_y, test_y = train_test_split(Daten_X,data_y,test_size=0.05)
train_X, gültig_X, train_y, valid_y = train_test_split(train_X,train_y,test_size=0.2)
drucken("Form des Trainingssets = ",train_X.shape)
drucken("Form des Validierungssatzes = ",valid_X.shape)
drucken("Testsatzform = ",test_X.shape)

370992-8677907

Vorverarbeitung der Bilder für die rotes neuronales

In einem Vorverarbeitungsschritt, Wir verarbeiten die Merkmale vor (Bilder) Graustufen, Normalisierung und Verbesserung mit Histogramm-Entzerrung. Danach, Konvertieren Sie sie in NumPp-Arrays und ändern Sie sie dann und erhöhen Sie die Daten.

def Vorbereitung(img):
img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) #Bild in Graustufen erstellen
img = cv2.equalizeHist(img) #Histogramm-Entzerrung zur Kontrastverbesserung
img = img/255 #normalisieren
    zurück img
train_X = np.array(aufführen(Karte(Vorbereitung, train_X)))
test_X = np.array(aufführen(Karte(Vorbereitung, test_X)))
valid_X= np.array(aufführen(Karte(Vorbereitung, gültig_X)))
#Umformung der Bilder
train_X = train_X.reshape(train_X.shape[0], train_X.shape[1], train_X.shape[2],1)
test_X = test_X.reshape(test_X.shape[0], test_X.shape[1], test_X.shape[2],1)
valid_X = valid_X.reshape(valid_X.shape[0], valid_X.shape[1], valid_X.shape[2],1)
#Augmentation
datagen = ImageDataGenerator(width_shift_range=0,1, height_shift_range=0,1, zoom_range=0.2, Scherbereich=0,1, rotation_range=10)
datagen.fit(train_X)

Eine heiße Codierung

In diesem Abschnitt, Wir werden One-Hot-Codierung verwenden, um die Klassen zu beschriften.

train_y = to_categorical(train_y, Datenklassen)
test_y = to_categorical(gereizt, Datenklassen)
valid_y = to_kategorial(valid_y, Datenklassen)

MODELLBAU

Wir verwenden eine rote neuronale Faltung für den Modellbau. Es besteht aus den folgenden Schritten:

#Erstellen eines neuronalen Netzwerks
Modell = Sequentiell()
model.add((Conv2D(60,(5,5),input_shape=(32, 32, 1) ,padding = 'Same' ,aktivierung='relu')))
model.add((Conv2D(60, (5,5),Polsterung="gleich",aktivierung='relu')))
model.add(MaxPooling2D(pool_size=(2,2)))
#model.add(Aussteigen(0.25))
model.add((Conv2D(30, (3,3),Polsterung="gleich", aktivierung='relu')))
model.add((Conv2D(30, (3,3), Polsterung="gleich", aktivierung='relu')))
model.add(MaxPooling2D(pool_size=(2,2), Schritte =(2,2)))
model.add(Aussteigen(0.5))
model.add(Ebnen())
model.add(Dicht(500,aktivierung='relu'))
model.add(Aussteigen(0.5))
model.add(Dicht(10, aktivierung='softmax'))
Modell.Zusammenfassung()
156573-8520115

In diesem Schritt, Wir stellen das Modell zusammen und testen das Modell auf dem Testset wie unten gezeigt:

#Modell kompilieren
Optimierer = RMSprop(lr=0,001, rho=0,9, Epsilon = 1e-08, Zerfall=0.0)
model.compile(Optimierer=Optimierer,Verlust="kategoriale_Kreuzentropie",Metriken=[Genauigkeit])
#Passen Sie das Modell an
Geschichte = model.fit(datagen.flow(train_X, train_y, batch_size=32),
                              Epochen = 30, Validierung_Daten = (gültig_X, valid_y),
                              ausführlich = 2, steps_per_epoch= 200)

# Testen des Modells auf dem Testset
score = model.evaluate(test_X, gereizt, ausführlich=0)
drucken('Test Score=",Spielstand[0])
drucken("Test Accuracy =', Spielstand[1])

961804-4400475

Teil 2: Sudoku aus einem Bild lesen und erkennen

LESEN SIE DAS PUZZLE-SUDOKU

Lesen Sie ein Sudoku mit OpenCv mit dem folgenden Code:

# Wählen Sie zufällig ein Bild aus dem Datensatz aus
Ordner="Sudoku-Box-Erkennung/Aug"
a=zufällige.Auswahl(os.listdir(Mappe))
drucken(ein)
sudoku_a = cv2.imread(folder+'/'+a)
plt.figur()
plt.imshow(sudoku_a)
plt.zeigen()

726645-9693366

Verarbeiten Sie das Bild für die weitere Analyse mit dem folgenden Code vor;

#Vorverarbeitung des zu lesenden Bildes
sudoku_a = cv2.resize(sudoku_a, (450,450))
# Funktion auf Graustufen, verwischen und ändern Sie die Aufnahmeschwelle des Bildes
def Vorverarbeitung(Bild):
grau = cv2.cvtColor(Bild, cv2.COLOR_BGR2GRAY) 
Unschärfe = cv2.GaussianBlur(grau, (3,3),6) 
    #Unschärfe = cv2.bilateralFilter(grau,9,75,75)
Schwellenwert_img = cv2.adaptiveThreshold(verwischen,255,1,1,11,2)
    Rückgabeschwelle_img
Schwelle = Vorverarbeitung(sudoku_a)
#let's look at what we have got
plt.figure()
plt.imshow(Schwelle)
plt.zeigen()

411897-4000394

KONTUR ERKENNEN

In diesem Abschnitt, lass uns die kontur erkennen. Wir erkennen weiterhin die größte Kontur des Bildes.

# Den Umriss des Sudoku-Puzzles im Bild finden
Kontur_1 = sudoku_a.copy()
Kontur_2 = sudoku_a.copy()
Kontur, Hierarchie = cv2.findContours(Schwelle,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(Kontur_1, Kontur,-1,(0,255,0),3)
#let's see what we got
plt.figure()
plt.imshow(Kontur_1)
plt.zeigen()
411897-4000394

Der folgende Code wird verwendet, um das Sudoku durch Umformen zu trimmen und gut auszurichten.

def main_outline(Kontur):
größte = np.array([])
max_area = 0
    für i in Kontur:
Bereich = cv2.contourArea(ich)
        wenn Bereich >50:
peri = cv2.arcLength(ich, Wahr)
approx = cv2.approxPolyDP(ich , 0.02* peri, Wahr)
            wenn Bereich > max_area und len(ca) ==4:
größte = ca
max_area = Bereich
    Rückkehr am größten ,max_area
def reframe(Punkte):
Punkte = Punkte.Umformen((4, 2))
points_new = np.zeros((4,1,2),dtype = zB int32)
add = Punkte.Summe(1)
Punkte_neu[0] = Punkte[z.B. argmin(hinzufügen)]
Punkte_neu[3] = Punkte[z.B. armax(hinzufügen)]
diff = np.diff(Punkte, Achse =1)
Punkte_neu[1] = Punkte[z.B. argmin(unterschied)]
Punkte_neu[2] = Punkte[z.B. armax(unterschied)]
    Rückgabepunkte_neu
def Splitzellen(img):
Zeilen = np.vsplit(img,9)
Kästen = []
    für r in Reihen:
cols = np.hsplit(R,9)
        für Box in Spalten:
boxen.anhängen(Kasten)
    Rückgabeboxen
black_img = np.zeros((450,450,3), z.B. uint8)
größte, maxArea = main_outline(Kontur)
wenn größte.Größe != 0:
größte = umrahmen(größte)
cv2.drawContours(Kontur_2, größte,-1, (0,255,0),10)
pts1 = zB float32(größte)
pts2 = zB float32([[0,0],[450,0],[0,450],[450,450]])
Matrix = cv2.getPerspectiveTransform(pts1, pts2)
  imagewrap = cv2.warpPerspective(sudoku_a,Matrix,(450,450))
imagewrap =cv2.cvtColor(Bildumbruch, cv2.COLOR_BGR2GRAY)
plt.figur()
plt.imshow(Bildumbruch)
plt.zeigen()
190758-1948091
# Importieren des zu lösenden Rätsels
puzzle = cv2.imread("su-puzzle / su.jpg")
#let's see what we got
plt.figure()
plt.imshow(Puzzle)
plt.zeigen()

212949-4691475
# Den Umriss des Sudoku-Puzzles im Bild finden
su_contour_1= su_puzzle.copy()
su_contour_2= sudoku_a.copy()
su_contour, Hierarchie = cv2.findContours(su_puzzle,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(su_contour_1, su_contour,-1,(0,255,0),3)
black_img = np.zeros((450,450,3), z.B. uint8)
su_größte, su_maxArea = main_outline(su_contour)
if su_biggest.size != 0:
su_biggest = umrahmen(su_größte)
cv2.drawContours(su_contour_2,su_biggest,-1, (0,255,0),10)
su_pts1 = np.float32(su_größte)
su_pts2 = np.float32([[0,0],[450,0],[0,450],[450,450]])
su_matrix = cv2.getPerspectiveTransform(su_pts1, su_pts2)  
su_imagewrap = cv2.warpPerspective(Puzzle,su_matrix,(450,450))
su_imagewrap =cv2.cvtColor(su_imagewrap, cv2.COLOR_BGR2GRAY)
plt.figur()
plt.imshow(su_imagewrap)
plt.zeigen()

3045010-1333975

TEILEN SIE DIE ZELLEN UND KLASSIFIZIEREN SIE DIE ZIFFERN

In diesem Abschnitt, lass uns die Zellen teilen und die Ziffern klassifizieren.

  • Unterteile das Sudoku zuerst in 81 Zellen mit leeren Ziffern oder Leerzeichen
  • Zuschneiden der Zellen
  • Verwenden Sie das Modell, um die Ziffern in den Zellen zu ordnen, sodass leere Zellen als Null sortiert werden
  • Schließlich, Erkenne die Ausgabe in einem Array von 81 Ziffern.
sudoku_cell = splitcells(su_imagewrap)
#Let's have alook at the last cell
plt.figure()
plt.imshow(sudoku_cell[58])
plt.zeigen()
1812811-6273401
def CropCell(Zellen):
Zellen_croped = []
    für Bild in Zellen:
img = np.array(Bild)
img = img[4:46, 6:46]
img = Image.fromarray(img)
Cells_croped.append(img)
    zurück Cells_croped
sudoku_cell_croped=CropCell(sudoku_cell)
#Let's have alook at the last cell
plt.figure()
plt.imshow(sudoku_cell_croped[58])
plt.zeigen()

6533912-4040611

Teil 3: LÖSEN SIE DAS SODOKU

In diesem Abschnitt werden wir zwei Operationen durchführen:

  • Ummodellieren der Matrix in eine Matrix von 9 x 9
  • Lösen Sie das Array mit Rekursion
# Umformen des Rasters in eine 9x9-Matrix
Raster = np.reshape(Netz,(9,9))
Netz

5033013-7585453
#Zum Vergleichen
plt.figur()
plt.imshow(su_imagewrap)
plt.zeigen()

8856414-1830466

Überprüfen Sie den folgenden Code, um das Sudoku weiter zu lösen:

def next_box(Quiz):
    für Reihe im Bereich(9):
        für Spalte in Reichweite(9):
            wenn quiz[Reihe][col] == 0:
                Rückkehr (Reihe, col)
    falsch zurückgeben
#Funktion zum Ausfüllen der möglichen Werte durch Auswertung von Zeilen, Spalten und kleineren Zellen
auf jeden Fall möglich (Quiz,Reihe, col, n):
    #globales Quiz
    für mich in Reichweite (0,9):
        wenn quiz[Reihe][ich] == n und Reihe != ich:
            falsch zurückgeben
    für mich in Reichweite (0,9):
        wenn quiz[ich][col] == n und col != ich:
            falsch zurückgeben
Zeile0 = (Reihe)//3
col0 = (col)//3
    für mich in Reichweite(Reihe0*3, Reihe0*3 + 3):
        für j im Bereich(col0 * 3, col0 * 3 + 3):
            wenn quiz[ich][J]==n und (ich,J) != (Reihe, col):
                falsch zurückgeben
    true zurückgeben
#Rekursionsfunktion zum Durchlaufen, bis eine gültige Antwort gefunden wurde. 
auf jeden Fall lösen(Quiz):
val = next_box(Quiz)
    wenn val falsch ist:
        true zurückgeben
    anders:
Reihe, col = val
        für n im Bereich(1,10): #n ist die mögliche Lösung
            wenn möglich(Quiz,Reihe, col, n):
Quiz[Reihe][col]=n
                wenn lösen(Quiz):
                    true zurückgeben
                anders:
Quiz[Reihe][col]=0
        Rückkehr
def Gelöst(Quiz):
    für Reihe im Bereich(9):
        wenn Zeile % 3 == 0 und Reihe != 0:
drucken("....................")
        für Spalte in Reichweite(9):
            wenn col % 3 == 0 und col != 0:
drucken("|", Ende=" ")
            wenn col == 8:
drucken(Quiz[Reihe][col])
            anders:
drucken(str(Quiz[Reihe][col]) + " ", Ende="")
lösen(Netz)
2026115-4831192

Überprüfen Sie den folgenden Code, um das Endergebnis zu erhalten:

wenn lösen(Netz):
Gelöst(Netz)
anders:
drucken("Lösungen existieren nicht. Modell falsch gelesene Ziffern.")
8873816-4869295

Viva!! Wir sind fertig mit dem Auflösung des Sudokus mit dem tiefes Lernen. Wenn Sie mehr Informationen wünschen, siehe links unten:

https://www.youtube.com/watch?v=G_UYXzGuqvM

https://www.kaggle.com/yashchoudhary/deep-sudoku-solver-multiple-approaches

https://www.youtube.com/watch?v = QR66rMS_ZfA

Abschließende Anmerkungen

Dann, In diesem Artikel, wir hatten ein ausführliches Gespräch über Lösen Sie Sudoku mit Deep Learning. Ich hoffe, Sie lernen etwas aus diesem Blog und helfen Ihnen in Zukunft. Danke fürs Lesen und deine Geduld. Viel Glück!

Sie können meine Artikel hier einsehen: Artikel

E-Mail-Identifikation: [E-Mail geschützt]

Verbinden Sie sich mit mir auf LinkedIn: LinkedIn.

Die in diesem Artikel gezeigten Medien sind nicht Eigentum von DataPeaker und werden nach Ermessen des Autors verwendet.

Abonniere unseren Newsletter

Wir senden Ihnen keine SPAM-Mail. Wir hassen es genauso wie du.

Datenlautsprecher