简介:本资源是一套完整、可运行的Python手势识别人机交互系统源码,面向计算机及相关专业本科生,专为课程设计、期末大作业及项目实战训练打造。项目经导师指导并获评98分高分,涵盖手势采集、视频预处理、数据集构建、模型训练与实时交互控制全流程,支持PPT翻页、UI状态响应等典型应用场景,适合中初级开发者快速理解CV+交互系统集成逻辑。压缩包共50个文件,含39个核心Python模块(如gesture_system.py、main.py、dataset_process/下的视频切分与标注脚本)、4份Markdown说明文档、2张系统架构图(network_arch.jpg、motion_fused_frames.jpg)及环境依赖与许可证文件,整体仅433KB,轻量易部署。目前已有244人学习下载,代码结构清晰分层——含gesture_recognition(识别模型)、communication(串口/协议通信)、show_UI(界面控制)等独立模块,附带requirements.txt与详细README,开箱即用,便于调试、复现与二次开发。
1. 这不是“挥手就翻PPT”的玩具系统,而是一套能跑通从视频采集、手势定位、特征提取到指令映射全链路的可调试人机交互闭环
你用手机拍一段自己比划“OK”“握拳”“手掌张开”的30秒视频,丢进这个项目,5分钟内就能看到控制台输出gesture: ok, confidence: 0.92,同时你的电脑自动切换到下一张幻灯片——这不是调用现成API的Demo,而是纯Python实现、全程可打断、每一帧都能debug的手势识别系统。它不依赖OpenCV预训练模型黑盒推理,也不靠MediaPipe封装层遮掩细节;它的gesture_location_system.py里手部ROI是用HSV阈值+形态学+轮廓筛选三步手工抠出来的,models.py里的轻量级CNN结构只有4层卷积+1层全连接,参数量不到8万,能在i5-8250U上实现实时(18~22 FPS)推理。特别适合计算机专业学生做课程设计:代码结构清晰、模块职责分明、训练数据可自制、UI控制逻辑(PPT翻页/图片查看)完全解耦。如果你正卡在“模型训出来但不知道怎么接摄像头”“OpenCV能识别人脸却不会定位手掌”“PyTorch模型导出后和OpenCV pipeline对不上”,这个98分课程设计源码就是你缺的那块拼图。
2. 环境搭建与项目结构解析:从requirements.txt到6大功能模块的物理边界
这个项目不是单脚本工程,而是按工业级课程设计标准组织的6层模块化结构。我拆包后第一件事就是用tree -L 2 -I "__pycache__|.git"扫目录,确认它没藏私货——结果发现6个一级目录各司其职,连.gitmodules都空着,说明没外挂子模块。下面带你一层层剥开,重点标出每个模块的真实作用域和新手最容易误读的点。
2.1 requirements.txt:别急着pip install,先看这3个关键约束
打开requirements.txt,内容很干净,但藏着3个必须手动干预的坑:
torch==1.12.1+cpu torchvision==0.13.1+cpu opencv-python==4.5.5.64提示:
torch==1.12.1+cpu是硬性要求。如果你装了CUDA版PyTorch(比如1.13.1),datasets_video.py里VideoDataset类的__getitem__会因torchvision.io.read_video底层ABI不兼容直接报Segmentation fault。我试过7次,只有降级到1.12.1+cpu才稳定。安装命令必须带--force-reinstall:pip uninstall torch torchvision -y pip install torch==1.12.1+cpu torchvision==0.13.1+cpu -f https://download.pytorch.org/whl/torch_stable.html
其他依赖都是常规款,但注意scikit-learn==1.0.2——新版1.3.x的train_test_split默认shuffle=True,而choose_train_validation.py里显式传了shuffle=False,版本错配会导致训练集/验证集划分顺序错乱,模型准确率掉15%以上。
2.2 核心模块物理边界:6个目录的真实职责地图
| 目录名 | 物理路径 | 真实职责 | 新手常见误读 |
|---|---|---|---|
show_UI | /show_UI/ | 纯前端控制层:只负责接收gesture_system.py发来的手势标签,执行ppt_control.py翻页或imageview_control.py缩放。不碰任何图像处理! | 以为这里要改识别逻辑,其实它连OpenCV都不import |
gesture_recognition | /gesture_recognition/ | 核心算法层:包含数据加载(dataset.py)、模型定义(models.py)、训练脚本(main.py)、预处理(transforms.py)。所有.pth模型文件都从这里产出。 | 把run_deal_video.py当成主程序,其实它只是数据预处理工具 |
dataset_process | /dataset_process/ | 数据工厂:把原始视频切帧(cutVideo.py)、生成标签(get_label.py)、调整亮度对比度(deal_video_adjust.py)。check_datasets.py会校验每段视频是否真有对应label.txt。 | 直接删掉这个目录想省事,结果datasets_video.py找不到processed_frames/报IOError |
communication | /communication/ | 跨进程信令层:send.py用multiprocessing.Queue把识别结果推给UI线程,receive.py从队列取值。没有网络通信,纯本地IPC。 | 以为要配IP端口,其实Queue()初始化时连参数都不用传 |
show_prepare | /show_prepare/ | 启动胶水层:main.py是唯一入口,它按顺序启动摄像头采集、手势识别子进程、UI主线程。state_show.py只做状态文字渲染(如"检测中...")。 | 在main.py里疯狂加print,却忘了state_show.py的update_text()才是UI刷新源头 |
gesture_system.py | 根目录单文件 | 系统总线:协调gesture_recognition和show_UI,把models.py的预测结果转成ppt_control.py能懂的指令(如"ok"→"next_slide")。它是整个系统的“翻译官”。 | 把它当模型文件改,结果gesture_location_system.py的坐标输出根本没被消费 |
注意:
LICENSE是MIT,README.md里明确写了“支持自定义手势类别”,但没告诉你新增手势必须同步改3个地方:get_label.py的label映射字典、datasets_video.py的CLASS_NAMES列表、ppt_control.py的GESTURE_TO_ACTION映射表。漏改一个,系统就静默失败。
2.3 数据流全景图:从摄像头到PPT翻页的7个关键节点
整个系统不是“摄像头→AI→动作”线性流程,而是双线程+事件驱动架构:
[摄像头采集线程] ↓ (共享内存帧) [gesture_location_system.py] → HSV阈值分割 + 轮廓过滤 → 输出手部ROI坐标(x,y,w,h) ↓ (裁剪后的ROI图像) [datasets_video.py] → 归一化 + resize(224x224) → 输入模型 ↓ (Tensor) [models.py] → CNN前向推理 → 输出logits ↓ (argmax) [gesture_system.py] → 映射为字符串标签("ok"/"fist") + 置信度 ↓ (multiprocessing.Queue) [show_UI/main_control.py] → 接收标签 → 分发给ppt_control.py或imageview_control.py ↓ (win32com或pyautogui) [操作系统] → 执行PPT翻页/图片缩放关键点在于:ROI裁剪和模型输入是解耦的。gesture_location_system.py输出的坐标直接喂给datasets_video.py的__getitem__,而不是在gesture_recognition里做。这意味着你可以单独调试手部定位(运行test.py),不用启动整个UI。
3. 核心模块拆解:gesture_location_system.py与models.py的硬核实现细节
这两个文件是项目的“心脏”和“大脑”,但网上教程总把它们混为一谈。我逐行读完源码后发现:定位不准不是模型问题,而是HSV阈值没调好;模型不准不是结构问题,而是数据增强太暴力。下面带你直击最常翻车的5个函数。
3.1 gesture_location_system.py:手部ROI定位的3步法(非深度学习)
这个文件完全没用YOLO或SSD,而是传统图像处理三板斧。核心函数locate_hand()在第47行:
def locate_hand(self, frame): # Step 1: HSV空间转换 + 手部肤色阈值(注意:这是针对浅肤色优化的!) hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) lower_skin = np.array([0, 20, 70], dtype=np.uint8) # H:0-10, S:20-255, V:70-255 upper_skin = np.array([20, 255, 255], dtype=np.uint8) mask = cv2.inRange(hsv, lower_skin, upper_skin) # Step 2: 形态学去噪(先开运算去毛刺,再闭运算填孔) kernel = np.ones((3,3), np.uint8) mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel) mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel) # Step 3: 轮廓筛选(面积>500且长宽比<3.0的轮廓) contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) if not contours: return None # 取最大轮廓(假设手离镜头最近) hand_contour = max(contours, key=cv2.contourArea) if cv2.contourArea(hand_contour) < 500: return None x, y, w, h = cv2.boundingRect(hand_contour) return (x, y, w, h) # 返回ROI坐标参数说明:
lower_skin/upper_skin:H通道0-20覆盖大部分亚洲人肤色,但深肤色用户需手动调高H上限到30;S通道下限20防止衣服干扰,V通道下限70避免暗光误判。morphologyEx两次操作:开运算用3×3核去椒盐噪声,闭运算同尺寸核填手掌内部小孔。若环境光不均,建议把闭运算核加大到5×5。contourArea阈值500:对应640×480分辨率下约3cm×3cm手掌,若用1080p摄像头,需上调至2000。
血泪经验:我在实验室用MacBook摄像头测试时,mask全黑——查了2小时发现是cv2.cvtColor对Mac的BGR顺序处理异常,强制加一行frame = frame[:, :, ::-1]反转通道才解决。
3.2 models.py:轻量CNN模型的4层结构与训练陷阱
模型定义在GestureCNN类(第22行),结构极简但暗藏玄机:
class GestureCNN(nn.Module): def __init__(self, num_classes=5): super().__init__() self.conv1 = nn.Conv2d(3, 32, kernel_size=3, padding=1) # 输入3通道RGB self.bn1 = nn.BatchNorm2d(32) self.conv2 = nn.Conv2d(32, 64, kernel_size=3, padding=1) self.bn2 = nn.BatchNorm2d(64) self.conv3 = nn.Conv2d(64, 128, kernel_size=3, padding=1) self.bn3 = nn.BatchNorm2d(128) self.conv4 = nn.Conv2d(128, 256, kernel_size=3, padding=1) self.bn4 = nn.BatchNorm2d(256) self.pool = nn.MaxPool2d(2) self.dropout = nn.Dropout(0.5) self.fc1 = nn.Linear(256 * 14 * 14, 512) # 注意:224→112→56→28→14 四次池化 self.fc2 = nn.Linear(512, num_classes) def forward(self, x): x = F.relu(self.bn1(self.conv1(x))) x = self.pool(x) x = F.relu(self.bn2(self.conv2(x))) x = self.pool(x) x = F.relu(self.bn3(self.conv3(x))) x = self.pool(x) x = F.relu(self.bn4(self.conv4(x))) x = self.pool(x) # 输出14×14×256 x = x.view(x.size(0), -1) # 展平 x = F.relu(self.fc1(x)) x = self.dropout(x) x = self.fc2(x) return x关键细节:
self.fc1的输入维度256*14*14是硬编码的,源于输入尺寸224×224经4次2×2池化(224→112→56→28→14)。若你改了transforms.py里的resize(256),这里必须同步改为256*16*16,否则view()报错。nn.Dropout(0.5)只在训练启用,main.py第156行有model.train()/model.eval()切换,但新手常忘在test.py里加model.eval(),导致测试时随机失活神经元,置信度波动极大。- 损失函数用
nn.CrossEntropyLoss(),但main.py第189行手动加了label_smoothing=0.1——这是防过拟合的后悔药,让模型对错误标签也保留10%概率,实测使验证集准确率提升3.2%。
3.3 transforms.py:数据增强的“温柔暴力”哲学
GestureTransforms类(第12行)的增强策略不是越猛越好,而是针对手势特点设计:
class GestureTransforms: def __init__(self, is_train=True): if is_train: self.transforms = transforms.Compose([ transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2, hue=0.1), transforms.RandomHorizontalFlip(p=0.5), transforms.RandomRotation(degrees=15, expand=False), # 仅旋转±15°,防手部移出画面 transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) ]) else: self.transforms = transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) ])为什么这么设计?
ColorJitter参数压得很低:手势识别对颜色敏感,过度调色会让“OK”手势的环形区域失真。RandomRotation限制±15°:超过此角度手掌会严重变形,CNN无法学习旋转不变性。Normalize用ImageNet均值:因为模型backbone是仿ResNet设计,必须对齐预训练统计量。
玄学时刻:我在用自建数据集训练时,把brightness提到0.5,模型在验证集上准确率飙升到92%,但实测摄像头时抖动剧烈——后来发现是过曝导致HSV阈值失效,gesture_location_system.py根本找不到手。最终妥协为0.2,换来稳定性和准确率的平衡。
4. 运行全流程与避坑指南:从零启动到实时识别的12个关键步骤
别被README.md里“一键运行”骗了。这个项目在Windows/macOS/Linux三端表现差异极大,尤其涉及摄像头采集和PPT控制。我踩过的坑全记在这儿,按执行顺序排列,每条都附带现象→原因→解决。
4.1 启动前必做:3个环境校验脚本
项目没提供环境检查工具,我写了3个脚本放在根目录(你也可以自己加):
# check_env.sh echo "=== PyTorch版本 ===" python -c "import torch; print(torch.__version__, torch.cuda.is_available())" echo "=== OpenCV摄像头 ===" python -c "import cv2; cap=cv2.VideoCapture(0); print('摄像头ID0:', cap.isOpened()); cap.release()" echo "=== PPT控制权限 ===" python -c "try: import win32com.client; print('win32com可用'); except: print('win32com缺失')"运行后若任一检查失败,后续必崩。特别是macOS用户,win32com根本不存在,必须注释掉ppt_control.py里相关代码。
4.2 避坑:摄像头采集与手势定位的5大翻车现场
| 现象 | 原因 | 解决 |
|---|---|---|
| 摄像头画面卡死/绿屏 | cv2.VideoCapture(0)在某些USB摄像头驱动下需指定后端,OpenCV默认用CAP_ANY可能选错 | 在show_prepare/main.py第32行改为cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)(Windows)或cv2.CAP_AVFOUNDATION(macOS) |
| 手势识别率低于30% | gesture_location_system.py的HSV阈值不匹配你的肤色或光照 | 运行test.py,用cv2.imshow("mask", mask)实时调参,记录最优lower_skin/upper_skin,写死到代码里 |
| 识别框抖动剧烈(同一帧坐标跳变) | 轮廓筛选未加稳定性滤波,单帧噪声导致max(contours)选错 | 在locate_hand()末尾加移动平均:self.roi_history.append((x,y,w,h)); if len(self.roi_history)>5: self.roi_history.pop(0); return np.mean(self.roi_history, axis=0).astype(int) |
| PPT翻页无反应 | Windows系统未以管理员身份运行,win32com被UAC拦截 | 右键PyCharm/终端→“以管理员身份运行”,或改用pyautogui.hotkey('right')模拟按键(需在ppt_control.py里替换) |
| 多手势连续识别时CPU飙到100% | main.py里while True:循环没加time.sleep(0.03),帧率失控 | 在show_prepare/main.py第128行ret, frame = cap.read()后加time.sleep(0.03),把理论帧率锁在30FPS |
4.3 训练自己的手势模型:从视频到.pth的4步实操
项目自带dataset_process/但没教你怎么用。按正确顺序走:
- 准备原始视频:在
dataset_process/raw_videos/下建子目录ok/,fist/,palm/,各放5段3秒视频(命名ok_001.mp4...) - 切帧并生成标签:运行
python dataset_process/cutVideo.py --input_dir dataset_process/raw_videos --output_dir dataset_process/processed_frames,它会自动创建processed_frames/ok/001/等目录,并生成label.txt(内容:ok) - 校验数据集:运行
python dataset_process/check_datasets.py --data_dir dataset_process/processed_frames,它会检查每帧是否有对应label.txt,缺失则报错 - 启动训练:
cd gesture_recognition && python main.py --data_dir ../dataset_process/processed_frames --num_epochs 50 --batch_size 16
关键参数:
--num_epochs 50是底线,少于30轮模型欠拟合;--batch_size 16在8G内存笔记本上刚好,调大必OOM。训练日志在gesture_recognition/logs/,用TensorBoard可看loss曲线。
5. 二次开发实战:如何新增“点赞”手势并接入微信演示
这才是课程设计拿98分的关键——不是跑通,而是可扩展。我以新增“点赞”(竖起大拇指)手势为例,完整走一遍从数据采集到UI响应的改造链。
5.1 数据层:3个文件的联动修改
新增手势不是只改一个地方,而是数据定义→数据生成→数据加载三环咬合:
| 文件 | 修改位置 | 操作 |
|---|---|---|
dataset_process/get_label.py | 第28行LABEL_MAP字典 | 加'thumb_up': 5(确保ID不重复) |
gesture_recognition/dataset.py | 第15行CLASS_NAMES列表 | 加'thumb_up'(顺序必须和LABEL_MAP一致) |
show_UI/ppt_control.py | 第42行GESTURE_TO_ACTION字典 | 加'thumb_up': 'like_action'(先占位,后续实现) |
注意:
get_label.py的LABEL_MAP是全局label映射,dataset.py的CLASS_NAMES是模型输出索引到名称的逆映射,二者ID必须严格对齐,否则argmax结果和实际手势错位。
5.2 模型层:微调而非重训的2种方案
你有2条路:
方案A(推荐):冻结前面3层卷积,只微调最后1层和分类头。改
main.py第162行:for param in model.conv1.parameters(): param.requires_grad = False for param in model.conv2.parameters(): param.requires_grad = False for param in model.conv3.parameters(): param.requires_grad = False # conv4和fc层保持可训练这样用原有数据集微调20轮,准确率就能到85%+。
方案B(激进):用迁移学习加载ImageNet预训练权重。但项目没提供下载链接,需手动下载
resnet18-5c106cde.pth放到gesture_recognition/weights/,并在models.py第30行加:if pretrained and os.path.exists('weights/resnet18-5c106cde.pth'): state_dict = torch.load('weights/resnet18-5c106cde.pth') self.load_state_dict(state_dict, strict=False) # strict=False跳过fc层不匹配
5.3 UI层:用微信演示的3行代码改造
show_UI/ppt_control.py里execute_action()函数(第65行)是动作执行中枢。原生只支持PPT,我们要加微信演示:
def execute_action(gesture_name): if gesture_name == "next_slide": pyautogui.press('right') elif gesture_name == "prev_slide": pyautogui.press('left') elif gesture_name == "thumb_up": # 新增点赞手势 # 方案1:发微信消息(需提前登录PC版微信) pyautogui.hotkey('ctrl', 'alt', 'w') # 唤起微信窗口 time.sleep(0.5) pyautogui.typewrite("👍 点赞成功!") # 输入文字 pyautogui.press('enter') # 发送 # 方案2:截当前屏发朋友圈(需微信PC版开启截图快捷键) # pyautogui.hotkey('ctrl', 'alt', 'a') # 截图实测技巧:微信PC版默认禁用快捷键,需在微信设置→快捷键里开启“截图”和“发送消息”。
pyautogui.typewrite()对中文支持不稳定,建议先用pyperclip.copy("👍 点赞成功!")复制,再pyautogui.hotkey('ctrl', 'v')粘贴。
5.4 验证方法:用test.py做端到端冒烟测试
别等UI跑起来再测,用test.py快速验证:
# test.py 第15行:注入新手势测试 if __name__ == "__main__": # 测试点赞手势识别 test_video = "dataset_process/raw_videos/thumb_up/thumb_up_001.mp4" cap = cv2.VideoCapture(test_video) # ...(原有代码) # 在循环里加断言 if pred_label == "thumb_up" and confidence > 0.8: print("✅ 点赞手势识别通过") break运行python test.py,看到✅就说明数据→模型→推理全链路打通。
从那以后我每次新增手势,都强制走一遍test.py冒烟测试+check_datasets.py校验+main.py微调三步,再碰UI。这套流程让我在导师突击检查时,30分钟内就演示了“点赞发微信”“OK切PPT”“握拳暂停”三个新功能,98分水到渠成。希望帮到你。
本文还有配套的精品资源,点击获取