Korniza MediaPipe në Python

Korniza MediaPipe Python jep akses të drejtpërdrejtë në komponentët thelbësorë të kornizës MediaPipe C++ siç janë Timestamp, Packet dhe CalculatorGraph, ndërsa zgjidhjet e gatshme për përdorim Python fshehin detajet teknike të kornizës dhe thjesht kthejnë rezultatet e përfundimit të modelit të lexueshëm. tek telefonuesit.

Korniza MediaPipe qëndron në krye të bibliotekës pybind11 . Korniza thelbësore C++ ekspozohet në Python nëpërmjet një lidhjeje të gjuhës C++/Python. Përmbajtja e mëposhtme supozon se lexuesi tashmë ka një kuptim bazë të kornizës MediaPipe C++. Përndryshe, mund të gjeni informacione të dobishme në Konceptet Kornizë .

Pako

Paketa është njësia bazë e rrjedhës së të dhënave në MediaPipe. Një paketë përbëhet nga një vulë kohore numerike dhe një tregues i përbashkët drejt një ngarkese të pandryshueshme. Në Python, një paketë MediaPipe mund të krijohet duke thirrur një nga metodat e krijimit të paketave në modulin mp.packet_creator . Në përputhje me rrethanat, ngarkesa e paketës mund të merret duke përdorur një nga metodat e marrjes së paketave në modulin mp.packet_getter . Vini re se ngarkesa e paketës bëhet e pandryshueshme pas krijimit të paketës. Kështu, modifikimi i përmbajtjes së paketës së marrë nuk ndikon në ngarkesën aktuale në paketë. Korniza MediaPipe Python API mbështet llojet e të dhënave më të përdorura të MediaPipe (p.sh. ImageFrame, Matrix, Protocol Buffers dhe llojet primitive të të dhënave) në lidhjen thelbësore. Tabela gjithëpërfshirëse më poshtë tregon paraqitjet e tipit midis tipit të të dhënave Python dhe C++ së bashku me krijuesin e paketave dhe metodën e marrjes së përmbajtjes për çdo lloj të dhënash të mbështetur nga API-ja e kornizës së Python MediaPipe.

Lloji i të dhënave Python Lloji i të dhënave C++ Krijuesi i paketave Marrësi i përmbajtjes
bool bool create_bool (E vërtetë) get_bool (paketë)
int ose np.intc int_t create_int (1) get_int (paketë)
int ose np.int8 int8_t create_int8(2**7-1) get_int (paketë)
int ose np.int16 int16_t create_int16(2**15-1) get_int (paketë)
int ose np.int32 int32_t create_int32(2**31-1) get_int (paketë)
int ose np.int64 int64_t create_int64(2**63-1) get_int (paketë)
int ose np.uint8 uint8_t create_uint8(2**8-1) get_uint (paketë)
int ose np.uint16 uint16_t create_uint16(2**16-1) get_uint (paketë)
int ose np.uint32 uint32_t create_uint32(2**32-1) get_uint (paketë)
int ose np.uint64 uint64_t create_uint64(2**64-1) get_uint (paketë)
float ose np.float32 noton create_float (1.1) get_float (paketë)
float ose np.dyfish dyfishtë create_double (1.1) get_float (paketë)
rr (UTF-8) std::string krijimi_string ('abc') get_str (paketë)
byte std::string krijimi_string (b'\xd0\xd0\xd0') get_bytes (paketë)
mp.Paketë mp::Paketë krijimi_paketë(p) marrë_paketë (paketë)
Lista[bool] std::vector<bool> create_bool_vector ([E vërtetë, e gabuar]) get_bool_list (paketë)
Lista[int] ose Lista[np.intc] int[] create_int_array ([1, 2, 3]) get_int_list (paketë, madhësi=10)
Lista[int] ose Lista[np.intc] std::vector<int> Creative_int_vector([1, 2, 3]) get_int_list (paketë)
Lista[float] ose Lista[np.float] noton[] create_float_arrary ([0.1, 0.2]) get_float_list (paketë, madhësi=10)
Lista[float] ose Lista[np.float] std::vector<float> Creative_float_vector ([0.1, 0.2]) get_float_list (paketë, madhësi=10)
Lista[str] std::vektor<std::string> create_string_vector (['a']) get_str_list (paketë)
Lista [mp.Paketë] std::vektori<mp::Paketa> krijimi_vektori_paketë(
[paketë1, paketë2])
merrni_lista_paketë(p)
Hartë[rr, Paketa] std:: hartë create_string_to_packet_map(
{'a': paketa1, 'b': paketa2})
get_str_to_packet_dict (paketë)
np.ndarray
(cv.mat dhe PIL.Image)
mp::Image Frame krijimin_kornizën_image(
format=ImageFormat.SRGB,
të dhëna=mat)
marrë_image_kornizë (paketë)
np.ndarray mp::Matricë make_matrica (të dhëna) get_matrica (paketë)
Mesazh Google Proto Mesazh Google Proto krijimi_proto (proto) get_proto (paketë)
Lista[Proto] std::vector<Proto> n/a get_proto_list (paketë)

