├── HandTrackingModule.py ├── Main.py └── README.md /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, detectionCon=0.5, trackCon=0.5): 8 | self.mode = mode 9 | self.maxHands = maxHands 10 | self.detectionCon = detectionCon 11 | self.trackCon = trackCon 12 | 13 | self.mpHands = mp.solutions.hands 14 | self.hands = self.mpHands.Hands(self.mode, self.maxHands, 15 | 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, 27 | self.mpHands.HAND_CONNECTIONS) 28 | return img 29 | 30 | def findPosition(self, img, handNo=0, draw=True, color = (255, 0, 255), z_axis=False): 31 | 32 | lmList = [] 33 | if self.results.multi_hand_landmarks: 34 | myHand = self.results.multi_hand_landmarks[handNo] 35 | for id, lm in enumerate(myHand.landmark): 36 | # print(id, lm) 37 | h, w, c = img.shape 38 | if z_axis == False: 39 | cx, cy = int(lm.x * w), int(lm.y * h) 40 | # print(id, cx, cy) 41 | lmList.append([id, cx, cy]) 42 | elif z_axis: 43 | cx, cy, cz = int(lm.x * w), int(lm.y * h), round(lm.z,3) 44 | # print(id, cx, cy, cz) 45 | lmList.append([id, cx, cy, cz]) 46 | 47 | if draw: 48 | cv2.circle(img, (cx, cy),5,color, cv2.FILLED) 49 | 50 | return lmList 51 | 52 | 53 | def main(): 54 | pTime = 0 55 | cTime = 0 56 | cap = cv2.VideoCapture(1) 57 | detector = handDetector(maxHands=1) 58 | while True: 59 | success, img = cap.read() 60 | img = detector.findHands(img) 61 | lmList = detector.findPosition(img,z_axis=True,draw=False) 62 | if len(lmList) != 0: 63 | print(lmList[4]) 64 | 65 | cTime = time.time() 66 | fps = 1 / (cTime - pTime) 67 | pTime = cTime 68 | 69 | cv2.putText(img, str(int(fps)), (10, 70), cv2.FONT_HERSHEY_PLAIN, 3, 70 | (255, 0, 255), 3) 71 | 72 | cv2.imshow("Image", img) 73 | if cv2.waitKey(1) & 0xFF == ord('q'): 74 | break 75 | 76 | 77 | if __name__ == "__main__": 78 | main() -------------------------------------------------------------------------------- /Main.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import time, math, numpy as np 3 | import HandTrackingModule as htm 4 | import pyautogui, autopy 5 | from ctypes import cast, POINTER 6 | from comtypes import CLSCTX_ALL 7 | from pycaw.pycaw import AudioUtilities, IAudioEndpointVolume 8 | 9 | wCam, hCam = 640, 480 10 | cap = cv2.VideoCapture(1) 11 | cap.set(3,wCam) 12 | cap.set(4,hCam) 13 | pTime = 0 14 | #cTime = 0 15 | 16 | detector = htm.handDetector(maxHands=1, detectionCon=0.85, trackCon=0.8) 17 | 18 | devices = AudioUtilities.GetSpeakers() 19 | interface = devices.Activate( 20 | IAudioEndpointVolume._iid_, CLSCTX_ALL, None) 21 | volume = cast(interface, POINTER(IAudioEndpointVolume)) 22 | volRange = volume.GetVolumeRange() #(-63.5, 0.0, 0.5) min max 23 | 24 | minVol = -63 25 | maxVol = volRange[1] 26 | print(volRange) 27 | hmin = 50 28 | hmax = 200 29 | volBar = 400 30 | volPer = 0 31 | vol = 0 32 | color = (0,215,255) 33 | 34 | tipIds = [4, 8, 12, 16, 20] 35 | mode = '' 36 | active = 0 37 | 38 | pyautogui.FAILSAFE = False 39 | while True: 40 | success, img = cap.read() 41 | img = detector.findHands(img) 42 | lmList = detector.findPosition(img, draw=False) 43 | # print(lmList) 44 | fingers = [] 45 | 46 | if len(lmList) != 0: 47 | 48 | #Thumb 49 | if lmList[tipIds[0]][1] > lmList[tipIds[0 -1]][1]: 50 | if lmList[tipIds[0]][1] >= lmList[tipIds[0] - 1][1]: 51 | fingers.append(1) 52 | else: 53 | fingers.append(0) 54 | elif lmList[tipIds[0]][1] < lmList[tipIds[0 -1]][1]: 55 | if lmList[tipIds[0]][1] <= lmList[tipIds[0] - 1][1]: 56 | fingers.append(1) 57 | else: 58 | fingers.append(0) 59 | 60 | for id in range(1,5): 61 | if lmList[tipIds[id]][2] < lmList[tipIds[id] - 2][2]: 62 | fingers.append(1) 63 | else: 64 | fingers.append(0) 65 | 66 | 67 | # print(fingers) 68 | if (fingers == [0,0,0,0,0]) & (active == 0 ): 69 | mode='N' 70 | elif (fingers == [0, 1, 0, 0, 0] or fingers == [0, 1, 1, 0, 0]) & (active == 0 ): 71 | mode = 'Scroll' 72 | active = 1 73 | elif (fingers == [1, 1, 0, 0, 0] ) & (active == 0 ): 74 | mode = 'Volume' 75 | active = 1 76 | elif (fingers == [1 ,1 , 1, 1, 1] ) & (active == 0 ): 77 | mode = 'Cursor' 78 | active = 1 79 | 80 | ############# Scroll 👇👇👇👇############## 81 | if mode == 'Scroll': 82 | active = 1 83 | # print(mode) 84 | putText(mode) 85 | cv2.rectangle(img, (200, 410), (245, 460), (255, 255, 255), cv2.FILLED) 86 | if len(lmList) != 0: 87 | if fingers == [0,1,0,0,0]: 88 | #print('up') 89 | #time.sleep(0.1) 90 | putText(mode = 'U', loc=(200, 455), color = (0, 255, 0)) 91 | pyautogui.scroll(300) 92 | 93 | if fingers == [0,1,1,0,0]: 94 | #print('down') 95 | # time.sleep(0.1) 96 | putText(mode = 'D', loc = (200, 455), color = (0, 0, 255)) 97 | pyautogui.scroll(-300) 98 | elif fingers == [0, 0, 0, 0, 0]: 99 | active = 0 100 | mode = 'N' 101 | ################# Volume 👇👇👇#################### 102 | if mode == 'Volume': 103 | active = 1 104 | #print(mode) 105 | putText(mode) 106 | if len(lmList) != 0: 107 | if fingers[-1] == 1: 108 | active = 0 109 | mode = 'N' 110 | print(mode) 111 | 112 | else: 113 | 114 | # print(lmList[4], lmList[8]) 115 | x1, y1 = lmList[4][1], lmList[4][2] 116 | x2, y2 = lmList[8][1], lmList[8][2] 117 | cx, cy = (x1 + x2) // 2, (y1 + y2) // 2 118 | cv2.circle(img, (x1, y1), 10, color, cv2.FILLED) 119 | cv2.circle(img, (x2, y2), 10, color, cv2.FILLED) 120 | cv2.line(img, (x1, y1), (x2, y2), color, 3) 121 | cv2.circle(img, (cx, cy), 8, color, cv2.FILLED) 122 | 123 | length = math.hypot(x2 - x1, y2 - y1) 124 | # print(length) 125 | 126 | # hand Range 50-300 127 | # Volume Range -65 - 0 128 | vol = np.interp(length, [hmin, hmax], [minVol, maxVol]) 129 | volBar = np.interp(vol, [minVol, maxVol], [400, 150]) 130 | volPer = np.interp(vol, [minVol, maxVol], [0, 100]) 131 | print(vol) 132 | volN = int(vol) 133 | if volN % 4 != 0: 134 | volN = volN - volN % 4 135 | if volN >= 0: 136 | volN = 0 137 | elif volN <= -64: 138 | volN = -64 139 | elif vol >= -11: 140 | volN = vol 141 | 142 | # print(int(length), volN) 143 | volume.SetMasterVolumeLevel(vol, None) 144 | if length < 50: 145 | cv2.circle(img, (cx, cy), 11, (0, 0, 255), cv2.FILLED) 146 | 147 | cv2.rectangle(img, (30, 150), (55, 400), (209, 206, 0), 3) 148 | cv2.rectangle(img, (30, int(volBar)), (55, 400), (215, 255, 127), cv2.FILLED) 149 | cv2.putText(img, f'{int(volPer)}%', (25, 430), cv2.FONT_HERSHEY_COMPLEX, 0.9, (209, 206, 0), 3) 150 | 151 | 152 | ####################################################################### 153 | if mode == 'Cursor': 154 | active = 1 155 | #print(mode) 156 | putText(mode) 157 | cv2.rectangle(img, (110, 20), (620, 350), (255, 255, 255), 3) 158 | 159 | if fingers[1:] == [0,0,0,0]: #thumb excluded 160 | active = 0 161 | mode = 'N' 162 | print(mode) 163 | else: 164 | if len(lmList) != 0: 165 | x1, y1 = lmList[8][1], lmList[8][2] 166 | w, h = autopy.screen.size() 167 | X = int(np.interp(x1, [110, 620], [0, w - 1])) 168 | Y = int(np.interp(y1, [20, 350], [0, h - 1])) 169 | cv2.circle(img, (lmList[8][1], lmList[8][2]), 7, (255, 255, 255), cv2.FILLED) 170 | cv2.circle(img, (lmList[4][1], lmList[4][2]), 10, (0, 255, 0), cv2.FILLED) #thumb 171 | 172 | if X%2 !=0: 173 | X = X - X%2 174 | if Y%2 !=0: 175 | Y = Y - Y%2 176 | print(X,Y) 177 | autopy.mouse.move(X,Y) 178 | # pyautogui.moveTo(X,Y) 179 | if fingers[0] == 0: 180 | cv2.circle(img, (lmList[4][1], lmList[4][2]), 10, (0, 0, 255), cv2.FILLED) # thumb 181 | pyautogui.click() 182 | 183 | cTime = time.time() 184 | fps = 1/((cTime + 0.01)-pTime) 185 | pTime = cTime 186 | 187 | cv2.putText(img,f'FPS:{int(fps)}',(480,50), cv2.FONT_ITALIC,1,(255,0,0),2) 188 | cv2.imshow('Hand LiveFeed',img) 189 | 190 | if cv2.waitKey(1) & 0xFF == ord('q'): 191 | break 192 | 193 | def putText(mode,loc = (250, 450), color = (0, 255, 255)): 194 | cv2.putText(img, str(mode), loc, cv2.FONT_HERSHEY_COMPLEX_SMALL, 195 | 3, color, 3) 196 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | python packages needs to install 2 | 3 | opencv-python, 4 | pycaw, 5 | pyautogui, 6 | autopy, 7 | mediapipe, 8 | numpy 9 | --------------------------------------------------------------------------------