গ্রাফ

চিত্রলেখ

একটি CalculatorGraphConfig প্রোটো একটি MediaPipe গ্রাফের টপোলজি এবং কার্যকারিতা নির্দিষ্ট করে। গ্রাফের প্রতিটি node একটি নির্দিষ্ট ক্যালকুলেটর বা সাবগ্রাফের প্রতিনিধিত্ব করে এবং প্রয়োজনীয় কনফিগারেশন নির্দিষ্ট করে, যেমন নিবন্ধিত ক্যালকুলেটর/সাবগ্রাফের ধরন, ইনপুট, আউটপুট এবং ঐচ্ছিক ক্ষেত্র, যেমন নোড-নির্দিষ্ট বিকল্প, ইনপুট নীতি এবং নির্বাহক, সিঙ্ক্রোনাইজেশনে আলোচনা করা হয়েছে।

গ্লোবাল গ্রাফ-লেভেল সেটিংস কনফিগার করার জন্য CalculatorGraphConfig এর আরও বেশ কিছু ক্ষেত্র রয়েছে, যেমন গ্রাফ এক্সিকিউটর কনফিগারেশন, থ্রেডের সংখ্যা এবং ইনপুট স্ট্রীমের সর্বোচ্চ সারি আকার। বিভিন্ন প্ল্যাটফর্মে (যেমন, ডেস্কটপ বনাম মোবাইল) গ্রাফের কর্মক্ষমতা টিউন করার জন্য বেশ কিছু গ্রাফ-স্তরের সেটিংস কার্যকর। উদাহরণস্বরূপ, মোবাইলে, একটি পৃথক নির্বাহকের সাথে একটি ভারী মডেল-অনুমান ক্যালকুলেটর সংযুক্ত করা একটি রিয়েল-টাইম অ্যাপ্লিকেশনের কর্মক্ষমতা উন্নত করতে পারে কারণ এটি থ্রেড লোকেলিটি সক্ষম করে।

নীচে একটি তুচ্ছ CalculatorGraphConfig উদাহরণ যেখানে আমাদের পাসথ্রু ক্যালকুলেটরগুলির সিরিজ রয়েছে:

# 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"
}

MediaPipe জটিল গ্রাফের জন্য একটি বিকল্প C++ উপস্থাপনা অফার করে (যেমন ML পাইপলাইন, হ্যান্ডলিং মডেল মেটাডেটা, ঐচ্ছিক নোড ইত্যাদি)। উপরের গ্রাফটি দেখতে এরকম হতে পারে:

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();
}

C++ এ বিল্ডিং গ্রাফে আরও বিস্তারিত দেখুন।

সাবগ্রাফ

একটি CalculatorGraphConfig সাব-মডিউলগুলিতে মডুলারাইজ করতে এবং উপলব্ধি সমাধানগুলির পুনরায় ব্যবহারে সহায়তা করতে, একটি মিডিয়াপাইপ গ্রাফকে একটি Subgraph হিসাবে সংজ্ঞায়িত করা যেতে পারে। একটি সাবগ্রাফের পাবলিক ইন্টারফেস একটি ক্যালকুলেটরের পাবলিক ইন্টারফেসের মতো ইনপুট এবং আউটপুট স্ট্রিমগুলির একটি সেট নিয়ে গঠিত। সাবগ্রাফটিকে তখন CalculatorGraphConfig কনফিগারে অন্তর্ভুক্ত করা যেতে পারে যেন এটি একটি ক্যালকুলেটর। যখন একটি MediaPipe গ্রাফ একটি CalculatorGraphConfig থেকে লোড করা হয়, তখন প্রতিটি সাবগ্রাফ নোড ক্যালকুলেটরের সংশ্লিষ্ট গ্রাফ দ্বারা প্রতিস্থাপিত হয়। ফলস্বরূপ, সাবগ্রাফের শব্দার্থবিদ্যা এবং কর্মক্ষমতা ক্যালকুলেটরগুলির সংশ্লিষ্ট গ্রাফের সাথে অভিন্ন।

