Python में MediaPipe फ़्रेमवर्क

MediaPipe Python फ़्रेमवर्क, timestamp, पैकेट, और CalculatorGraph जैसे MediaPipe C++ फ़्रेमवर्क के मुख्य कॉम्पोनेंट को सीधे तौर पर ऐक्सेस करने की सुविधा देता है, जबकि इस्तेमाल के लिए तैयार Python समाधान, फ़्रेमवर्क की तकनीकी जानकारी छिपा देते हैं और आसानी से पढ़ने लायक मॉडल अनुमान के नतीजे कॉलर को वापस लौटा देते हैं.

MediaPipe फ़्रेमवर्क, pybind11 लाइब्रेरी के सबसे ऊपर मौजूद होता है. C++ कोर फ़्रेमवर्क को Python में C++/Python लैंग्वेज बाइंडिंग के ज़रिए एक्सपोज़ किया जाता है. नीचे दिए गए कॉन्टेंट के हिसाब से, लोगों को MediaPipe C++ फ़्रेमवर्क की बुनियादी जानकारी पहले से ही मौजूद है. इसके अलावा, फ़्रेमवर्क कॉन्सेप्ट में आपको काम की जानकारी मिल सकती है.

पैकेट

पैकेट, MediaPipe में बेसिक डेटा फ़्लो यूनिट है. पैकेट में संख्या वाला टाइमस्टैंप और नहीं बदले जा सकने वाले पेलोड के लिए शेयर किया गया पॉइंटर होता है. Python में, mp.packet_creator मॉड्यूल में पैकेट क्रिएटर के किसी एक तरीके को कॉल करके, MediaPipe पैकेट बनाया जा सकता है. इसी तरह, mp.packet_getter मॉड्यूल में मौजूद पैकेट गेटर के किसी एक तरीके का इस्तेमाल करके, पैकेट पेलोड को वापस पाया जा सकता है. ध्यान दें कि पैकेट बन जाने के बाद, पैकेट पेलोड बदला नहीं जा सकता. इसलिए, फ़ेच किए गए पैकेट के कॉन्टेंट में बदलाव करने से, पैकेट के असल पेलोड पर कोई असर नहीं पड़ता है. MediaPipe फ़्रेमवर्क Python API, सबसे ज़्यादा इस्तेमाल किए जाने वाले MediaPipe के डेटा टाइप (उदाहरण के लिए, कोर बाइंडिंग में ImageFrame, मैट्रिक्स, प्रोटोकॉल बफ़र, और प्रिमिटिव डेटा टाइप). नीचे दी गई पूरी टेबल में Python और C++ डेटा टाइप के बीच, पैकेट क्रिएटर और MediaPipe Python फ़्रेमवर्क एपीआई के साथ काम करने वाले हर डेटा टाइप के लिए कॉन्टेंट पाने वाले तरीके के बीच टाइप मैपिंग के बारे में बताया गया है.

Python डेटा टाइप C++ डेटा टाइप पैकेट क्रिएटर कॉन्टेंट पाने वाला
bool bool create_bool(True) get_bool(packet)
int या np.intc int_t create_int(1) get_int(packet)
int या np.int8 int8_t create_int8(2**7-1) के साथ get_int(packet)
int या np.int16 int16_t create_int16(2**15-1) के साथ get_int(packet)
int या np.int32 int32_t create_int32(2**31-1) के साथ get_int(packet)
int या np.int64 int64_t create_int64(2**63-1) के साथ get_int(packet)
int या np.uint8 uint8_t create_uint8(2**8-1) get_uint(packet)
int या np.uint16 uint16_t create_uint16(2**16-1) get_uint(packet)
int या np.uint32 uint32_t create_uint32(2**32-1) get_uint(packet)
int या np.uint64 uint64_t create_uint64(2**64-1) get_uint(packet)
फ़्लोट या np.float32 float create_float(1.1) get_float(packet)
फ़्लोट या np.डबल दोगुनी create_double(1.1) get_float(packet)
एसटीआर (UTF-8) std::string create_string('abc') get_str(packet)
बाइट std::string create_string(b'\xd0\xd0\xd0') get_bytes(packet)
mp.Packet mp::पैकेट create_packet(p) get_packet(packet)
सूची[बूल] std::vector<bool> create_bool_वेक्टर([सही, गलत]) get_bool_list(packet)
List[int] या List[np.intc] int[] create_int_array([1, 2, 3]) get_int_list(पैकेट, साइज़=10)
List[int] या List[np.intc] std::vector<int> create_int_वेक्टर([1, 2, 3]) get_int_list(packet)
List[float] या List[np.float] float[] create_float_arrary([0.1, 0.2]) get_float_list(पैकेट, साइज़=10)
List[float] या List[np.float] std::vector<float> create_फ़्लोट_वेक्टर([0.1, 0.2]) get_float_list(पैकेट, साइज़=10)
सूची[str] std::vector<std::string> create_string_वेक्टर(['a']) get_str_list(packet)
सूची[mp.Packet] std::vector<mp::Packet> create_packet_वेक्टर(
[packet1, पैकेट2])
get_packet_list(p)
मैपिंग[str, पैकेट] std::map<std::string, पैकेट=""></std::string,> create_string_to_packet_map(
        {'a': packet1, 'b': packet2})
