Khung MediaPipe Python cấp quyền truy cập trực tiếp vào các thành phần cốt lõi của khung MediaPipe C++ như Timestamp, Packet và CalculatorGraph, trong khi các giải pháp Python sẵn sàng sử dụng ẩn các chi tiết kỹ thuật của khung và chỉ cần trả về mô hình dễ đọc kết quả suy luận trở lại cho phương thức gọi.
Khung MediaPipe nằm ở trên cùng thư viện pybind11. Khung cốt lõi C++ được cung cấp trong Python thông qua liên kết ngôn ngữ C++/Python. Nội dung dưới đây giả định rằng người đọc đã có hiểu biết cơ bản về khung MediaPipe C++. Nếu không, bạn có thể tìm thấy thông tin hữu ích trong Framework Concepts (Khái niệm về khung).
Gói
Gói là đơn vị luồng dữ liệu cơ bản trong MediaPipe. Một gói dữ liệu bao gồm
dấu thời gian dạng số và con trỏ dùng chung đến tải trọng không thể thay đổi. Trong Python, một
Có thể tạo gói MediaPipe bằng cách gọi một trong các phương thức trình tạo gói trong
thời gian
mp.packet_creator
. Tương ứng, tải trọng gói có thể được truy xuất bằng cách sử dụng một trong
trong phương thức getter gói tin
mp.packet_getter
. Lưu ý rằng tải trọng gói sẽ không thể thay đổi sau gói dữ liệu
sáng tạo. Do đó, việc sửa đổi nội dung gói được truy xuất không ảnh hưởng đến
tải trọng thực tế trong gói. API Python khung MediaPipe hỗ trợ
các loại dữ liệu MediaPipe thường dùng nhất (ví dụ: Khung hình ảnh, Ma trận, Giao thức
Vùng đệm và kiểu dữ liệu gốc) trong liên kết lõi. Giải pháp toàn diện
bảng dưới đây cho thấy mối liên kết kiểu giữa Python và kiểu dữ liệu C++
cùng với trình tạo gói và phương thức getter nội dung cho từng loại dữ liệu
được API khung MediaPipe Python hỗ trợ.
Loại dữ liệu Python | Loại dữ liệu C++ | Người tạo gói tin | Phương thức getter nội dung |
---|---|---|---|
bool | bool | create_bool(True) | get_bool(packet) |
int hoặc np.intc | int_t | create_int(1) | get_int(packet) |
int hoặc np.int8 | int8_t | create_int8(2**7-1) | get_int(packet) |
int hoặc np.int16 | int16_t | create_int16(2**15-1) | get_int(packet) |
int hoặc np.int32 | int32_t | create_int32(2**31-1) | get_int(packet) |
int hoặc np.int64 | int64_t | create_int64(2**63-1) | get_int(packet) |
int hoặc np.uint8 | uint8_t | create_uint8(2**8-1) | get_uint(packet) |
int hoặc np.uint16 | uint16_t | create_uint16(2**16-1) | get_uint(packet) |
int hoặc np.uint32 | uint32_t | create_uint32(2**32-1) | get_uint(packet) |
int hoặc np.uint64 | uint64_t | create_uint64(2**64-1) | get_uint(packet) |
float hoặc np.float32 | số thực dấu phẩy động | create_float(1.1) | get_float(packet) |
độ chính xác đơn hoặc np.double | gấp đôi | create_double(1.1) | get_float(packet) |
str (UTF-8) | std::chuỗi | create_string('abc') | get_str(packet) |
byte | std::chuỗi | create_string(b'\xd0\xd0\xd0') | get_bytes(packet) |
mp.Packet | mp::Packet | create_packet(p) | get_packet(packet) |
Danh sách[bool] | std::vector<bool> | create_bool_vector([Đúng, Sai]) | get_bool_list(packet) |
List[int] hoặc List[np.intc] | int[] | create_int_array([1, 2, 3]) | get_int_list(gói; kích thước=10) |
List[int] hoặc List[np.intc] | std::vector<int> | create_int_vector([1, 2, 3]) | get_int_list(packet) |
List[float] hoặc List[np.float] | float[] | create_float_arrary([0.1, 0.2]) | get_float_list(gói; kích thước=10) |
List[float] hoặc List[np.float] | std::vector<float> | create_float_vector([0,1; 0,2]) | get_float_list(gói; kích thước=10) |
Danh sách[str] | std::vector<std::string> | create_string_vector(['a']) | get_str_list(packet) |
Danh sách[mp.Packet] | std::vector<mp::Packet> | create_packet_vector( [packet1, packageet2]) |
get_packet_list(p) |
ánh xạ[str, Packet] | std::map<std::string, packageet=""></std::string,> | create_string_to_packet_map( {'a': packet1, 'b': packet2}) |
get_str_to_packet_dict(packet) |
np.ndarray (cv.mat và 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) |
Thông báo Proto của Google | Thông báo Proto của Google | create_proto(proto) | get_proto(packet) |
Danh sách[Proto] | std::vector<Proto> | không áp dụng | get_proto_list(packet) |
Nhiều người dùng tạo các lớp C++ tuỳ chỉnh rồi gửi các lớp đó vào đồ thị và máy tính. Để cho phép sử dụng các lớp tuỳ chỉnh trong Python bằng Khung MediaPipe, bạn có thể mở rộng Packet API cho một loại dữ liệu mới trong các bước sau:
Viết pybind11 mã liên kết lớp hoặc một công cụ nhập loại tuỳ chỉnh cho loại tuỳ chỉnh trong tệp 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(...); }
Tạo một trình tạo gói và phương thức getter mới thuộc loại tuỳ chỉnh trong một tệp cc riêng.
#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
Thêm 2 quy tắc xây dựng bazel cho liên kết kiểu tuỳ chỉnh và gói mới trong tệp 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" ], )
Tạo các mục tiêu tiện ích pybind (có hậu tố .so) của Bazel và di chuyển các thư viện động đã tạo vào một trong các thư viện $LD_ từng_PATH.
Sử dụng các mô-đun liên kết bằng 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)
Dấu thời gian
Mỗi gói chứa dấu thời gian tính bằng đơn vị micrô giây. Trong Python,
Packet API cung cấp một phương thức packet.at()
tiện lợi để xác định giá trị
của một gói. Nhìn chung, packet.timestamp
là lớp gói
để truy cập vào dấu thời gian cơ bản. Để chuyển đổi thời gian bắt đầu của hệ thống Unix thành
Dấu thời gian MediaPipe,
API Dấu thời gian
cung cấp một phương thức mp.Timestamp.from_seconds()
cho mục đích này.
ImageFrame
ImageFrame là vùng chứa để lưu trữ hình ảnh hoặc khung video. Định dạng
được hỗ trợ bởi ImageFrame được liệt kê trong
enum ImageFormat.
Các pixel được mã hoá hàng chính với các thành phần màu xen kẽ và ImageFrame
hỗ trợ uint8, uint16 và float làm loại dữ liệu. MediaPipe cung cấp
API ImageFrame Python
để truy cập lớp ImageFrame C++. Trong Python, cách dễ nhất để truy xuất
dữ liệu pixel là gọi image_frame.numpy_view()
để lấy một ndarray numpy. Ghi chú
rằng numpy ndarray được trả về, tham chiếu đến dữ liệu pixel nội bộ, là
không thể ghi. Nếu phương thức gọi cần sửa đổi numpy ndarray, thì bắt buộc phải
gọi rõ ràng thao tác sao chép để lấy bản sao. Khi MediaPipe nhận một số
ndarray để tạo ImageFrame, giả định rằng dữ liệu được lưu trữ liền kề.
Tương ứng, dữ liệu pixel của ImageFrame sẽ được căn chỉnh lại để
tiếp giáp khi được trả về phía Python.
Biểu đồ
Trong Khung MediaPipe, tất cả quá trình xử lý đều diễn ra trong bối cảnh CalculatorGraph. API CalculatorGraph Python là liên kết trực tiếp với lớp C++ CalculatorGraph. Điểm khác biệt chính là API CalculatorGraph Python phát sinh lỗi Python thay vì trả về một Trạng thái không OK khi xảy ra lỗi. Do đó, là người dùng Python, bạn có thể xử lý như bạn thường làm. Vòng đời của CalculatorGraph chứa ba giai đoạn: khởi tạo và thiết lập, chạy biểu đồ và tắt biểu đồ.
Khởi chạy CalculatorGraph có protobuf hoặc tệp nhị phân CalculatorGraphConfig tệp protobuf và cung cấp(các) phương thức gọi lại để quan sát kết quả luồng.
Tùy chọn 1. Khởi chạy CalculatorGraph bằng một protobuf CalculatorGraphConfig hoặc phần trình bày văn bản của văn bản đó và quan sát(các) luồng đầu ra:
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)))
Tùy chọn 2. Khởi chạy CalculatorGraph có tệp protobuf nhị phân, và quan sát(các) luồng đầu ra.
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}'))
Bắt đầu chạy biểu đồ và đưa các gói vào biểu đồ.
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))
Đóng biểu đồ sau khi hoàn tất. Bạn có thể chạy lại biểu đồ cho một đồ thị khác chạy sau lệnh gọi đến
close()
.graph.close()
Tập lệnh Python có thể được chạy bởi môi trường thời gian chạy Python cục bộ của bạn.