কিভাবে TwoPassThroughSubgraph নামে একটি সাবগ্রাফ তৈরি করতে হয় তার একটি উদাহরণ নিচে দেওয়া হল।

  1. সাবগ্রাফ সংজ্ঞায়িত করা।

    # This subgraph is defined in two_pass_through_subgraph.pbtxt
    # and is registered as "TwoPassThroughSubgraph"
    
    type: "TwoPassThroughSubgraph"
    input_stream: "out1"
    output_stream: "out3"
    
    node {
        calculator: "PassThroughCalculator"
        input_stream: "out1"
        output_stream: "out2"
    }
    node {
        calculator: "PassThroughCalculator"
        input_stream: "out2"
        output_stream: "out3"
    }
    

    সাবগ্রাফের পাবলিক ইন্টারফেসটি নিয়ে গঠিত:

    • গ্রাফ ইনপুট স্ট্রীম
    • গ্রাফ আউটপুট স্ট্রীম
    • গ্রাফ ইনপুট সাইড প্যাকেট
    • গ্রাফ আউটপুট সাইড প্যাকেট
  2. BUILD নিয়ম mediapipe_simple_subgraph ব্যবহার করে সাবগ্রাফ নিবন্ধন করুন। পরামিতি register_as নতুন সাবগ্রাফের জন্য উপাদানের নাম সংজ্ঞায়িত করে।

    # Small section of BUILD file for registering the "TwoPassThroughSubgraph"
    # subgraph for use by main graph main_pass_throughcals.pbtxt
    
    mediapipe_simple_subgraph(
        name = "twopassthrough_subgraph",
        graph = "twopassthrough_subgraph.pbtxt",
        register_as = "TwoPassThroughSubgraph",
        deps = [
                "//mediapipe/calculators/core:pass_through_calculator",
                "//mediapipe/framework:calculator_graph",
        ],
    )
    
  3. মূল গ্রাফে সাবগ্রাফ ব্যবহার করুন।

    # This main graph is defined in main_pass_throughcals.pbtxt
    # using subgraph called "TwoPassThroughSubgraph"
    
    input_stream: "in"
    node {
        calculator: "PassThroughCalculator"
        input_stream: "in"
        output_stream: "out1"
    }
    node {
        calculator: "TwoPassThroughSubgraph"
        input_stream: "out1"
        output_stream: "out3"
    }
    node {
        calculator: "PassThroughCalculator"
        input_stream: "out3"
        output_stream: "out4"
    }
    

গ্রাফ অপশন

মিডিয়াপাইপ ক্যালকুলেটরের জন্য নির্দিষ্ট করা Calculator Options প্রোটোবাফের অনুরূপ মিডিয়াপাইপ গ্রাফের জন্য একটি "গ্রাফ বিকল্প" প্রোটোবাফ নির্দিষ্ট করা সম্ভব। এই "গ্রাফ বিকল্পগুলি" নির্দিষ্ট করা যেতে পারে যেখানে একটি গ্রাফ আহ্বান করা হয়, এবং গ্রাফের মধ্যে ক্যালকুলেটর বিকল্প এবং সাবগ্রাফ বিকল্পগুলি তৈরি করতে ব্যবহৃত হয়।

একটি CalculatorGraphConfig এ, গ্রাফ বিকল্পগুলি একটি সাবগ্রাফের জন্য ঠিক ক্যালকুলেটর বিকল্পগুলির মতো নির্দিষ্ট করা যেতে পারে, যেমনটি নীচে দেখানো হয়েছে:

node {
  calculator: "FlowLimiterCalculator"
  input_stream: "image"
  output_stream: "throttled_image"
  node_options: {
    [type.googleapis.com/mediapipe.FlowLimiterCalculatorOptions] {
      max_in_flight: 1
    }
  }
}

node {
  calculator: "FaceDetectionSubgraph"
  input_stream: "IMAGE:throttled_image"
  node_options: {
    [type.googleapis.com/mediapipe.FaceDetectionOptions] {
      tensor_width: 192
      tensor_height: 192
    }
  }
}

