diff --git a/board_detector.py b/board_detector.py index 233242525d4234377f439c11a78fbf3422521ec5..18f995f58c02635e3124c8066cf8333652f4de77 100644 --- a/board_detector.py +++ b/board_detector.py @@ -10,32 +10,6 @@ def init_show_cv(val): def find_longest_lines(img): gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) - hsv_img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) - - # hsv_mask = cv2.inRange(hsv_img[:,:,1], 50, 255) - # hsv_after = cv2.bitwise_and(img, img, mask=hsv_mask) - # if (show_cv): - # cv2.imshow('HSV', hsv_after) - # cv2.waitKey(0) - # cv2.destroyAllWindows() - - # gray_hsv = cv2.cvtColor(hsv_after, cv2.COLOR_HSV2BGR) - # gray_hsv = cv2.cvtColor(hsv_after, cv2.COLOR_BGR2GRAY) - # gray_img = gray_img - gray_hsv - - # # sobel gradients - # sobel_x = cv2.Sobel(gray_img, cv2.CV_64F, 1, 0, ksize=3) - # sobel_y = cv2.Sobel(gray_img, cv2.CV_64F, 0, 1, ksize=3) - # abs_sobel_x = np.absolute(sobel_x) - # abs_sobel_y = np.absolute(sobel_y) - - # # threshold on abs values of sobel gradients and combine them - # _, threshold_x = cv2.threshold(abs_sobel_x, 25, 255, cv2.THRESH_BINARY) - # _, threshold_y = cv2.threshold(abs_sobel_y, 25, 255, cv2.THRESH_BINARY) - # combined_threshold = cv2.bitwise_or(threshold_x, threshold_y) - # combined_threshold = np.uint8(combined_threshold) # median blur needs this - # combined_threshold = cv2.medianBlur(combined_threshold, 5) # this gets rid of outliers so weird diagonal lines don't get made - # edges = combined_threshold edges = cv2.Canny(gray_img, 50, 100, apertureSize=3) if (show_cv): @@ -43,41 +17,24 @@ def find_longest_lines(img): cv2.waitKey(0) cv2.destroyAllWindows() - # lines = cv2.HoughLinesP(edges, 1, np.pi/180, 200, minLineLength=400, maxLineGap=15) - # print(lines) - # lines = cv2.HoughLines(edges, 1, np.pi / 180, 200) - # lines = cv2.HoughLines(edges, rho=1, theta=np.pi/180, threshold=80, min_theta=0, max_theta=np.pi) - theta_thresh = 50 - horizontal_lines = cv2.HoughLines(edges, rho=1, theta=np.pi/180, threshold=80, min_theta=(theta_thresh/2-1)*np.pi/theta_thresh, max_theta=(theta_thresh/2+1)*np.pi/theta_thresh) - vertical_lines = cv2.HoughLines(edges, rho=1, theta=np.pi/180, threshold=80, min_theta=-np.pi/theta_thresh, max_theta=np.pi/theta_thresh) - - vertical_line_points = convert_lines(vertical_lines) - horizontal_line_points = convert_lines(horizontal_lines) - - # vertical_lines = [] - # horizontal_lines = [] - - # # separate horizontal and vertical lines - # if line_points is not None: - # for line in line_points: - # x1, y1, x2, y2 = line[0] - # if abs(x2 - x1) < abs(y2 - y1): # vertical line - # vertical_lines.append(line) - # else: - # horizontal_lines.append(line) - - # # filter lines too close to each other + theta_thresh = 60 + horizontal_lines = cv2.HoughLines(edges, rho=1, theta=np.pi/180, threshold=60, min_theta=(theta_thresh/2-1)*np.pi/theta_thresh, max_theta=(theta_thresh/2+1)*np.pi/theta_thresh) + vertical_lines = cv2.HoughLines(edges, rho=1, theta=np.pi/180, threshold=60, min_theta=-np.pi/theta_thresh, max_theta=np.pi/theta_thresh) + + vertical_line_points = convert_to_cartesian(vertical_lines) + horizontal_line_points = convert_to_cartesian(horizontal_lines) + + # filter lines too close to each other filtered_vertical = filter_lines(vertical_line_points, 50) filtered_horizontal = filter_lines(horizontal_line_points, 50) + # get the 9 largest lines sorted_vertical = sorted(filtered_vertical, key=lambda line: min(line[0][1], line[0][3]))[:9] sorted_horizontal = sorted(filtered_horizontal, key=lambda line: min(line[0][0], line[0][2]))[:9] return sorted_vertical, sorted_horizontal - # return vertical_line_points, horizontal_line_points - -def convert_lines(lines): +def convert_to_cartesian(lines): line_points = [] if lines is not None: for line in lines: @@ -151,12 +108,30 @@ def detect_board(img): board_lines = cv2.bitwise_or(vertical_mask, horizontal_mask) contours, hierarchy = cv2.findContours(board_lines, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) - intersection_points, hierarchy = cv2.findContours(intersection, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) + intersections, hierarchy = cv2.findContours(intersection, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) + + # find midpoints of intersection contours (this gives us exact coordinates of the corners of the grid) + intersection_points = [] + for contour in intersections: + M = cv2.moments(contour) + if (M["m00"] != 0): + midpoint_x = int(M["m10"] / M["m00"]) + midpoint_y = int(M["m01"] / M["m00"]) + intersection_points.append((midpoint_x, midpoint_y)) + + # sort the coordinates from left to right then top to bottom + sorted_intersection_points = sort_square_grid_coords(intersection_points, unpacked=False) if (show_cv): board_lines_img = img.copy() cv2.drawContours(board_lines_img, contours, -1, (255, 255, 0), 2) - cv2.drawContours(board_lines_img, intersection_points, -1, (0, 0, 255), 2) + # cv2.drawContours(board_lines_img, intersections, -1, (0, 0, 255), 2) + i = 0 + for points in sorted_intersection_points: + for point in points: + cv2.circle(board_lines_img, point, 5, (255 - (3 * i), (i % 9) * 28, 3 * i), -1) + i += 1 + print(i) cv2.imshow('Lines of Board', board_lines_img) cv2.waitKey(0) cv2.destroyAllWindows() @@ -201,23 +176,22 @@ def detect_board(img): corners = max_rect.reshape(-1, 2) # reshapes it so each row has 2 elements corners = [tuple(corner) for corner in corners] # convert to tuples print(corners) - # corners.sort(key=lambda coord: (coord[0], coord[1])) # sort coords. goes from bottom left clockwise to bottom right - corners_sorted = sort_square_grid_coords(corners) + # corners.sort(key=lambda coord: (coord[0], coord[1])) # sort coords. goes from bottom left clockwise to bottom right - DIDN'T WORK + corners_sorted = sort_square_grid_coords(corners, unpacked=True) print(corners_sorted) - corners = corners_sorted - corners_img = img.copy() - for i, corner in enumerate(corners): - cv2.circle(corners_img, corner, 5, (60 * i, 60 * i, 60 * i), -1) - if (show_cv): - cv2.imshow('Canny Filter', corners_img) - cv2.waitKey(0) - cv2.destroyAllWindows() + # corners_img = img.copy() + # for i, corner in enumerate(corners_sorted): + # cv2.circle(corners_img, corner, 5, (60 * i, 60 * i, 60 * i), -1) + # if (show_cv): + # cv2.imshow('Canny Filter', corners_img) + # cv2.waitKey(0) + # cv2.destroyAllWindows() - tl = corners[0] - tr = corners[1] - bl = corners[2] - br = corners[3] + tl = corners_sorted[0] + tr = corners_sorted[1] + bl = corners_sorted[2] + br = corners_sorted[3] src = np.float32([list(tl), list(tr), list(bl), list(br)]) dest = np.float32([[0,0], [width, 0], [0, height], [width, height]]) M = cv2.getPerspectiveTransform(src, dest) @@ -228,7 +202,7 @@ def detect_board(img): M = cv2.getPerspectiveTransform(src, dest) Minv = cv2.getPerspectiveTransform(dest, src) warped_ip = img.copy() - warped_ip = cv2.drawContours(warped_ip, intersection_points, -1, (0, 0, 255), 2) + warped_ip = cv2.drawContours(warped_ip, intersections, -1, (0, 0, 255), 2) warped_ip = cv2.warpPerspective(np.uint8(warped_ip), M, (width, height)) if (show_cv): @@ -254,7 +228,7 @@ def detect_board(img): # cv2.waitKey(0) # cv2.destroyAllWindows() -def sort_square_grid_coords(coordinates): +def sort_square_grid_coords(coordinates, unpacked): sqrt_len = int(math.sqrt(len(coordinates))) sorted_coords = sorted(coordinates, key=lambda coord: coord[1]) # first sort by y values # then group rows of the square (for example, 9x9 grid would be 81 coordinates so split into 9 arrays of 9) @@ -262,6 +236,8 @@ def sort_square_grid_coords(coordinates): for group in groups: group.sort(key=lambda coord: coord[0]) # now sort each row by x - collapsed_groups = [coord for sublist in groups for coord in sublist] - + if (unpacked == False): + return groups + + collapsed_groups = [coord for sublist in groups for coord in sublist] # unpack/collapse groups to just be an array of tuples return collapsed_groups \ No newline at end of file