import cv2 import numpy as np import math from sklearn.linear_model import LinearRegression import pytesseract import re pytesseract.pytesseract.tesseract_cmd = "C:/Program Files/Tesseract-OCR/tesseract.exe" pytesseract.pytesseract.tesseract_cmd = "/usr/bin/tesseract" def first_preprocessing(image): gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY) canny = cv2.Canny(gray,75,25) contours,hierarchies = cv2.findContours(canny,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE) sorted_contours = sorted(contours,key = cv2.contourArea,reverse = True) largest_contour = sorted_contours[0] box = cv2.boundingRect(sorted_contours[0]) x = box[0] y = box[1] w = box[2] h = box[3] result = cv2.rectangle(image, (x, y), (x + w, y + h), (255, 255, 255), -1) return result def remove_head(image): custom_config = r'--oem 3 --psm 6' # Tesseract OCR configuration detected_text = pytesseract.image_to_string(image, config=custom_config) lines = detected_text.split('\n') # Find the first line containing some text line_index = 0 for i, line in enumerate(lines): if line.strip() != '': line_index = i break first_newline_idx = detected_text.find('\n') result = cv2.rectangle(image, (0, line_index), (image.shape[1], first_newline_idx), (255,255,255), thickness=cv2.FILLED) return result def second_preprocessing(image): gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY) canny = cv2.Canny(gray,75,25) contours,hierarchies = cv2.findContours(canny,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE) sorted_contours = sorted(contours,key = cv2.contourArea,reverse = True) largest_contour = sorted_contours[0] box2 = cv2.boundingRect(sorted_contours[0]) x = box2[0] y = box2[1] w = box2[2] h = box2[3] result2 = cv2.rectangle(image, (x, y), (x + w, y + h), (255, 255, 255), -1) return result2 def find_vertical_profile(image): gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) _, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU) vertical_profile = np.sum(binary, axis=0) return vertical_profile def detect_steepest_changes(projection_profile, threshold=0.4, start_idx=0, min_valley_width=10, min_search_width=50): differences = np.diff(projection_profile) change_points = np.where(np.abs(differences) > threshold * np.max(np.abs(differences)))[0] left_boundaries = [] right_boundaries = [] for idx in change_points: if idx <= start_idx: continue if idx - start_idx >= min_search_width: decreasing_profile = projection_profile[idx:] if np.any(decreasing_profile > 0): right_boundary = idx + np.argmin(decreasing_profile) right_boundaries.append(right_boundary) else: continue valley_start = max(start_idx, idx - min_valley_width) valley_start = valley_start-40 valley_end = min(idx + min_valley_width, len(projection_profile) - 1) valley = valley_start + np.argmin(projection_profile[valley_start:valley_end]) left_boundaries.append(valley) break return left_boundaries, right_boundaries def crop_text_columns(image, projection_profile, threshold=0.4): start_idx = 0 text_columns = [] while True: left_boundaries, right_boundaries = detect_steepest_changes(projection_profile, threshold, start_idx) if not left_boundaries or not right_boundaries: break left = left_boundaries[0] right = right_boundaries[0] text_column = image[:, left:right] text_columns.append(text_column) start_idx = right return text_columns def parse_clues(clue_text): lines = clue_text.split('\n') clues = {} number = None column = 0 for line in lines: if "column separation" in line: column += 1 continue pattern = r"^(\d+(?:\.\d+)?)\s*(.+)" # Updated pattern to handle decimal point numbers for clues match = re.search(pattern, line) if match: number = float(match.group(1)) # Convert the matched number to float if there is a decimal point if number not in clues: clues[number] = [column,match.group(2).strip()] else: continue elif number is None: continue elif clues[number][0] != column: continue else: clues[number][1] += " " + line.strip() # Append to the previous clue if it's a multiline clue return clues def parse_crossword_clues(text): # Check if "Down" clues are present match = re.search(r'[dD][oO][wW][nN]\n', text) if match: across_clues, down_clues = re.split(r'[dD][oO][wW][nN]\n', text) else: # If "Down" clues are not present, set down_clues to an empty string across_clues, down_clues = text, "" across = parse_clues(across_clues) down = parse_clues(down_clues) return across, down def classify_text(filtered_columns): text = "" custom_config = r'--oem 3 --psm 6' for i, column in enumerate(filtered_columns): column2 = cv2.cvtColor(column, cv2.COLOR_BGR2RGB) scale_factor = 2.0 # You can adjust this value # Calculate the new dimensions after scaling new_width = int(column2.shape[1] * scale_factor) new_height = int(column2.shape[0] * scale_factor) # Resize the image using OpenCV scaled_image = cv2.resize(column2, (new_width, new_height), interpolation=cv2.INTER_LINEAR) # Apply image enhancement techniques denoised_image = cv2.fastNlMeansDenoising(scaled_image, None, h=10, templateWindowSize=7, searchWindowSize=21) enhanced_image = cv2.cvtColor(denoised_image, cv2.COLOR_BGR2GRAY) # Convert to grayscale # Apply histogram equalization detected_text = pytesseract.image_to_string(enhanced_image, config=custom_config) # print(detected_text) text+=detected_text across_clues, down_clues = parse_crossword_clues(text) return across_clues,down_clues def get_text(image): image = cv2.cvtColor(image,cv2.COLOR_GRAY2BGR) result = first_preprocessing(image) result1 = remove_head(result) result2 = second_preprocessing(result1) vertical_profile = find_vertical_profile(result2) combined_columns = crop_text_columns(result2,vertical_profile) across,down = classify_text(combined_columns) return across,down ################################ Grid Extraction begins here ########################### ######################################################################################## # for applying non max suppression of the contours def calculate_iou(image, contour1, contour2): # Create masks for each contour mask1 = np.zeros_like(image, dtype=np.uint8) cv2.drawContours(mask1, [contour1], -1, 255, thickness=cv2.FILLED) mask2 = np.zeros_like(image, dtype=np.uint8) cv2.drawContours(mask2, [contour2], -1, 255, thickness=cv2.FILLED) # Find the intersection between the two masks intersection = cv2.bitwise_and(mask1, mask2) # Calculate the intersection area intersection_area = cv2.countNonZero(intersection) # Calculate the union area (Not the accurate one but works alright XD !) union_area = cv2.contourArea(cv2.convexHull(np.concatenate((contour1, contour2)))) # Calculate the IoU iou = intersection_area / union_area return iou # remove overlapping contours, non square and not quardatic contours # this check every contour with every other contour so be careful def filter_contours(img_gray2, contours, iou_threshold = 0.6, asp_ratio = 1,tolerance = 0.5): # Remove overlapping contours, removing that are not square filtered_contours = [] epsilon = 0.02 for contour in contours: # Approximate the contour to reduce the number of points epsilon_multiplier = epsilon * cv2.arcLength(contour, True) approximated_contour = cv2.approxPolyDP(contour, epsilon_multiplier, True) # find the aspect ratio of the contour, if it is close to 1 then keep it otherwise discard _,_,w,h = cv2.boundingRect(approximated_contour) if(abs(float(w)/h - asp_ratio) > tolerance ): continue # Calculate the IoU with all existing contours iou_values = [calculate_iou(img_gray2,np.array(approximated_contour), np.array(existing_contour)) for existing_contour in filtered_contours] # If the IoU value with all existing contours is below the threshold, add the current contour if not any(iou_value > iou_threshold for iou_value in iou_values): filtered_contours.append(approximated_contour) return filtered_contours # https://stackoverflow.com/questions/383480/intersection-of-two-lines-defined-in-rho-theta-parameterization/383527#383527 # Define the parametricIntersect function def parametricIntersect(r1, t1, r2, t2): ct1 = np.cos(t1) st1 = np.sin(t1) ct2 = np.cos(t2) st2 = np.sin(t2) d = ct1 * st2 - st1 * ct2 if d != 0.0: x = int((st2 * r1 - st1 * r2) / d) y = int((-ct2 * r1 + ct1 * r2) / d) return x, y else: return None # Group the coordinate to a list such that each point in a list may belong to a line def group_lines(coordinates,axis=0,threshold=10): sorted_coordinates = list(sorted(coordinates,key=lambda x: x[axis])) groups = [] current_group = [] for i in range(len(sorted_coordinates)): if i!=0 and abs(current_group[0][axis] - sorted_coordinates[i][axis]) > threshold: # condition to change the group if len(current_group) > 4: groups.append(current_group) current_group = [] current_group.append(sorted_coordinates[i]) # condition to append to the group if(len(current_group) > 4): groups.append(current_group) return groups # Use the Grouped Lines to Fit a line using Linear Regression def fit_lines(grouped_lines,is_horizontal = False): actual_lines = [] for coordinates in grouped_lines: # Converting into numpy array coordinates_arr = np.array(coordinates) # Separate the x and y coordinates x = coordinates_arr[:, 0] y = coordinates_arr[:, 1] # Fit a linear regression model regressor = LinearRegression() regressor.fit(y.reshape(-1, 1), x) # Get the slope and intercept of the fitted line slope = regressor.coef_[0] intercept = regressor.intercept_ if(is_horizontal): intercept = np.mean(y) actual_lines.append((slope,intercept)) return actual_lines # Calculates difference between two consecutive elements in an array def average_distance(arr): n = len(arr) distance_sum = 0 for i in range(n - 1): distance_sum += abs(arr[i+1] - arr[i]) average = distance_sum / (n - 1) return average # If two adjacent lines are near than some threshold, then merge them # Returns Results in y = mx + b from def average_out_similar_lines(lines_m_c,lines_coord,del_threshold,is_horizontal=False): averaged_lines = [] i = 0 while(i < len(lines_m_c) - 1): _, intercept1 = lines_m_c[i] _, intercept2 = lines_m_c[i + 1] if abs(intercept2 - intercept1) < del_threshold: new_points = np.array(lines_coord[i] + lines_coord[i+1][:-1]) # Separate the x and y coordinates x = new_points[:, 0] y = new_points[:, 1] # Fit a linear regression model regressor = LinearRegression() regressor.fit(y.reshape(-1, 1), x) # Get the slope and intercept of the fitted line slope = regressor.coef_[0] intercept = regressor.intercept_ if(is_horizontal): intercept = np.mean(y) averaged_lines.append((slope,intercept)) i+=2 else: averaged_lines.append(lines_m_c[i]) i+=1 if(i < len(lines_m_c)): averaged_lines.append(lines_m_c[i]) return averaged_lines # If two adjacent lines are near than some threshold, then merge them # Returns Results in normalized vector form def average_out_similar_lines1(lines_m_c,lines_coord,del_threshold): averaged_lines = [] i = 0 while(i < len(lines_m_c) - 1): _, intercept1 = lines_m_c[i] _, intercept2 = lines_m_c[i + 1] if abs(intercept2 - intercept1) < del_threshold: new_points = np.array(lines_coord[i] + lines_coord[i+1][:-1]) coordinates = np.array(new_points) points = coordinates[:, None, :].astype(np.int32) # Fit a line using linear regression [vx, vy, x, y] = cv2.fitLine(points, cv2.DIST_L2, 0, 0.01, 0.01) averaged_lines.append((vx, vy, x, y)) i+=2 else: new_points = np.array(lines_coord[i]) coordinates = np.array(new_points) points = coordinates[:, None, :].astype(np.int32) # Fit a line using linear regression [vx, vy, x, y] = cv2.fitLine(points, cv2.DIST_L2, 0, 0.01, 0.01) averaged_lines.append((vx, vy, x, y)) i+=1 if(i < len(lines_m_c)): new_points = np.array(lines_coord[i]) coordinates = np.array(new_points) points = coordinates[:, None, :].astype(np.int32) # Fit a line using linear regression [vx, vy, x, y] = cv2.fitLine(points, cv2.DIST_L2, 0, 0.01, 0.01) averaged_lines.append((vx, vy, x, y)) return averaged_lines def get_square_color(image, box): # Determine the size of the square region square_size = (box[1][0] - box[0][0]) / 3 # Determine the coordinates of the square region inside the box top_left = (box[0][0] + square_size, box[0][1] + square_size) bottom_right = (box[0][0] + square_size*2, box[0][1] + square_size*2) # Extract the square region from the image square_region = image[int(top_left[1]):int(bottom_right[1]), int(top_left[0]):int(bottom_right[0])] # Calculate the mean pixel value of the square region mean_value = np.mean(square_region) # Determine whether the square region is predominantly black or white if mean_value < 128: square_color = "." else: square_color = "A" return square_color # accepts image in grayscale def extract_grid(image): # Apply Gaussian blur to reduce noise and improve edge detection blurred = cv2.GaussianBlur(image, (3, 3), 0) # Apply Canny edge detection edges = cv2.Canny(blurred, 50, 150) # Apply dilation to connect nearby edges and make them more contiguous kernel = np.ones((5, 5), np.uint8) dilated = cv2.dilate(edges, kernel, iterations=1) # # Applying canny edge detector # detecting contours on the canny image contours, _ = cv2.findContours(dilated, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE) # sorting the contours by the descending order area of the contour sorted_contours = list(sorted(contours, key=cv2.contourArea,reverse=True)) # filtering out the top 10 largest by applying NMS and only selecting square ones (Apsect ratio 1) filtered_contours = filter_contours(image, sorted_contours[0:10],iou_threshold=0.6,asp_ratio=1,tolerance=0.2) # largest Contour Extraction largest_contour = [] if(len(filtered_contours)): largest_contour = filtered_contours[0] else: largest_contour = sorted_contours[0] # --- Performing Perspective warp of the largest contour --- coordinates_list = [] if(largest_contour.shape != (4,1,2)): largest_contour = cv2.convexHull(largest_contour) if(largest_contour.shape != (4,1,2)): rect = cv2.minAreaRect(largest_contour) largest_contour = cv2.boxPoints(rect) largest_contour = largest_contour.astype('int') coordinates_list = largest_contour.reshape(4, 2).tolist() # Convert coordinates_list to a numpy array coordinates_array = np.array(coordinates_list) # Find the convex hull of the points hull = cv2.convexHull(coordinates_array) # Find the extreme points of the convex hull extreme_points = np.squeeze(hull) # Sort the extreme points by their x and y coordinates to determine the order sorted_points = extreme_points[np.lexsort((extreme_points[:, 1], extreme_points[:, 0]))] # Extract top left, bottom right, top right, and bottom left points tl = sorted_points[0] tr = sorted_points[1] bl = sorted_points[2] br = sorted_points[3] if(tr[1] < tl[1]): tl,tr = tr,tl if(br[1] < bl[1]): bl,br = br,bl # Define pts1 pts1 = [tl, bl, tr, br] # Calculate the bounding rectangle coordinates x, y, w, h = 0,0,400,400 # Define pts2 as the corners of the bounding rectangle pts2 = [[3, 3], [400, 3], [3, 400], [400, 400]] # Calculate the perspective transformation matrix matrix = cv2.getPerspectiveTransform(np.float32(pts1), np.float32(pts2)) # Apply the perspective transformation to the cropped_image transformed_img = cv2.warpPerspective(image, matrix, (403, 403)) cropped_image = transformed_img.copy() # if the largest contour was not exactly quadilateral # -- Performing Hough Transform -- similarity_threshold = math.floor(w/30) # Thresholds for filtering Similar Hough Lines # Applying Gaussian Blur to reduce noice and improve dege detection blurred = cv2.GaussianBlur(cropped_image, (5, 5), 0) # Perform Canny edge detection on the GrayScale Image edges = cv2.Canny(blurred, 50, 150) lines = cv2.HoughLines(edges, 1, np.pi/180, 200) # Filter out similar lines filtered_lines = [] for line in lines: for r_theta in lines: arr = np.array(r_theta[0], dtype=np.float64) rho, theta = arr is_similar = False for filtered_line in filtered_lines: filtered_rho, filtered_theta = filtered_line # similarity threshold is 10 if abs(rho - filtered_rho) < similarity_threshold and abs(theta - filtered_theta) < np.pi/180 * similarity_threshold: is_similar = True break if not is_similar: filtered_lines.append((rho, theta)) # Filter out the horizontal and the vertical lines horizontal_lines = [] vertical_lines = [] for rho, theta in filtered_lines: a = np.cos(theta) b = np.sin(theta) x0 = a * rho y0 = b * rho x1 = int(x0 + 1000 * (-b)) y1 = int(y0 + 1000 * (a)) x2 = int(x0 - 1000 * (-b)) y2 = int(y0 - 1000 * (a)) slope = (y2 - y1) / (x2 - x1 + 0.0001) # do taninv(0.17) it is nearly equal to 10 if( abs(slope) <= 0.18 ): horizontal_lines.append((rho,theta)) elif (abs(slope) > 6): vertical_lines.append((rho,theta)) # Find the intersection points of horizontal and vertical lines hough_corners = [] for h_rho, h_theta in horizontal_lines: for v_rho, v_theta in vertical_lines: x, y = parametricIntersect(h_rho, h_theta, v_rho, v_theta) if x is not None and y is not None: hough_corners.append((x, y)) # -- Performing Harris Corner Detection -- # Create CLAHE object with specified clip limit clahe = cv2.createCLAHE(clipLimit=3, tileGridSize=(8, 8)) clahe_image = clahe.apply(cropped_image) # harris corner detection for CLHAE IMAGE dst = cv2.cornerHarris(clahe_image,2,3,0.04) ret,dst = cv2.threshold(dst,0.1*dst.max(),255,0) dst = np.uint8(dst) dst = cv2.dilate(dst,None) ret, labels, stats, centroids = cv2.connectedComponentsWithStats(dst) criteria = (cv2.TERM_CRITERIA_EPS+cv2.TermCriteria_MAX_ITER,100,0.001) harris_corners = cv2.cornerSubPix(clahe_image,np.float32(centroids),(5,5),(-1,-1),criteria) drawn_image = cv2.cvtColor(cropped_image, cv2.COLOR_GRAY2BGR) for i in harris_corners: x,y = i image2 = cv2.circle(drawn_image, (int(x),int(y)), radius=0, color=(0, 0, 255), thickness=3) # -- Using Regression Model to approximate horizontal and vertical Lines # reducing to 0 decimal places corners1 = list(map(lambda coord: (round(coord[0], 0), round(coord[1], 0)), harris_corners)) # adding the corners obtained from hough transform corners1 += hough_corners # removing the duplicate corners corners_no_dup = list(set(corners1)) min_cell_width = w/30 min_cell_height = h/30 # grouping coordinates into probabale array that could fit a horizontal and vertical lien vertical_lines = group_lines(corners_no_dup,0,min_cell_height) horizontal_lines = group_lines(corners_no_dup,1,min_cell_height) actual_vertical_lines = fit_lines(vertical_lines) actual_horizontal_lines = fit_lines(horizontal_lines,is_horizontal=True) # Lines obtained from above method are not appropriate, we have to refine them x_probable = [i[1] for i in actual_horizontal_lines] # looking at the intercepts y_probable = [i[1] for i in actual_vertical_lines] del_x_avg = average_distance(x_probable) del_y_avg = average_distance(y_probable) averaged_horizontal_lines1 = [] # This step here is fishy and needs refinement averaged_vertical_lines1 = [] multiplier = 0.95 i = 0 while(1): averaged_horizontal_lines = average_out_similar_lines(actual_horizontal_lines,horizontal_lines,del_y_avg*multiplier,is_horizontal=True) averaged_vertical_lines = average_out_similar_lines(actual_vertical_lines,vertical_lines,del_x_avg*multiplier,is_horizontal=False) i += 1 if(i >= 20 or len(averaged_horizontal_lines) == len(averaged_vertical_lines)): break else: multiplier -= 0.05 averaged_horizontal_lines1 = average_out_similar_lines1(actual_horizontal_lines,horizontal_lines,del_y_avg*multiplier) averaged_vertical_lines1 = average_out_similar_lines1(actual_vertical_lines,vertical_lines,del_x_avg*multiplier) # plotting the lines to image to find the intersection points drawn_image6 = np.ones_like(cropped_image)*255 for vx,vy,cx,cy in averaged_horizontal_lines1 + averaged_vertical_lines1: w = cropped_image.shape[1] cv2.line(drawn_image6, (int(cx-vx*w), int(cy-vy*w)), (int(cx+vx*w), int(cy+vy*w)), (0, 0, 255),1,cv2.LINE_AA) # -- Finding Intersection points -- # Applying Harris Corner Detection to find the intersection points mesh_image = drawn_image6.copy() dst = cv2.cornerHarris(mesh_image,2,3,0.04) ret,dst = cv2.threshold(dst,0.1*dst.max(),255,0) dst = np.uint8(dst) dst = cv2.dilate(dst,None) ret, labels, stats, centroids = cv2.connectedComponentsWithStats(dst) criteria = (cv2.TERM_CRITERIA_EPS+cv2.TermCriteria_MAX_ITER,100,0.001) harris_corners = cv2.cornerSubPix(mesh_image,np.float32(centroids),(5,5),(-1,-1),criteria) drawn_image = cv2.cvtColor(drawn_image6, cv2.COLOR_GRAY2BGR) harris_corners = list(sorted(harris_corners[1:],key = lambda x : x[1])) # -- Finding out the grid color -- grayscale = cropped_image.copy() # Perform adaptive thresholding to obtain binary image _, binary = cv2.threshold(grayscale, 128, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU) # Perform morphological operations to remove small text regions kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3)) binary = cv2.morphologyEx(binary, cv2.MORPH_ELLIPSE, kernel, iterations=1) # Invert the binary image inverted_binary = cv2.bitwise_not(binary) # Restore the image by blending the inverted binary image with the grayscale image restored_image = cv2.bitwise_or(inverted_binary, grayscale) # Apply morphological opening to remove small black dots kernel_opening = np.ones((3, 3), np.uint8) opened_image = cv2.morphologyEx(restored_image, cv2.MORPH_OPEN, kernel_opening, iterations=1) # Apply morphological closing to further refine the restored image kernel_closing = np.ones((5, 5), np.uint8) refined_image = cv2.morphologyEx(opened_image, cv2.MORPH_CLOSE, kernel_closing, iterations=1) # finding out the grid corner grid = [] grid_nums = [] across_clue_num = [] down_clue_num = [] # extracting the grid color and filling up the grid variable sorted_corners = np.array(list(sorted(harris_corners,key=lambda x:x[1]))) if(len(sorted_corners) == len(averaged_horizontal_lines1) * len(averaged_vertical_lines1)): sorted_corners_grouped = [] for i in range(0,len(sorted_corners),len(averaged_vertical_lines1)): temp_arr = sorted_corners[i:i+len(averaged_vertical_lines1)] temp_arr = list(sorted(temp_arr,key=lambda x: x[0])) sorted_corners_grouped.append(temp_arr) for h_line_idx in range(0,len(sorted_corners_grouped)-1): for corner_idx in range(0,len(sorted_corners_grouped[h_line_idx])-1): # grabbing the four box coordinates box = [sorted_corners_grouped[h_line_idx][corner_idx],sorted_corners_grouped[h_line_idx][corner_idx+1], sorted_corners_grouped[h_line_idx+1][corner_idx],sorted_corners_grouped[h_line_idx+1][corner_idx+1]] grid.append(get_square_color(refined_image,box)) grid_formatted = [] for i in range(0, len(grid), len(averaged_vertical_lines1) - 1): grid_formatted.append(grid[i:i + len(averaged_vertical_lines1) - 1]) # if (x,y) is present in these array the cell (x,y) is already accounted as a part of answer of across or down in_horizontal = [] in_vertical = [] num = 0 for x in range(0, len(averaged_vertical_lines1) - 1): for y in range(0, len(averaged_horizontal_lines1) - 1): # if the cell is black there's no need to number if grid_formatted[x][y] == '.': grid_nums.append(0) continue # if the cell is part of both horizontal and vertical cell then there's no need to number horizontal_presence = (x, y) in in_horizontal vertical_presence = (x, y) in in_vertical # present in both 1 1 if horizontal_presence and vertical_presence: grid_nums.append(0) continue # present in one i.e 1 0 if not horizontal_presence and vertical_presence: horizontal_length = 0 temp_horizontal_arr = [] # iterate in x direction until the end of the grid or until a black box is found while x + horizontal_length < len(averaged_horizontal_lines1) - 1 and grid_formatted[x + horizontal_length][y] != '.': temp_horizontal_arr.append((x + horizontal_length, y)) horizontal_length += 1 # if horizontal length is greater than 1, then append the temp_horizontal_arr to in_horizontal array if horizontal_length > 1: in_horizontal.extend(temp_horizontal_arr) num += 1 across_clue_num.append(num) grid_nums.append(num) continue grid_nums.append(0) # present in one 1 0 if not vertical_presence and horizontal_presence: # do the same for vertical vertical_length = 0 temp_vertical_arr = [] # iterate in y direction until the end of the grid or until a black box is found while y + vertical_length < len(averaged_vertical_lines1) - 1 and grid_formatted[x][y+vertical_length] != '.': temp_vertical_arr.append((x, y+vertical_length)) vertical_length += 1 # if vertical length is greater than 1, then append the temp_vertical_arr to in_vertical array if vertical_length > 1: in_vertical.extend(temp_vertical_arr) num += 1 down_clue_num.append(num) grid_nums.append(num) continue grid_nums.append(0) if(not horizontal_presence and not vertical_presence): horizontal_length = 0 temp_horizontal_arr = [] # iterate in x direction until the end of the grid or until a black box is found while x + horizontal_length < len(averaged_horizontal_lines1) - 1 and grid_formatted[x + horizontal_length][y] != '.': temp_horizontal_arr.append((x + horizontal_length, y)) horizontal_length += 1 # if horizontal length is greater than 1, then append the temp_horizontal_arr to in_horizontal array # do the same for vertical vertical_length = 0 temp_vertical_arr = [] # iterate in y direction until the end of the grid or until a black box is found while y + vertical_length < len(averaged_vertical_lines1) - 1 and grid_formatted[x][y+vertical_length] != '.': temp_vertical_arr.append((x, y+vertical_length)) vertical_length += 1 # if vertical length is greater than 1, then append the temp_vertical_arr to in_vertical array if horizontal_length > 1 and horizontal_length > 1: in_horizontal.extend(temp_horizontal_arr) in_vertical.extend(temp_vertical_arr) num += 1 across_clue_num.append(num) down_clue_num.append(num) grid_nums.append(num) elif vertical_length > 1: in_vertical.extend(temp_vertical_arr) num += 1 down_clue_num.append(num) grid_nums.append(num) elif horizontal_length > 1: in_horizontal.extend(temp_horizontal_arr) num += 1 across_clue_num.append(num) grid_nums.append(num) else: grid_nums.append(0) size = { 'rows' : len(averaged_horizontal_lines1)-1, 'cols' : len(averaged_vertical_lines1)-1, } dict = { 'size' : size, 'grid' : grid, 'gridnums': grid_nums, 'across_nums': down_clue_num, 'down_nums' : across_clue_num, } return dict if __name__ == "__main__": img = cv2.imread("D:\\D\\Major Project files\\opencv\\movie.png",0) down = extract_grid(img) print(down) # img = Image.open("chalena3.jpg") # img_gray = img.convert("L") # print(extract_grid(img_gray))