একটি CalculatorGraphConfig এ, গ্রাফ বিকল্পগুলি গ্রহণ করা যেতে পারে এবং ক্যালকুলেটর বিকল্পগুলি পূরণ করতে ব্যবহার করা যেতে পারে, যেমনটি নীচে দেখানো হয়েছে:

graph_options: {
  [type.googleapis.com/mediapipe.FaceDetectionOptions] {}
}

node: {
  calculator: "ImageToTensorCalculator"
  input_stream: "IMAGE:image"
  node_options: {
    [type.googleapis.com/mediapipe.ImageToTensorCalculatorOptions] {
        keep_aspect_ratio: true
        border_mode: BORDER_ZERO
    }
  }
  option_value: "output_tensor_width:options/tensor_width"
  option_value: "output_tensor_height:options/tensor_height"
}

node {
  calculator: "InferenceCalculator"
  node_options: {
    [type.googleapis.com/mediapipe.InferenceCalculatorOptions] {}
  }
  option_value: "delegate:options/delegate"
  option_value: "model_path:options/model_path"
}

এই উদাহরণে, FaceDetectionSubgraph FaceDetectionOptions প্রোটোবাফ গ্রাফ বিকল্প গ্রহণ করে। FaceDetectionOptions ক্যালকুলেটর অপশন ImageToTensorCalculatorOptions এবং সাবগ্রাফ অপশন InferenceCalculatorOptions এ কিছু ক্ষেত্রের মান নির্ধারণ করতে ব্যবহৃত হয়। ক্ষেত্রের মানগুলি option_value: সিনট্যাক্স ব্যবহার করে সংজ্ঞায়িত করা হয়।

CalculatorGraphConfig::Node protobuf-এ, node_options: এবং option_value: ক্ষেত্রগুলি একসাথে একটি ক্যালকুলেটরের জন্য বিকল্প মান নির্ধারণ করে যেমন ImageToTensorCalculatornode_options: ক্ষেত্রটি টেক্সট প্রোটোবুফ সিনট্যাক্স ব্যবহার করে আক্ষরিক ধ্রুবক মানের একটি সেট সংজ্ঞায়িত করে। প্রতিটি option_value: ক্ষেত্রটি এনক্লোসিং গ্রাফ থেকে তথ্য ব্যবহার করে একটি প্রোটোবাফ ক্ষেত্রের মান নির্ধারণ করে, বিশেষ করে এনক্লোসিং গ্রাফের গ্রাফ বিকল্পের ক্ষেত্রের মান থেকে। উপরের উদাহরণে, option_value: "output_tensor_width:options/tensor_width" FaceDetectionOptions.tensor_width এর মান ব্যবহার করে ImageToTensorCalculatorOptions.output_tensor_width ক্ষেত্রটিকে সংজ্ঞায়িত করে।

option_value: এর সিনট্যাক্স input_stream: সিনট্যাক্স হল option_value: "LHS:RHS" । LHS একটি ক্যালকুলেটর বিকল্প ক্ষেত্র সনাক্ত করে এবং RHS একটি গ্রাফ বিকল্প ক্ষেত্র সনাক্ত করে। আরও বিশেষভাবে, LHS এবং RHS প্রতিটি প্রোটোবাফ ক্ষেত্রের নামগুলির একটি সিরিজ নিয়ে গঠিত যা নেস্টেড প্রোটোবাফ বার্তা এবং '/' দ্বারা পৃথক করা ক্ষেত্রগুলিকে চিহ্নিত করে। এটি "ProtoPath" সিনট্যাক্স নামে পরিচিত। LHS বা RHS-এ উল্লেখ করা নেস্টেড বার্তাগুলিকে ইতিমধ্যেই এনক্লোসিং প্রোটোবাফে সংজ্ঞায়িত করা আবশ্যক যাতে option_value: ব্যবহার করে অতিক্রম করা যায়।

চক্র

