MediaPipe介绍
我们直接引用前人的智慧:MediaPipe介绍
而 MediaPipeUnityPlugin,则帮我们把MediaPipe(C++)移植到了C#,使其能被Unity调用
MediaPipeUnityPlugin
使用这个插件,你可以:
– 用 C# 编写 MediaPipe 代码
– 在Unity上运行 MediaPipe 的官方解决方案
– 在Unity上运行您的自定义 Calculator 和 CalculatorGraph
(根据输入/输出的类型,您可能需要编写 C++ 代码)
1. 安装
github地址:https://github.com/homuler/MediaPipeUnityPlugin
下载地址:https://github.com/homuler/MediaPipeUnityPlugin/releases
本文使用的版本:MediaPipeUnity.0.11.0
建议Unity版本:2021.3以上
2. 运行一个官方解决方案
当然,该插件已经提供了案例,此处目的是了解它的运作方式,以便你想要按自己的需求搭一个环境
GraphRunner
Graph运行基类,封装了config的加载及初始化配置、MediaPipe数据流的输入输出,相当于MVC中的M
- 应用层 xxxGraph : GraphRunner
声明具体的输出流并配置
定义输入流的输入接口
定义获取同步输出的接口
定义异步输出监听的接口
加载需要的数据文件
Solution
操作基类,负责图像处理程序的生命周期,管理并获取图像源,相当于MVC中的C
-
派生类 ImageSourceSolution<T> : Solution
-T : GraphRunner
-管理GraphRunner实例及一些组件
-实现具体的每帧更新逻辑
– 抽象接口:
– OnStartRun() 初始化
– AddTextureFrameToInputStream(TextureFrame textureFrame) 将图像传给输入流
– WaitForNextValue() 获取同步输出 -
应用层 xxxSolution : ImageSourceSolution<xxxGraph>
-声明回调,在 OnStartRun() 中绑定Graph的异步监听接口,获取异步输出,并处理
-在 AddTextureFrameToInputStream() 中将得到的图像传给自己的Graph实例
-在 WaitForNextValue() 中获取Graph的同步输出,并处理
Bootstrap
引导程序,选择图像输入源、资源加载方式、CPU或GPU计算,由Solution在Start()中调用
- 图像源:WebCamera、Image、Video
根据选择的图像源,需要挂上相应的组件
– WebCamSource
– StaticImageSource
– VideoSource - 资源加载:StreamingAssets、AssetBundle、Local
TextureFramePool
TextureFrame 的对象池,ImageSourceSolution 会用到
Screen
将图像源输出到一个 RawImage,由 ImageSourceSolution 管理
3. 原始流程
插件文档:https://github.com/homuler/MediaPipeUnityPlugin/wiki/Getting-Started
1)CalculatorGraph
MediaPipe的核心计算器
- 每个 CalculatorGraph 都有自己的配置(CalculatorGraphConfig),在初始化时进行配置:
(这部分由GraphRunner帮我们完成)
方式一:声明时直接传入文本
new CalculatorGraph("Config文本");
方式二:构建一个CalculatorGraphConfig然后传入
var graph = new CalculatorGraph();
var config = CalculatorGraphConfig.Parser.ParseFromTextFormat("Config文本");
graph.Initialize(config);
如下为一个简单的config:
var configText = @"
input_stream: ""in""
output_stream: ""out""
node {
calculator: ""PassThroughCalculator""
input_stream: ""in""
output_stream: ""out1""
}
node {
calculator: ""PassThroughCalculator""
input_stream: ""out1""
output_stream: ""out""
}
";
var graph = new CalculatorGraph(configText);
插件中已预设多个config可以直接使用
- 完事后调用 StartRun() 启动
(这一步需要我们在xxxGraph中调用)
graph.StartRun().AssertOk();
StartRun会返回一个 Status 表示结果
调用 Status.AssertOk() 检查结果,如果不正常则抛出异常
插件中大部分方法都会返回一个Status
如果我们的config中配置了副产物 SidePacket,则启动时还需要传一个进去
graph.StartRun(sidePacket).AssertOk();
2)Packet
在 MediaPipe 中,输入输出是数据包Packet的形式进行传输
Packet需要具体的派生,并指定数据的类型
- 要将一个输入Packet传递给CalculatorGraph,我们使用 CalculatorGraph.AddPacketToInputStream()
var input = new StringPacket("Hello World!"); // 这是一个字符串Packet
graph.AddPacketToInputStream("输入流的名称", input).AssertOk();
(这部分也由GraphRunner帮我们完成,但需要xxxGraph提供一个外部输入接口)
其中 “输入流的名称” 在config中定义,即 “input_stream”,同理下面的 “输出流的名称” 为config中的 “output_stream”
- 要从CalculatorGraph中获取一个输出Packet,我们先要添加一个输出流
var poller = graph.AddOutputStreamPoller<string>("输出流的名称").Value();
实际使用时建议先检查一下status
var statusOfPoller = graph.AddOutputStreamPoller<string>("输出流的名称");
if (statusOfPoller.Ok())
{
var poller = statusOfPoller.Value();
}
然后可以获取输出
var output = new StringPacket();
poller.Next(output); // 输出到这个Packet
Debug.Log(output.Get()); // 获取Packet里的数据
(这部分封装了一个OutputStream类帮我们完成)
3)传输图像
- ImageSourceSolution 从 WebCamera 或其他图像源中得到画面Texture
- Texture 经过 Solution 处理成为 TextureFrame,传递给Graph
- GraphRunner 将 TextureFrame 处理成为 ImageFrame,然后打包成 ImageFramePacket 输入到 CalculatorGraph