这是一个专门解决"手指被遮挡时到底看没看见"问题的深度学习模型,它能对每根手指的每个关节单独判断可见性,精度拉满,速度飞快。

简单上手玩了下这个手指关键点检测





一、它到底解决了什么问题?
想象一下这个场景:你用手抓着一个杯子,从摄像头看过去,你的手指有些被杯子挡住了,有些露在外面。现在有一个AI系统想要重建你手的3D模型------问题来了,那些被挡住的手指关节,到底该不该参与计算?
传统的3D手部姿态估计模型(比如HaMeR、WiLoR)会一股脑把所有21个关键点都当作"可见"来处理。结果就是:被遮挡的关节位置被强行拟合,整个手的3D模型就歪了。这就好比你明明看不见一个人的脚,却非要猜他脚朝哪个方向------猜错的概率当然高。
Hand Visibility Detector 干的事情非常直接:给每只手的21个关键点各打一个"可见性分数"(0到1之间)。绿色代表"我看得清清楚楚",红色代表"这个关节被挡住了"。下游的3D重建模块拿到这个信号后,就可以放心地忽略那些被遮挡的关节,只拿真正看得见的点去拟合3D模型。
这个问题听起来简单,做起来可不容易。难点在于:
- 遮挡模式千变万化:手指交叉、握拳、抓物体、手背朝镜头......每种遮挡模式都不一样,模型需要处理各种复杂的遮挡情况
- 关键点之间有关联:如果指尖被挡住了,它的父关节大概率也看不见------模型需要理解这种空间关系和运动学链的约束
- 实时性要求:作为3D手部的"前处理模块",它不能太慢,否则整个pipeline都卡住了,用户体验会很差
- 数据标注困难:准确标注每个关键点的可见性需要大量人工工作,而且不同标注者的判断标准可能不一致
这个项目的创新之处就在于:它用一个极其轻量的"可见性头"(Visibility Head),接在现有的手部检测骨干网络后面,几乎不增加计算量,就能给出高精度的逐关键点可见性预测。这种设计既保证了性能,又不会拖慢整个系统的速度。
二、整体架构:一条流水线,三步搞定

整个系统的工作流程可以概括为三步:
第一步:手部检测 + 3D姿态估计(WiLoR)
系统首先调用 WiLoR(一个轻量化的手部检测与3D姿态估计模型)来完成两件事:
- 在图像中找到所有的手,给出边界框(bounding box)和左右手分类
- 对每只手估计21个关键点的3D坐标(基于MANO参数化手模型)
WiLoR内部用的是YOLO做检测,ViT(Vision Transformer)做特征提取,再加一个MANO回归头。这一步输出的21个关键点坐标是"假设所有点都可见"的情况下的最佳估计。YOLO检测器的速度非常快,可以在毫秒级完成手部定位,这为后续的可见性预测提供了坚实的基础。
第二步:裁剪 + 标准化
对于每只检测到的手,系统会:
- 把边界框扩展1.25倍,确保手指尖不会被裁掉
- 裁剪出正方形区域,缩放到256×256像素
- 如果是左手,水平翻转------这样模型只需要处理"标准右手"
- 用ImageNet的均值和标准差做归一化
这个"左手翻转为右手"的trick非常聪明:它让模型的学习负担直接减半,因为不需要分别学习左手和右手的遮挡模式。翻转后,所有的关键点索引保持一致,模型可以用统一的逻辑处理所有手部。
第三步:可见性预测(Visibility Net)
裁剪好的256×256图像被送进可见性网络。这个网络的结构非常精巧,我们后面会详细展开。简单来说,它输出一个21维的向量,每个值在0到1之间,代表对应关键点可见的概率。
最终结果是一个 HandResult 对象,包含了:
- 边界框和检测置信度
- 左右手分类
- 21个关键点的2D和3D坐标
- 21个关键点的可见性概率
- 可选的手腕和手指旋转信息(axis-angle和欧拉角)
这个数据结构设计得非常合理,既包含了基础的检测结果,也提供了丰富的3D信息,方便下游任务使用。
三、核心创新:Visibility Head 的精妙设计

