| | |
| | | import cv2 |
| | | import numpy as np |
| | | import pandas as pd |
| | | import math |
| | | from screeninfo import get_monitors |
| | | |
| | | """ |
| | | This is the first attempt of identifying MTG cards using only classical computer vision technique. |
| | | Most of the processes are similar to the process used in opencv_dnn.py, but it instead tries to use |
| | | Hough transformation to identify straight edges of the card. |
| | | However, there were difficulties trying to associate multiple edges into a rectangle, as some of them |
| | | either didn't show up or was too short to intersect. |
| | | There were also no method to dynamically adjust various threshold, even finding all the edges were |
| | | very conditional. |
| | | """ |
| | | |
| | | def detect_a_card(img, thresh_val=80, blur_radius=None, dilate_radius=None, min_hyst=80, max_hyst=200, |
| | | min_line_length=None, max_line_gap=None, debug=False): |
| | | dim_img = (len(img[0]), len(img)) # (width, height) |
| | |
| | | if blur_radius is None: |
| | | blur_radius = math.floor(min(dim_img) / 100 + 0.5) // 2 * 2 + 1 # Rounded to the nearest odd |
| | | if dilate_radius is None: |
| | | dilate_radius = math.floor(min(dim_img) / 100) |
| | | dilate_radius = math.floor(min(dim_img) / 67 + 0.5) |
| | | if min_line_length is None: |
| | | min_line_length = min(dim_img) / 10 |
| | | if max_line_gap is None: |
| | | max_line_gap = min(dim_img) / 10 |
| | | |
| | | thresh_radius = math.floor(min(dim_img) / 20 + 0.5) // 2 * 2 + 1 # Rounded to the nearest odd |
| | | |
| | | img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) |
| | | # Median blur better removes background textures than Gaussian blur |
| | | img_blur = cv2.medianBlur(img_gray, blur_radius) |
| | | # Truncate the bright area while detecting the border |
| | | _, img_thresh = cv2.threshold(img_blur, thresh_val, 255, cv2.THRESH_TRUNC) |
| | | img_thresh = cv2.adaptiveThreshold(img_blur, 255, cv2.ADAPTIVE_THRESH_MEAN_C, |
| | | cv2.THRESH_BINARY_INV, thresh_radius, 20) |
| | | #_, img_thresh = cv2.threshold(img_blur, thresh_val, 255, cv2.THRESH_TRUNC) |
| | | |
| | | # Dilate the image to emphasize thick borders around the card |
| | | kernel_dilate = np.ones((dilate_radius, dilate_radius), np.uint8) |
| | | img_dilate = cv2.dilate(img_thresh, kernel_dilate, iterations=1) |
| | | #img_dilate = cv2.dilate(img_thresh, kernel_dilate, iterations=1) |
| | | img_dilate = cv2.erode(img_thresh, kernel_dilate, iterations=1) |
| | | |
| | | img_contour = img_dilate.copy() |
| | | _, contours, _ = cv2.findContours(img_contour, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) |
| | | img_contour = cv2.cvtColor(img_contour, cv2.COLOR_GRAY2BGR) |
| | | img_contour = cv2.drawContours(img_contour, contours, -1, (128, 128, 128), 1) |
| | | card_found = contours is not None |
| | | print(len(contours)) |
| | | print([len(contour) for contour in contours]) |
| | | |
| | | # find the biggest area |
| | | c = max(contours, key=cv2.contourArea) |
| | | |
| | | x, y, w, h = cv2.boundingRect(c) |
| | | # draw the book contour (in green) |
| | | img_contour = cv2.drawContours(img_contour, [c], -1, (0, 255, 0), 1) |
| | | |
| | | # Canny edge - low minimum hysteresis to detect glowed area, |
| | | # and high maximum hysteresis to compensate for high false positives. |
| | | img_canny = cv2.Canny(img_dilate, min_hyst, max_hyst) |
| | | |
| | | #img_canny = img_dilate |
| | | # Apply Hough transformation to detect the edges |
| | | ''' |
| | | detected_lines = cv2.HoughLines(img_canny, 1, np.pi / 180, 200) |
| | | if detected_lines is not None: |
| | | img_hough = cv2.cvtColor(img_dilate.copy(), cv2.COLOR_GRAY2BGR) |
| | | for line in detected_lines: |
| | | rho, theta = line[0] |
| | | 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)) |
| | | cv2.line(img_hough, (x1, y1), (x2, y2), (0, 0, 255), 2) |
| | | else: |
| | | print('Hough couldn\'t find any lines') |
| | | return False |
| | | ''' |
| | | detected_lines = cv2.HoughLinesP(img_canny, 1, np.pi / 180, threshold=60, |
| | | detected_lines = cv2.HoughLinesP(img_dilate, 1, np.pi / 180, threshold=60, |
| | | minLineLength=min_line_length, |
| | | maxLineGap=max_line_gap) |
| | | card_found = detected_lines is not None |
| | | print(len(detected_lines)) |
| | | |
| | | if card_found: |
| | | if debug: |
| | | img_hough = cv2.cvtColor(img_dilate.copy(), cv2.COLOR_GRAY2BGR) |
| | | for line in detected_lines: |
| | | x1, y1, x2, y2 = line[0] |
| | | cv2.line(img_hough, (x1, y1), (x2, y2), (0, 0, 255), 3) |
| | | cv2.line(img_hough, (x1, y1), (x2, y2), (0, 0, 255), 1) |
| | | elif not debug: |
| | | print('Hough couldn\'t find any lines') |
| | | |
| | | # Debug: display intermediate results from various steps |
| | | if debug: |
| | | ''' |
| | | cv2.imshow('Original', img) |
| | | cv2.imshow('Thresholded', img_thresh) |
| | | cv2.imshow('Dilated', img_dilate) |
| | | cv2.imshow('Canny Edge', img_canny) |
| | | if card_found: |
| | | cv2.imshow('Detected Lines', img_hough) |
| | | ''' |
| | | img_blank = np.zeros((len(img), len(img[0]), 3), np.uint8) |
| | | img_thresh = cv2.cvtColor(img_thresh, cv2.COLOR_GRAY2BGR) |
| | | #img_dilate = cv2.cvtColor(img_dilate, cv2.COLOR_GRAY2BGR) |
| | | img_canny = cv2.cvtColor(img_canny, cv2.COLOR_GRAY2BGR) |
| | | img_dilate = cv2.cvtColor(img_dilate, cv2.COLOR_GRAY2BGR) |
| | | #img_canny = cv2.cvtColor(img_canny, cv2.COLOR_GRAY2BGR) |
| | | if not card_found: |
| | | img_hough = img_blank |
| | | |
| | | # Append all images together |
| | | img_row_1 = np.concatenate((img, img_thresh), axis=1) |
| | | img_row_2 = np.concatenate((img_canny, img_hough), axis=1) |
| | | img_row_2 = np.concatenate((img_contour, img_hough), axis=1) |
| | | img_result = np.concatenate((img_row_1, img_row_2), axis=0) |
| | | |
| | | # Resize the final image to fit into the main monitor's resolution |
| | |
| | | # TODO: output meaningful data |
| | | return card_found |
| | | |
| | | |
| | | def main(): |
| | | img_test = cv2.imread('data/tilted_card_2.jpg') |
| | | img_test = cv2.imread('data/li38_handOfCards.jpg') |
| | | card_found = detect_a_card(img_test, |
| | | dilate_radius=2, |
| | | min_hyst=30, |
| | | max_hyst=80, |
| | | min_line_length=10, |
| | | max_line_gap=50, |
| | | #dilate_radius=5, |
| | | #thresh_val=100, |
| | | #min_hyst=40, |
| | | #max_hyst=160, |
| | | #min_line_length=50, |
| | | #max_line_gap=100, |
| | | debug=True) |
| | | if card_found: |
| | | return |
| | | |
| | | return |
| | | for dilate_radius in range(1, 6): |
| | | for min_hyst in range(50, 91, 10): |
| | | for max_hyst in range(180, 119, -20): |