Today, I’d like to introduce Dan Doherty from technical marketing to talk about using Deep Learning with Raspberry Pi using MATLAB.
A few members of our development team are self-proclaimed Raspberry Pi and Texas hold'em nerds, so we decided to build a Raspberry Pi Poker Player to bring to games to help make smart bets.
Here is a video of it in action. The poker player performs 3 main steps: (1) identify cards, (2) identify hands, and (3) make bets. I will show how you can use MATLAB to program these steps and then deploy the algorithm onto a Raspberry Pi.
Hardware Used
- 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.
Fig 1: Hardware used in project; shown is Raspberry Pi 3B+
*Keeping performance issues in mind, it's recommended to use RPi 3B+ and higher
Identify Cards
Fig 2: Webcam images are used as the input to the neural network that identifies cards
The 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.
Fig 3: Collect data for transfer learning
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
NumImages = 200; % Max = 1000
suits = ['D' 'C' 'H' 'S'];
rank = ['A' '2' '3' '4' '5' '6' '7' '8' '9' 'T' 'J' 'Q' 'K'];
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
...
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 bets
After identifying individual cards, the Pi poker player is ready to identify hands. The flowchart in Figure 4 describes how this is implemented.
Fig 4: Flowchart to identify hands
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.
Example hands |
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 |
Rank |
44849TQ |
6J849TQ |
JJ849TQ |
Suit |
CDSHSSS |
DCSHSSS |
HSSHSSS |
Step 2. Sort both rank and suit |
Sorted rank |
44489TQ |
4689TJQ |
489TJJQ |
Sorted suit |
CDHSSSS |
CDHSSSS |
HHSSSSS |
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 |
After identifying cards and hands, the (Pi) Poker Player is ready to place bets. The betting algorithm is very simple and based solely on the identified hand. It starts at 1 USD for a Highcard and increases by 1 USD for each subsequent
ranked hand up to 10 USD for a Royal flush. Of course, if you are working on this algorithm, you can rework the algorithm to be more (or less) aggressive depending on how you’re feeling.
Embed algorithm onto Raspberry Pi
We 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.
Fig 5: Generate code and deploy to the Raspberry Pi using MATLAB Coder
To see this workflow in more detail and find additional resources, refer to the video
here.
Conclusion
With 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 raspberrypi@mathworks.com.
Comments
To leave a comment, please click here to sign in to your MathWorks Account or create a new one.