Projet d'informatique - Sarah Dépernet L3A

SUJET : Sudoku (résolution d’une grille, constructeur de grilles avec solution unique, estimer la difficulté d’une grille)

This is easy and you can find the code to do it on GitHub (I did). But what if it's in a jpg

img

Pour ce projet, on va donc importer une grille de sudoku depuis une page web pour la résoudre et la renvoyer remplie, sous format jpeg, avec une écriture manuscrite. La méthode employée se sépare en plusieurs étapes :

1) De l'image à la matrice

2) Résolution du sudoku

3) Remplissage de la grille avec les chiffres manuscrits

On commence par importer des bibliothèques utiles pour importer, traiter les images entre autres.

1 - De l'image à la matrice

On va chercher des grilles sur un site dédié. J'avais initialement prévu de prendre une grille aléatoirement parmi celle du site, mais puisque la reconnaissance des caractères n'est pas encore fiable à 100%, j'ai choisi de fixer une grille, la n°6733, pour pouvoir contourner les erreurs de l'OCR et tester la fin du programme.

Ensuite, on va traduire la grille sous forme d'un tableau d'images représentant chacune un chiffre.

Connaissant le format du sudoku, propre à ceux de planete-sudoku, on peut le rogner pour obtenir un carré de côté multiple de 9, pour faciliter la division en cases. Ici, je rogne à 414 pixels de côté, 414 étant le plus grand multiple de 9 inférieur à 420.

Ceci n'est adapté que pour les sudoku que l'on a importé, mais pour un fonction plus générale, il aurait fallu rogner intelligemment l'image autour de la grille du jeu puis s'assurer qu'une erreur de 8 pixels est encore négligeable dans la séparation des cases par la suite. On pourrait même détecter un sudoku que l'on aurait pris en photo pour approfondir et élargir l'usage, mais ici je me contente de traiter un format spécifique.

On va isoler chaque case du sudoku, et les ranger dans un tableau. J'introduis une marge pour ne pas garder les pixels des lignes du sudoku. La fonction découpe se charge de créer une image 28x28 correspondant à la case aux coordonnées indiquées. Ces images sont en négatif, et calibrées pour s'adapter au format des échantillons sur lesquels on s'entraîne.

On va faire un nouveau tableau, où on rangera les chiffres reconnus.

Premier essai (et finalement le dernier à être conservé) de reconnaissance des chiffres

Cette partie est longue à charger (pour moi ça a pris une bonne demi-heure) et ne doit être exécutée qu'une seule fois. C'est la fameuse partie d'apprentissage de l'automate, qui n'est pas encore suffisante pour recopier exactement le sudoku.

J'ai eu un score de 0.9194. Le taux d'erreur est encore grand, c'est pourquoi la reconnaissance n'est pas fiable pour transcrire le sudoku en matrice et espérer le résoudre. Néanmoins, c'est une méthode que je peux appliquer à chacune des cases extraites du sudoku.

On fait maintenant un premier test sur une ligne du sudoku.

On voit que dans la reconnaissance des chiffres, les cases vides sont identifiées au chiffre 5. On va donc les traiter à part en les remplaçant par zéro avant de traduire les autres chiffres du sudoku.

On représente une case vide par le chiffre 0. On ne traite une case que si un certain nombre de pixels sont sombres. Aussi on cherche un seuil approprié.

On constate qu'il y a encore beaucoup trop d'erreurs pour espérer recopier proprement et résoudre le sudoku...

J'ai tenté aussi une méthode utilisant Tesseract, mais la reconnaissance nécessitait l'enregistrement des cases en images, et cela m'a semblé trop lourd et laborieux pour l'usage qu'on en faisait. Mon essai suivant était avec OpenCV, mais je ne le laisse pas ici pour ne pas alourdir le code, et parce que ce n'est pas totalement abouti.

2 - Résolution du sudoku

Ce bout de code est essentiellement tiré du programme d'un étudiant de Blaise Pascal basé sur le «Back-Tracking». Il s'est aussi intéressé à l'échelle permettant d'évaluer la difficulté d'un sudoku. Là, avec les informations dont on dispose, on peut par exemple situer le niveau du sudoku grâce au nombre de chiffres testés lors de la résolution.

Pour une grille difficile, le nombre de chiffres testés est plutôt de l'ordre du million voire de la dizaine de millions, de même que le nombre d'appels à la fonction principale, est_valide. (Essayer d'appeler la fonction avec grille_d pour un exemple de grille difficile)

3 - Remplissage de la grille avec les chiffres en écriture manuscrite

On récupère chaque chiffre en écriture manuscrite à partir de l'image fournie.

Digits.jpg

Avec chacun des chiffres en écriture manuscrite, on va créer une vignette carrée que l'on n'aura plus qu'à coller à la bonne place sur l'image.

Ainsi tous les chiffres peuvent être traités de la même manière.

On implémente une fonction ecrit_chiffre qui remplace les pixels correspondant dans l'image de départ (ici appelée s) pour inscrire le chiffre donné en argument aux coordonnées indiquées.