Nuk është e pazakontë që përdoruesit të krijojnë klasa me porosi C++ dhe t'i dërgojnë ato në grafikë dhe kalkulatorë. Për të lejuar që klasat e personalizuara të përdoren në Python me MediaPipe Framework, mund të zgjeroni API-në e paketave për një lloj të ri të dhënash në hapat e mëposhtëm:

  1. Shkruani kodin lidhës të klasës pybind11 ose një kaster të tipit të personalizuar për llojin e personalizuar në një skedar cc.

    #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. Krijoni një metodë të re krijuesi dhe marrësi të paketave të llojit të personalizuar në një skedar të veçantë 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. Shtoni dy rregulla të ndërtimit bazel për lidhjen e tipit të personalizuar dhe metodat e reja të paketave në skedarin 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. Ndërtoni objektivat e zgjerimit pybind (me prapashtesën .so) nga Bazel dhe zhvendosni bibliotekat dinamike të krijuara në një nga drejtimet $LD_LIBRARY_PATH.

  5. Përdorni modulet lidhëse në 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)
    

Vula kohore

Çdo paketë përmban një vulë kohore që është në njësi mikrosekonda. Në Python, Packet API ofron një metodë komode packet.at() për të përcaktuar vulën kohore numerike të një pakete. Në përgjithësi, packet.timestamp është vetia e klasës së paketës për të hyrë në vulën kohore themelore. Për të kthyer një epokë Unix në një vulë kohore MediaPipe, API Timestamp ofron një metodë mp.Timestamp.from_seconds() për këtë qëllim.

Korniza e imazhit

ImageFrame është kontejneri për ruajtjen e një imazhi ose një kornize video. Formatet e mbështetura nga ImageFrame janë të listuara në numrin ImageFormat . Pixelët janë të koduar në rreshtin kryesor me komponentë ngjyrash të ndërthurura dhe ImageFrame mbështet uint8, uint16 dhe float si llojet e tij të të dhënave. MediaPipe ofron një API ImageFrame Python për të hyrë në klasën ImageFrame C++. Në Python, mënyra më e lehtë për të marrë të dhënat e pikselit është të telefononi image_frame.numpy_view() për të marrë një ndarray numpy. Vini re se ndarray numpy i kthyer, një referencë për të dhënat e brendshme të pikselit, është i pashkruar. Nëse thirrësit duhet të modifikojnë numpy ndarray, kërkohet që në mënyrë eksplicite të thërrasë një operacion kopjimi për të marrë një kopje. Kur MediaPipe merr një ndarray numpy për të krijuar një ImageFrame, supozon se të dhënat ruhen në mënyrë të vazhdueshme. Në përputhje me rrethanat, të dhënat e pikselit të një ImageFrame do të riorganizohen për të qenë të ngjitur kur të kthehen në anën e Python.

Grafiku

Në Kornizën MediaPipe, i gjithë përpunimi bëhet brenda kontekstit të një CalculatorGraph. CalculatorGraph Python API është një lidhje e drejtpërdrejtë me klasën C++ CalculatorGraph. Dallimi kryesor është se CalculatorGraph Python API ngre një gabim Python në vend që të kthejë një status jo-OK kur ndodh një gabim. Prandaj, si përdorues i Python, ju mund t'i trajtoni përjashtimet siç bëni zakonisht. Cikli i jetës së një CalculatorGraph përmban tre faza: inicializimi dhe konfigurimi, ekzekutimi i grafikut dhe mbyllja e grafikut.

  1. Inicializoni një CalculatorGraph me një protobuf CalculatorGraphConfig ose skedar protobuf binar dhe siguroni metodën(et) e kthimit të thirrjes për të vëzhguar rrjedhën(at) e daljes.

    Opsioni 1. Inicializoni një CalculatorGraph me një protobuf CalculatorGraphConfig ose paraqitjen e tij të tekstit dhe vëzhgoni rrjedhën e daljes:

    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)))
    

    Opsioni 2. Inicializoni një CalculatorGraph me një skedar protobuf binar dhe vëzhgoni rrymën e daljes.

    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. Filloni ekzekutimin e grafikut dhe futni paketat në grafik.

    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. Mbyllni grafikun pas përfundimit. Ju mund të rinisni grafikun për një grafik tjetër të ekzekutuar pas thirrjes për të close() .

    graph.close()
    

Skripti Python mund të ekzekutohet nga koha juaj lokale e ekzekutimit të Python.