Python'da MediaPipe Çerçevesi

MediaPipe Python çerçevesi, MediaPipe C++ çerçevesinin Timestamp, Packet ve CalculatorGraph gibi temel bileşenlerine doğrudan erişim sağlar. Kullanıma hazır Python çözümleri ise çerçevenin teknik ayrıntılarını gizler ve basitçe okunabilir model çıkarım sonuçlarını arayanlara geri döndürür.

MediaPipe çerçevesi, pybind11 kitaplığının üzerinde yer alır. C++ çekirdek çerçevesi, Python'da C++/Python dil bağlaması aracılığıyla tanıtılır. Aşağıdaki içerik, okuyucunun MediaPipe C++ çerçevesi hakkında temel bilgilere sahip olduğu varsayılır. Aksi takdirde Framework Concepts'te faydalı bilgiler bulabilirsiniz.

Paket

Paket, MediaPipe'teki temel veri akışı birimidir. Paket, sayısal bir zaman damgası ve sabit bir yüke götüren paylaşılan işaretçiden oluşur. Python'da, mp.packet_creator modülündeki paket oluşturma yöntemlerinden biri çağrılarak bir MediaPipe paketi oluşturulabilir. Buna karşılık olarak, paket yükü mp.packet_getter modülündeki paket alıcı yöntemlerinden biri kullanılarak alınabilir. Paket yükünün, paket oluşturulduktan sonra değişmez hale geldiğini unutmayın. Dolayısıyla, alınan paket içeriğinde yapılan değişiklik, paketteki gerçek yükü etkilemez. MediaPipe çerçevesi Python API, en yaygın kullanılan MediaPipe veri türlerini (ör. Resim Çerçevesi, Matris, Protokol Arabellekleri ve temel veri türleri) ekleyebilirsiniz. Aşağıdaki kapsamlı tabloda, MediaPipe Python Framework API tarafından desteklenen her bir veri türü için paket oluşturucu ve içerik alıcı yöntemi ile birlikte Python ve C++ veri türü arasındaki tür eşlemeleri gösterilmektedir.

Python Veri Türü C++ Veri Türü Paket Oluşturucu İçerik Alıcı
bool bool create_bool(True) get_bool(packet)
int veya np.intc int_t create_int(1) get_int(packet)
int veya np.int8 int8_t create_int8(2**7-1) get_int(packet)
int veya np.int16 int16_t create_int16(2**15-1) get_int(packet)
int veya np.int32 int32_t create_int32(2**31-1) get_int(packet)
int veya np.int64 int64_t create_int64(2**63-1) get_int(packet)
int veya np.uint8 uint8_t create_uint8(2**8-1) get_uint(packet)
int veya np.uint16 uint16_t create_uint16(2**16-1) get_uint(packet)
int veya np.uint32 uint32_t create_uint32(2**32-1) get_uint(packet)
int veya np.uint64 uint64_t create_uint64(2**64-1) get_uint(packet)
kayan nokta veya np.float32 float create_float(1.1) get_float(packet)
kayan nokta veya np.double double create_double(1.1) get_float(packet)
str (UTF-8) std::string create_string('abc') get_str(packet)
bayt std::string create_string(b'\xd0\xd0\xd0') get_bytes(packet)
mp.Packet mp::Paket create_packet(p) get_packet(packet)
Liste[bool] std::vector<bool> create_bool_Vektör([Doğru; Yanlış]) get_bool_list(packet)
Liste[int] veya Liste[np.intc] int[] create_int_array([1; 2; 3]) get_int_list(paket; boyut=10)
Liste[int] veya Liste[np.intc] std::vector<int> create_int_Vektör([1; 2; 3]) get_int_list(packet)
Liste[kayan] veya Liste[np.float] float[] create_float_arrary([0,1; 0,2]) get_float_list(paket; boyut=10)
Liste[kayan] veya Liste[np.float] std::vector<float> create_float_Vektör([0,1; 0,2]) get_float_list(paket; boyut=10)
Liste[str] std::vector<std::string> create_string_Vektör(['a']) get_str_list(packet)
Liste[mp.Packet] std::vector<mp::Packet> create_packet_vector(
[paket1, paket2])
get_packet_list(p)
Eşleme[str, Paket] std::map<std::string, büfe=""></std::string,> create_string_to_packet_map(
        {'a': packet1, 'b': packet2})
get_str_to_packet_dict(packet)
np.ndarray
(cv.mat ve PIL.Image)
mp::ImageFrame create_image_frame(
format=ImageFormat.SRGB,
data=mat)
get_image_frame(packet)
np.ndarray mp::Matris create_matrix(data) get_matrix(packet)
Google Proto Mesajı Google Proto Mesajı create_proto(proto) get_proto(packet)
Liste[Proto] std::vector<Proto> Yok get_proto_list(packet)

