From aa1421d05b43fb5b4eb2e3ee2d7db4515fa24c5a Mon Sep 17 00:00:00 2001 From: Yatharth Saluja Date: Thu, 27 Apr 2017 02:51:34 +0530 Subject: [PATCH 1/3] Added opencv3 support / changed imread to grayscale --- scripts/helpers.py | 6 ++++-- scripts/sudokuExtractor.py | 4 +--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/helpers.py b/scripts/helpers.py index b96da71..6a5a78b 100644 --- a/scripts/helpers.py +++ b/scripts/helpers.py @@ -49,8 +49,10 @@ 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 cv2.__version__.startswith("2"): + contours, h = cv2.findContours(image, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) + else: + image, 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() diff --git a/scripts/sudokuExtractor.py b/scripts/sudokuExtractor.py index e06fec1..b893624 100644 --- a/scripts/sudokuExtractor.py +++ b/scripts/sudokuExtractor.py @@ -1,7 +1,6 @@ import cv2 import numpy as np import pickle - from helpers import Helpers from cells import Cells @@ -24,7 +23,7 @@ def __init__(self, path): 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.' @@ -32,7 +31,6 @@ def loadImage(self, path): def preprocess(self): print 'Preprocessing...', - self.image = cv2.cvtColor(self.image, cv2.COLOR_BGR2GRAY) 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) From ccb05652206f45179daffa886a25150dc93d1b84 Mon Sep 17 00:00:00 2001 From: Yatharth Saluja Date: Thu, 27 Apr 2017 04:12:27 +0530 Subject: [PATCH 2/3] Code ported to python3 --- scripts/builder.py | 6 +++--- scripts/cells.py | 12 ++++++------ scripts/digit.py | 28 ++++++++++++++-------------- scripts/helpers.py | 14 +++++++------- scripts/loader.py | 2 +- scripts/sudokuExtractor.py | 24 ++++++++++++------------ scripts/sudoku_str.py | 6 +++--- scripts/sudopy.py | 16 ++++++++-------- scripts/test.py | 2 +- scripts/train.py | 6 +++--- sudoku.py | 8 ++++---- 11 files changed, 62 insertions(+), 62 deletions(-) diff --git a/scripts/builder.py b/scripts/builder.py index 3ab4cb3..9a712e6 100644 --- a/scripts/builder.py +++ b/scripts/builder.py @@ -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: @@ -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( diff --git a/scripts/cells.py b/scripts/cells.py index 4f55472..90d1643 100644 --- a/scripts/cells.py +++ b/scripts/cells.py @@ -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): @@ -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 = [] @@ -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): diff --git a/scripts/digit.py b/scripts/digit.py index 451b0f5..f79e2c1 100644 --- a/scripts/digit.py +++ b/scripts/digit.py @@ -1,6 +1,6 @@ import cv2 import numpy as np -import Queue +import queue class Digit(object): @@ -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]: diff --git a/scripts/helpers.py b/scripts/helpers.py index 6a5a78b..fa8764d 100644 --- a/scripts/helpers.py +++ b/scripts/helpers.py @@ -74,8 +74,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 @@ -137,19 +137,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 @@ -161,7 +161,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 @@ -172,6 +172,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 diff --git a/scripts/loader.py b/scripts/loader.py index 364e85c..9ea425f 100644 --- a/scripts/loader.py +++ b/scripts/loader.py @@ -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) diff --git a/scripts/sudokuExtractor.py b/scripts/sudokuExtractor.py index b893624..df5a55d 100644 --- a/scripts/sudokuExtractor.py +++ b/scripts/sudokuExtractor.py @@ -1,8 +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): @@ -15,39 +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,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...', + 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 diff --git a/scripts/sudoku_str.py b/scripts/sudoku_str.py index 746c191..09cc8d6 100644 --- a/scripts/sudoku_str.py +++ b/scripts/sudoku_str.py @@ -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: @@ -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.') diff --git a/scripts/sudopy.py b/scripts/sudopy.py index 28dc8b2..8cfedd6 100644 --- a/scripts/sudopy.py +++ b/scripts/sudopy.py @@ -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 ################ @@ -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 @@ -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 ################ @@ -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 ################ @@ -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): diff --git a/scripts/test.py b/scripts/test.py index e1fd8b8..ce3b127 100644 --- a/scripts/test.py +++ b/scripts/test.py @@ -1,2 +1,2 @@ -import sudokuExtractor as s +from . import sudokuExtractor as s s.Extractor('../train/image3.jpg') \ No newline at end of file diff --git a/scripts/train.py b/scripts/train.py index 5c91846..f95a698 100644 --- a/scripts/train.py +++ b/scripts/train.py @@ -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)] @@ -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 diff --git a/sudoku.py b/sudoku.py index 17a6c2a..36f97b6 100644 --- a/sudoku.py +++ b/sudoku.py @@ -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)) @@ -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.') @@ -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]))) From 56fd476744fc69a644b39cb09a26fd05adad699b Mon Sep 17 00:00:00 2001 From: Yatharth Saluja Date: Thu, 27 Apr 2017 05:13:46 +0530 Subject: [PATCH 3/3] using isCv2 to check openCV version --- scripts/helpers.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/scripts/helpers.py b/scripts/helpers.py index fa8764d..4135deb 100644 --- a/scripts/helpers.py +++ b/scripts/helpers.py @@ -49,10 +49,12 @@ def largestContour(self, image): return max(contours, key=cv2.contourArea) def largest4SideContour(self, image): - if cv2.__version__.startswith("2"): - 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: - image, contours, h = cv2.findContours(image, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) + _, 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()