许可优化
许可优化
产品
产品
解决方案
解决方案
服务支持
服务支持
关于
关于
软件库
当前位置:服务支持 >  软件文章 >  基于MediaPipeUnityPlugin的三维手势追踪(Unity+MediaPipe)

基于MediaPipeUnityPlugin的三维手势追踪(Unity+MediaPipe)

阅读数 10
点赞 0
article_banner

简述

目前比较好用的几种手势追踪,很多都是基于AR,需要硬件支持

而Google的MediaPipe则门槛比较低,用电脑 摄像头  也能跑

   同时,又有MediaPipeUnityPlugin已经帮我们移植进了Unity

但 MediaPipe 是基于图像识别的,因此我手的往前往后,近大远小 在它看来只是在放大缩小,得到的数据都只发生在一个平面上,缺少硬件的支持所以没有深度信息。

   幸运的是,即使只是一个平面,强大的Google至少帮我们做到了骨骼的相对深度
请添加图片描述

于是,本文要做的是,如最上面视频所示,模拟出手的深度,让我们的手真正在一个三维空间里面活动

如果你尚不了解 MediaPipeUnityPlugin,先看看我上一篇 MediaPipe-UnityPlugin的基本使用

   如果你已经认识 MediaPipeUnityPlugin 的 HandTracking,可以直接跳到 [5.获取坐标] 环节


参考案例

Assets\MediaPipeUnity\Samples\Scenes\Hand Tracking\Hand Tracking.unity
在这里插入图片描述

虽说插件已经提供了完整的案例,但因为我们要在这基础上加入额外的功能,所以还是模仿着搭一个自己的Graph和Solution,同时去掉一些用不到的内容,以简化代码

1. 声明一个 MyGraph

public class MyHandTrackingGraph : GraphRunner
{
   
   
	public override void StartRun(ImageSource imageSource)
    {
   
   
    	// 此处进行输出流的启动 和 CalculatorGraph的StartRun
    }
    protected override IList<WaitForResult> RequestDependentAssets()
    {
   
   
    	// 此处加载用到的数据文件
    }
}
  • 首先声明我们的输出流 和 输出流的名称

输出流我们使用插件提供的 OutputStream<TPacket, TValue>

   手部追踪的输出流有6种:
手掌检测基于手掌检测的矩形区手节点的坐标手节点的世界坐标基于坐标的矩形区左右手检测

分别如下:

// 手掌检测
OutputStream<DetectionVectorPacket, List<Detection>> _palmDetectionsStream;
const string _PalmDetectionsStreamName = "palm_detections";
// 基于手掌检测的矩形区
OutputStream<NormalizedRectVectorPacket, List<NormalizedRect>> _handRectsFromPalmDetectionsStream;
const string _HandRectsFromPalmDetectionsStreamName = "hand_rects_from_palm_detections";
// 手节点的坐标
OutputStream<NormalizedLandmarkListVectorPacket, List<NormalizedLandmarkList>> _handLandmarksStream;
const string _HandLandmarksStreamName = "hand_landmarks";
// 手节点的世界坐标
OutputStream<LandmarkListVectorPacket, List<LandmarkList>> _handWorldLandmarksStream;
const string _HandWorldLandmarksStreamName = "hand_world_landmarks";
// 基于坐标的矩形区
OutputStream<NormalizedRectVectorPacket, List<NormalizedRect>> _handRectsFromLandmarksStream;
const string _HandRectsFromLandmarksStreamName = "hand_rects_from_landmarks";
// 左右手检测
OutputStream<ClassificationListVectorPacket, List<ClassificationList>> _handednessStream;
const string _HandednessStreamName = "handedness";

还有我们输入流的名称

const string _InputStreamName = "input_video";

我们此次用到的只有手节点的坐标,所以只需要

const string _InputStreamName = "input_video";

OutputStream<NormalizedLandmarkListVectorPacket, List<NormalizedLandmarkList>> _handLandmarksStream;
const string _HandLandmarksStreamName = "hand_landmarks";
  • 随后在 ConfigureCalculatorGraph() 对它进行一下初始化和配置
