เฟรมเวิร์ก MediaPipe Python ให้สิทธิ์เข้าถึงคอมโพเนนต์หลักของ เฟรมเวิร์ก MediaPipe C++ เช่น Timestamp, Packet และ CalculatorGraph ขณะที่โซลูชัน Python ที่พร้อมใช้งาน รายละเอียดทางเทคนิคของเฟรมเวิร์ก แล้วแสดงผลโมเดลที่อ่านได้ ผลการอนุมานกลับไปยังผู้โทร
เฟรมเวิร์ก MediaPipe อยู่ด้านบน ไลบรารี pybind11 เฟรมเวิร์กหลักของ C++ จะแสดงใน Python ผ่านการเชื่อมโยงภาษา C++/Python เนื้อหาด้านล่างนี้ถือว่าผู้อ่านมีความเข้าใจเบื้องต้นเกี่ยวกับ เฟรมเวิร์ก MediaPipe C++ มิฉะนั้น คุณสามารถค้นหาข้อมูลที่เป็นประโยชน์ได้ใน แนวคิดของเฟรมเวิร์ก
แพ็คเก็ต
แพ็กเก็ตคือหน่วยโฟลว์ข้อมูลพื้นฐานใน MediaPipe แพ็กเก็ตประกอบด้วย
การประทับเวลาที่เป็นตัวเลขและตัวชี้ที่แชร์ไปยังเพย์โหลดที่เปลี่ยนแปลงไม่ได้ ใน Python พารามิเตอร์
สามารถสร้างแพ็กเก็ต MediaPipe ได้โดยการเรียกใช้เมธอดผู้สร้างแพ็กเก็ตหนึ่งในวิธี
เวลา
mp.packet_creator
นอกจากนี้ สามารถดึงเพย์โหลดแพ็กเก็ตได้โดยใช้
เมธอด Getter แพ็กเก็ตใน
mp.packet_getter
โปรดทราบว่าเพย์โหลดแพ็กเก็ตจะเปลี่ยนแปลงไม่ได้หลังจากแพ็กเก็ต
งานสร้างสรรค์ ดังนั้น การแก้ไขเนื้อหาของแพ็กเก็ตที่เรียกมาจะไม่ส่งผล
เพย์โหลดจริงในแพ็กเก็ต Python API เฟรมเวิร์ก MediaPipe สนับสนุน
ประเภทข้อมูลที่ใช้กันโดยทั่วไปของ MediaPipe (เช่น เฟรมรูปภาพ เมทริกซ์ โปรโตคอล
บัฟเฟอร์ และประเภทข้อมูลพื้นฐาน) ในการเชื่อมโยงหลัก ภาพรวม
ตารางด้านล่างแสดงการแมปประเภทระหว่างประเภทข้อมูล Python และ C++
พร้อมด้วยเครื่องมือสร้างแพ็กเก็ตและเมธอด Getter เนื้อหาสำหรับข้อมูลแต่ละประเภท
รองรับโดย MediaPipe Python Framework API
ประเภทข้อมูล 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.Flo32 | จำนวนลอยตัว | create_float(1.1) | get_float(packet) |
Float หรือ np.double | 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::vector<bool> | create_bool_vector([จริง, เท็จ]) | 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_vector([1, 2, 3]) | get_int_list(packet) |
List[Flo] หรือ List[np.Flo] | float[] | create_Flo_arrary([0.1, 0.2]) | get_Flo_list(แพ็กเก็ต, ขนาด=10) |
List[Flo] หรือ List[np.Flo] | std::vector<float> | create_Flo_vector([0.1, 0.2]) | get_Flo_list(แพ็กเก็ต, ขนาด=10) |
รายการ[str] | std::vector<std::string> | สร้าง_สตริง_เวกเตอร์(['a']) | get_str_list(packet) |
รายการ [mp.Packet] | std::vector<mp::Packet> | create_packet_vector( [packet1, packet2]) |
get_packet_list(p) |
การแมป[str, แพ็กเก็ต] | std::map<std::string, multipack=""></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::Matrix | create_matrix(data) | get_matrix(packet) |
ข้อความ Google Proto | ข้อความ Google Proto | create_proto(proto) | get_proto(packet) |
รายการ [โปรโตคอล] | std::vector<โปรโตคอล> | ไม่มี | get_proto_list(packet) |
ผู้ใช้มักจะสร้างชั้นเรียน C++ ที่กำหนดเองแล้วส่งคลาสเหล่านั้นไปยัง กราฟและเครื่องคิดเลข เมื่อต้องการอนุญาตให้ใช้คลาสที่กำหนดเองใน Python เมื่อใช้เฟรมเวิร์ก MediaPipe คุณสามารถขยาย Packet API สำหรับข้อมูลประเภทใหม่ใน ขั้นตอนต่อไปนี้
เขียน 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(...); }
สร้างเครื่องมือสร้างแพ็กเก็ตและเมธอด Getter ของประเภทที่กำหนดเองใน ไฟล์สำเนา
#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
เพิ่มกฎการสร้าง Bazel 2 ข้อสำหรับการเชื่อมโยงประเภทที่กำหนดเองและแพ็กเก็ตใหม่ ในไฟล์ 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" ], )
สร้างเป้าหมายส่วนขยาย pybind (ที่มีคำต่อท้าย .so) โดย Bazel และย้ายไลบรารีแบบไดนามิกที่สร้างขึ้นไปยังหนึ่งในไดเรกทอรี $LD_LIBRARY_PATH
ใช้โมดูลการเชื่อมโยงใน 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 API มีวิธีอำนวยความสะดวก packet.at()
ในการกำหนดตัวเลข
การประทับเวลาของแพ็กเก็ต โดยทั่วไป packet.timestamp
คือคลาสแพ็กเก็ต
สำหรับการเข้าถึงการประทับเวลาที่สำคัญ วิธีแปลง Unix Epoch เป็น
การประทับเวลา MediaPipe
Timestamp API
เสนอ Method mp.Timestamp.from_seconds()
สำหรับวัตถุประสงค์นี้
ImageFrame
ImageFrame เป็นคอนเทนเนอร์สำหรับจัดเก็บรูปภาพหรือเฟรมวิดีโอ รูปแบบ
ที่ ImageFrame รองรับจะแสดงอยู่ใน
enum สำหรับ ImageFormat
พิกเซลจะเป็นแถวหลักที่เข้ารหัสโดยมีองค์ประกอบสีแบบแทรกสลับ และ ImageFrame
รองรับ uint8, uint16 และ Float เป็นประเภทข้อมูล MediaPipe มี
API ของ ImageFrame Python API
เพื่อเข้าถึงคลาส ImageFrame C++ ใน Python วิธีที่ง่ายที่สุดในการเรียกข้อมูล
ข้อมูลพิกเซลคือการเรียก image_frame.numpy_view()
เพื่อรับอาร์เรย์ numpy หมายเหตุ
ที่ส่งคืน numpy ndarray ซึ่งเป็นการอ้างอิงข้อมูลพิกเซลภายใน
ไม่สามารถเขียนได้ ถ้าผู้โทรจำเป็นต้องแก้ไข numpy ndarray จะต้อง
เรียกใช้การดำเนินการคัดลอกอย่างชัดแจ้งเพื่อรับสำเนา เมื่อ MediaPipe ใช้ค่าตัวเลข
ndarray สร้าง ImageFrame จะถือว่าเก็บข้อมูลไว้ต่อเนื่องกัน
ข้อมูลพิกเซลของ ImageFrame จะได้รับการปรับใหม่ตาม
ต่อเนื่องกันเมื่อส่งกลับไปที่ด้าน Python
กราฟของฟังก์ชัน
ในเฟรมเวิร์ก MediaPipe การประมวลผลทั้งหมดจะเกิดขึ้นในบริบทของ CalculatorGraph ClculatorGraph Python API จะเชื่อมโยงโดยตรงกับคลาส CalculatorGraph ของ C++ ความแตกต่างที่สำคัญคือ CalculatorGraph Python API จะทําให้เกิดข้อผิดพลาด Python แทนการส่งคืน สถานะ "ไม่ปกติ" เมื่อเกิดข้อผิดพลาด ดังนั้น ในฐานะผู้ใช้ Python คุณสามารถจัดการกับ ยกเว้นตามปกติ วงจรชีวิตของ CalculatorGraph มี 3 ขั้นตอน ได้แก่ การเริ่มต้นและการตั้งค่า การเรียกใช้กราฟ และการปิดกราฟ
เริ่มต้น CalculatorGraph ด้วย CalculatorGraphConfig ต้นแบบหรือไบนารี Protobuf ของคุณ และระบุ Method ของ Callback เพื่อสังเกตผลลัพธ์ สตรีม
ตัวเลือกที่ 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: เริ่มต้น CalculatorGraph ด้วยไฟล์ Protobuf แบบไบนารี และ สังเกตสตรีมเอาต์พุต
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}'))
เริ่มการเรียกใช้กราฟและส่งแพ็กเก็ตข้อมูลลงในกราฟ
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))
ปิดกราฟเมื่อเสร็จแล้ว คุณอาจเริ่มกราฟอีกครั้งเพื่อสร้างกราฟอื่น ทำงานหลังจากการเรียกไปยัง
close()
graph.close()
รันไทม์ Python ในเครื่องสามารถเรียกใช้สคริปต์ Python ได้