Solutions(解决方案)
MediaPipe Solutions 提供了一套库和工具,可以将这些解决方案插入到自己的应用程序中,根据需求定制它们,并在多个开发中使用它们的平台。MediaPipe Solutions是MediaPipe开源的一部分项目,可以进一步自定义Solutions的代码,以满足我们自己的应用需求。
-
MediaPipe Tasks:用于部署的跨平台API和库的solutions。
-
MediaPipe models:预训练、随时可运行的模型,用于每个模型的solution。
通过这些工具,可以自定义和评估解决方案:
-
MediaPipe Model Marker:使用自己的数据自定义solutions。
-
MediaPipe Studio:在浏览器中对solutions进行可视化、评估和基准测试。
MediaPipe Object Detection Graph
透明框表示 Calculator(计算节点),实心框表示 Graph(图)的输入/输出,进入节点顶部和离开底部的线表示输入流/输出流,某些节点左侧的端口表示输入侧 Packet(数据包)。
流程:
- 图的开始有2个分支,一个是检测的慢分支,一个是跟踪的快分支。
- 在检测分支中:帧选择节点首先基于限制频率或场景变化分析来选择要进行检测的帧,并将它们传递到检测器,同时丢弃不相关的帧。目标检测节点消耗机器学习模型和相关联的标签映射作为输入侧数据包,使用推理引擎输出检测结果。
- 在跟踪分支中,更新较早的检测并将它们的位置推进到当前相机帧。
- 在检测合并节点中,将检测和跟踪的结果与来自较早的检测合并,基于他们在帧中的位置和类别接近度来移除重复的结果。 注:检测合并节点对新检测所源自的同一帧进行操作。这由该节点中的默认输入策略自动处理,因为它在一起处理两组检测结果之前对齐它们的时间戳。
- 在检测注释节点中,在摄像机帧顶部添加表示合并检测的注释的叠加,并且在此计算器中进行绘制之前,注释和摄像机帧之间的同步由默认输入策略自动处理。最终结果是稍微延迟的取景器输出(例如,通过几帧),其与计算和跟踪的检测完美地对准,从而以动态方式有效地隐藏模型延迟。
Framework(框架)
要开始使用 MediaPipe Framework,请安装 MediaPipe 构建并开始在 C++、Android 和 iOS 中构建示例应用程序
MediaPipe 框架 是用于构建高效的设备端机器学习的低级组件管道,类似于预制的 MediaPipe 解决方案。MediaPipe Framework实际上是由Packets、Graphs、Calculators构建起来的。
- Packets(数据包):Calculators通过发送和接收数据包进行通信。数据包可以包含任何类型的数据,例如单帧视频或单个整数检测计数。
通常为单个数据包在每个输入时间戳处沿每个输入流发送:
C++
// Create a packet containing some new data.
Packet p = MakePacket<MyDataClass>("constructor_argument");
// Make a new packet with the same data and a different timestamp.
Packet p2 = p.At(Timestamp::PostStream());
或者
c++
// Create some new data.
auto data = absl::make_unique<MyDataClass>("constructor_argument");
// Create a packet to own the data.
Packet p = Adopt(data.release()).At(Timestamp::PostStream());
数据包中的数据可通过以下方式访问 Packet::Get<T>()
- Graphs(图):CalculatorGraphConfig 原型指定 MediaPipe 图的拓扑和功能。图中的每个节点代表一个特定的计算器或子图,并指定必要的配置,例如注册的计算器/子图类型、输入、输出和可选字段,例如同步中讨论的特定于节点的选项、输入策略和执行器。CalculatorGraphConfig 还有几个其他字段来配置全局图形级别设置,例如 图形执行器配置、线程数和输入流的最大队列大小。
C++
# This graph named main_pass_throughcals_nosubgraph.pbtxt contains 4
# passthrough calculators.
input_stream: "in"
output_stream: "out"
node {
calculator: "PassThroughCalculator"
input_stream: "in"
output_stream: "out1"
}
node {
calculator: "PassThroughCalculator"
input_stream: "out1"
output_stream: "out2"
}
node {
calculator: "PassThroughCalculator"
input_stream: "out2"
output_stream: "out3"
}
node {
calculator: "PassThroughCalculator"
input_stream: "out3"
output_stream: "out"
}
C++
CalculatorGraphConfig BuildGraphConfig() {
Graph graph;
// Graph inputs
Stream<AnyType> in = graph.In(0).SetName("in");
auto pass_through_fn = [](Stream<AnyType> in,
Graph& graph) -> Stream<AnyType> {
auto& node = graph.AddNode("PassThroughCalculator");
in.ConnectTo(node.In(0));
return node.Out(0);
};
Stream<AnyType> out1 = pass_through_fn(in, graph);
Stream<AnyType> out2 = pass_through_fn(out1, graph);
Stream<AnyType> out3 = pass_through_fn(out2, graph);
Stream<AnyType> out4 = pass_through_fn(out3, graph);
// Graph outputs
out4.SetName("out").ConnectTo(graph.Out(0));
return graph.GetConfig();
}
-
Calculators(计算器):计算器是通过定义类的新子类、实现许多方法并将新的子类注册到 MediaPipe。新的计算器必须实现以下四种方法:
GetContract()
:Calculator 作者可以在 GetContract() 中指定Calculator 的预期输入和输出类型。初始化 Graph 时,框架会调用静态方法来验证连接的输入和输出的数据包类型是否与本规范中的信息匹配。Open()
:Graph 启动后,框架调用 Open()。此时输入侧 Packet 可供 Calculator 使用。Open() 解释节点配置操作并准备 Calculator 的每个 Graph 的运行状态。此函数还可以将 Packet 写入 Calculator 输出。Open() 期间的错误可能会终止 Graph 运行。Process()
:对于具有输入的 Calculator,每当至少一个输入流有可用 Packet 时,framework 就会重复调用 Process()。默认情况下,framework 保证所有输入具有相同的时间戳。当启用并行执行时,可以同时调用多个 Process() 调用。如果在 Process() 期间发生错误,框架将调用 Close() 并且 Graph 运行终止。Close()
:对 Process() 的所有调用完成后或所有输入流关闭后,framework 将调用 Close()。如果调用 Open() 并成功,即使 Graph 运行因错误而终止,也始终会调用此函数。在 Close() 期间,任何输入流都无法提供输入,但它仍然可以访问输入侧 Packet,因此可以写入输出。Close() 返回后,Calculator 应被视为死节点。Graph 运行完毕后,Calculator 对象就会被销毁。