מסגרת ה-Python של MediaPipe מעניקה גישה ישירה לרכיבי הליבה של באמצעות framework C++ של MediaPipe, כמו Timestamp, Packet ו-מחשבון, ואילו פתרונות Python המוכנים לשימוש מסתירים את הפרטים הטכניים של ה-framework ולהחזיר את המודל הקריא תוצאות הסקת המסקנות חזרה למתקשרים.
מסגרת MediaPipe נמצאת מעל ספריית pybind11. מסגרת הליבה של C++ נחשפת ב-Python באמצעות קישור שפות ב-C++ או ב-Python. התוכן הבא מניח שלקורא יש כבר הבנה בסיסית של במסגרת MediaPipe C++. אחרת, אפשר למצוא מידע שימושי ב מושגי framework.
מנה
החבילה היא היחידה הבסיסית של זרימת הנתונים ב-MediaPipe. חבילה מורכבת
חותמת זמן מספרית וסמן משותף למטען ייעודי (payload) שלא ניתן לשינוי. ב-Python,
אפשר ליצור חבילת MediaPipe באמצעות הפעלה של אחת מהשיטות ליצירת חבילות ב-
ה
mp.packet_creator
של מודל טרנספורמר. בנוסף, ניתן לאחזר את המטען הייעודי של המנות באמצעות אחד
שיטות למקבל החבילות
mp.packet_getter
של מודל טרנספורמר. שימו לב שמטען הייעודי (Payload) של המנות הופך ללא ניתן לשינוי אחרי המנה
במהלך היצירה. לכן, השינוי של תוכן המנות שאוחזר לא משפיע על
את המטען הייעודי (Payload) בפועל בחבילה. API של MediaPipe framework Python תומך
סוגי הנתונים הנפוצים ביותר של MediaPipe (למשל, ImageFrame, מטריצה, פרוטוקול
מאגרי נתונים זמניים וסוגי נתונים ראשוניים) בקישור הליבה. התוכנית המקיפה
בטבלה הבאה מוצגים מיפויי הסוגים בין סוג הנתונים Python לסוג הנתונים C++
יחד עם יוצר החבילות והשיטה של מקבל התוכן עבור כל סוג נתונים.
שנתמך על ידי 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.float32 | מספר ממשי (float) | 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::vector<bool> | create_bool_vector([True, False]) | get_bool_list(packet) |
List[int] או List[np.intc] | int[] | create_int_array([1, 2, 3]) | get_int_list(package, size=10) |
List[int] או List[np.intc] | std::vector<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(package, size=10) |
List[float] או List[np.float] | std::vector<float> | create_float_vector([0.1, 0.2]) | get_float_list(package, size=10) |
List[str] | std::vector<std::string> | create_string_vector(['a']) | get_str_list(packet) |
רשימה[mp.Packet] | std::vector<mp::Packet> | create_package_vector( [package1, package2]) |
get_packet_list(p) |
מיפוי[str, Packet] | std::map<std::string, package=""></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) |
רשימה[Proto] | std::vector<Proto> | לא רלוונטי | get_proto_list(packet) |
במקרים מסוימים משתמשים יוצרים כיתות C++ מותאמות אישית ושולחים אותן אל את התרשימים ומחשבונים. כדי לאפשר שימוש בכיתות המותאמות אישית ב-Python עם MediaPipe Framework, תוכלו להרחיב את Packet API לסוג נתונים חדש את השלבים הבאים:
כתבו את pybind11 קוד קישור מחלקה או משתנה מסוג מותאם אישית לסוג המותאם אישית בקובץ 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(...); }
ליצור שיטה חדשה של יוצר חבילות ושיטה חדשה מהסוג המותאם אישית עותק נפרד של 'עותקים'.
#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
מוסיפים שני כללים build של bazel לקישור בין סוגי הפריטים בהתאמה אישית ולחבילה החדשה בקובץ 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 ומעבירים את הספריות הדינמיות שנוצרו לאחת מ-dirs של $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,
ה-API של חותמת הזמן
מציע את השיטה mp.Timestamp.from_seconds()
למטרה הזו.
ImageFrame
ImageFrame הוא הקונטיינר לאחסון תמונה או פריים בסרטון. פורמטים
שנתמכות על ידי ImageFrame, הן
enum ב-ImageFormat.
פיקסלים מקודדים שורה גדולה עם רכיבי צבע משולבים ו-ImageFrame.
שתומך ב-uint8, ב-uint16 וב-float בתור סוגי הנתונים שלו. אפליקציית MediaPipe מספקת
API של ImageFrame ב-Python
כדי לגשת למחלקה של ImageFrame C++. ב-Python, הדרך הקלה ביותר לאחזר את
נתוני פיקסלים יקבלו קריאה ל-image_frame.numpy_view()
כדי לקבל ndסידור נומרי. הערה
שהמערך המספרי המוחזר, שהוא הפניה לנתוני הפיקסלים הפנימיים,
בלתי ניתן לכתיבה. אם המתקשרים צריכים לשנות את ה-ndarray המספרי, הוא צריך
להפעיל פעולת העתקה באופן מפורש כדי לקבל עותק. כש-MediaPipe לוקחת מספר
ndarray כדי ליצור ImageFrame, מבוסס על ההנחה שהנתונים מאוחסנים באופן רציף.
לאחר מכן, נתוני הפיקסלים של ImageFrame יותאמו מחדש
הוא רציף כשחוזרים לצד של Python.
תרשים
במסגרת MediaPipe, כל העיבוד מתרחש בהקשר של תרשים המחשבון. ה-API של המחשבון ל-Python הוא קישור ישיר למחלקה 'מחשבון המחשבון C++'. ההבדל העיקרי הוא ממשק ה-API של Python שירותי Python מעלה שגיאת Python במקום להחזיר שגיאת Python סטטוס לא תקין כשמתרחשת שגיאה. לכן, כמשתמשי Python, אתם יכולים החריגים, כרגיל. מחזור החיים של מחשבון Chart Graph מכיל שלושה שלבים: אתחול והגדרה, הרצת תרשים וכיבוי תרשים.
אתחול phoneGraph באמצעות קובץ protobuf או בינארי של GraphConfig קובץ protobuf, ולספק שיטות קריאה חוזרת כדי לצפות בפלט זרמים.
אפשרות 1. אתחול גרף הפונקציה עם Protobuf של שירותימחשבון גרפי או ייצוג טקסטואלי שלו, ובוחנים את זרמי הפלט:
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. לאתחל Computer Graph עם קובץ פרוטובוט בינארי, לבחון את זרמי הפלט.
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.