这是整个项目最核心的贡献------一个轻量但高效的可见性预测头。它的设计灵感来自 Contact4D(一个接触估计的工作),但在手部场景下做了专门的适配。
3.1 整体结构
Visibility Head 由四个模块串联组成:
输入特征图 (B, C, H, W)
↓
[Conv 1×1] → 通道数压缩到 hidden_dim (256)
↓
[Flatten + FC] → 空间维度展平为 token 序列,线性变换
↓
[GAU] → 门控注意力单元,捕获关键点之间的关联
↓
[Reshape + Conv 1×1] → 恢复空间维度,输出 21 通道
↓
[Adaptive Avg Pool] → 全局平均池化,得到 21 维 logits
↓
[Sigmoid] → 0~1 的可见性概率
3.2 为什么这样设计?
这里的设计哲学非常值得品味:
Conv 1×1 降维:骨干网络输出的特征图通常有1280个通道(WiLoR ViT)或512-2048个通道(ResNet),直接处理太贵了。1×1卷积把通道数压到256,既保留了空间信息,又大幅降低了后续计算量。这种降维操作在计算机视觉中非常常见,是一种经典的效率优化手段。
Token 序列 + FC:把空间特征图展平成 token 序列(比如16×12=192个token),然后用一个全连接层做特征变换。这一步的作用是让每个空间位置的特征更加"语义化",为后续的注意力机制做准备。
GAU(门控注意力单元):这是整个设计的点睛之笔。传统的自注意力(Self-Attention)计算量是O(n²),对于192个token来说还算可以接受,但GAU用了一种更巧妙的方式来实现类似的效果------它用门控机制代替了softmax注意力,计算更快,参数更少,而且实验效果更好。
Conv 1×1 + 全局池化:最后把token序列重新排列回空间特征图,用1×1卷积把通道数映射到21(关键点数量),然后全局平均池化得到每个关键点的logit。这种设计让模型能够充分利用空间信息,同时保持输出的简洁性。
3.3 GAU:门控注意力单元

GAU(Gated Attention Unit)是这个模型中最有意思的模块。它的核心思想是:用门控机制来模拟注意力的效果,但避免softmax带来的计算开销。
具体流程如下:
- 输入归一化:先对输入做LayerNorm,稳定训练过程
- 三路投影 :一个线性层把输入投影成三部分:
u、v和baseu和v的维度是2 × hidden_dim(扩展2倍,增加表达能力)base的维度是s(默认128,用于生成注意力核)
- 门控 :
u和v分别经过SiLU激活(一种平滑的ReLU变体) - 注意力核 :
base通过两组可学习的(gamma, beta)参数生成q和k,然后计算qk = q × k^T / √s,再经过ReLU²得到注意力核 - 加权求和 :注意力核乘以
v,再与u做逐元素乘法 - 输出投影 + 残差:最后通过一个线性层投影回原维度,加上残差连接
这个设计的精妙之处在于:
ReLU(qk)²代替了softmax(qk),计算更快且梯度更稳定- 门控机制(
u × out)让模型能选择性地传递信息 - 参数量远小于标准自注意力,但效果相当甚至更好
- 残差连接保证了梯度流通,训练更稳定
四、多骨干网络支持:一个头,五个身体

这个项目的一个重大工程贡献是:它支持五种不同的骨干网络(backbone),而且只需要替换特征提取器,可见性头完全不用改。这种设计体现了优秀的软件工程思想------关注点分离和接口抽象。
4.1 五种骨干网络
| 骨干网络 | 类型 | 特征维度 | 输出尺寸 | 参数量 | 特点 |
|---|---|---|---|---|---|
| WiLoR ViT | Vision Transformer | 1280 | 16×12 | ~600M | 默认选择,与3D姿态共享骨干 |
| HaMeR ViT | ViTPose-H | 1280 | 16×12 | ~600M | 纯骨干,无MANO回归头 |
| ResNet | CNN | 512-2048 | 8×8 | 11M-60M | 轻量级,全卷积 |
| ViT | Vision Transformer | 768-1280 | 14×14-16×16 | 86M-632M | torchvision原生 |
| CSPNeXt | CNN (RTMDet) | 384-1280 | 8×8 | 5M-50M | 纯PyTorch实现,无需mmdet |
4.2 统一接口
所有骨干网络都遵循同一个契约:
- 输入:
(B, 3, 256, 256)的归一化图像 - 输出:
(B, C, H, W)的空间特征图 - 暴露一个
feat_dim属性(即C)
Visibility Head 只关心 feat_dim,不关心特征图是怎么来的。这意味着你可以随时换用不同的骨干网络来对比效果,而不用改任何头的代码。这种设计让实验变得非常灵活,研究者可以快速测试不同的backbone组合。
4.3 Head-only 训练策略
这里有一个非常实用的设计:冻结骨干,只训练头。
WiLoR的ViT骨干有约6亿参数,如果在可见性任务上微调它,显存和时间成本都很高。作者选择冻结骨干(freeze_backbone=True),只训练Visibility Head的约200万参数。实验表明,这种策略在精度上几乎不损失,但训练速度快了数倍,显存占用也大幅下降。
对于需要加载预训练骨干权重的场景(如HaMeR),系统支持"head-only checkpoint"------checkpoint里只保存头的权重,骨干权重从原始预训练模型加载。这大大减小了checkpoint的体积,方便分发和部署。
五、数据工程:两个数据集的巧妙融合

5.1 COCO-WholeBody
COCO-WholeBody是COCO数据集的扩展,为每个人体实例标注了133个关键点,其中包括每只手21个关键点。每个关键点有一个可见性标志 v:
v=0:未标注(不参与训练)v=1:已标注但被遮挡(target=0)v=2:已标注且可见(target=1)
这个数据集的优势是数据量大(数万张图像),但遮挡模式相对简单。大多数情况下,手部的遮挡来自于画面边缘或者其他身体部位,遮挡程度较轻。
5.2 HInt 数据集
HInt是一个专门针对手部遮挡的数据集,来源于Ego4D和Epic-Kitchens等第一人称视角视频。它的标注更加精细:
existence=1, occlusion=0:可见(target=1)existence=1, occlusion=1:被遮挡(target=0)existence=0:在画面外(也当作遮挡,target=0)
HInt的遮挡模式更加多样和真实,因为第一人称视角下手部经常被物体、另一只手或画面边缘遮挡。这种场景下的遮挡往往更加严重,手指可能被完全挡住,只露出一点点或者完全看不见。
5.3 数据增强策略
训练时使用了一套相当激进的数据增强:
- 随机仿射变换:旋转±30°,缩放0.8-1.2倍,模拟不同的拍摄角度和距离
- 随机水平翻转:50%概率,增加数据多样性
- 颜色抖动:亮度、对比度0.8-1.2倍,模拟不同的光照条件
- HSV扰动:色相0.5,饱和度0.5,明度0.5,增强对颜色变化的鲁棒性
- 高斯模糊:30%概率,核大小3-7,模拟运动模糊或失焦
- 灰度化:10-20%概率,强制模型学习形状信息而非颜色
这些增强确保了模型对各种光照、角度和模糊条件的鲁棒性。在实际应用中,手部图像的质量可能很差(低分辨率、运动模糊、光照不均),这些增强让模型能够应对这些挑战。
六、训练细节:小模型也能训出好效果

6.1 损失函数
训练使用掩码BCE损失(Masked Binary Cross-Entropy):
Loss = Σ mask_i × BCE(logits_i, target_i) / Σ mask_i
mask 的作用是忽略那些未标注的关键点(COCO中 v=0 的情况)。对于HInt数据集,所有关键点都有标注,所以mask全为1。
可选的 pos_weight 参数用于处理正负样本不均衡------被遮挡的关键点通常比可见的少,通过给正样本更高的权重来平衡。系统支持自动估计每个关键点的pos_weight,这在数据分布不均匀时非常有用。
6.2 优化策略
- 优化器:AdamW,学习率1e-3,权重衰减0.05。AdamW相比Adam有更好的泛化性能
- 学习率调度:LinearLR warmup(3个epoch) + CosineAnnealingLR。warmup让训练初期更稳定,cosine调度让后期收敛更平滑
- 混合精度训练:AMP(自动混合精度),加速训练并减少显存。在支持Tensor Core的GPU上,速度可以提升2-3倍
- 梯度裁剪:最大梯度范数1.0,防止梯度爆炸。这在训练深层网络时非常重要
- 批量大小:256(通过梯度累积实现)。大batch size有助于稳定训练,但显存有限时可以通过累积来模拟
6.3 评估指标
- Accuracy:所有有效关键点的分类准确率。这是最直观的指标
- mAP:每个关键点单独计算Average Precision,再取平均。mAP对类别不均衡更鲁棒
- F1 Score:精确率和召回率的调和平均。F1在正负样本不均衡时比accuracy更有意义
- PR曲线:微平均的Precision-Recall曲线。可以直观看到模型在不同阈值下的表现
- ROC曲线:微平均的ROC曲线。展示模型在不同阈值下的真阳性和假阳性率
七、推理流程:从图像到结果

