Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Code ported to Python3 and fixed a bug relating to openCV version #30

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions scripts/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import pickle
import numpy as np

from sudokuExtractor import Extractor
from .sudokuExtractor import Extractor

def load(filename):
with open(filename) as in_file:
Expand Down Expand Up @@ -36,8 +36,8 @@ def __init__(self, imgDir=None, rebuild=False):
[np.reshape(cell, (784, 1)) for cell in row] for row in cells]
trainingResults = [[self.vectorizedResult(
int(digit)) for digit in row] for row in results]
for i in xrange(9):
for j in xrange(9):
for i in range(9):
for j in range(9):
if trainingResults[i][j] == None:
continue
self.trainingData.append(
Expand Down
12 changes: 6 additions & 6 deletions scripts/cells.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
import cv2
import pickle

from helpers import Helpers
from digit import Digit
from .helpers import Helpers
from .digit import Digit


class Cells(object):
Expand All @@ -13,15 +13,15 @@ class Cells(object):
'''

def __init__(self, sudoku):
print 'Extracting cells...',
print('Extracting cells...', end=' ')
self.helpers = Helpers()
self.cells = self.extractCells(sudoku)
print 'done.'
print('done.')

def extractCells(self, sudoku):
cells = []
W, H = sudoku.shape
cell_size = W / 9
cell_size = W // 9
i, j = 0, 0
for r in range(0, W, cell_size):
row = []
Expand All @@ -47,7 +47,7 @@ def clean(self, cell):
cell = self.helpers.make_it_square(cell[y:y + h, x:x + w], 28)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (2, 2))
cell = cv2.morphologyEx(cell, cv2.MORPH_CLOSE, kernel)
cell = 255 * (cell / 130)
cell = 255 * (cell // 130)
return cell

def centerDigit(self, digit):
Expand Down
28 changes: 14 additions & 14 deletions scripts/digit.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import cv2
import numpy as np
import Queue
import queue


class Digit(object):
Expand All @@ -12,38 +12,38 @@ class Digit(object):
def __init__(self, image):
self.graph = image.copy()
self.W, self.H = self.graph.shape
self.visited = [[False for _ in xrange(
self.H)] for _ in xrange(self.W)]
self.digit = [[None for _ in xrange(self.H)] for _ in xrange(self.W)]
self.visited = [[False for _ in range(
self.H)] for _ in range(self.W)]
self.digit = [[None for _ in range(self.H)] for _ in range(self.W)]
self.buildDigit()

def buildDigit(self):
componentId = 0
A, C = self.H / 4, 3 * self.H / 4 + 1
B, D = self.W / 4, 3 * self.W / 4 + 1
for i in xrange(A, C):
for j in xrange(B, D):
A, C = self.H // 4, 3 * self.H // 4 + 1
B, D = self.W // 4, 3 * self.W // 4 + 1
for i in range(A, C):
for j in range(B, D):
if not self.visited[i][j]:
self.bfs(i, j, componentId)
componentId += 1
componentSizes = [0 for _ in xrange(componentId)]
componentSizes = [0 for _ in range(componentId)]
for row in self.digit:
for cell in row:
if cell is not None:
componentSizes[cell] += 1
largest = componentSizes.index(max(componentSizes))
for i in xrange(self.H):
for j in xrange(self.W):
for i in range(self.H):
for j in range(self.W):
self.digit[i][j] = 255 if self.digit[i][j] == largest else 0
self.digit = np.asarray(self.digit, dtype=np.uint8)

def bfs(self, i, j, num):
q = Queue.Queue()
q = queue.Queue()
q.put((i, j))
while not q.empty():
i, j = q.get()
inValidRow = i not in xrange(0, self.H)
inValidCol = j not in xrange(0, self.W)
inValidRow = i not in range(0, self.H)
inValidCol = j not in range(0, self.W)
invalidCell = inValidRow or inValidCol
invalidPixel = invalidCell or self.graph[i][j] != 255
if invalidPixel or self.visited[i][j]:
Expand Down
22 changes: 13 additions & 9 deletions scripts/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,12 @@ def largestContour(self, image):
return max(contours, key=cv2.contourArea)

def largest4SideContour(self, image):
contours, h = cv2.findContours(
image, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
if self.isCv2():
contours, h = cv2.findContours(
image, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
else:
_, contours, h = cv2.findContours(
image, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
contours = sorted(contours, key=cv2.contourArea, reverse=True)
for cnt in contours[:min(5,len(contours))]:
#im = image.copy()
Expand All @@ -72,8 +76,8 @@ def cut_out_sudoku_puzzle(self, image, contour):
return self.make_it_square(image, min(image.shape))

def binarized(self, image):
for i in xrange(image.shape[0]):
for j in xrange(image.shape[1]):
for i in range(image.shape[0]):
for j in range(image.shape[1]):
image[i][j] = 255 * int(image[i][j] != 255)
return image

Expand Down Expand Up @@ -135,19 +139,19 @@ def getTopLine(self, image):
return None

def getBottomLine(self, image):
for i in xrange(image.shape[0] - 1, -1, -1):
for i in range(image.shape[0] - 1, -1, -1):
if np.any(image[i]):
return i
return None

def getLeftLine(self, image):
for i in xrange(image.shape[1]):
for i in range(image.shape[1]):
if np.any(image[:, i]):
return i
return None

def getRightLine(self, image):
for i in xrange(image.shape[1] - 1, -1, -1):
for i in range(image.shape[1] - 1, -1, -1):
if np.any(image[:, i]):
return i
return None
Expand All @@ -159,7 +163,7 @@ def rowShift(self, image, start, end, length):
elif end + length >= image.shape[0]:
length = image.shape[0] - 1 - end

for row in xrange(start, end + 1):
for row in range(start, end + 1):
shifted[row + length] = image[row]
return shifted

Expand All @@ -170,6 +174,6 @@ def colShift(self, image, start, end, length):
elif end + length >= image.shape[1]:
length = image.shape[1] - 1 - end

for col in xrange(start, end + 1):
for col in range(start, end + 1):
shifted[:, col + length] = image[:, col]
return shifted
2 changes: 1 addition & 1 deletion scripts/loader.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import pickle

from train import NeuralNetwork
from .train import NeuralNetwork

with open('train', 'r') as in_file:
training = pickle.load(in_file)
Expand Down
28 changes: 13 additions & 15 deletions scripts/sudokuExtractor.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import cv2
import numpy as np
import pickle

from helpers import Helpers
from cells import Cells
from .helpers import Helpers
from .cells import Cells


class Extractor(object):
Expand All @@ -16,40 +15,39 @@ def __init__(self, path):
self.helpers = Helpers() # Image helpers
self.image = self.loadImage(path)
self.preprocess()
#self.helpers.show(self.image, 'After Preprocessing')
# self.helpers.show(self.image, 'After Preprocessing')
sudoku = self.cropSudoku()
#self.helpers.show(sudoku, 'After Cropping out grid')
# self.helpers.show(sudoku, 'After Cropping out grid')
sudoku = self.straighten(sudoku)
#self.helpers.show(sudoku, 'Final Sudoku grid')
# self.helpers.show(sudoku, 'Final Sudoku grid')
self.cells = Cells(sudoku).cells

def loadImage(self, path):
color_img = cv2.imread(path)
color_img = cv2.imread(path,0)
if color_img is None:
raise IOError('Image not loaded')
print 'Image loaded.'
print('Image loaded.')
return color_img

def preprocess(self):
print 'Preprocessing...',
self.image = cv2.cvtColor(self.image, cv2.COLOR_BGR2GRAY)
print('Preprocessing...', end=' ')
self.image = self.helpers.thresholdify(self.image)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (2, 2))
self.image = cv2.morphologyEx(self.image, cv2.MORPH_CLOSE, kernel)
print 'done.'
print('done.')

def cropSudoku(self):
print 'Cropping out Sudoku...',
print('Cropping out Sudoku...', end=' ')
contour = self.helpers.largestContour(self.image.copy())
sudoku = self.helpers.cut_out_sudoku_puzzle(self.image.copy(), contour)
print 'done.'
print('done.')
return sudoku

def straighten(self, sudoku):
print 'Straightening image...',
print('Straightening image...', end=' ')
largest = self.helpers.largest4SideContour(sudoku.copy())
app = self.helpers.approx(largest)
corners = self.helpers.get_rectangle_corners(app)
sudoku = self.helpers.warp_perspective(corners, sudoku)
print 'done.'
print('done.')
return sudoku
6 changes: 3 additions & 3 deletions scripts/sudoku_str.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env python
# coding: utf-8

import sudopy # see: http://norvig.com/sudopy.shtml
from . import sudopy # see: http://norvig.com/sudopy.shtml

"""
SudokuStr(sudoku) can take three kinds of input:
Expand Down Expand Up @@ -97,9 +97,9 @@ def solve(self):
assert s.s == SudokuStr(s1).s, 'Multiline str failure'
assert s.s == SudokuStr(s2).s, 'List failure'
assert s.s == SudokuStr(tuple(s2)).s, 'Tuple failure'
print(repr(s))
print((repr(s)))
print(s)
try:
print('\nSolving...\n\n{}'.format(s.solve()))
print(('\nSolving...\n\n{}'.format(s.solve())))
except ValueError:
print('No solution found. Please rescan the puzzle.')
16 changes: 8 additions & 8 deletions scripts/sudopy.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def test():
assert peers['C2'] == set(['A2', 'B2', 'D2', 'E2', 'F2', 'G2', 'H2', 'I2',
'C1', 'C3', 'C4', 'C5', 'C6', 'C7', 'C8', 'C9',
'A1', 'A3', 'B1', 'B3'])
print 'All tests pass.'
print('All tests pass.')

################ Parse a Grid ################

Expand All @@ -50,7 +50,7 @@ def parse_grid(grid):
return False if a contradiction is detected."""
## To start, every square can be any digit; then assign values from the grid.
values = dict((s, digits) for s in squares)
for s,d in grid_values(grid).items():
for s,d in list(grid_values(grid).items()):
if d in digits and not assign(values, s, d):
return False ## (Fail if we can't assign d to square s.)
return values
Expand All @@ -59,7 +59,7 @@ def grid_values(grid):
"Convert grid into a dict of {square: char} with '0' or '.' for empties."
chars = [c for c in grid if c in digits or c in '0.']
assert len(chars) == 81
return dict(zip(squares, chars))
return dict(list(zip(squares, chars)))

################ Constraint Propagation ################

Expand Down Expand Up @@ -103,10 +103,10 @@ def display(values):
width = 1+max(len(values[s]) for s in squares)
line = '+'.join(['-'*(width*3)]*3)
for r in rows:
print ''.join(values[r+c].center(width)+('|' if c in '36' else '')
for c in cols)
if r in 'CF': print line
print
print(''.join(values[r+c].center(width)+('|' if c in '36' else '')
for c in cols))
if r in 'CF': print(line)
print()

################ Search & Solve ################

Expand All @@ -116,7 +116,7 @@ def time_solve(grid, timelimit):
t = time.clock()-start
## Display puzzle that take long enough
if timelimit is not 0 and t > timelimit:
print "Puzzle takes unusual amount of time to be solved"
print("Puzzle takes unusual amount of time to be solved")
return solved(parse_grid(getResult(values)))

def solve(grid, timelimit = 0):
Expand Down
2 changes: 1 addition & 1 deletion scripts/test.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
import sudokuExtractor as s
from . import sudokuExtractor as s
s.Extractor('../train/image3.jpg')
6 changes: 3 additions & 3 deletions scripts/train.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,11 @@ def SGD(self, training_data, MBsize, eta, epochs, test, Lambda=0.0):
does `epochs` number of iterations on the training data with
learning rate `eta`.
'''
for iteration in xrange(epochs):
for iteration in range(epochs):
if iteration % (epochs // 4) == 0:
percent = str(100 * float(iteration) / epochs)
correctness = str(self.evaluate(test))
print "At {}% correctness is: {}".format(percent, correctness)
print("At {}% correctness is: {}".format(percent, correctness))
shuffle(training_data)
mini_batches = [training_data[i:i + MBsize]
for i in range(0, len(training_data), MBsize)]
Expand Down Expand Up @@ -120,7 +120,7 @@ def backprop(self, x, y):
nabla_w[-1] = np.dot(delta, activations[-2].transpose())
# Here, l = 1 means the last layer of neurons, l = 2 is the
# second-last layer, and so on.
for l in xrange(2, self.layers):
for l in range(2, self.layers):
z = zs[-l]
sp = sigmoid_prime(z)
delta = np.dot(self.wts[-l + 1].transpose(), delta) * sp
Expand Down
8 changes: 4 additions & 4 deletions sudoku.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@


def create_net(rel_path):
with open(os.getcwd() + rel_path) as in_file:
sizes, biases, wts = pickle.load(in_file)
with open(os.getcwd() + rel_path,'rb') as in_file:
sizes, biases, wts = pickle.load(in_file,encoding='latin1')
return NeuralNetwork(customValues=(sizes, biases, wts))


Expand All @@ -32,7 +32,7 @@ def snap_sudoku(image_path):
grid = ''.join(cell for cell in get_cells(image_path))
s = SudokuStr(grid)
try:
print('\nSolving...\n\n{}'.format(s.solve()))
print(('\nSolving...\n\n{}'.format(s.solve())))
except ValueError:
print('No solution found. Please rescan the puzzle.')

Expand All @@ -42,4 +42,4 @@ def snap_sudoku(image_path):
snap_sudoku(image_path=sys.argv[1])
except IndexError:
fmt = 'usage: {} image_path'
print(fmt.format(__file__.split('/')[-1]))
print((fmt.format(__file__.split('/')[-1])))