ডিফল্টরূপে, মিডিয়াপাইপের ক্যালকুলেটর গ্রাফগুলিকে অ্যাসাইক্লিক হতে হবে এবং একটি গ্রাফের চক্রগুলিকে ত্রুটি হিসাবে বিবেচনা করে৷ যদি একটি গ্রাফের উদ্দেশ্য সাইকেল থাকে, তাহলে চক্রগুলিকে গ্রাফ কনফিগারেশনে টীকা করতে হবে। এই পৃষ্ঠাটি কীভাবে তা করতে হবে তা বর্ণনা করে।

দ্রষ্টব্য: বর্তমান পদ্ধতি পরীক্ষামূলক এবং পরিবর্তন সাপেক্ষে। আমরা আপনার প্রতিক্রিয়া স্বাগত জানাই.

নমুনা কোড হিসাবে mediapipe/framework/calculator_graph_test.ccCalculatorGraphTest.Cycle ইউনিট পরীক্ষা ব্যবহার করুন। পরীক্ষায় সাইক্লিক গ্রাফটি নীচে দেখানো হয়েছে। যোগকারীর sum আউটপুট হল পূর্ণসংখ্যা উৎস ক্যালকুলেটর দ্বারা উত্পন্ন পূর্ণসংখ্যার যোগফল।

a cyclic graph that adds a stream of integers

এই সাধারণ গ্রাফটি সাইক্লিক গ্রাফের সমর্থনে সমস্ত সমস্যাকে চিত্রিত করে।

ব্যাক এজ টীকা

আমরা চাই যে প্রতিটি চক্রের একটি প্রান্ত পিছনের প্রান্ত হিসাবে টীকা করা হোক। এটি মিডিয়াপাইপের টপোলজিকাল সাজানোর কাজ করতে দেয়, পিছনের সব প্রান্ত অপসারণের পরে।

পিছনের প্রান্তগুলি নির্বাচন করার জন্য সাধারণত একাধিক উপায় রয়েছে। কোন প্রান্তগুলিকে পিছনের প্রান্ত হিসাবে চিহ্নিত করা হয় তা প্রভাবিত করে কোন নোডগুলিকে আপস্ট্রিম হিসাবে বিবেচনা করা হয় এবং কোন নোডগুলিকে ডাউনস্ট্রিম হিসাবে বিবেচনা করা হয়, যা ফলস্বরূপ MediaPipe নোডগুলিতে নির্ধারিত অগ্রাধিকারগুলিকে প্রভাবিত করে৷

উদাহরণস্বরূপ, CalculatorGraphTest.Cycle পরীক্ষা old_sum প্রান্তটিকে পিছনের প্রান্ত হিসাবে চিহ্নিত করে, তাই বিলম্ব নোডটিকে অ্যাডার নোডের একটি ডাউনস্ট্রিম নোড হিসাবে বিবেচনা করা হয় এবং এটিকে উচ্চতর অগ্রাধিকার দেওয়া হয়। বিকল্পভাবে, আমরা বিলম্ব নোডে sum ইনপুটটিকে পিছনের প্রান্ত হিসাবে চিহ্নিত করতে পারি, এই ক্ষেত্রে বিলম্ব নোডটিকে অ্যাডার নোডের একটি আপস্ট্রিম নোড হিসাবে বিবেচনা করা হবে এবং এটিকে নিম্ন অগ্রাধিকার দেওয়া হবে।

প্রাথমিক প্যাকেট

যখন পূর্ণসংখ্যা উৎস থেকে প্রথম পূর্ণসংখ্যা আসে তখন অ্যাডার ক্যালকুলেটর চালানোর জন্য, আমাদের একটি প্রাথমিক প্যাকেট প্রয়োজন, যার মান 0 এবং একই টাইমস্ট্যাম্প সহ, অ্যাডারের old_sum ইনপুট স্ট্রীমে। এই প্রাথমিক প্যাকেটটি Open() পদ্ধতিতে বিলম্ব ক্যালকুলেটর দ্বারা আউটপুট করা উচিত।

একটি লুপে বিলম্ব