推理时的完整流程如下:
- 输入RGB图像 → WiLoR pipeline
- YOLO检测:找到所有手的边界框 + 左右手分类。YOLO的速度非常快,可以在几毫秒内完成
- 图像裁剪:对每只手,扩展边界框,裁剪正方形区域。扩展是为了确保手指尖不会被裁掉
- 抗锯齿预处理:如果裁剪区域比模型输入大很多,先做高斯模糊再缩放(避免混叠效应)。这一步很重要,可以避免缩放时出现锯齿
- WiLoR推理:得到21个关键点的3D坐标和MANO参数。这一步同时完成了检测和3D姿态估计
- 可见性推理:裁剪区域送入Visibility Net,得到21维可见性概率。这一步非常快,几乎不增加延迟
- 结果组装:合并检测框、3D关键点、可见性概率,可选地计算旋转信息
整个流程是端到端的,用户只需要传入一张图像,就能拿到所有需要的信息。这种设计大大简化了使用流程,降低了用户的上手难度。
7.1 旋转可视化
一个很酷的功能是旋转可视化。系统不仅能告诉你哪些关键点可见,还能画出每个关节的3D旋转:
- 全局朝向(global_orient):手腕在相机坐标系中的旋转,用RGB三轴表示。红色=X轴,绿色=Y轴,蓝色=Z轴
- 逐关节旋转(hand_pose):沿MANO运动学链累积的旋转。每个关节的旋转是相对于父关节的
- 指尖旋转:远端指骨的旋转(继承自最近的MANO关节)。指尖的旋转对于精细操作任务非常重要
这些旋转信息以axis-angle和欧拉角(roll, pitch, yaw)两种形式提供。axis-angle更适合计算机处理,欧拉角更直观易懂。
八、可视化系统:一眼看懂遮挡

系统的可视化设计非常直观:
- 绿色:关键点可见(visibility ≈ 1.0)。模型对这个关键点的预测很有信心
- 红色:关键点被遮挡(visibility ≈ 0.0)。模型认为这个关键点被挡住了
- 渐变色:中间值表示模型不太确定。黄色、橙色表示模型在犹豫
骨架线条的颜色取两端关键点可见性的最小值------只要有一端被遮挡,整条线就偏红。这种设计让遮挡区域一目了然。如果一根手指的两个关节都可见,那么连接它们的骨头就是绿色的;如果其中一个被遮挡,骨头就会变红。
边界框用橙色标出,并标注左右手(L/R)和检测置信度。置信度越高,说明YOLO对检测结果越有信心。
九、实际效果展示

从上面的GIF可以看到,当手抓握物体时,被物体挡住的手指关节会变成红色,而露在外面的关节保持绿色。这种实时的可见性预测对于机器人抓取、VR交互等应用非常有价值。

当双手交叉时,被另一只手遮挡的部分会被正确识别为红色。这种场景在手势识别、动作捕捉中很常见,准确的可见性预测可以大大提高下游任务的性能。

在第一人称视角下,手部经常被物体、另一只手或画面边缘遮挡。模型能够准确判断哪些关节可见,哪些被遮挡,为AR/VR应用提供可靠的手部跟踪。

即使在快速运动的场景下,可见性预测依然稳定。这说明模型对运动模糊有一定的鲁棒性,能够应对实际应用中常见的图像质量问题。

在多人场景中,每只手都能独立处理,互不干扰。这种能力对于多人协作、社交场景分析等应用非常重要。
十、工程亮点:开箱即用

