Python虚拟鼠标项目调用自定义手部追踪模块报ValueError错误求解
错误原因
- 直接触发报错的核心问题:你自行编写的
handDetector类中findPosition方法仅返回1个值lmList,但虚拟鼠标主程序中使用lmList, bbox = detector.findPosition(img)尝试接收2个返回值,变量数量不匹配触发ValueError。 - 代码还存在多处其他潜在错误,后续运行也会触发报错:
- 主程序调用
cvzone.HandTrackingModule.HandDetector.fingersUp()是直接调用类方法,没有实例化对象也未传入当前手部关键点数据,逻辑完全错误 - 自定义的
handDetector类没有实现findDistance方法,后续调用detector.findDistance(8,12,img)会触发属性不存在错误 - 主程序判断点击的条件
if length [40]写法错误,length是数值类型不能用索引访问 - 主程序中帧率计算、窗口展示的代码缩进错误,被包在了点击逻辑的if分支里,只有触发点击才会更新画面和帧率
- 代码中所有
"是HTML转义字符,需要替换为普通双引号",否则会触发语法错误
- 主程序调用
解决方案
1. 修改自定义手部追踪模块代码
完整修改后的HandTrackingModule.py代码如下:
import cv2 import mediapipe as mp import time class handDetector(): def __init__(self, mode=False, maxHands=2, detectionCon=0.5, trackCon=0.5): self.mode = mode self.maxHands = maxHands self.detectionCon = detectionCon self.trackCon = trackCon self.mpHands = mp.solutions.hands # 注意:mediapipe 0.10+版本需要新增model_complexity参数 self.hands = self.mpHands.Hands(self.mode, self.maxHands, model_complexity=0, min_detection_confidence=self.detectionCon, min_tracking_confidence=self.trackCon) self.mpDraw = mp.solutions.drawing_utils self.lmList = [] def findHands(self, img, draw=True): 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: self.mpDraw.draw_landmarks(img, handLms, self.mpHands.HAND_CONNECTIONS) return img def findPosition(self, img, handNo=0, draw=True): self.lmList = [] bbox = [] if self.results.multi_hand_landmarks: myHand = self.results.multi_hand_landmarks[handNo] xList = [] yList = [] for id, lm in enumerate(myHand.landmark): h, w, c = img.shape cx, cy = int(lm.x * w), int(lm.y * h) xList.append(cx) yList.append(cy) self.lmList.append([id, cx, cy]) if draw: cv2.circle(img, (cx, cy), 7, (255, 0, 255), cv2.FILLED) # 计算手部边界框 xmin, xmax = min(xList), max(xList) ymin, ymax = min(yList), max(yList) bbox = [xmin, ymin, xmax, ymax] if draw: cv2.rectangle(img, (xmin-20, ymin-20), (xmax+20, ymax+20), (0, 255, 0), 2) return self.lmList, bbox def fingersUp(self): fingers = [] # 拇指判断 if self.lmList[4][1] > self.lmList[3][1]: fingers.append(1) else: fingers.append(0) # 其余四指判断 for id in range(8, 21, 4): if self.lmList[id][2] < self.lmList[id-2][2]: fingers.append(1) else: fingers.append(0) return fingers def findDistance(self, p1, p2, img, draw=True): x1, y1 = self.lmList[p1][1], self.lmList[p1][2] x2, y2 = self.lmList[p2][1], self.lmList[p2][2] cx, cy = (x1 + x2) // 2, (y1 + y2) // 2 if draw: cv2.circle(img, (x1, y1), 15, (255, 0, 255), cv2.FILLED) cv2.circle(img, (x2, y2), 15, (255, 0, 255), cv2.FILLED) cv2.line(img, (x1, y1), (x2, y2), (255, 0, 255), 3) cv2.circle(img, (cx, cy), 15, (255, 0, 255), cv2.FILLED) length = ((x2 - x1)**2 + (y2 - y1)**2)**0.5 return length, img, [x1, y1, x2, y2, cx, cy] def main(): pTime = 0 cap = cv2.VideoCapture(0) detector = handDetector() while True: success, img = cap.read() img = detector.findHands(img) lmList, bbox = detector.findPosition(img) if len(lmList) != 0: print(lmList[4]) cTime = time.time() fps = 1 / (cTime - pTime) pTime = cTime cv2.putText(img, str(int(fps)), (10, 70), cv2.FONT_HERSHEY_PLAIN, 3, (255, 0, 255), 3) cv2.imshow("Image", img) if cv2.waitKey(1) == ord("q"): break if __name__ == "__main__": main()
2. 修改虚拟鼠标主程序代码
完整修改后的主程序代码如下:
import cv2 import numpy as np import HandTrackingModule as htm import time import autopy ########################## wCam, hCam = 640, 480 frameR = 100 # 帧边界缩减 smoothening = 7 ######################### pTime = 0 plocX, plocY = 0, 0 clocX, clocY = 0, 0 cap = cv2.VideoCapture(0) # 摄像头索引如果打不开可以换1 cap.set(3, wCam) cap.set(4, hCam) detector = htm.handDetector(maxHands=1) # 只识别一只手提升性能 wScr, hScr = autopy.screen.size() while True: # 1. 获取手部关键点 success, img = cap.read() img = detector.findHands(img) lmList, bbox = detector.findPosition(img) # 2. 获取食指和中指指尖坐标 if len(lmList) != 0: x1, y1 = lmList[8][1:] x2, y2 = lmList[12][1:] # 3. 检测手指抬起状态 fingers = detector.fingersUp() cv2.rectangle(img, (frameR, frameR), (wCam - frameR, hCam - frameR), (255, 0, 255), 2) # 4. 仅食指抬起:鼠标移动模式 if fingers[1] == 1 and fingers[2] == 0: # 5. 坐标转换 x3 = np.interp(x1, (frameR, wCam - frameR), (0, wScr)) y3 = np.interp(y1, (frameR, hCam - frameR), (0, hScr)) # 6. 平滑处理 clocX = plocX + (x3 - plocX) / smoothening clocY = plocY + (y3 - plocY) / smoothening # 7. 移动鼠标 autopy.mouse.move(wScr - clocX, clocY) cv2.circle(img, (x1, y1), 15, (255, 0, 255), cv2.FILLED) plocX, plocY = clocX, clocY # 8. 食指和中指都抬起:点击模式 if fingers[1] == 1 and fingers[2] == 1: # 9. 计算两指间距 length, img, lineInfo = detector.findDistance(8, 12, img) # 10. 间距小于阈值触发点击 if length < 40: cv2.circle(img, (lineInfo[4], lineInfo[5]), 15, (0, 255, 0), cv2.FILLED) autopy.mouse.click() # 11. 计算帧率 cTime = time.time() fps = 1 / (cTime - pTime) pTime = cTime cv2.putText(img, str(int(fps)), (20, 50), cv2.FONT_HERSHEY_PLAIN, 3, (255, 0, 0), 3) # 12. 展示画面 cv2.imshow("Image", img) cv2.waitKey(1)
依赖安装说明
如果运行提示缺少依赖,执行以下命令安装:
pip install opencv-python mediapipe numpy autopy cvzone
注意autopy仅支持Python 3.8及以下版本,如果Python版本过高可以用pyautogui替代对应功能。
内容的提问来源于stack exchange,提问作者Raiyan Khan
相关产品推荐
相关产品推荐

