Tags

, , , , , , , , , ,

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):

playingcards_3hearts
playingcards_set

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()[1]
         
        # 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:

playingcards_3hearts_10spades

First up, the three of hearts has been detected next to the ten of spades

playingcards_3hearts_kingdiamonds

Next, the three of hearts has been detected aside the king of diamonds

playingcards_3hearts_4hearts

It has even picked out the three of hearts next to a similar looking four of hearts

playingcards_4hearts_3hearts

And does so again, when we reverse the order of the cards.

playingcards_3hearts_3diamonds

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.