إطار عمل MediaPipe في بايثون

يمنح إطار عمل MediaPipe Python الوصول المباشر إلى المكونات الأساسية إطار عمل MediaPipe C++ مثل الطابع الزمني والحزمة وCalculatorGraph، في حين أن حلول بايثون الجاهزة للاستخدام تخفي التفاصيل الفنية لإطار العمل وعرض النموذج القابل للقراءة ببساطة نتائج الاستنتاج إلى المتصلين.

يرتكز إطار عمل MediaPipe على مكتبة pybind11. يتعرض إطار العمل الأساسي C++ في بايثون من خلال ربط لغة C++/Python. يفترض المحتوى أدناه أن القارئ لديه بالفعل فهم أساسي إطار عمل MediaPipe C++. خلاف ذلك، يمكنك العثور على معلومات مفيدة في مفاهيم إطار العمل:

الحزمة

الحزمة هي وحدة تدفق البيانات الأساسية في MediaPipe. تتكون الحزمة من طابع زمني رقمي ومؤشر مشترك يشير إلى حمولة غير قابلة للتغيير في بايثون، يمكن إنشاء حزمة MediaPipe من خلال استدعاء إحدى طرق إنشاء الحزم في الـ mp.packet_creator واحدة. وفي المقابل، يمكن استرداد حمولة الحزمة باستخدام أحد طرق الحصول على حزم البيانات في mp.packet_getter واحدة. تجدر الإشارة إلى أنّ حمولة الحزمة تصبح غير قابلة للتغيير بعد إضافة الحزمة الإنشاء. وبالتالي، لا يؤثر تعديل محتوى الحزمة المستردة في الحمولة الفعلية في الحزمة. تتيح واجهة Python API الخاصة بإطار عمل MediaPipe أنواع البيانات الأكثر استخدامًا من MediaPipe (على سبيل المثال، إطار الصورة، المصفوفة، بروتوكول الموارد الاحتياطية وأنواع البيانات الأولية) في الربط الأساسي. خريطة الموقع الشاملة في الجدول أدناه، تعيينات الأنواع بين نوعي بيانات Python وC++ بالإضافة إلى منشئ الحزمة وطريقة الحصول على المحتوى لكل نوع بيانات التي تتوافق معها واجهة برمجة تطبيقات إطار عمل MediaPipe Python.

نوع بيانات Python نوع بيانات C++ أداة إنشاء حِزم البيانات منشئ المحتوى
قيمة منطقية قيمة منطقية 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)
float أو np.float32 عدد عائم create_float(1.1) get_float(packet)
float أو np.double مزدوج create_double(1.1) get_float(packet)
str (UTF-8) std::string create_string('abc') get_str(packet)
بايت std::string create_string(b'\xd0\xd0\xd0') get_bytes(packet)
mp.Packet mp::Packet create_packet(p) get_packet(packet)
القائمة[قيمة منطقية] std::ect<bool> create_bool_ موسيقية([صحيح, خطأ]) get_bool_list(packet)
List[int] أو List[np.intc] int[] create_int_array([1, 2, 3]) get_int_list(packet, size=10)
List[int] أو List[np.intc] std::ect<int> create_int_vector([1, 2, 3]) get_int_list(packet)
List[float] أو List[np.float] float[] create_float_arrary([0.1, 0.2]) get_float_list(packet, size=10)
List[float] أو List[np.float] std::vector&lt;float&gt; create_float_ect([0.1, 0.2]) get_float_list(packet, size=10)
القائمة[str] std::ect<std::string> إنشاء_سلسلة_متّجه(['a']) get_str_list(packet)
القائمة[mp.Packet] std::vector&lt;mp::Packet&gt; create_packet_ للحفاظ على النشاط(
) [packet1, package2])
get_packet_list(p)
تصميم الخرائط[str, Packet] std::map<std::string, package=""></std::string,> create_string_to_packet_map(
        {&#39;a&#39;: packet1, &#39;b&#39;: 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::Matrix create_matrix(data) get_matrix(packet)
رسالة Proto من Google رسالة Proto من Google create_proto(proto) get_proto(packet)
القائمة [Proto] std::ect<Proto> timing fixed in amara get_proto_list(packet)

من الشائع أن ينشئ المستخدمون فئات C++ مخصصة ويرسلونها إلى الرسوم البيانية والآلات الحاسبة. للسماح باستخدام الفئات المخصصة في بايثون باستخدام إطار عمل MediaPipe، يمكنك توسيع واجهة برمجة تطبيقات الحزم لنوع جديد من البيانات الخطوات التالية:

  1. كتابة 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. أنشِئ أهداف إضافة pybind (مع اللاحقة .so) من Bazel وانقل المكتبات الديناميكية التي تم إنشاؤها إلى إحدى البرامج $LD_LIBRARY_PATH.

  5. استخدم وحدات الربط في بايثون.

    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)
    

الطابع الزمني

تحتوي كل حزمة على طابع زمني بوحدات الميكرو ثانية. في لغة بايثون، توفر واجهة برمجة تطبيقات الحزم طريقة ملائمة packet.at() لتعريف القيم الرقمية الطابع الزمني للحزمة. بشكل عام، packet.timestamp هي فئة الحزمة للوصول إلى الطابع الزمني الأساسي. لتحويل حقبة يونكس إلى الطابع الزمني لـ MediaPipe، واجهة برمجة التطبيقات للطابع الزمني توفر الطريقة mp.Timestamp.from_seconds() لهذا الغرض.

ImageFrame

ImageFrame هو حاوية لتخزين صورة أو إطار فيديو. التنسيقات التي يدعمها ImageFrame مُدرجة في تعداد ImageFormat. تكون وحدات البكسل مشفرة بشكل رئيسي مع عناصر ألوان متداخلة وImageFrame تدعم uint8 وuint16 وfloat كأنواع بيانات لها. يوفّر MediaPipe واجهة برمجة تطبيقات ImageFrame Python للوصول إلى فئة ImageFrame C++. في بايثون، تعد أسهل طريقة لاسترداد بيانات pixel هي الاتصال بـ image_frame.numpy_view() للحصول على مكتبة numpy ndarray. ملاحظة أن numpy ndarray المعروضة، إشارة إلى بيانات البكسل الداخلية، وغير قابل للكتابة. إذا احتاج المتصلون إلى تعديل numpy ndarray، يجب بعملية النسخ للحصول على نسخة بشكل صريح. عندما يأخذ MediaPipe numpy لإنشاء ImageFrame، يفترض أن يتم تخزين البيانات بشكل متبادل. وفي المقابل، ستتم إعادة محاذاة بيانات البكسل لإطار ImageFrame لتكون متجاورة عند إعادتها إلى جانب بايثون.

رسم بياني للدالة

في إطار عمل MediaPipe، تتم جميع عمليات المعالجة في سياق عملية الرسم البياني للآلة الحاسبة. واجهة برمجة تطبيقات CalculatorGraph Python هو ربط مباشر بفئة C++ CalculatorGraph. الاختلاف الرئيسي هو تعرض واجهة برمجة تطبيقات CalculatorGraph Python خطأ Python بدلاً من عرض حالة غير جيدة عند حدوث خطأ. وبالتالي، وبصفتك مستخدم بايثون، يمكنك التعامل مع الاستثناءات كما تفعل عادةً. تحتوي دورة حياة الرسم البياني للآلة الحاسبة على ثلاث مراحل: الإعداد والإعداد، وتشغيل الرسم البياني، وإيقاف الرسم البياني.

  1. إعداد CalculatorGraph باستخدام النموذج الأوّلي للأداة CalculatorGraphConfig أو البرنامج الثنائي وتوفير طرق معاودة الاتصال لمراقبة الناتج مصادر البيانات

    الخيار رقم 1. إعداد CalculatorGraph باستخدام النموذج الأوّلي للأداة CalculatorGraphConfig أو تمثيله النصي، ومراقبة مصادر الإخراج:

    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. تهيئة الرسم البياني للآلة الحاسبة باستخدام ملف نموذج أولي ثنائي مراقبة مصادر الإخراج.

    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 المحلية.