Created
September 27, 2024 21:36
-
-
Save Lokno/c0b36f8b97b78f4a9932d91901d96d07 to your computer and use it in GitHub Desktop.
Python to detect the rank and value of a playing card
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Author: Lokno Ketchup | |
# Description : Script to detect the rank and value of a playing card | |
# using OpenCV 2.4.12 template matching. User must provide | |
# a local directory "templates" with cropped PNG files | |
# of each rank and suit, labeled using the keys in | |
# dictionaries 'ranks' and 'suits' below. For example, | |
# the image for the ace rank template will be A.png | |
# | |
# Rotation, distance, and perspective are not accounted for, | |
# so the templates must match roughly what the camera will | |
# capture. | |
import sys | |
import cv2 | |
import numpy as np | |
# script expects a folder 'templates' with PNGs labeled with the keys of these dictionaries | |
ranks = { 'A' : None, '2' : None, '3' : None, '4' : None, '5' : None, '6' : None, | |
'7' : None, '8' : None, '9' : None, '10' : None, 'J' : None, 'Q' : None, 'K' : None } | |
suits = { 'D' : None, 'S' : None, 'C' : None, 'H' : None } | |
# translates shorthand for labels to display | |
suitsFull = { 'D' : 'Diamonds', 'S' : 'Spades', 'C' : 'Clubs', 'H' : 'Hearts' } | |
valuesFull = { 'A' : 'Ace', '2' : '2', '3' : '3', '4' : '4', '5' : '5', '6' : '6', | |
'7' : '7', '8' : '8', '9' : '9', '10' : '10', | |
'J' : 'Jack', 'Q' : 'Queen', 'K' : 'King' } | |
# checks an image against a template and check for best match | |
def checkTemplate(img, template, method, idx, bestIdx, bestVal): | |
res = cv2.matchTemplate(img,template,method) | |
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res) | |
if bestVal < max_val: | |
bestVal = max_val | |
bestIdx = idx | |
return bestVal,bestIdx | |
# determine the suit and rank of a playing card in img, using method | |
# and requiring a minimum value of at least threshold | |
def determine( img, method, threshold = 0.8 ): | |
bestRank = 0 | |
bestSuit = 0 | |
rankValue = 0 | |
suitValue = 0 | |
# determine rank | |
c = 0 | |
for t in ranks.keys(): | |
rankValue,bestRank = checkTemplate(img, ranks[t], method, c, bestRank, rankValue) | |
c += 1 | |
# determine suit | |
c = 0 | |
for t in suits.keys(): | |
suitValue,bestSuit = checkTemplate(img, suits[t], method, c, bestSuit, suitValue) | |
c += 1 | |
ret = '' | |
if suitValue > threshold and rankValue > threshold: | |
ret = '%s of %s' % (valuesFull[ranks.keys()[bestRank]], suitsFull[suits.keys()[bestSuit]]) | |
else: | |
ret = 'Cannot Read Card' | |
return ret | |
# load template images | |
for t in ranks.keys(): | |
ranks[t] = cv2.imread('templates/' + t + '.png',0) | |
if ranks[t] == None: | |
print 'ERROR : Could not load template images' | |
sys.exit(-1) | |
for t in suits.keys(): | |
suits[t] = cv2.imread('templates/' + t + '.png',0) | |
if suits[t] == None: | |
print 'ERROR' | |
# The method I used because it worked best in my case | |
method = cv2.TM_CCOEFF_NORMED | |
font = cv2.FONT_HERSHEY_SIMPLEX | |
# Main Loop | |
cap = cv2.VideoCapture(0) | |
while(True): | |
# Capture frame-by-frame | |
ret, frame = cap.read() | |
# get grayscale image | |
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) | |
# resize to half size (640x400 on test camera) | |
img = cv2.resize(gray,None,fx=0.5, fy=0.5, interpolation = cv2.INTER_AREA) | |
# process card | |
cardID = determine( img, method ) | |
# add result to image for display | |
cv2.putText(img,cardID, | |
(200,30), # postion of text in image | |
font, 1, (255,255,255),2) | |
# Display the resulting frame | |
cv2.imshow('Template Matching',img) | |
# quits on escape (in window focus) | |
if cv2.waitKey(1) & 0xFF == 27: | |
break | |
cv2.destroyAllWindows() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment