├── HandTrackingModule.py ├── Readme.md ├── img ├── 1.gif └── HandLandmarks.png ├── libraries.bat └── main.py /HandTrackingModule.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import mediapipe as mp 3 | import time 4 | 5 | 6 | class handDetector(): 7 | def __init__(self, mode=False, maxHands=2, modelComplexity=1, detectionCon=0.5, trackCon=0.5): 8 | self.mode = mode 9 | self.maxHands = maxHands 10 | self.modelComplexity = modelComplexity 11 | self.detectionCon = detectionCon 12 | self.trackCon = trackCon 13 | 14 | self.mpHands = mp.solutions.hands 15 | self.hands = self.mpHands.Hands(self.mode, self.maxHands, self.modelComplexity, self.detectionCon, self.trackCon) 16 | self.mpDraw = mp.solutions.drawing_utils 17 | 18 | def findHands(self, img, draw=True): 19 | imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) 20 | self.results = self.hands.process(imgRGB) 21 | #print(results.multi_hand_landmarks) 22 | 23 | if self.results.multi_hand_landmarks: 24 | for handLms in self.results.multi_hand_landmarks: 25 | if draw: 26 | self.mpDraw.draw_landmarks(img, handLms, self.mpHands.HAND_CONNECTIONS) 27 | return img 28 | 29 | def findPosition(self, img, handNo=0, draw=True): 30 | 31 | lmList = [] 32 | if self.results.multi_hand_landmarks: 33 | myHand = self.results.multi_hand_landmarks[handNo] 34 | for id, lm in enumerate(myHand.landmark): 35 | #print(id, lm) 36 | h, w, c = img.shape 37 | cx, cy = int(lm.x*w), int(lm.y*h) 38 | #print(id, cx, cy) 39 | lmList.append([id, cx, cy]) 40 | if draw: 41 | cv2.circle(img, (cx, cy), 15, (255,0,255), cv2.FILLED) 42 | 43 | return lmList 44 | 45 | def main(): 46 | pTime = 0 47 | cTime = 0 48 | cap = cv2.VideoCapture(0) 49 | detector = handDetector() 50 | while True: 51 | success, img = cap.read() 52 | img = detector.findHands(img) 53 | lmList = detector.findPosition(img) 54 | if len(lmList) != 0: 55 | print(lmList[1]) 56 | 57 | cTime = time.time() 58 | fps = 1. / (cTime - pTime) 59 | pTime = cTime 60 | 61 | cv2.putText(img, str(int(fps)), (10,70), cv2.FONT_HERSHEY_PLAIN, 3, (255,0,255), 3) 62 | 63 | cv2.imshow("Image", img) 64 | cv2.waitKey(1) 65 | 66 | 67 | if __name__ == "__main__": 68 | main() -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Gesture volume control 2 | In this project I am going to learn how to use Gesture Control to change the volume of a computer. 3 | I first look into hand tracking and then I will use the hand landmarks to find gesture of my hand to change the volume. 4 | 5 | ## The second version 6 | Follow the [link](https://github.com/paveldat/gesture_volume_control_v2) to see my second version of the "Gesture volume control" 7 | 8 | ## Features 9 | * Can track your hand in real-time 10 | * Can change your computer's volume based on your hand activity 11 | 12 | ## How to install 13 | 1. Clone this repository on your computer 14 | `https://github.com/paveldat/gesture_volume_control.git` 15 | 2. Install all the requirements 16 | `run libraries.bat` or 17 | `pip install -r requirements.txt` 18 | 3. Run the program 19 | `python main.py` 20 | 21 | ## Help 22 | You might face issue with webcam not showing and you get errors. 23 | To solve it just change the value in this line (for example to `1`). 24 | `cap = cv2.VideoCapture(0)` 25 | Increment this number until you see your webcam. 26 | 27 | ## Hand Landmarks 28 | 29 | 30 | ## Result 31 | ![Alt Text](https://github.com/paveldat/gesture_volume_control/blob/main/img/1.gif) -------------------------------------------------------------------------------- /img/1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paveldat/gesture_volume_control/d8a0d8f1b337a1056d4f87b5d8c426bb6f64a20b/img/1.gif -------------------------------------------------------------------------------- /img/HandLandmarks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paveldat/gesture_volume_control/d8a0d8f1b337a1056d4f87b5d8c426bb6f64a20b/img/HandLandmarks.png -------------------------------------------------------------------------------- /libraries.bat: -------------------------------------------------------------------------------- 1 | pip install mediapipe 2 | pip install opencv-python 3 | pip install numpy 4 | pip install pycaw -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import time 3 | import numpy as np 4 | import HandTrackingModule as htm 5 | import math 6 | from ctypes import cast, POINTER 7 | from comtypes import CLSCTX_ALL 8 | from pycaw.pycaw import AudioUtilities, IAudioEndpointVolume 9 | 10 | wCam, hCam = 1280, 720 11 | 12 | cap = cv2.VideoCapture(0) 13 | cap.set(3, wCam) 14 | cap.set(4, hCam) 15 | pTime = 0 16 | 17 | detector = htm.handDetector(detectionCon=0.7) 18 | 19 | devices = AudioUtilities.GetSpeakers() 20 | interface = devices.Activate(IAudioEndpointVolume._iid_, CLSCTX_ALL, None) 21 | volume = cast(interface, POINTER(IAudioEndpointVolume)) 22 | volRange = volume.GetVolumeRange() 23 | volume.SetMasterVolumeLevel(0, None) 24 | minVol = volRange[0] 25 | maxVol = volRange[1] 26 | vol = 0 27 | volBar = 400 28 | volPer = 0 29 | 30 | while True: 31 | success, img = cap.read() 32 | img = detector.findHands(img) 33 | lmList = detector.findPosition(img, draw=False) 34 | if len(lmList) != 0: 35 | 36 | x1, y1 = lmList[4][1], lmList[4][2] 37 | x2, y2 = lmList[8][1], lmList[8][2] 38 | cx, cy = (x1+x2)//2, (y1+y2)//2 39 | 40 | cv2.circle(img, (x1,y1), 15, (255,0,255), cv2.FILLED) 41 | cv2.circle(img, (x2,y2), 15, (255,0,255), cv2.FILLED) 42 | cv2.line(img, (x1,y1), (x2,y2), (255,0,255), 3) 43 | cv2.circle(img, (cx,cy), 15, (255,0,255), cv2.FILLED) 44 | 45 | length = math.hypot(x2-x1, y2-y1) 46 | 47 | # Hand range 50 - 300 48 | # Volume range -65 - 0 49 | 50 | vol = np.interp(length, [50,300], [minVol, maxVol]) 51 | volBar = np.interp(length, [50,300], [400, 150]) 52 | volPer = np.interp(length, [50,300], [0, 100]) 53 | 54 | volume.SetMasterVolumeLevel(vol, None) 55 | 56 | 57 | if length<50: 58 | cv2.circle(img, (cx,cy), 15, (0,255,0), cv2.FILLED) 59 | 60 | cv2.rectangle(img, (50, 150), (85, 400), (0,255,0), 3) 61 | cv2.rectangle(img, (50, int(volBar)), (85, 400), (0,255,0), cv2.FILLED) 62 | cv2.putText(img, f'{int(volPer)} %', (40,450), cv2.FONT_HERSHEY_COMPLEX, 1, (0,255,0), 3) 63 | 64 | cTime = time.time() 65 | fps = 1 / (cTime - pTime) 66 | pTime = cTime 67 | 68 | cv2.putText(img, f'FPS: {int(fps)}', (40,70), cv2.FONT_HERSHEY_COMPLEX, 1, (255,0,100), 3) 69 | 70 | cv2.imshow("Img", img) 71 | cv2.waitKey(1) --------------------------------------------------------------------------------