def crop_grid(img): """Identify the Sudoku grid, crop it from the image and skew it into a square to compensate for the camera angle.""" proc = pre_process_image(img) if 'basic' in classification_mode(): bbox = cv.get_four_corners(cv.find_largest_polygon(proc)) else: # Gets the four corners of the inner contours of the image. Compensates for the grid outline accurately. bbox, area, outer, outer_area = cv.find_four_corners_inner(proc) # If the inner contours are less than 95% of the outer contour we may be losing valuable information. # Image 8 is one such example of this occurring # In these cases, use a different algorithm to estimate the width of the Sudoku border if (area / outer_area) < 0.95: bbox = cv.get_four_corners(outer) bbox = inner_sudoku_bbox( proc, bbox) # Estimates the width of the grid outline area = outer_area # Some images have the Sudoku grid surrounded by a large rectangular box and the contour algorithm will pick that # instead of the Sudoku grid. In these cases, using the largest feature algorithm is more reliable. Identify when # the contour area meets a threshold ratio of the image size. if (area / (proc.shape[0] * proc.shape[1])) > 0.95: # Find the largest connected pixel structure, this should be the outer grid for Sudoku proc, bbox, seed = cv.find_largest_feature(proc) bbox = inner_sudoku_bbox( proc, bbox) # Estimates the width of the grid outline # Return a warped version of the image return cv.crop_and_warp(img, bbox, square=True)
def extract_digit(img, rect, size, mode, include_gray_channel=False): """Extracts a digit (if one exists) from a Sudoku square.""" digit = cv.cut_from_rect(img, rect) # Get the digit box from the whole square # Use thresholding to expose the digit in the centre of the each box digit = grid_square_threshold(digit, mode) # Skip digit extraction, depending on the mode if 'cell' in mode: digit = cv2.resize(digit, (28, 28)) return digit # Use fill feature finding to get the largest feature in middle of the box # Margin used to define an area in the middle we would expect to find a pixel belonging to the digit h, w = digit.shape[:2] margin = int(np.mean([h, w]) / 2.5) discard, bbox, seed = cv.find_largest_feature(digit, [margin, margin], [w - margin, h - margin]) digit, bbox = cv.get_bbox_from_seed(digit, seed) # Scale and pad the digit so that it fits a square of the digit size we're using for machine learning w = bbox[1][0] - bbox[0][0] h = bbox[1][1] - bbox[0][1] # Ignore any small bounding boxes if w > 0 and h > 0 and (w * h) > 100: digit = cv.cut_from_rect(digit, bbox) digit = cv.scale_and_centre(digit, size, 4) if include_gray_channel: digit = digit.reshape((size, size, 1)) return digit else: return None