Mediapipe 在RK3399PRO上的初探(二)(自定义Calculator)

PS:要转载请注明出处,本人版权所有。

PS: 这个只是基于《我自己》的理解,

如果和你的原则及想法相冲突,请谅解,勿喷。

环境说明
  • Ubuntu 18.04
  • gcc version 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04)
  • RK3399PRO 板卡

前言


  本文有一篇前置文章为《Mediapipe 在RK3399PRO上的初探(一)(编译、运行CPU和GPU Demo, RK OpenglES 填坑,编译bazel)》 https://blog.csdn.net/u011728480/article/details/115838306 ,本文是在前置文章上的一点点探索与发现。

  在前置文章中,我们介绍了一些编译和使用Mediapipe的注意事项,也初步跑起来了一点点demo,算是对这个框架有了一个初步的认知。但是前置文章也说了,了解这个框架的意义是替换我们小组现有的框架,而且能够支撑我们的板卡产品。于是我们还需要一个最最最最最最重要的功能就是“Custormer Calculator”,就是自定义计算节点,因为这个框架的核心就是计算节点。下文我们将会讲到这个框架的一些基本概念,这些概念都是来至于官方文档的机翻+我自己的理解。同时,我会给出一个我定义的计算节点的实例,算是给大家一个感性认知。





Mediapipe的一些概念(本小节基本来至于官方文档的机翻+我自己的理解,不感兴趣,请直接看下一小节)


  本小节内容,主要参考 https://github.com/google/mediapipe/tree/master/docs/framework_concepts 的几个介绍概念的md文件。
  MediaPipe 感觉中文直译为“媒体管道”,为啥会有这个名字呢?因为它把数据+处理组合成一个计算节点,然后把一个一个节点连接起来,用数据来驱动整个核心逻辑。如果大家对Caffe、ncnn类似的框架源码有一点点了解的话,就会觉得他们非常像,但是又不像。像是说,都是通过配置文件定义计算节点逻辑图,然后通过运算,得到我们想得到的逻辑图中节点的数据。不像的话,就是说的是Mediapipe的调度机制了,极大的增加了节点计算的并行功能,而那些框架是按照图节点的上下顺序进行执行的。
  上面这段话可能有点抽象,我想表达的就是 Mediapipe 就是把任何一个“操作”都可以变为一个Calculator,因为我们的每一个项目的逻辑抽象出来都是 Calculator0+Data0->Calculator1+Data1->Calculator2+Data2->… …,然后,Mediapipe 做的是基于这种calculator的调度和执行。这里我举个栗子:

  • 人脸图+检测算法=人脸检测结果,这是一个Calculator
  • RTSP流+解码模块=解码之后的图片,这是一个Calculator

好了,其他的就不过分的解读了,下面就使用MediaPipe的helloworld example( https://github.com/google/mediapipe/blob/master/mediapipe/examples/desktop/hello_world/hello_world.cc )为例,简单的说说以下几个概念。



Packet

  Packet,是mediapipe中的数据单元,它可以接收任意类型的数据。也是mediapipe中的数据流动单元。就是在mediapipe中,我们设计的Graph中,所有的逻辑流动都是通过packet流动来实现的。

  实例代码片段:

//MakePacket<std::string>("Hello World!") 创建一个packet,顺带说一句,我不喜欢这里的宏,不利于维护
MP_RETURN_IF_ERROR(graph.AddPacketToInputStream("in", MakePacket<std::string>("Hello World!").At(Timestamp(i))));

//从packet中获取数据
mediapipe::Packet packet;
packet.Get<std::string>();


Graph

  Graph是由各个Calculator组成的,可以直接把Calculator理解为数据结构中图的节点。而Graph直接把他当做图就行了。Graph是我们定义的逻辑流程的具体载体,也就是说我们的业务逻辑是什么样子的,那么Graph里面就会有相应的逻辑流程。可以具备多输入输出。

  实例代码片段:

CalculatorGraph graph;


Caculator(Node)

  上面不是介绍了Graph和Packet嘛,这里的Calculator就是Graph里面的节点,也是处理Packet的具体单元。可以具备多输入输出。

  实例代码片段:

//比如mediapipe/calculators/core/pass_through_calculator.h里面的定义,这个Calculator被Helloworld这个例子使用,作用就是把输入的数据直接传递到输出,不做任何处理,类似NOP
class PassThroughCalculator : public CalculatorBase 


Stream

  Stream 就是 Caculator 之间的连接起来后,形成的一个数据流动路径。



Side packets

  Side packets 可以直接理解为一些静态的数据packet,在graph创建之后就不会改变的数据。

… …





自定义实现Calculator


  Talk is cheap, show me the code.

/*
 * @Description: 
 * @Author: Sky
 * @Date: 
 * @LastEditors: Sky
 * @LastEditTime: 
 * @Github: 
 */
#include <cstdio>
#include "mediapipe/framework/calculator_graph.h"
#include "mediapipe/framework/port/logging.h"
#include "mediapipe/framework/port/parse_text_proto.h"
#include "mediapipe/framework/port/status.h"

//customer calculator
#include "mediapipe/framework/calculator_framework.h"
#include "mediapipe/framework/port/canonical_errors.h"


class CustomerDataType{
   
   

  public:
  CustomerDataType(int i, float f, bool b, const std::string & str):
  val_i(i),
  val_f(f),
  val_b(b),
  s_str(str)
  {
   
   }
  int val_i = 1;
  float val_f = 11.f;
  bool val_b = true;
  std::string s_str = "customer str.";
};



namespace mediapipe {
   
   

  class MyStringProcessCalculator : public CalculatorBase {
   
   
  public:
    /*
    Calculator authors can specify the expected types of inputs and outputs of a calculator in GetContract(). 
    When a graph is initialized, the framework calls a static method to verify if the packet types of the connected inputs and outputs match the information in this specification.
    */
    static absl::Status GetContract(CalculatorContract* cc) {
   
   
      
      /*
      class InputStreamShard;
      typedef internal::Collection<InputStreamShard> InputStreamShardSet;
      class OutputStreamShard;
      typedef internal::Collection<OutputStreamShard> OutputStreamShardSet;
      */
      //cc->Inputs().NumEntries() returns the number of input streams
      // if (!cc->Inputs().TagMap()->SameAs(*cc->Outputs().TagMap())) {
   
   

      //   return absl::InvalidArgumentError("Input and output streams's TagMap can't be same.");
      // }

      //set stream
      // for (CollectionItemId id = cc->Inputs().BeginId(); id < cc->Inputs().EndId(); ++id) {
   
   

      //   cc->Inputs().Get(id).SetAny();
      //   cc->Outputs().Get(id).SetSameAs(&cc->Inputs().Get(id));
      // }
      cc->Inputs().Index(0).SetAny();
      cc->Inputs().Index(1).Set<CustomerDataType>();

      cc->Outputs().Index(0).SetSameAs(&cc->Inputs().Index(0));


      //set stream package
      // for (CollectionItemId id = cc->InputSidePackets().BeginId(); id < cc->InputSidePackets().EndId(); ++id) {
   
   
        
      //   cc->InputSidePackets().Get(id).SetAny();
      // }
      // cc->InputSidePackets().Index(0).SetAny();
      // cc->InputSidePackets().Index(1).Set<CustomerDataType>();//set customer data-type


      if (cc->OutputSidePackets().NumEntries<
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值