├── .gitignore
├── LICENSE
├── README.md
├── SnakeGame.py
├── game-control-using-object-tracking-multithreaded.py
├── game-control-using-object-tracking.py
├── images
├── Game-Play.png
└── SnakeGameWelcomeScreen.png
├── object-detection.py
├── object-tracking-direction-detection.py
├── requirements.txt
├── settingsSnakeFun.py
└── sounds
├── appleEatSound.wav
└── bgmusic.mid
/.gitignore:
--------------------------------------------------------------------------------
1 | #Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 |
5 | #PyInstaller
6 | *.manifest
7 | *.spec
8 | build/
9 | dist/
10 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Mohit Singh
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Gesture Controlled Snake Game
2 | This program can be used to play a Snake Game (or like it) by detecting hand gestures to control movement of the snake on the screen. It has been implemented in Python using OpenCV library. Although, the demo shown here refers to playing a Snake Game, it can be used to play any game or control any application using hand gestures only.
3 | This program shall be used to play a Snake Game by detecting hand gestures to control movement of the snake on the screen. It has been implemented in Python using OpenCV library. Although, the demo shown here refers to playing a Snake Game, it can be used to play any game or control any application using hand gestures only.
4 |
5 |
6 |
7 | Watch on Youtube
8 |
9 | [Gesture Controlled Snake Game Using OpenCV and Python](http://www.youtube.com/watch?v=PE_rgc2K0sg)
10 |
11 | ## Getting Started
12 | ### Prerequisites
13 | The program depends on the following libraries-
14 |
15 | numpy==1.15.2
16 | imutils==0.5.1
17 | PyAutoGUI==0.9.38
18 | opencv_python==3.4.3.18
19 | pygame==1.9.4
20 |
21 | Install the libraries using `pip install -r requirements.txt`
22 |
23 | ### Installing
24 |
25 | 1. Clone the repository in your local computer.
26 | 2. Use `python ` to run specific files, which are described below.
27 |
28 | ## Built With
29 |
30 | - [OpenCV](https://opencv.org/) - The Open Computer Vision Library
31 | - [PyAutoGUI](https://pypi.org/project/PyAutoGUI/) - Cross platform GUI Automation Python Module
32 |
33 | ## Contributing
34 | Please feel free to contribute to the project and Pull Requests are open for everyone willing to improve upon the project. Feel free to provide any suggestions.
35 |
36 | ## License
37 | This project is licensed under the MIT License - see the [LICENSE.md](https://github.com/mohitwildbeast/Gesture-Controlled-Snake-Game/blob/master/LICENSE) file for details.
38 | ## Acknowledgements
39 |
40 | - The inspiration for the project came through PyImageSearch blog's article- [OpenCV Tracking Object in Images](https://www.pyimagesearch.com/2015/09/21/opencv-track-object-movement/) blog post.
41 | - PyAutoGUI helped a lot for keyboard automation tasks.
42 | ## File Contents
43 | The entire project has been made using a bottom up approach, and the project consists of the following files which are described below-
44 |
45 | ## [Object Detection](https://github.com/mohitwildbeast/Gesture-Controlled-Snake-Game/blob/master/object-detection.py)
46 |
47 | > This script detects a object of specified object colour from the webcam video feed. Using OpenCV library for vision tasks and HSV color space for detecting object of given specific color.
48 | >See the demo on Youtube - [ Object Detection and Motion Tracking in OpenCV](https://www.youtube.com/watch?v=mtGBuMlusXQ)
49 |
50 |
51 |
52 | ## [Object Tracking and Direction Detection](https://github.com/mohitwildbeast/Gesture-Controlled-Snake-Game/blob/master/object-tracking-direction-detection.py)
53 |
54 | > This script can detect objects specified by the HSV color and also sense
55 | direction of their movement.
56 |
57 | > See the demo on Youtube - [Object Tracking and Direction Detection using OpenCV](https://www.youtube.com/watch?v=zapq9QT9uwc)
58 |
59 | ## [Game Control Using Object Tracking (Multithreaded Implementation)](https://github.com/mohitwildbeast/Gesture-Controlled-Snake-Game/blob/master/game-control-using-object-tracking-multithreaded.py)
60 | > This script can detect objects specified by the HSV color and also sense the
61 | direction of their movement.Using this script a Snake Game which has been loaded in the repo, can be played. Implemented using OpenCV. Uses seperate thread for reading frames through OpenCV.
62 |
63 | >See the demo on Youtube - [Gesture Controlled Snake Game Playing with OpenCV and Computer Vision](https://www.youtube.com/watch?v=PE_rgc2K0sg)
64 |
65 | ## Snake Game
66 | >The Snake Game present in this video, has been taken from my previous repository [SnakeFun](https://github.com/mohitwildbeast/SnakeFun). The files from the game corrrespond to SnakeFun.py and settingsSnakeFun.py
67 |
68 | >Run the game using the code ```python SnakeFun.py```
69 |
70 |
71 |
--------------------------------------------------------------------------------
/SnakeGame.py:
--------------------------------------------------------------------------------
1 | import random
2 | import pygame
3 | import sys
4 | from pygame.locals import *
5 | from settingsSnakeFun import *
6 |
7 | def main():
8 | global CLOCK, SCREEN, FONT
9 |
10 | pygame.init()
11 | CLOCK = pygame.time.Clock()
12 | SCREEN = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
13 | FONT = pygame.font.Font('freesansbold.ttf', 18)
14 | pygame.display.set_caption('Snake Game')
15 |
16 | showStartScreen()
17 | while True:
18 | pygame.mixer.music.play(-1,0.0)
19 | runGame()
20 | pygame.mixer.music.stop()
21 | showGameOverScreen()
22 |
23 | def runGame():
24 | #Set a random starting point
25 | startx = random.randint(5, CELLWIDTH - 6)
26 | starty = random.randint(5, CELLHEIGHT - 6)
27 | global wormCoords
28 | wormCoords = [{'x' : startx, 'y' : starty}, {'x': startx - 1, 'y':starty}, {'x':startx - 2, 'y':starty}]
29 | direction = RIGHT
30 |
31 | apple = getRandomLocation()
32 |
33 | while True:
34 | for event in pygame.event.get():
35 | if event.type == QUIT:
36 | terminate()
37 | elif event.type == KEYDOWN:
38 | if (event.key == K_LEFT or event.key == K_a) and direction != RIGHT:
39 | direction = LEFT
40 | elif (event.key == K_RIGHT or event.key == K_d) and direction != LEFT:
41 | direction = RIGHT
42 | elif (event.key == K_UP or event.key == K_w) and direction != DOWN:
43 | direction = UP
44 | elif (event.key == K_DOWN or event.key == K_s) and direction != UP:
45 | direction = DOWN
46 | elif event.key == K_ESCAPE:
47 | terminate()
48 | #Collision Detection
49 | #Check Collision with edges
50 | if wormCoords[HEAD]['x'] == -1 or wormCoords[HEAD]['x'] == CELLWIDTH or wormCoords[HEAD]['y'] == -1 or wormCoords[HEAD]['y'] == CELLHEIGHT:
51 | return
52 | #Check Collision with snake's body
53 | for wormBody in wormCoords[1:]:
54 | if wormBody['x'] == wormCoords[HEAD]['x'] and wormBody['y'] == wormCoords[HEAD]['y']:
55 | return
56 | #Check Collision with Apple
57 | if wormCoords[HEAD]['x'] == apple['x'] and wormCoords[HEAD]['y'] == apple['y']:
58 | APPLEEATSOUND.play()
59 | apple = getRandomLocation()
60 | else:
61 | del wormCoords[-1]
62 |
63 | #Moving the Snake
64 | if direction == UP:
65 | newHead = {'x': wormCoords[HEAD]['x'], 'y': wormCoords[HEAD]['y'] - 1}
66 | elif direction == DOWN:
67 | newHead = {'x': wormCoords[HEAD]['x'], 'y': wormCoords[HEAD]['y'] + 1}
68 | elif direction == RIGHT:
69 | newHead = {'x': wormCoords[HEAD]['x'] + 1, 'y': wormCoords[HEAD]['y']}
70 | elif direction == LEFT:
71 | newHead = {'x': wormCoords[HEAD]['x'] - 1, 'y': wormCoords[HEAD]['y']}
72 | wormCoords.insert(0, newHead)
73 |
74 | #Drawing the Screen
75 | SCREEN.fill(BGCOLOR)
76 | drawGrid()
77 | drawWorm(wormCoords)
78 | drawApple(apple)
79 | drawScore((len(wormCoords) - 3) * 10)
80 | pygame.display.update()
81 | CLOCK.tick(FPS)
82 |
83 | def getTotalScore():
84 | return ((len(wormCoords) - 3) * 10)
85 |
86 | def drawPressKeyMsg():
87 | pressKeyText = FONT.render('Press A Key To Play', True, YELLOW)
88 | pressKeyRect = pressKeyText.get_rect()
89 | pressKeyRect.center = (WINDOWWIDTH - 200, WINDOWHEIGHT - 100)
90 | SCREEN.blit(pressKeyText, pressKeyRect)
91 |
92 | def drawSettingsMsg():
93 | SCREEN.blit(SETTINGSBUTTON, (WINDOWWIDTH - SETTINGSBUTTON.get_width(), WINDOWHEIGHT - SETTINGSBUTTON.get_height()))
94 |
95 | def checkForKeyPress():
96 | if len(pygame.event.get(QUIT)) > 0:
97 | terminate()
98 |
99 | keyUpEvents = pygame.event.get(KEYUP)
100 | if len(keyUpEvents) == 0:
101 | return None
102 | if keyUpEvents[0].key == K_ESCAPE:
103 | terminate()
104 | return keyUpEvents[0].key
105 |
106 | def showStartScreen():
107 | titlefont = pygame.font.Font('freesansbold.ttf', 100)
108 | titleText = titlefont.render('SNAKE FUN', True, DARKGREEN)
109 | while True:
110 | SCREEN.fill(BGCOLOR)
111 | titleTextRect = titleText.get_rect()
112 | titleTextRect.center = (WINDOWWIDTH / 2, WINDOWHEIGHT / 2)
113 | SCREEN.blit(titleText, titleTextRect)
114 |
115 | drawPressKeyMsg()
116 | if checkForKeyPress():
117 | pygame.event.get()
118 | return
119 | pygame.display.update()
120 | CLOCK.tick(FPS)
121 |
122 | def terminate():
123 | pygame.quit()
124 | sys.exit()
125 |
126 | def getRandomLocation():
127 | return {'x': random.randint(0, CELLWIDTH - 1), 'y': random.randint(0, CELLHEIGHT - 1)}
128 |
129 | def showGameOverScreen():
130 | gameOverFont = pygame.font.Font('freesansbold.ttf', 100)
131 | gameOverText = gameOverFont.render('Game Over', True, WHITE)
132 | gameOverRect = gameOverText.get_rect()
133 | totalscoreFont = pygame.font.Font('freesansbold.ttf', 40)
134 | totalscoreText = totalscoreFont.render('Total Score: %s' % (getTotalScore()), True, WHITE)
135 | totalscoreRect = totalscoreText.get_rect()
136 | totalscoreRect.midtop = (WINDOWWIDTH/2, 150)
137 | gameOverRect.midtop = (WINDOWWIDTH/2, 30)
138 | SCREEN.fill(BGCOLOR)
139 | SCREEN.blit(gameOverText, gameOverRect)
140 | SCREEN.blit(totalscoreText, totalscoreRect)
141 | drawPressKeyMsg()
142 | pygame.display.update()
143 | pygame.time.wait(1000)
144 | checkForKeyPress()
145 |
146 | while True:
147 | if checkForKeyPress():
148 | pygame.event.get()
149 | return
150 |
151 | def drawScore(score):
152 | scoreText = FONT.render('Score: %s' % (score), True, WHITE)
153 | scoreRect = scoreText.get_rect()
154 | scoreRect.center = (WINDOWWIDTH - 100, 30)
155 | SCREEN.blit(scoreText, scoreRect)
156 |
157 | def drawWorm(wormCoords):
158 | x = wormCoords[HEAD]['x'] * CELLSIZE
159 | y = wormCoords[HEAD]['y'] * CELLSIZE
160 | wormHeadRect = pygame.Rect(x, y, CELLSIZE, CELLSIZE)
161 | pygame.draw.rect(SCREEN, YELLOW, wormHeadRect)
162 |
163 | for coord in wormCoords[1:]:
164 | x = coord['x'] * CELLSIZE
165 | y = coord['y'] * CELLSIZE
166 | wormSegmentRect = pygame.Rect(x, y, CELLSIZE, CELLSIZE)
167 | pygame.draw.rect(SCREEN, GREEN, wormSegmentRect)
168 |
169 | def drawApple(coord):
170 | x = coord['x'] * CELLSIZE
171 | y = coord['y'] * CELLSIZE
172 | appleRect = pygame.Rect(x, y, CELLSIZE, CELLSIZE)
173 | pygame.draw.rect(SCREEN, RED, appleRect)
174 |
175 | def drawGrid():
176 | for x in range(0, WINDOWWIDTH, CELLSIZE):
177 | pygame.draw.line(SCREEN, DARKGRAY, (x, 0), (x, WINDOWHEIGHT))
178 | for y in range(0, WINDOWHEIGHT, CELLSIZE):
179 | pygame.draw.line(SCREEN, DARKGRAY, (0, y), (WINDOWWIDTH, y))
180 |
181 | if __name__ == '__main__':
182 | main()
183 |
--------------------------------------------------------------------------------
/game-control-using-object-tracking-multithreaded.py:
--------------------------------------------------------------------------------
1 | '''This script can detect objects specified by the HSV color and also sense the
2 | direction of their movement.Using this script a Snake Game which has been loaed
3 | in the repo, can be played. Implemented using OpenCV.
4 | Uses seperate thread for reading frames through OpenCV.'''
5 |
6 | #Import necessary modules
7 | import cv2
8 | import imutils
9 | import numpy as np
10 | from collections import deque
11 | import time
12 | import pyautogui
13 | from threading import Thread
14 |
15 | #Class implemeting seperate threading for reading of frames.
16 | class WebcamVideoStream:
17 | def __init__(self):
18 | self.stream = cv2.VideoCapture(0)
19 | self.ret, self.frame = self.stream.read()
20 | self.stopped = False
21 | def start(self):
22 | Thread(target = self.update, args=()).start()
23 | return self
24 | def update(self):
25 | while True:
26 | if self.stopped:
27 | return
28 | self.ret, self.frame = self.stream.read()
29 | def read(self):
30 | return self.frame
31 | def stop(self):
32 | self.stopped = True
33 |
34 | """class VideoShow:
35 | def __init__(self, frame = None):
36 | self.frame = frame
37 | self.stopped = False
38 | def start(self):
39 | while not self.stopped:
40 | cv2.imshow('Game Control Window', self.frame)
41 | if(cv2.waitKey(1) == ord('q')):
42 | self.stopped = True
43 | def stop(self):
44 | self.stopped = True
45 | """
46 | #Define HSV colour range for green colour objects
47 | greenLower = (29, 86, 6)
48 | greenUpper = (64, 255, 255)
49 |
50 | #Used in deque structure to store no. of given buffer points
51 | buffer = 20
52 |
53 | #Used so that pyautogui doesn't click the center of the screen at every frame
54 | flag = 0
55 |
56 | #Points deque structure storing 'buffer' no. of object coordinates
57 | pts = deque(maxlen = buffer)
58 | #Counts the minimum no. of frames to be detected where direction change occurs
59 | counter = 0
60 | #Change in direction is stored in dX, dY
61 | (dX, dY) = (0, 0)
62 | #Variable to store direction string
63 | direction = ''
64 | #Last pressed variable to detect which key was pressed by pyautogui
65 | last_pressed = ''
66 |
67 | #Sleep for 2 seconds to let camera initialize properly.
68 | time.sleep(2)
69 |
70 | #Use pyautogui function to detect width and height of the screen
71 | width,height = pyautogui.size()
72 |
73 | #Start video capture in a seperate thread from main thread.
74 | vs = WebcamVideoStream().start()
75 | #video_shower = VideoShow(vs.read()).start()
76 |
77 | #Click on the centre of the screen, game window should be placed here.
78 | pyautogui.click(int(width/2), int(height/2))
79 |
80 | while True:
81 |
82 | '''game_window = pyautogui.locateOnScreen(r'images\SnakeGameWelcomeScreen.png')
83 | game_window_center = pyautogui.center(game_window)
84 | pyautogui.click(game_window_center)'''
85 |
86 | #Store the readed frame in frame
87 | frame = vs.read()
88 | #Flip the frame to avoid mirroring effect
89 | frame = cv2.flip(frame,1)
90 | #Resize the given frame to a 600*600 window
91 | frame = imutils.resize(frame, width = 600)
92 | #Blur the frame using Gaussian Filter of kernel size 11, to remove excessivve noise
93 | blurred_frame = cv2.GaussianBlur(frame, (5,5), 0)
94 | #Convert the frame to HSV, as HSV allow better segmentation.
95 | hsv_converted_frame = cv2.cvtColor(blurred_frame, cv2.COLOR_BGR2HSV)
96 |
97 | #Create a mask for the frame, showing green values
98 | mask = cv2.inRange(hsv_converted_frame, greenLower, greenUpper)
99 | #Erode the masked output to delete small white dots present in the masked image
100 | mask = cv2.erode(mask, None, iterations = 2)
101 | #Dilate the resultant image to restore our target
102 | mask = cv2.dilate(mask, None, iterations = 2)
103 |
104 | #Find all contours in the masked image
105 | _,cnts,_ = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
106 |
107 | #Define center of the ball to be detected as None
108 | center = None
109 |
110 | #If any object is detected, then only proceed
111 | if(len(cnts) > 0):
112 | #Find the contour with maximum area
113 | c = max(cnts, key = cv2.contourArea)
114 | #Find the center of the circle, and its radius of the largest detected contour.
115 | ((x, y), radius) = cv2.minEnclosingCircle(c)
116 | #Calculate the centroid of the ball, as we need to draw a circle around it.
117 | M = cv2.moments(c)
118 | center = (int(M['m10'] / M['m00']), int(M['m01'] / M['m00']))
119 |
120 | #Proceed only if a ball of considerable size is detected
121 | if radius > 10:
122 | #Draw circles around the object as well as its centre
123 | cv2.circle(frame, (int(x), int(y)), int(radius), (0,255,255), 2)
124 | cv2.circle(frame, center, 5, (0,255,255), -1)
125 | #Append the detected object in the frame to pts deque structure
126 | pts.appendleft(center)
127 |
128 | #Using numpy arange function for better performance. Loop till all detected points
129 | for i in np.arange(1, len(pts)):
130 | #If no points are detected, move on.
131 | if(pts[i-1] == None or pts[i] == None):
132 | continue
133 |
134 | #If atleast 10 frames have direction change, proceed
135 | if counter >= 10 and i == 1 and pts[-10] is not None:
136 | #Calculate the distance between the current frame and 10th frame before
137 | dX = pts[-10][0] - pts[i][0]
138 | dY = pts[-10][1] - pts[i][1]
139 | (dirX, dirY) = ('', '')
140 |
141 | #If distance is greater than 50 pixels, considerable direction change has occured.
142 | if np.abs(dX) > 50:
143 | dirX = 'West' if np.sign(dX) == 1 else 'East'
144 |
145 | if np.abs(dY) > 50:
146 | dirY = 'North' if np.sign(dY) == 1 else 'South'
147 |
148 | #Set direction variable to the detected direction
149 | direction = dirX if dirX != '' else dirY
150 | #Write the detected direction on the frame.
151 | cv2.putText(frame, direction, (20,40), cv2.FONT_HERSHEY_SIMPLEX, 1, (0,0,255), 3)
152 |
153 | #Draw a trailing red line to depict motion of the object.
154 | thickness = int(np.sqrt(buffer / float(i + 1)) * 2.5)
155 | cv2.line(frame, pts[i - 1], pts[i], (0, 0, 255), thickness)
156 |
157 | #If deteced direction is East, press right button
158 | if direction == 'East':
159 | if last_pressed != 'right':
160 | pyautogui.press('right')
161 | last_pressed = 'right'
162 | print("Right Pressed")
163 | #pyautogui.PAUSE = 2
164 | #If deteced direction is West, press Left button
165 | elif direction == 'West':
166 | if last_pressed != 'left':
167 | pyautogui.press('left')
168 | last_pressed = 'left'
169 | print("Left Pressed")
170 | #pyautogui.PAUSE = 2
171 | #if detected direction is North, press Up key
172 | elif direction == 'North':
173 | if last_pressed != 'up':
174 | last_pressed = 'up'
175 | pyautogui.press('up')
176 | print("Up Pressed")
177 | #pyautogui.PAUSE = 2
178 | #If detected direction is South, press down key
179 | elif direction == 'South':
180 | if last_pressed != 'down':
181 | pyautogui.press('down')
182 | last_pressed = 'down'
183 | print("Down Pressed")
184 | #pyautogui.PAUSE = 2
185 |
186 |
187 | #video_shower.frame = frame
188 | #Show the output frame.
189 | cv2.imshow('Game Control Window', frame)
190 | key = cv2.waitKey(1) & 0xFF
191 | #Update counter as the direction change has been detected.
192 | counter += 1
193 |
194 | #If pyautogui has not clicked on center, click it once to focus on game window.
195 | if (flag == 0):
196 | pyautogui.click(int(width/2), int(height/2))
197 | flag = 1
198 |
199 | #If q is pressed, close the window
200 | if(key == ord('q')):
201 | break
202 | #After all the processing, release webcam and destroy all windows
203 | vs.stop()
204 | cv2.destroyAllWindows()
205 |
--------------------------------------------------------------------------------
/game-control-using-object-tracking.py:
--------------------------------------------------------------------------------
1 | '''This script can detect objects specified by the HSV color and also sense the
2 | direction of their movement.Using this script a Snake Game which has been loaed
3 | in the repo, can be played. Implemented using OpenCV.'''
4 |
5 | #Import necessary modules
6 | import cv2
7 | import imutils
8 | import numpy as np
9 | from collections import deque
10 | import time
11 | import pyautogui
12 |
13 | #Define HSV colour range for green colour objects
14 | greenLower = (29, 86, 6)
15 | greenUpper = (64, 255, 255)
16 |
17 | #Used in deque structure to store no. of given buffer points
18 | buffer = 20
19 |
20 | #Used so that pyautogui doesn't click the center of the screen at every frame
21 | flag = 0
22 |
23 | #Points deque structure storing 'buffer' no. of object coordinates
24 | pts = deque(maxlen = buffer)
25 | #Counts the minimum no. of frames to be detected where direction change occurs
26 | counter = 0
27 | #Change in direction is stored in dX, dY
28 | (dX, dY) = (0, 0)
29 | #Variable to store direction string
30 | direction = ''
31 | #Last pressed variable to detect which key was pressed by pyautogui
32 | last_pressed = ''
33 |
34 | #Start video capture
35 | video_capture = cv2.VideoCapture(0)
36 |
37 | #Sleep for 2 seconds to let camera initialize properly.
38 | time.sleep(2)
39 |
40 | #Use pyautogui function to detect width and height of the screen
41 | width,height = pyautogui.size()
42 |
43 |
44 | #Loop until OpenCV window is not closed
45 | while True:
46 |
47 | '''game_window = pyautogui.locateOnScreen(r'images\SnakeGameWelcomeScreen.png')
48 | game_window_center = pyautogui.center(game_window)
49 | pyautogui.click(game_window_center)'''
50 |
51 |
52 | #Store the readed frame in frame, ret defines return value
53 | ret, frame = video_capture.read()
54 | #Flip the frame to avoid mirroring effect
55 | frame = cv2.flip(frame,1)
56 | #Resize the given frame to a 600*600 window
57 | frame = imutils.resize(frame, width = 600)
58 | #Blur the frame using Gaussian Filter of kernel size 11, to remove excessivve noise
59 | blurred_frame = cv2.GaussianBlur(frame, (11,11), 0)
60 | #Convert the frame to HSV, as HSV allow better segmentation.
61 | hsv_converted_frame = cv2.cvtColor(blurred_frame, cv2.COLOR_BGR2HSV)
62 |
63 | #Create a mask for the frame, showing green values
64 | mask = cv2.inRange(hsv_converted_frame, greenLower, greenUpper)
65 | #Erode the masked output to delete small white dots present in the masked image
66 | mask = cv2.erode(mask, None, iterations = 2)
67 | #Dilate the resultant image to restore our target
68 | mask = cv2.dilate(mask, None, iterations = 2)
69 |
70 | #Find all contours in the masked image
71 | _,cnts,_ = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
72 |
73 | #Define center of the ball to be detected as None
74 | center = None
75 |
76 | #If any object is detected, then only proceed
77 | if(len(cnts) > 0):
78 | #Find the contour with maximum area
79 | c = max(cnts, key = cv2.contourArea)
80 | #Find the center of the circle, and its radius of the largest detected contour.
81 | ((x, y), radius) = cv2.minEnclosingCircle(c)
82 | #Calculate the centroid of the ball, as we need to draw a circle around it.
83 | M = cv2.moments(c)
84 | center = (int(M['m10'] / M['m00']), int(M['m01'] / M['m00']))
85 |
86 | #Proceed only if a ball of considerable size is detected
87 | if radius > 10:
88 | #Draw circles around the object as well as its centre
89 | cv2.circle(frame, (int(x), int(y)), int(radius), (0,255,255), 2)
90 | cv2.circle(frame, center, 5, (0,255,255), -1)
91 | #Append the detected object in the frame to pts deque structure
92 | pts.appendleft(center)
93 |
94 | #Using numpy arange function for better performance. Loop till all detected points
95 | for i in np.arange(1, len(pts)):
96 | #If no points are detected, move on.
97 | if(pts[i-1] == None or pts[i] == None):
98 | continue
99 |
100 | #If atleast 10 frames have direction change, proceed
101 | if counter >= 10 and i == 1 and pts[-10] is not None:
102 | #Calculate the distance between the current frame and 10th frame before
103 | dX = pts[-10][0] - pts[i][0]
104 | dY = pts[-10][1] - pts[i][1]
105 | (dirX, dirY) = ('', '')
106 |
107 | #If distance is greater than 50 pixels, considerable direction change has occured.
108 | if np.abs(dX) > 50:
109 | dirX = 'West' if np.sign(dX) == 1 else 'East'
110 |
111 | if np.abs(dY) > 50:
112 | dirY = 'North' if np.sign(dY) == 1 else 'South'
113 |
114 | #Set direction variable to the detected direction
115 | direction = dirX if dirX != '' else dirY
116 |
117 | #Draw a trailing red line to depict motion of the object.
118 | thickness = int(np.sqrt(buffer / float(i + 1)) * 2.5)
119 | cv2.line(frame, pts[i - 1], pts[i], (0, 0, 255), thickness)
120 |
121 | #If deteced direction is East, press right button
122 | if direction == 'East':
123 | if last_pressed != 'right':
124 | pyautogui.press('right')
125 | last_pressed = 'right'
126 | print("Right Pressed")
127 | #pyautogui.PAUSE = 2
128 | #If deteced direction is West, press Left button
129 | elif direction == 'West':
130 | if last_pressed != 'left':
131 | pyautogui.press('left')
132 | last_pressed = 'left'
133 | print("Left Pressed")
134 | #pyautogui.PAUSE = 2
135 | #if detected direction is North, press Up key
136 | elif direction == 'North':
137 | if last_pressed != 'up':
138 | last_pressed = 'up'
139 | pyautogui.press('up')
140 | print("Up Pressed")
141 | #pyautogui.PAUSE = 2
142 | #If detected direction is South, press down key
143 | elif direction == 'South':
144 | if last_pressed != 'down':
145 | pyautogui.press('down')
146 | last_pressed = 'down'
147 | print("Down Pressed")
148 | #pyautogui.PAUSE = 2
149 |
150 |
151 | #Write the detected direction on the frame.
152 | cv2.putText(frame, direction, (20,40), cv2.FONT_HERSHEY_SIMPLEX, 1, (0,0,255), 3)
153 |
154 | #Show the output frame.
155 | cv2.imshow('Game Control Window', frame)
156 | key = cv2.waitKey(1) & 0xFF
157 | #Update counter as the direction change has been detected.
158 | counter += 1
159 |
160 | #If pyautogui has not clicked on center, click it once to focus on game window.
161 | if (flag == 0):
162 | #Click on the centre of the screen, game window should be placed here.
163 | pyautogui.click(int(width/2), int(height/2))
164 | flag = 1
165 |
166 | #If q is pressed, close the window
167 | if(key == ord('q')):
168 | break
169 | #After all the processing, release webcam and destroy all windows
170 | video_capture.release()
171 | cv2.destroyAllWindows()
172 |
--------------------------------------------------------------------------------
/images/Game-Play.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msindev/Gesture-Controlled-Snake-Game/0819c15d9c746fc4d423df6c1ae03ee12e6031c1/images/Game-Play.png
--------------------------------------------------------------------------------
/images/SnakeGameWelcomeScreen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msindev/Gesture-Controlled-Snake-Game/0819c15d9c746fc4d423df6c1ae03ee12e6031c1/images/SnakeGameWelcomeScreen.png
--------------------------------------------------------------------------------
/object-detection.py:
--------------------------------------------------------------------------------
1 | ''' This script detects a object of specified object colour from the webcam video feed.
2 | Using OpenCV library for vision tasks and HSV color space for detecting object of given specific color.'''
3 |
4 | #Import necessary modules
5 | import cv2
6 | import imutils
7 | import numpy as np
8 | from collections import deque
9 | import time
10 | import math
11 |
12 | #Define HSV colour range for green colour objects
13 | greenLower = (29, 86, 6)
14 | greenUpper = (64, 255, 255)
15 |
16 | #Used in deque structure to store no. of given buffer points
17 | buffer = 20
18 |
19 | #Points deque structure storing 'buffer' no. of object coordinates
20 | pts = deque(maxlen = buffer)
21 |
22 | #Start video capture
23 | video_capture = cv2.VideoCapture(0)
24 |
25 | #Sleep for 2 seconds to let camera initialize properly.
26 | time.sleep(2)
27 |
28 | #Loop until OpenCV window is not closed
29 | while True:
30 | #Store the readed frame in frame, ret defines return value
31 | ret, frame = video_capture.read()
32 | #Flip the frame to avoid mirroring effect
33 | frame = cv2.flip(frame,1)
34 | #Resize the given frame to a 600*600 window
35 | frame = imutils.resize(frame, width = 600)
36 | #Blur the frame using Gaussian Filter of kernel size 5, to remove excessivve noise
37 | blurred_frame = cv2.GaussianBlur(frame, (5,5), 0)
38 | #Convert the frame to HSV, as HSV allow better segmentation.
39 | hsv_converted_frame = cv2.cvtColor(blurred_frame, cv2.COLOR_BGR2HSV)
40 |
41 | #Create a mask for the frame, showing green values
42 | mask = cv2.inRange(hsv_converted_frame, greenLower, greenUpper)
43 | #Erode the masked output to delete small white dots present in the masked image
44 | mask = cv2.erode(mask, None, iterations = 2)
45 | #Dilate the resultant image to restore our target
46 | mask = cv2.dilate(mask, None, iterations = 2)
47 |
48 | #Display the masked output in a different window
49 | cv2.imshow('Masked Output', mask)
50 |
51 | #Find all contours in the masked image
52 | _,cnts,_ = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
53 |
54 | #Define center of the ball to be detected as None
55 | center = None
56 |
57 | #If any object is detected, then only proceed
58 | if(len(cnts)) > 0:
59 | #Find the contour with maximum area
60 | c = max(cnts, key = cv2.contourArea)
61 | #Find the center of the circle, and its radius of the largest detected contour.
62 | ((x,y), radius) = cv2.minEnclosingCircle(c)
63 |
64 | #Calculate the centroid of the ball, as we need to draw a circle around it.
65 | M = cv2.moments(c)
66 | center = (int(M['m10'] / M['m00']), int(M['m01'] / M['m00']))
67 |
68 | #Proceed only if a ball of considerable size is detected
69 | if radius > 10:
70 | #Draw circles around the object as well as its centre
71 | cv2.circle(frame, (int(x), int(y)), int(radius), (0,255,255), 2)
72 | cv2.circle(frame, center, 5, (0,255,255), -1)
73 |
74 | #Append the detected object in the frame to pts deque structure
75 | pts.appendleft(center)
76 |
77 | #This function makes the trailing line behind the detected object
78 | for i in range(1, len(pts)):
79 | if pts[i-1] is None or pts[i] is None:
80 | continue
81 |
82 | thickness = int(np.sqrt(buffer / float(i + 1)) * 2.5)
83 | cv2.line(frame, pts[i - 1], pts[i], (0, 0, 255), thickness)
84 |
85 | #Show the output frame
86 | cv2.imshow('Frame', frame)
87 | key = cv2.waitKey(1) & 0xFF
88 |
89 | #If q is pressed, close the window
90 | if(key == ord('q')):
91 | break
92 | #After all the processing, release webcam and destroy all windows
93 | video_capture.release()
94 | cv2.destroyAllWindows()
95 |
--------------------------------------------------------------------------------
/object-tracking-direction-detection.py:
--------------------------------------------------------------------------------
1 | '''This script can detect objects specified by the HSV color and also sense the
2 | direction of their movement. Implemented using OpenCV.'''
3 |
4 | #Import necessary modules
5 | import cv2
6 | import imutils
7 | import numpy as np
8 | from collections import deque
9 | import time
10 |
11 | #Define HSV colour range for green colour objects
12 | greenLower = (29, 86, 6)
13 | greenUpper = (64, 255, 255)
14 |
15 | #Used in deque structure to store no. of given buffer points
16 | buffer = 20
17 |
18 | #Points deque structure storing 'buffer' no. of object coordinates
19 | pts = deque(maxlen = buffer)
20 | #Counts the minimum no. of frames to be detected where direction change occurs
21 | counter = 0
22 | #Change in direction is stored in dX, dY
23 | (dX, dY) = (0, 0)
24 | #Variable to store direction string
25 | direction = ''
26 |
27 | #Start video capture
28 | video_capture = cv2.VideoCapture(0)
29 |
30 | #Sleep for 2 seconds to let camera initialize properly.
31 | time.sleep(2)
32 |
33 | #Loop until OpenCV window is not closed
34 | while True:
35 | #Store the readed frame in frame, ret defines return value
36 | ret, frame = video_capture.read()
37 | #Flip the frame to avoid mirroring effect
38 | frame = cv2.flip(frame,1)
39 | #Resize the given frame to a 600*600 window
40 | frame = imutils.resize(frame, width = 600)
41 | #Blur the frame using Gaussian Filter of kernel size 5, to remove excessivve noise
42 | blurred_frame = cv2.GaussianBlur(frame, (5,5), 0)
43 | #Convert the frame to HSV, as HSV allow better segmentation.
44 | hsv_converted_frame = cv2.cvtColor(blurred_frame, cv2.COLOR_BGR2HSV)
45 |
46 | #Create a mask for the frame, showing green values
47 | mask = cv2.inRange(hsv_converted_frame, greenLower, greenUpper)
48 | #Erode the masked output to delete small white dots present in the masked image
49 | mask = cv2.erode(mask, None, iterations = 2)
50 | #Dilate the resultant image to restore our target
51 | mask = cv2.dilate(mask, None, iterations = 2)
52 |
53 | #Find all contours in the masked image
54 | _,cnts,_ = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
55 |
56 | #Define center of the ball to be detected as None
57 | center = None
58 |
59 | #If any object is detected, then only proceed
60 | if(len(cnts) > 0):
61 | #Find the contour with maximum area
62 | c = max(cnts, key = cv2.contourArea)
63 | #Find the center of the circle, and its radius of the largest detected contour.
64 | ((x, y), radius) = cv2.minEnclosingCircle(c)
65 | #Calculate the centroid of the ball, as we need to draw a circle around it.
66 | M = cv2.moments(c)
67 | center = (int(M['m10'] / M['m00']), int(M['m01'] / M['m00']))
68 |
69 | #Proceed only if a ball of considerable size is detected
70 | if radius > 10:
71 | #Draw circles around the object as well as its centre
72 | cv2.circle(frame, (int(x), int(y)), int(radius), (0,255,255), 2)
73 | cv2.circle(frame, center, 5, (0,255,255), -1)
74 | #Append the detected object in the frame to pts deque structure
75 | pts.appendleft(center)
76 |
77 | #Using numpy arange function for better performance. Loop till all detected points
78 | for i in np.arange(1, len(pts)):
79 | #If no points are detected, move on.
80 | if(pts[i-1] == None or pts[i] == None):
81 | continue
82 |
83 | #If atleast 10 frames have direction change, proceed
84 | if counter >= 10 and i == 1 and pts[-10] is not None:
85 | #Calculate the distance between the current frame and 10th frame before
86 | dX = pts[-10][0] - pts[i][0]
87 | dY = pts[-10][1] - pts[i][1]
88 | (dirX, dirY) = ('', '')
89 |
90 | #If distance is greater than 100 pixels, considerable direction change has occured.
91 | if np.abs(dX) > 100:
92 | dirX = 'West' if np.sign(dX) == 1 else 'East'
93 |
94 | if np.abs(dY) > 100:
95 | dirY = 'North' if np.sign(dY) == 1 else 'South'
96 |
97 | #Set direction variable to the detected direction
98 | direction = dirX if dirX != '' else dirY
99 |
100 | #Draw a trailing red line to depict motion of the object.
101 | thickness = int(np.sqrt(buffer / float(i + 1)) * 2.5)
102 | cv2.line(frame, pts[i - 1], pts[i], (0, 0, 255), thickness)
103 |
104 | #Write the detected direction on the frame.
105 | cv2.putText(frame, direction, (20,40), cv2.FONT_HERSHEY_SIMPLEX, 1, (0,0,255), 3)
106 |
107 | #Show the output frame.
108 | cv2.imshow('Window- Direction Detection', frame)
109 | key = cv2.waitKey(1) & 0xFF
110 | #Update counter as the direction change has been detected.
111 | counter += 1
112 |
113 | #If q is pressed, close the window
114 | if(key == ord('q')):
115 | break
116 | #After all the processing, release webcam and destroy all windows
117 | video_capture.release()
118 | cv2.destroyAllWindows()
119 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | numpy==1.15.2
2 | imutils==0.5.1
3 | PyAutoGUI==0.9.38
4 | opencv_python==3.4.3.18
5 | pygame==1.9.4
6 |
--------------------------------------------------------------------------------
/settingsSnakeFun.py:
--------------------------------------------------------------------------------
1 | import pygame
2 | from pygame.locals import *
3 |
4 | pygame.init()
5 | pygame.mixer.init()
6 | pygame.font.init()
7 |
8 | FPS = 3
9 | WINDOWWIDTH = 640
10 | WINDOWHEIGHT = 480
11 | CELLSIZE = 20
12 | assert WINDOWHEIGHT % CELLSIZE == 0, "Window Height must be a multiple of Cell Size"
13 | assert WINDOWWIDTH % CELLSIZE == 0, "Window Width must be a multiple of Cell Size"
14 | CELLWIDTH = int(WINDOWWIDTH / CELLSIZE)
15 | CELLHEIGHT = int(WINDOWHEIGHT / CELLSIZE)
16 |
17 | #Colour Codes
18 | # R G B
19 | WHITE = (255, 255, 255)
20 | BLACK = (0, 0, 0)
21 | RED = (255, 0, 0)
22 | GREEN = (0, 255, 0)
23 | DARKGREEN= (0, 155, 0)
24 | DARKGRAY = (40, 40, 40)
25 | YELLOW = (255, 255, 0)
26 |
27 | BGCOLOR = BLACK
28 |
29 | #Control Keys
30 | UP = 'up'
31 | DOWN = 'down'
32 | LEFT = 'left'
33 | RIGHT = 'right'
34 |
35 | HEAD = 0 #Index of the snake's head
36 |
37 | #Game Sounds
38 | APPLEEATSOUND = pygame.mixer.Sound(r"sounds/appleEatSound.wav")
39 | BGMUSIC = pygame.mixer.music.load(r"sounds/bgmusic.mid")
40 |
41 | def levelSelect():
42 | global FPS
43 | if level == "EASY":
44 | FPS = 4
45 | elif level == "MEDIUM":
46 | FPS = 7
47 | elif level == "HARD":
48 | FPS = 10
49 |
--------------------------------------------------------------------------------
/sounds/appleEatSound.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msindev/Gesture-Controlled-Snake-Game/0819c15d9c746fc4d423df6c1ae03ee12e6031c1/sounds/appleEatSound.wav
--------------------------------------------------------------------------------
/sounds/bgmusic.mid:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/msindev/Gesture-Controlled-Snake-Game/0819c15d9c746fc4d423df6c1ae03ee12e6031c1/sounds/bgmusic.mid
--------------------------------------------------------------------------------