- Raspberry Pi: the brains of the project.*
- USB webcam: to read images of the cards.
- Raspberry Pi Display: to assist in visualization.
- Push buttons: to call/fold bets.
Identify CardsThe first step is for the Raspberry Pi to identify cards. The webcam connected to the Pi captures images of the cards and a deep learning model identifies them. MATLAB supports several popular deep learning networks including SqueezeNet, AlexNet and GoogLeNet. SqueezeNet was chosen as it is sufficiently small to be deployed to a Raspberry Pi and it has relatively high prediction accuracy. Here is a simple doc example that shows how to perform transfer learning using AlexNet, but you could easily swap AlexNet for other (more modern) networks like SqueezeNet or ResNet. To train the deep learning algorithm, we need to collect images of the cards we want to be able to identify. We took a series of snapshots using a webcam making sure to show cards in different lighting and orientations. After manually selecting images that captured the features of each card with minimal blur / distortion, we rotated each image to avoid overfitting the network and to ensure it can recognize cards for different orientations. The code for generating training data is fairly straightforward, and the full code is in generateCardData.m
%% Initialize variables NumImages = 200; % Max = 1000 suits = ['D' 'C' 'H' 'S']; rank = ['A' '2' '3' '4' '5' '6' '7' '8' '9' 'T' 'J' 'Q' 'K']; %% Get input from command line and validate disp('Show the card to the webcam and enter the card name in short form. ex: "2D" for Diamond-Two, AC for Club-Ace'); cardname = input('Enter card name in short form:','s'); if ~ischar(cardname) || (numel(cardname) ~= 2) || ~contains(rank,cardname(1)) || ~contains(suits,cardname(2)) error('Invalid cardname'); end ... % Capture input from webcam and save to file. for i = 1:1000 waitbar(i/1000,waitBarHandle,['Saving image: ' num2str(i)]); im = snapshot(cam); if mod(i,2) im = imrotate(im,180); end if i <= NumImages imwrite(im,fullfile(cardsDirectory,[cardname,num2str(i) '.png'])); else imwrite(im,fullfile(unusedImgsDirectory,[cardname,num2str(i) '.png'])); end end waitbar(1,waitBarHandle,['Capturing images for ',cardname,' completed.']);We ended up with over 10,000 images which were used to train the model. Instead of loading each image file into memory during training, we used imageDatastore which let us work with the overall collection of images. The function augmentedImageDatastore was used to resize the images to the predefined input size of SqueezeNet. This function is efficient at quickly resizing images in one line of code as shown below.
net = squeezenet; inputSize = net.Layers(1).InputSize; audsTrain = augmentedImageDatastore(inputSize(1:2),imdsTrain); augimdsValidation = augmentedImageDatastore(inputSize(1:2),imdsValidation);After applying transfer learning using these images, our network was able to effectively identify cards for all test images.
Identify hands and make betsAfter identifying individual cards, the Pi poker player is ready to identify hands. The flowchart in Figure 4 describes how this is implemented. Let’s use the example hands in the table below to better understand this flowchart. Note that the final 5 cards in each hand are community cards that are shared among all players.
|Hand||Player 1||Player 2||Player 3|
|Unique cards||4C, 4D||6D, JC||JH, JS|
|Shared community cards||8S, 4H, 9S, TS, QS|
|Step 1. Separate into Rank and Suit|
|Step 2. Sort both rank and suit|
|Step 3. Difference between subsequent ranks and suits|
|Diff in sorted rank||004111||221111||411101|
|Diff in sorted suit (binary)||111000||111000||010000|
|Step 4. Count and decide|
|# of zeros in rank (from Step 3) 0 = Highcard 1 = Pair 2 (in sequence) = 3 of a kind 2 (not in sequence) = 2 pair 3 (in sequence) = 4 of a kind >=3 (with 2 in sequence) = Full house||Count: 2 (in sequence) 3 of a kind||Count: 0 Highcard||Count: 1 Pair|
|# of ones in rank (from Step 3) <4 = Not a straight >=4 = Straight||Count: 3 Not a straight||Count: 4 Straight||Count: 4 Straight|
|# of zeros in suit (from Step 3) <4 = Not any flush >=4 = Flush||Count: 3 Not a flush||Count: 3 Not a flush||Count: 5 Flush|
|Hand||3 of a kind||Straight||Straight flush|
Embed algorithm onto Raspberry PiWe generate C++ code from our MATLAB algorithms and embed it onto the Raspberry Pi using MATLAB Coder. We wrote a function main_poker_player.cpp that serves as the main script of the executable, where we specify which functions to generate code for. The generated code and executable (including modified AlexNet and betting logic algorithm) are downloaded directly to the Pi for standalone execution – we just need to plug in a camera and run the .exe file. To see this workflow in more detail and find additional resources, refer to the video here.
ConclusionWith that we have seen how to build a Raspberry Pi Poker Player in MATLAB. While this is a good start there are certainly areas for improvement, and I encourage you to modify the code to make this even better. For example, you could try to build a more sophisticated betting algorithm that is able to escalate bets or even bluff. The code is available on FileExchange and GitHub. Share your comments below and ideas on how to improve the poker player! To request help with Raspberry Pi and MATLAB for your specific application, contact firstname.lastname@example.org.
To leave a comment, please click here to sign in to your MathWorks Account or create a new one.