这个项目的工程质量也非常值得学习:
10.1 模块化设计
pipeline.py:端到端推理流程,封装了所有细节visibility_net.py:可见性网络定义,包含Visibility Head和GAUbackbones/:五种骨干网络的统一接口,方便扩展transforms.py:图像预处理,包括裁剪、翻转、归一化rotations.py:旋转数学工具,axis-angle和欧拉角转换visualization.py:可视化绘制,颜色编码和骨架绘制training/:完整的训练和评估代码,包含数据集、模型、训练循环
10.2 多种使用方式
- Python API :
from hand_visibility_detector import HandVisibilityPipeline,几行代码即可使用 - 命令行工具 :
python demo.py image.jpg -o output.jpg,快速测试 - 视频处理 :
python demo_video.py video.mp4 -o output.mp4,批量处理视频 - Gradio Web UI :
python demo_gradio.py,支持图片和视频上传,交互式体验 - HuggingFace Demo:在线体验,无需安装,直接在浏览器中使用
10.3 依赖管理
使用 uv 做包管理,依赖分为三组:
- base:推理所需的最小依赖,包括wilor-mini、huggingface-hub等
- demo:+Gradio Web UI,用于交互式演示
- train:+训练和评估工具,包括omegaconf、wandb、opencv等
这种分组设计让用户可以根据自己的需求选择安装,避免不必要的依赖。
10.4 预训练模型
在HuggingFace上发布了两个预训练checkpoint:
best.pt:WiLoR骨干,默认选择best_hamer.pt:HaMeR骨干,用于对比实验
自动下载,开箱即用。用户不需要手动下载和配置权重文件。
十一、性能与局限
优势
- 精度高:在HInt和COCO-WholeBody上都达到了SOTA的可见性预测精度
- 速度快:冻结骨干后,可见性头的推理几乎不增加延迟,适合实时应用
- 灵活性强:五种骨干网络可选,适配不同场景和硬件条件
- 工程完善:从训练到推理到可视化,全链路打通,开箱即用
- 文档齐全:README详细,代码注释清晰,易于理解和扩展
局限
- 依赖WiLoR的检测质量:如果手都没检测到,可见性就无从谈起。检测失败会直接影响整个pipeline
- 二分类可见性:目前只判断"可见/不可见",没有区分"部分遮挡"的程度。某些应用可能需要更细粒度的可见性信息
- 训练数据有限:HInt数据集规模相对较小,泛化到极端场景可能不够。需要更多多样化的训练数据
- 2D假设:模型假设关键点要么完全可见要么完全不可见,没有考虑深度方向的遮挡。这在某些3D场景中可能不够准确
Hand Visibility Detector 用一种极其优雅的方式解决了一个看似简单但实际很棘手的问题。它的核心贡献是:
- 问题定义清晰:把手指可见性预测从3D手部估计中独立出来,作为一个专门的子任务。这种解耦让问题更容易处理
- 架构设计精巧:GAU + Conv的轻量级头,在几乎不增加计算量的前提下实现了高精度。这种设计平衡了性能和效率
- 工程实现完善:多骨干支持、完整的训练/评估/推理链路、开箱即用的预训练模型。从研究到部署的全流程覆盖
这个工作告诉我们:在深度学习时代,"小而美"的专用模块往往比"大而全"的通用模型更实用。与其让一个模型同时做所有事情,不如让每个模块专注做好一件事,然后通过pipeline组合起来。这种模块化设计不仅更容易优化,也更容易维护和扩展。对于计算机视觉研究者来说,这个项目提供了一个很好的范例:如何定义一个清晰的问题,设计一个高效的解决方案,并用工程化的方式实现它。对于应用开发者来说,这个项目提供了一个开箱即用的工具,可以立即集成到自己的系统中。
论文信息:
- 标题:Hand Visibility Detector: Per-Keypoint Visibility Estimation for Hands
- 作者:Ryosei Hara, Masashi Hatano, Rintaro Yanagi, Atsushi Hashimoto, Takuma Yagi, Mariko Isogawa
- 机构:庆应义塾大学、AIST、OMRON SINIC X、东京大学
- 论文:arXiv:2608.11574
- 代码:GitHub
- 模型:HuggingFace