প্রতিটি লুপ পরবর্তী পূর্ণসংখ্যা ইনপুটের সাথে পূর্ববর্তী sum আউটপুট সারিবদ্ধ করতে বিলম্বিত হওয়া উচিত। এটি বিলম্ব নোড দ্বারাও করা হয়। তাই বিলম্ব নোডকে পূর্ণসংখ্যা উৎস ক্যালকুলেটরের টাইমস্ট্যাম্প সম্পর্কে নিম্নলিখিতগুলি জানতে হবে:

  • প্রথম আউটপুটের টাইমস্ট্যাম্প।

  • ধারাবাহিক আউটপুটগুলির মধ্যে টাইমস্ট্যাম্প ডেল্টা৷

আমরা একটি বিকল্প সময়সূচী নীতি যোগ করার পরিকল্পনা করছি যা শুধুমাত্র প্যাকেট অর্ডারের বিষয়ে চিন্তা করে এবং প্যাকেট টাইমস্ট্যাম্প উপেক্ষা করে, যা এই অসুবিধা দূর করবে।

একটি ইনপুট স্ট্রীম সম্পন্ন হলে একটি ক্যালকুলেটরের প্রাথমিক সমাপ্তি৷

ডিফল্টরূপে, MediaPipe একটি নন-সোর্স ক্যালকুলেটরের Close() পদ্ধতিকে কল করে যখন এর সমস্ত ইনপুট স্ট্রীম সম্পন্ন হয়। উদাহরণ গ্রাফে, পূর্ণসংখ্যার উত্সটি সম্পন্ন হওয়ার সাথে সাথে আমরা অ্যাডার নোড বন্ধ করতে চাই। এটি একটি বিকল্প ইনপুট স্ট্রীম হ্যান্ডলার, EarlyCloseInputStreamHandler দিয়ে অ্যাডার নোড কনফিগার করার মাধ্যমে সম্পন্ন করা হয়।

প্রাসঙ্গিক উত্স কোড

বিলম্ব ক্যালকুলেটর

Open() এর কোডটি নোট করুন যা প্রাথমিক প্যাকেটটি আউটপুট করে এবং Process() এর কোড যা ইনপুট প্যাকেটগুলিতে একটি (ইউনিট) বিলম্ব যোগ করে। উপরে উল্লিখিত হিসাবে, এই বিলম্ব নোড অনুমান করে যে এর আউটপুট স্ট্রীমটি প্যাকেট টাইমস্ট্যাম্প 0, 1, 2, 3, ... সহ একটি ইনপুট স্ট্রিমের পাশাপাশি ব্যবহৃত হয়।

class UnitDelayCalculator : public Calculator {
 public:
  static absl::Status FillExpectations(
      const CalculatorOptions& extendable_options, PacketTypeSet* inputs,
      PacketTypeSet* outputs, PacketTypeSet* input_side_packets) {
    inputs->Index(0)->Set<int>("An integer.");
    outputs->Index(0)->Set<int>("The input delayed by one time unit.");
    return absl::OkStatus();
  }

  absl::Status Open() final {
    Output()->Add(new int(0), Timestamp(0));
    return absl::OkStatus();
  }

  absl::Status Process() final {
    const Packet& packet = Input()->Value();
    Output()->AddPacket(packet.At(packet.Timestamp().NextAllowedInStream()));
    return absl::OkStatus();
  }
};

গ্রাফ কনফিগারেশন

back_edge টীকা এবং বিকল্প input_stream_handler নোট করুন।

node {
  calculator: 'GlobalCountSourceCalculator'
  input_side_packet: 'global_counter'
  output_stream: 'integers'
}
node {
  calculator: 'IntAdderCalculator'
  input_stream: 'integers'
  input_stream: 'old_sum'
  input_stream_info: {
    tag_index: ':1'  # 'old_sum'
    back_edge: true
  }
  output_stream: 'sum'
  input_stream_handler {
    input_stream_handler: 'EarlyCloseInputStreamHandler'
  }
}
node {
  calculator: 'UnitDelayCalculator'
  input_stream: 'sum'
  output_stream: 'old_sum'
}