AI 隔空虚拟白板 v1.0 —— 用摄像头和手势写字
本文最后更新于37 天前,其中的信息可能已经过时,如有错误请发送邮件到 sunrisetsn@gmail.com

〇 前言

一直想做点”隔空操控”的东西——不用碰到屏幕,对着摄像头挥挥手就能写字。于是花了一周把想法落地成了这个程序:它借助 MediaPipe 实时识别手部关键点,捏合拇指和食指即“下笔”,指尖在空中的移动轨迹会变成屏幕上带着颗粒感的粉笔笔迹,从起点一步到可打包分发的单文件 exe。

期间踩了一堆坑,最离谱的是“中文用户名让 exe 黑屏”的玄学 bug,后面会讲。

一 功能

  • 摄像头实时画面 + 虚拟画布一体叠加
  • 拇指+食指捏合 = 落笔,移动指尖即是书写
  • 屏幕内工具条(捏合点击或鼠标点击):7 种粉笔颜色 / 橡皮开关 / 一键清空
  • 粗细滑杆:按住拖动连续调节(6~80)
  • 颗粒质感粉笔笔迹
  • 鼠标模式与手势模式互不干扰,可混用
  • PyInstaller 单文件 exe,模型内嵌、开箱即用

二 技术栈

技术用途
Python 3开发语言
OpenCV (cv2)摄像头采集、帧翻转缩放、画布渲染与图像合成
MediaPipe Tasks手部 21 关键点实时检测(HandLandmarker)
PySide6 (Qt)GUI 界面、视频帧显示、鼠标交互
PyInstaller打包为单文件 exe

三 使用到的主要模块

①OpenCV

负责采集与渲染:

cv2.VideoCapture()
cv2.flip()摄像头镜像,书写方向更自然
cv2.resize()统一 1280×720 工作区
cv2.addWeighted()画布 82% + 视频 18% 半透明合成
cv2.circle/rectangle屏幕内工具条绘制

②MediaPipe(核心)

新版本移除了旧的 mp.solutions,改用 Tasks API 的 HandLandmarker(float16 模型),一次 detect 返回 21 个归一化关键点:

捏合判定 = 拇指尖(4)↔食指尖(8) 距离 / 掌宽(0–9) ratio < 0.33 → 落笔;带 0.46 滞回 → 抬笔

笔尖取拇指与食指指尖的中点,更贴近“握笔”的感觉。

③PySide6

  • QThread 摄像头工作线程,Signal(np.ndarray) 每帧把合成画面发给主线程
  • QLabel 显示画面,鼠标事件坐标等比换算到画布坐标并喂给同一套笔处理逻辑
  • 窗口缩放、居中、坐标往返不漏

四 实现原理

摄像头
  │ 读取帧
  ▼
翻转(镜像) → 缩放 1280×720
  ▼
HandLandmarker 检测 21 关键点
  ▼
捏合判定 → 工具条命中 / 写线
  ▼
粉笔圆章(颗粒噪声) 绘制到画布
  ▼
画布(0.82) ⊕ 视频(0.18) 合成
  ▼
Qt 线程显示

状态机很简单:每帧维护“上一笔点 last”,捏合拖动时从 last 到新点插值画线;抬起清空。按下瞬间只判定一次工具按钮,杜绝连发。

五 粉笔颗粒效果

笔迹不是直线,而是沿线每 2.5px 盖一个“噪声圆章”:像素亮度随机扰动、随机留空洞、0.62 透明度混合进画布。于是笔画越叠越浓,颗粒感就像真的粉笔:

mask[rnd > speckle] = 0随机空洞(颗粒)
jit = 0.55 + 0.45 * rand(h,w,1)亮度随机扰动
canvas[y0:y1,x0:x1] = sub*(1-a) + color*jit*a

六 踩过的坑(重点)

  • 新版 MediaPipe 没了 mp.solutions:老教程全是 mp.solutions.hands,新版直接 ImportError。改成 tasks.HandLandmarker 还得自带 .task 模型文件,需要一起捆绑进 exe。
  • 中文用户名导致 exe 黑屏:源码跑得好好的,打包后一片漆黑。排查到 PyInstaller 把内嵌模型解压到 %TEMP%\_MEIxxxx\assets\,而我的系统用户名路径带中文,MediaPipe 的 C++ 原生文件 IO 打不开中文路径 → 模型加载失败 → 识别线程静默死亡。修复:启动时检测路径是否含非 ASCII 字符,是则把模型复制到 exe 旁边的 ASCII 目录再加载,并写 app.log 兜底日志。
  • 工具条“按了没反应”:捏合保持时每帧都会命中按钮,导致动作被反复触发(橡皮开了又关)。改成按下瞬间只触发一次。
  • 快速点击丢失:按下+松开发生在一帧内(<33ms)就轮询不到,点击全部丢失。改为“按下事件即时处理 + 拖动事件采样”,彻底解决。
  • 点击滑条崩溃hit() 返回值结构不一致导致对 list 调用 startswith() 抛 AttributeError,修复返回值并给交互区加了 try/except 兜底。

七 开发记录

这是第一个从零到一再用“摄像头 + 手势 + 桌面 GUI”的组合,把图像处理、神经网络推理、线程和界面串起来的完整项目。体会到最关键的是调试手段:源码跑不通一眼就看到了,但从打包好的黑窗里捞原因,比写代码本身麻烦得多。

目前是 v1.0 够用了,之后想加:画布导出 PNG、笔画截图、笔迹平滑、多手势指令(双指拖屏等),把它一步步变得更接近“能挂在墙上代替白板”的样子。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