get_str_to_packet_dict(packet)
np.ndarray
(cv.mat और PIL.Image)
mp::ImageFrame create_image_frame(
format=ImageFormat.SRGB,
data=mat)
get_image_frame(packet)
np.ndarray mp::मैट्रिक्स create_matrix(data) get_matrix(packet)
Google Proto मैसेज Google Proto मैसेज create_proto(proto) get_proto(packet)
सूची[प्रोटो] std::vector<Proto> लागू नहीं get_proto_list(packet)

यह आम बात है कि उपयोगकर्ता कस्टम C++ क्लास बनाते हैं और उन्हें ग्राफ़ और कैलकुलेटर में भेजते हैं. Python में MediaPipe फ़्रेमवर्क के साथ कस्टम क्लास का इस्तेमाल करने के लिए, नीचे दिए गए चरणों का पालन करके नए डेटा प्रकार के लिए पैकेट एपीआई को बढ़ाया जा सकता है:

  1. cc फ़ाइल में कस्टम टाइप के लिए pybind11 क्लास बाइंडिंग कोड या कस्टम टाइप कास्टर लिखें.

    #include "path/to/my_type/header/file.h"
    #include "pybind11/pybind11.h"
    
    namespace py = pybind11;
    
    PYBIND11_MODULE(my_type_binding, m) {
      // Write binding code or a custom type caster for MyType.
      py::class_<MyType>(m, "MyType")
          .def(py::init<>())
          .def(...);
    }
    
  2. एक अलग cc फ़ाइल में कस्टम टाइप का एक नया पैकेट क्रिएटर और गेटर मेथड बनाएं.

    #include "path/to/my_type/header/file.h"
    #include "mediapipe/framework/packet.h"
    #include "pybind11/pybind11.h"
    
    namespace mediapipe {
    namespace py = pybind11;
    
    PYBIND11_MODULE(my_packet_methods, m) {
      m.def(
          "create_my_type",
          [](const MyType& my_type) { return MakePacket<MyType>(my_type); });
    
      m.def(
          "get_my_type",
          [](const Packet& packet) {
            if(!packet.ValidateAsType<MyType>().ok()) {
              PyErr_SetString(PyExc_ValueError, "Packet data type mismatch.");
              return py::error_already_set();
            }
            return packet.Get<MyType>();
          });
    }
    }  // namespace mediapipe
    
  3. कस्टम टाइप बाइंडिंग के लिए, दो बैजल बिल्ड नियम और BUILD फ़ाइल में नए पैकेट तरीके जोड़ें.

    load("@pybind11_bazel//:build_defs.bzl", "pybind_extension")
    
    pybind_extension(
        name = "my_type_binding",
        srcs = ["my_type_binding.cc"],
        deps = [":my_type"],
    )
    
    pybind_extension(
        name = "my_packet_methods",
        srcs = ["my_packet_methods.cc"],
        deps = [
            ":my_type",
            "//mediapipe/framework:packet"
        ],
    )
    
  4. Bazel का pybind एक्सटेंशन टारगेट (.so सफ़िक्स .so के साथ) बनाएं और जनरेट की गई डाइनैमिक लाइब्रेरी को $LD_LIBRARY_PATH दिशा-निर्देशों में से किसी एक में ले जाएं.

  5. Python में बाइंडिंग मॉड्यूल इस्तेमाल करें.

    import my_type_binding
    import my_packet_methods
    
    packet = my_packet_methods.create_my_type(my_type_binding.MyType())
    my_type = my_packet_methods.get_my_type(packet)
    

टाइमस्टैंप

हर पैकेट में एक टाइमस्टैंप होता है, जो माइक्रोसेकंड की यूनिट में होता है. Python में, पैकेट के न्यूमेरिक टाइमस्टैंप की जानकारी देने के लिए, पैकेट एपीआई एक आसान तरीका packet.at() देता है. आम तौर पर, packet.timestamp एक पैकेट क्लास प्रॉपर्टी है, जो मौजूदा टाइमस्टैंप को ऐक्सेस करती है. Unix epoch को MediaPipe टाइमस्टैंप में बदलने के लिए, timestamp API में एक तरीका mp.Timestamp.from_seconds() दिया जाता है.

ImageFrame