Kullanıcıların özel C++ sınıfları oluşturup bunları grafiklere ve hesap makinelerine göndermesi çok normaldir. Özel sınıfların MediaPipe Framework ile Python'da kullanılmasına izin vermek için aşağıdaki adımları izleyerek Packet API'yi yeni bir veri türü için genişletebilirsiniz:

  1. Bir cc dosyasına pybind11 sınıf bağlama kodunu veya özel tür için özel tür bir yayıncı yazın.

    #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. Ayrı bir cc dosyasında, özel türde yeni bir paket oluşturucu ve alıcı yöntemi oluşturun.

    #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. DERLEME dosyasına, özel tür bağlama ve yeni paket yöntemleri için iki Bazel derleme kuralı ekleyin.

    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'in pybind uzantı hedeflerini (.so son ekiyle) oluşturun ve oluşturulan dinamik kitaplıkları $LD_LIBRARY_PATH dizinlerinden birine taşıyın.

  5. Python'da bağlama modüllerini kullanın.

    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)
    

Zaman damgası

Her paket, mikrosaniye birimleri cinsinden bir zaman damgası içerir. Python'da Packet API, bir paketin sayısal zaman damgasını tanımlamak için packet.at() rahatlık yöntemini sunar. Daha genel olarak açıklamak gerekirse packet.timestamp, temel zaman damgasına erişmek için kullanılan paket sınıfı özelliğidir. Timestamp API, Unix dönemini MediaPipe zaman damgasına dönüştürmek için bu amaçla mp.Timestamp.from_seconds() yöntemini sunar.

ImageFrame

ImageFrame, bir resmi veya video karesini depolamak için kullanılan kapsayıcıdır. ImageFrame tarafından desteklenen biçimler ImageFormat enum öğesinde listelenir. Pikseller araya eklemeli renk bileşenleriyle kodlanmış ana satır şeklinde kodlanmış olup ImageFrame ise veri türü olarak uint8, uint16 ve float'ı destekler. MediaPipe, ImageFrame C++ sınıfına erişmek için ImageFrame Python API'si sağlar. Python'da, piksel verilerini almanın en kolay yolu, numpy ndarray'i almak için image_frame.numpy_view() yöntemini çağırmaktır. Dahili piksel verilerine bir başvuru olan döndürülen numpy ndarray değerinin yazılamayacağını unutmayın. Arayanların numpy ndarray alanını değiştirmesi gerekiyorsa bir kopya almak için açıkça bir kopyalama işlemi çağırmak gerekir. MediaPipe bir ImageFrame oluşturmak için birkaç değer (darray) aldığında, verilerin bitişik olarak depolandığını varsayar. Buna karşılık olarak, ImageFrame'in piksel verileri, Python tarafına döndürüldüğünde bitişik olacak şekilde yeniden hizalanır.

Grafik

MediaPipe Çerçevesi'nde tüm işleme, bir Hesap Yöneticisi Grafiği bağlamında gerçekleşir. Hesap MakinesiGraph Python API, C++ CalculatorGraph sınıfıyla doğrudan bağlantıdır. En önemli fark, CalculatorGraph Python API'nin bir hata oluştuğunda normal olmayan bir Durum döndürmek yerine bir Python hatası vermesidir. Bu nedenle, Python kullanıcısı olarak istisnaları her zamanki gibi ele alabilirsiniz. CalculatorGraph'in yaşam döngüsü üç aşamadan oluşur: başlatma ve kurulum, grafik çalıştırma ve grafik kapatma.

  1. Bir CalculatorGraphConfig protobuf dosyası veya ikili protobuf dosyasıyla bir CalculatorGraph'i başlatın ve çıkış akışlarını gözlemlemek için geri çağırma yöntemleri sağlayın.

    1. Seçenek: Bir accountGraphConfig protobuf'u veya metin gösterimiyle bir CalculatorGraph'i başlatın ve çıkış akışlarını gözlemleyin:

    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. Seçenek İkili protobuf dosyasıyla bir CalculatorGraph'i başlatın ve çıkış akışlarını gözlemleyin.

    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. Grafiği çalıştırın ve paketleri grafiğe aktarın.

    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. İşlem tamamlandıktan sonra grafiği kapatın. Grafiği, close() çağrısından sonra çalıştırılan başka bir grafik için yeniden başlatabilirsiniz.

    graph.close()
    

Python komut dosyası, yerel Python çalışma zamanınız tarafından çalıştırılabilir.