protected override Status ConfigureCalculatorGraph(CalculatorGraphConfig config)
{
   
   
    _handLandmarksStream = new OutputStream<NormalizedLandmarkListVectorPacket, List<NormalizedLandmarkList>>(
        calculatorGraph, _HandLandmarksStreamName, config.AddPacketPresenceCalculator(_HandLandmarksStreamName), timeoutMicrosec);
        
    return base.ConfigureCalculatorGraph(config);
}

OutputStream的构造函数如下

/// <summary>
///   实例化一个 OutputStream class
///   图形必须具有 PacketPresenceCalculator 节点,用于计算流是否有输出
/// </summary>
/// <remarks>
///   当您希望同步获取输出,但不希望在等待输出时阻塞线程时,这很有用
/// </remarks>
/// <param name="calculatorGraph"> 流的所有者 </param>
/// <param name="streamName"> 输出流的名称 </param>
/// <param name="presenceStreamName"> 当输出存在时,输出true的流的名称 </param>
/// <param name="timeoutMicrosec"> 如果输出数据包为空,则 OutputStream 实例会丢弃数据包,直到此处指定的时间结束 </param>
public OutputStream(CalculatorGraph calculatorGraph, string streamName, string presenceStreamName, long timeoutMicrosec = 0) : this(calculatorGraph, streamName, false, timeoutMicrosec)
{
   
   
    this.presenceStreamName = presenceStreamName;
}
  • 然后定义一个异步输出监听的接口 和 一个获取同步输出的接口
public event EventHandler<OutputEventArgs<List<NormalizedLandmarkList>>> OnHandLandmarksOutput
{
   
   
    add => _handLandmarksStream.AddListener(value);
    remove => _handLandmarksStream.RemoveListener(value);
}

public bool TryGetNext(out List<NormalizedLandmarkList> handLandmarks, bool allowBlock = true)
{
   
   
    var currentTimestampMicrosec = GetCurrentTimestampMicrosec();
    return TryGetNext(_handLandmarksStream, out handLandmarks, allowBlock, currentTimestampMicrosec);
}
  • 在 StartRun() 和 Stop() 中进行启动和释放
public override void StartRun(ImageSource imageSource)
{
   
   
    _handLandmarksStream.StartPolling().AssertOk();
    calculatorGraph.StartRun().AssertOk();
}

public override void Stop()
{
   
   
    _handLandmarksStream?.Close();
    _handLandmarksStream = null;
    base.Stop();
}
  • 然后定义一个输入接口,用于将输入源传给输入流
public void AddTextureFrameToInputStream(TextureFrame textureFrame)
{
   
   
    AddTextureFrameToInputStream(_InputStreamName, textureFrame);
}
  • 至此,输入输出已经写好,我们的Graph目前应该是像这样
public class MyHandTrackingGraph : GraphRunner
{
   
   
    const string _InputStreamName = 
免责声明:本文系网络转载或改编,未找到原创作者,版权归原作者所有。如涉及版权,请联系删


相关文章
技术文档
QR Code
微信扫一扫,欢迎咨询~
customer

online

联系我们
武汉格发信息技术有限公司
湖北省武汉市经开区科技园西路6号103孵化器
电话:155-2731-8020 座机:027-59821821
邮件:tanzw@gofarlic.com
Copyright © 2023 Gofarsoft Co.,Ltd. 保留所有权利
遇到许可问题?该如何解决!?
评估许可证实际采购量? 
不清楚软件许可证使用数据? 
收到软件厂商律师函!?  
想要少购买点许可证,节省费用? 
收到软件厂商侵权通告!?  
有正版license,但许可证不够用,需要新购? 
联系方式 board-phone 155-2731-8020
close1
预留信息,一起解决您的问题
* 姓名:
* 手机:

* 公司名称:

姓名不为空

姓名不为空

姓名不为空
手机不正确

手机不正确

手机不正确
公司不为空

公司不为空

公司不为空