ImageFrame किसी इमेज या वीडियो फ़्रेम को स्टोर करने के लिए एक कंटेनर होता है. ImageFrame के साथ काम करने वाले फ़ॉर्मैट की सूची ImageFormat एनम में दी गई है. पिक्सल, इंटरलीव किए गए कलर कॉम्पोनेंट के साथ एन्कोड किए गए रो-मेजर टूल होते हैं और ImageFrame अपने डेटा टाइप के तौर पर uint8, uint16, और फ़्लोट के साथ काम करता है. MediaPipe की मदद से ImageFrame C++ क्लास को ऐक्सेस करने के लिए एक ImageFrame Python API उपलब्ध होता है. Python में, पिक्सल डेटा को वापस पाने का सबसे आसान तरीका numpy ndarray पाने के लिए image_frame.numpy_view() को कॉल करना है. ध्यान दें कि नतीजे में मिला numpy ndarray, जो अंदरूनी पिक्सल डेटा का रेफ़रंस है, लिखा नहीं जा सकता. अगर कॉलर को संख्यात्मक ndarray में बदलाव करना है, तो ज़रूरी है कि कॉपी पाने के लिए, आपको साफ़ तौर पर कॉपी कार्रवाई को कॉल करना होगा. जब MediaPipe एक ImageFrame बनाने के लिए एक numpy ndarray ले जाता है, तो वह मान लेता है कि डेटा एक-दूसरे के साथ सेव किया गया है. इसी तरह, Python साइड में लौटाए जाने पर, ImageFrame के पिक्सल डेटा को कंटिन्यूअस के तौर पर फिर से अलाइन किया जाएगा.

ग्राफ़

MediaPipe फ़्रेमवर्क में सभी प्रोसेसिंग, CalculatorGraph के संदर्भ में होती हैं. CalculatorGraph Python API, C++ CalculatorGraph क्लास से सीधे तौर पर जुड़ा हुआ है. मुख्य अंतर यह है कि CalculatorGraph Python API, कोई गड़बड़ी होने पर नॉन-OK स्टेटस दिखाने के बजाय, Python की गड़बड़ी दिखाता है. इसलिए, Python उपयोगकर्ता के तौर पर, अपवादों को पहले की तरह ही हैंडल किया जा सकता है. कैलकुलेटर ग्राफ़ के लाइफ़ साइकल में तीन चरण होते हैं: शुरू करना और सेटअप करना, ग्राफ़ चलाना, और ग्राफ़ को बंद करना.

  1. CalculatorGraphConfig प्रोटोबफ़ या बाइनरी प्रोटोबफ़ फ़ाइल की मदद से, CalculatorGraph को शुरू करें. साथ ही, आउटपुट स्ट्रीम पर नज़र रखने के लिए, कॉलबैक का तरीका (तरीका) उपलब्ध कराएं.

    विकल्प 1. CalculatorGraphConfig प्रोटोबफ़ या उसके टेक्स्ट निरूपण के साथ CalculatorGraph को शुरू करें और आउटपुट स्ट्रीम देखें:

    import mediapipe as mp
    
    config_text = """
      input_stream: 'in_stream'
      output_stream: 'out_stream'
      node {
        calculator: 'PassThroughCalculator'
        input_stream: 'in_stream'
        output_stream: 'out_stream'
      }
    """
    graph = mp.CalculatorGraph(graph_config=config_text)
    output_packets = []
    graph.observe_output_stream(
        'out_stream',
        lambda stream_name, packet:
            output_packets.append(mp.packet_getter.get_str(packet)))
    

    विकल्प 2. CalculatorGraph को बाइनरी प्रोटोबफ़ फ़ाइल से शुरू करें और आउटपुट स्ट्रीम देखें.

    import mediapipe as mp
    # resources dependency
    
    graph = mp.CalculatorGraph(
        binary_graph=os.path.join(
            resources.GetRunfilesDir(), 'path/to/your/graph.binarypb'))
    graph.observe_output_stream(
        'out_stream',
        lambda stream_name, packet: print(f'Get {packet} from {stream_name}'))
    
  2. ग्राफ़ में ग्राफ़ चलाना और फ़ीड पैकेट शुरू करें.

    graph.start_run()
    
    graph.add_packet_to_input_stream(
        'in_stream', mp.packet_creator.create_string('abc').at(0))
    
    rgb_img = cv2.cvtColor(cv2.imread('/path/to/your/image.png'), cv2.COLOR_BGR2RGB)
    graph.add_packet_to_input_stream(
        'in_stream',
        mp.packet_creator.create_image_frame(image_format=mp.ImageFormat.SRGB,
                                             data=rgb_img).at(1))
    
  3. पूरा होने के बाद ग्राफ़ बंद कर दें. close() पर कॉल करने के बाद, किसी दूसरे ग्राफ़ के लिए ग्राफ़ फिर से शुरू किया जा सकता है.

    graph.close()
    

Python स्क्रिप्ट को आपके लोकल Python रनटाइम से चलाया जा सकता है.