We all know the game ‘rock paper scissors’ that we used to play. I chose to recreate this game in an innovant way. Recently we have been talking a lot about AI, so I decided to create a rock paper scissors using your cam, your real hand and IA to play the game.
How I did it
First of all I created a file called ‘module_trace.py’ to initialize the hand detector AI. It is on the main file ‘jeu.py’ that the fun really is (there will be some variables with French name because it is my native language and the context in witch I code the game required it). The game start a really simple GUI interface on witch you can start playing. You are against the computer (a really simple algorithm to chose randomly between the three choices)
Here is the code of the module to detect the hands :
import cv2
import mediapipe as mp
import time
# création d'une classe pour detecter une main avec pour parametres : self, mode=False, maxHands=2,
#detectionCon=0.5,modelComplexity=1,trackCon=0.5
class handDetector():
"""Constructeur de l'objet """
def __init__(self, mode=False, maxHands=2, detectionCon=0.5,modelComplexity=1,trackCon=0.5):
self.mode = mode
self.maxHands = maxHands
self.detectionCon = detectionCon
self.modelComplex = modelComplexity
self.trackCon = trackCon
self.mpHands = mp.solutions.hands
self.hands = self.mpHands.Hands(self.mode, self.maxHands,self.modelComplex,
self.detectionCon, self.trackCon)
self.mpDraw = mp.solutions.drawing_utils #Fonction pour dessiner les points reliant les mains
def findHands(self,img,draw=True):
"""
Trouve les mains, crée les points puis les relei. C'est la structure
des mains
"""
# envoie une image en rbg
imgRGB = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
self.results = self.hands.process(imgRGB)
if self.results.multi_hand_landmarks:
for handLms in self.results.multi_hand_landmarks:
if draw:
#crée les points et les relie
self.mpDraw.draw_landmarks(img,handLms,
self.mpHands.HAND_CONNECTIONS)
return img
def findPosition(self,img, handNo=0, draw=True):
"""
Répertorie la position/le type de points de repère que nous donnons
dans la liste et dans la liste où nous avons stocké le type et la
position des points de repère. La liste a toutes les positions lm
"""
lmlist = []
# vérifier si landmarks est détecter
if self.results.multi_hand_landmarks:
#savoir de quelle main il est question
myHand = self.results.multi_hand_landmarks[handNo]
# avoir les info
for id, lm in enumerate(myHand.landmark):
# hauteur largeur
h,w,c = img.shape
#trouver la position
cx,cy = int(lm.x*w), int(lm.y*h)
lmlist.append([id,cx,cy])
# Dessiner les points
#if draw:
#cv2.circle(img,(cx,cy), 15 , (255,0,255), cv2.FILLED)
return lmlist
#Fonction maine, code princiaple
def main():
#Frame rates (fps)
pTime = 0
cTime = 0
#entrée numéros 0, port de la caméra utilisée
cap = cv2.VideoCapture(0)
detector = handDetector()
#boucle de pour la vidéo
while True:
#traiter l'image + liste pour les coordonée
success,img = cap.read()
img = detector.findHands(img)
lmList = detector.findPosition(img)
if len(lmList) != 0:
print(lmList[4])
#création des fps
cTime = time.time()
fps = 1/(cTime-pTime)
pTime = cTime
#mettre les fps sur l'ecran
cv2.putText(img,str(int(fps)),(10,70), cv2.FONT_HERSHEY_PLAIN,3,(255,0,255),3)
cv2.imshow("Video",img) #afficher la cam grace a cv2
if cv2.waitKey(1) == ord('q'): #appuyter sur 'q' pour quitter la cam
break
#dès que le fichier est appeler main se lace automatiquement
if __name__ == "__main__":
main()
Here is the code for the game 😀:
import cv2
import time
import os
import module_trace as htm
import tkinter as tk
from tkinter import *
import random
from PIL import Image, ImageTk
import time
############################################################## FUNCTION : USER INUPUT ##############################################################
def video():
"""
Using module 'module_trace.py' to:
- Use the webcam to create video (image list)
- Link a chosen pattern with the hand to 'rock, papper, scissors'
- Print an image on the screen of a sheet, stone, or scissors to help the user
"""
global cap
cap = cv2.VideoCapture(0)
global choix
choix = ""
# load images and put them in a list to use them later
fichier = "image"
myList = os.listdir(fichier)
overlayList = []
for im in myList:
image = cv2.imread(f'{fichier}/{im}')
overlayList.append(image)
overlayList[0] = overlayList[3] #bug in the loading so we change manually the parameters
print(overlayList[0])
pTime = 0
detector = htm.handDetector() #calling the module
tipIds = [4, 8, 12, 16, 20] #name the id for the special part of the finger (see documenation of mediapipe)
#main loop to run the game
while True :
success, img = cap.read()
img = detector.findHands(img)
lmList = detector.findPosition(img , draw=False)
if len(lmList) != 0: # using mediapipe and the tipIds we are cheking what is the choice of the user
if lmList[8][2] > lmList[6][2] and lmList[16][2] > lmList[14][2] and lmList[20][2] > lmList[18][2] and lmList[20][2] > lmList[18][2] :
choix = "pierre"
img[0: 146, 0:200] = overlayList[2]
if lmList[8][2] < lmList[6][2] and lmList[20][2] > lmList[18][2] and lmList[12][2] < lmList[10][2] :
choix = "ciseaux"
img[0: 112, 0:192] = overlayList[0]
if lmList[12][2] < lmList[10][2] and lmList[8][2] < lmList[6][2] and lmList[20][2] < lmList[18][2]:
choix = "feuille"
img[0: 150 , 0: 200] = overlayList[1]
cTime = time.time()
fps = 1/(cTime - pTime)
pTime = cTime
cv2.putText(img, "appuyez sur 'q' pour valider", (400,700), cv2.FONT_HERSHEY_PLAIN, 3, (255, 0, 255), 3 )
cv2.imshow("Image", img)
if cv2.waitKey(1) == ord('q'): # set an exit key
cv2.destroyAllWindows() #fclsoe the game
cv2.waitKey(1)
trouver_vainqueur() # check the winner (between us an the computer)
break
def c():
"""
set the game winner using trouver_vainqueur()
"""
global result2
result2 = ""
if gagner == 1:
result2 = "gagné"
elif gagner == 2:
result2 = "egalité"
elif gagner == 3 :
result2 = "perdu"
return result2
def trouver_vainqueur():
"""
first using a simple algorithm we are setting the choice of the computer and we retrun the winner
"""
global gagner
global result
global result2
global choix_ordi
gagner = 0
choix_ordi = ""
list_choix = ["pierre", "feuille", "ciseaux"]
a = random.randint(0,2)
choix_ordi = list_choix[a] # random choice for the computer
if choix == "pierre" and choix_ordi == "ciseaux":
gagner = 1
if choix == "ciseaux" and choix_ordi == "feuille":
gagner = 1
if choix == "feuille" and choix_ordi == "pierre":
gagner = 1
if choix == "pierre" and choix_ordi == "pierre":
gagner = 2
if choix == "feuille" and choix_ordi == "feuille":
gagner = 2
if choix == "ciseaux" and choix_ordi == "ciseaux":
gagner = 2
if choix == "pierre" and choix_ordi == "feuille":
gagner = 3
if choix == "feuille" and choix_ordi == "ciseaux":
gagner = 3
if choix == "ciseaux" and choix_ordi == "pierre":
gagner = 3
c() # finishing the game
f_print() # using the function tu print on the screen using t kinter
return choix, choix_ordi, result2
############################################################## GUI in TKINTER ##############################################################
def f_print():
"""print the result on the screen"""
if gagner == 1:
texte_r=Label(root,text = result2, font='Times 18 bold',fg='green')
texte_r.grid(row = 11, column = 5, columnspan= 2)
elif gagner == 2:
texte_r=Label(root,text = result2, font='Times 18 bold',fg='black')
texte_r.grid(row = 11, column = 5, columnspan= 2)
elif gagner == 3:
texte_r=Label(root,text = result2, font='Times 18 bold',fg='red')
texte_r.grid(row = 11, column = 5, columnspan= 2)
texte5=Label(root,text= choix_ordi ,font='Times 18 bold ',fg='blue')
texte5.grid(row =9, column =8)
texte4=Label(root,text= choix ,font='Times 18 bold ',fg='blue')
texte4.grid(row =9, column =2)
def f ():
""" create the window and the GUI of the game """
global root
root = Tk()
root.title('PFC')
#root.iconbitmap('/Users/piedroleyssieux/Desktop/projet_pfc/main_pierre.jpg')
root.geometry('500x400')
root.configure(bg='black')
texte1=Label(root,text='PFC',font='Times 22 bold')
texte1.grid(row =0, column =5, columnspan= 4)
space4= Label(root, text="", bg = "skyBlue1" )
space4.grid(row = 1, column=6)
texte6=Label(root,text='choissir entre : ',font='Times 18 bold ',fg='blue')
texte6.grid(row =2, column = 0, columnspan = 3)
img_pierre = ImageTk.PhotoImage(Image.open("main_pierre.jpg").resize((70,70)))
img_pierre_display = Label(image = img_pierre)
img_pierre_display.grid(row= 3, column=5)
texte7=Label(root,text='Pierre ',font='Times 18 bold ',fg='blue')
texte7.grid(row =4, column = 5)
img_ciseaux = ImageTk.PhotoImage(Image.open("main_ciseaux.jpg").resize((70,70)))
img_ciseaux_display = Label(image = img_ciseaux)
img_ciseaux_display.grid(row= 3, column=6)
texte7=Label(root,text='Ciseaux ',font='Times 18 bold ',fg='blue')
texte7.grid(row =4, column = 6)
img_feuille = ImageTk.PhotoImage(Image.open("main_feuille.jpg").resize((70,70)))
img_feuille_display = Label(image = img_feuille)
img_feuille_display.grid(row= 3, column=7)
texte7=Label(root,text='Feuille ',font='Times 18 bold ',fg='blue')
texte7.grid(row =4, column = 7)
space4= Label(root, text="", bg = "skyBlue1" )
space4.grid(row = 8, column=6, columnspan=2)
texte2=Label(root,text='Toi : ',font='Times 18 bold italic',fg='blue')
texte2.grid(row =9, column =1)
texte3=Label(root,text='Ordi : ',font='Times 18 bold italic',fg='blue')
texte3.grid(row =9, column =7)
space4= Label(root, text="", bg = "skyBlue1" )
space4.grid(row = 9, column=6, columnspan=2)
space5= Label(root, text="", bg = "skyBlue1" )
space5.grid(row = 10, column=6, columnspan=2)
space7= Label(root, text="", bg = "skyBlue1" )
space7.grid(row = 12, column=6, columnspan=2)
b = tk.Button(text ="jouer !",font='Times 18 bold ',fg='blue', command = video)
b.grid(row = 13, column =6, columnspan= 2)
root.mainloop()
f() # start the game
Note that I have been using tutorials, especially from Murtaza’s Workshop and the Google’s IA Mediapipe.
If you want to use easily the modules save the following text file as *.yml and upload it to a Conda virtual env.
name: RPC_game
channels:
- conda-forge
- defaults
dependencies:
- blas=1.0=openblas
- brotli=1.0.9=h1a28f6b_7
- brotli-bin=1.0.9=h1a28f6b_7
- bzip2=1.0.8=h3422bc3_4
- ca-certificates=2023.01.10=hca03da5_0
- contourpy=1.0.5=py39h525c30c_0
- cycler=0.11.0=pyhd3eb1b0_0
- fonttools=4.25.0=pyhd3eb1b0_0
- freetype=2.12.1=h1192e45_0
- giflib=5.2.1=h80987f9_3
- importlib_resources=5.2.0=pyhd3eb1b0_1
- jpeg=9e=h80987f9_1
- kiwisolver=1.4.4=py39h313beb8_0
- lcms2=2.12=hba8e193_0
- lerc=3.0=hc377ac9_0
- libbrotlicommon=1.0.9=h1a28f6b_7
- libbrotlidec=1.0.9=h1a28f6b_7
- libbrotlienc=1.0.9=h1a28f6b_7
- libcxx=14.0.6=h848a8c0_0
- libdeflate=1.17=h80987f9_0
- libffi=3.4.2=h3422bc3_5
- libgfortran=5.0.0=11_3_0_hca03da5_28
- libgfortran5=11.3.0=h009349e_28
- libopenblas=0.3.21=h269037a_0
- libpng=1.6.39=h80987f9_0
- libtiff=4.5.0=h313beb8_2
- libwebp=1.2.4=ha3663a8_1
- libwebp-base=1.2.4=h80987f9_1
- llvm-openmp=14.0.6=hc6e5704_0
- lz4-c=1.9.4=h313beb8_0
- matplotlib=3.7.1=py39hca03da5_1
- matplotlib-base=3.7.1=py39h78102c4_1
- munkres=1.1.4=py_0
- ncurses=6.3=h07bb92c_1
- numpy-base=1.23.5=py39h90707a3_0
- openssl=1.1.1t=h1a28f6b_0
- packaging=23.0=py39hca03da5_0
- pip=23.0.1=pyhd8ed1ab_0
- pyparsing=3.0.9=py39hca03da5_0
- python=3.9.16=hc0d8a6c_0
- python-dateutil=2.8.2=pyhd3eb1b0_0
- readline=8.2=h92ec313_1
- setuptools=67.6.0=pyhd8ed1ab_0
- six=1.16.0=pyhd3eb1b0_1
- sqlite=3.40.1=h7a7dc30_0
- tk=8.6.12=hb8d0fd4_0
- tornado=6.2=py39h1a28f6b_0
- wheel=0.40.0=pyhd8ed1ab_0
- xz=5.2.10=h80987f9_1
- zipp=3.11.0=py39hca03da5_0
- zlib=1.2.13=h5a0b063_0
- zstd=1.5.2=h8574219_0
- pip:
- absl-py==1.4.0
- attrs==23.1.0
- certifi==2022.12.7
- cffi==1.15.1
- charset-normalizer==3.1.0
- filelock==3.10.2
- flatbuffers==23.5.26
- idna==3.4
- jinja2==3.1.2
- joblib==1.2.0
- markupsafe==2.1.2
- mediapipe==0.10.1
- mpmath==1.3.0
- networkx==3.0
- numpy==1.24.2
- opencv-contrib-python==4.8.0.74
- opencv-python==4.8.0.74
- pandas==2.0.1
- pillow==9.4.0
- protobuf==3.20.3
- pycparser==2.21
- pytz==2023.3
- requests==2.28.2
- scikit-learn==1.2.2
- scipy==1.10.1
- sounddevice==0.4.6
- sympy==1.12rc1
- threadpoolctl==3.1.0
- torch==2.1.0.dev20230323
- torchaudio==2.1.0.dev20230323
- torchsummary==1.5.1
- torchvision==0.16.0.dev20230323
- typing-extensions==4.5.0
- tzdata==2023.3
- urllib3==1.26.15
variables:
CONDA_SUBDIR: osx-arm64
prefix: /Users/piedroleyssieux/miniconda3/envs/testTorch
No responses yet