Peters, my Dutch lodger, is a fabulous card player. A lousy human being, but a super poker face. ‘I’m sick of you beating me every time,’ I told him, ‘So I am going to build a mean card-playing machine to win back all my money.’ Peters scoffed, ‘Ha! I’d like to see you try.’
I will use my Raspberry Pi computer to play cards on my behalf. A webcam attached to the Raspberry Pi will take a photograph of the hand of cards laid before it. Python code on my Raspberry Pi will then determine which playing cards it can detect in the photograph and make a decision on what to do next. Easy.
But first, I need to use OpenCV to create a haar cascade classifier file for each playing card in the pack, so that the files can be used by my Python program to detect cards. My Guitar detection using OpenCV post has all the detail of how to create classifiers.
Let’s start of by creating a haar cascade classifier for the three of hearts playing card…
I have one positive image of the three of hearts playing card, taken from a pack of cards I will use solely for training the classifier (i.e. a different pack of cards will be used to test the classifier):
Normally I would have multiple positive images with varying backgrounds – but since all packs of playing cards generally look the same (is there an international standard or something?) and we will be placing them on a black background during the game, we can take a bit of a shortcut on this occasion and use just one positive image.
I have 100 negative images of the interior of my house.
I created my training samples:
perl createtrainsamples.pl positives.dat negatives.dat samples 250 "./opencv_createsamples -bgcolor 0 -bgthresh 0 -maxxangle 1.1 -maxyangle 1.1 maxzangle 0.5 -maxidev 40 -w 50 -h 75"
And used them to train a classifier:
opencv_haartraining -data haarcascade_3hearts -vec samples.vec -bg negatives.dat -nstages 20 -nsplits 2 -minhitrate 0.999 -maxfalsealarm 0.5 -npos 250 -nneg 100 -w 50 -h 75 -nonsym -mem 2048 -mode ALL
Training stalled at stage 5, so I used the convert_cascade application to spit out my classifier xml file:
convert_cascade --size="50x75" haarcascade_3hearts haarcascade_3hearts-inter.xml
Next I will write a Python Webcam class, which will obtain a photograph of a hand of cards from the webcam and then use the classifier to determine if any of the cards are the three of hearts:
import cv2 from datetime import datetime class Webcam(object): WINDOW_NAME = "Playing Card Detection System" # constructor def __init__(self): self.webcam = cv2.VideoCapture(0) # save image to disk def _save_image(self, path, image): filename = datetime.now().strftime('%Y%m%d_%Hh%Mm%Ss%f') + '.jpg' cv2.imwrite(path + filename, image) # detect cards in webcam def detect_cards(self, cascade_path): # get image from webcam img = self.webcam.read() # do card detection card_cascade = cv2.CascadeClassifier(cascade_path) gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) cards = card_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=4, minSize=(200, 300)) for (x,y,w,h) in cards: cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2) # save image to disk self._save_image('WebCam/Detection/', img) # show image in window cv2.imshow(self.WINDOW_NAME, img) cv2.waitKey(2000) cv2.destroyAllWindows() # indicate whether cards detected if len(cards) > 0: return True return False
Our main Python program will make use of the Webcam class:
from webcam import Webcam webcam = Webcam() # play a game of cards while True: # attempt to detect the three of hearts if webcam.detect_cards('haarcascade_3hearts.xml') == True: print "I have the cotton picking three of hearts" else: print "I do not have the darn gun slinging three of hearts"
We will print out a message to indicate whether the three of hearts has been detected. Once we have classifiers in place for each of the 52 playing cards in a pack, we can replace the message with some actual logic to determine what to do next e.g. play our hand or maybe ask the dealer for another card.
Time for a demo. Let’s point our webcam at a hand of playing cards and see if our Python program can detect the three of hearts:
First up, the three of hearts has been detected next to the ten of spades
Next, the three of hearts has been detected aside the king of diamonds
It has even picked out the three of hearts next to a similar looking four of hearts
And does so again, when we reverse the order of the cards.
But, alas, it is not all rosy as the three of diamonds has been falsely detected. Clearly our classifier needs more training.
‘Have you finished your poker bot yet?’ Peters asked me smugly, in his thick Dutch accent.
‘Not yet, there is still some way to go. But I will.’
Next up, I need to create classifiers for all 52 playing cards, and improve the detection so as to remove false-positives. I will then write some logic so that my Raspberry Pi can decide what to do with its hand of cards.
If all goes well, I will start to reclaim the lost rent from my obese Dutch lodger. The bastard has won so much cash and IOUs from me that he can live here scot-free for the next thirty-seven years, and I’ll be damned if an unemployed Ginsters munchin’ slob is going to be parked on my sofa for decades to come.
Time to send him South of Heaven.